1 module jssplitter; 2 3 import std.array; 4 import std.algorithm; 5 import std.stdio; 6 import std.string; 7 import std.json; 8 9 enum Type { 10 undefined, 11 strings, 12 call, 13 json, 14 digit, 15 mustache, 16 unknown, 17 } 18 19 Type findType(string[] lines) { 20 Type type = Type.undefined; 21 foreach(l; lines) { 22 bool isCall = l.indexOf("#{") != -1; 23 bool isMustache = l.indexOf("{{") != -1; 24 bool isJson = l.endsWith(": {") || l.endsWith(": ["); 25 bool isDigit = l.indexOf("##") != -1 || l.indexOf(".#") != -1 26 || l.indexOf("#") != -1; 27 28 Type t; 29 if(isCall && !isMustache && !isJson && !isDigit) { 30 t = Type.call; 31 } else if(!isCall && isMustache && !isJson && !isDigit) { 32 t = Type.mustache; 33 } else if(!isCall && !isMustache && isJson && !isDigit) { 34 t = Type.json; 35 } else if(!isCall && !isMustache && !isJson && isDigit) { 36 t = Type.digit; 37 } else { 38 t = Type.strings; 39 } 40 41 //writefln("%20s %s", t, l); 42 43 if(type == Type.undefined) { 44 //writefln("first %s %s %s %s '%s'", isCall, isMustache, isJson, isDigit, l); 45 type = t; 46 continue; 47 } else if(t != type && type != Type.json) { 48 //writefln("secon %s %s %s %s %s %s '%s'", isCall, isMustache, isJson, isDigit 49 // , t, type, l); 50 type = Type.unknown; 51 break; 52 } 53 } 54 return type; 55 } 56 57 string removeLicense(string code) { 58 if(code.startsWith("/*")) { 59 code = code[code.indexOf("*/", 2) + 2 .. $].stripLeft(); 60 } 61 return code; 62 } 63 64 struct TypeLines { 65 Type type; 66 string[] lines; 67 } 68 69 private string[] singleLineSplit(string s) { 70 string[] a = s.split("\n"); 71 string[][] b = a.map!(it => it.split(", ")).array; 72 string[] c = b.joiner().array; 73 auto ret2 = c 74 .map!(l => l.strip) 75 .filter!(l => !l.empty) 76 .filter!(l => !l.startsWith("//")) 77 .array; 78 return ret2; 79 } 80 81 TypeLines jssplit(string input, const string path) { 82 //string[] lines = split(input.removeLicense(), "\n") 83 string[] lines = singleLineSplit(input.removeLicense()) 84 .map!(a => a.strip("\", \t\n\r")) 85 .filter!(a => !a.empty && !a.startsWith("//")) 86 .array; 87 88 //if(lines.length < 3) { 89 // //writeln("\t\tshort"); 90 // return TypeLines(Type.undefined, []); 91 //} 92 string[] prefixes = [ "module[\"exports\"] = " 93 , "module.exports = " 94 , "module['exports'] = " 95 , "export default [" 96 , "export default Object.freeze([" 97 , "export default {" 98 ]; 99 auto pf = prefixes.find!((a,b) => b.startsWith(a))(lines.front); 100 assert(!pf.empty, lines.front ~ "\n\n" ~ path); 101 lines[0] = lines[0][pf.front.length .. $].strip(); 102 lines = lines[0 .. $] 103 .map!(strip) 104 .filter!(l => !l.empty) 105 .map!((a) => { 106 a = a.startsWith("'") ? a[1 .. $] : a; 107 a = a.endsWith("'") ? a[0 .. $ - 1] : a; 108 return a; 109 }()) 110 .array; 111 auto postFixes = ["];", "}", "};", "]", "]);"]; 112 auto psx = postFixes.find!((a,b) => b.endsWith(a))(lines.back); 113 assert(!psx.empty, "'" ~ lines.back ~ "'"); 114 lines.back = lines.back[0 .. $ - psx.front.length].strip(); 115 lines = lines.filter!(it => !it.empty).array; 116 117 Type type = findType(lines); 118 if(type == Type.unknown) { 119 //writefln("unknown %(%s\n%)", lines); 120 writefln("unknown %s", path); 121 } 122 123 return TypeLines(type, lines); 124 }