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 }