1 | "use strict";
|
2 |
|
3 | exports.compile = compileJS;
|
4 |
|
5 | const
|
6 | ToloframeworkPermissiveJson = require( "toloframework-permissive-json" ),
|
7 | ModuleAnalyser = require( "./module-analyser" ),
|
8 | CompilerINI = require( "./compiler-ini" ),
|
9 | Transpiler = require( "./transpiler" ),
|
10 | PathUtils = require( "./pathutils" ),
|
11 | Source = require( "./source" ),
|
12 | Fatal = require( "./fatal" ),
|
13 | Path = require( "path" ),
|
14 | Util = require( "./util" ),
|
15 | Tpl = require( "./template" ),
|
16 | Xjs = require( "./boilerplate" );
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | function compileJS( project, path, options ) {
|
30 | const src = new Source( project, path );
|
31 |
|
32 | ensureJavascriptFileExists( project, src );
|
33 | if ( src.isUptodate() ) return src;
|
34 |
|
35 | const
|
36 | moduleName = src.name(),
|
37 | watch = [],
|
38 | moduleShortName = Util.removeExtension( Util.removeFirstSubFolder( moduleName ) );
|
39 |
|
40 | console.log( `Compiling JS ${moduleShortName.yellow}` );
|
41 |
|
42 | const head = compileDEP( project, src, watch );
|
43 | compileINI( project, src );
|
44 | let code = convertIntoModule( src, moduleShortName, head );
|
45 |
|
46 |
|
47 | if ( path !== 'mod/$.js' ) {
|
48 | code += "module.exports._ = _;\n";
|
49 | }
|
50 | code += "})";
|
51 |
|
52 | const transpiled = Transpiler.transpile(
|
53 | code,
|
54 | src.getAbsoluteFilePath(),
|
55 | !( options.debug || options.dev )
|
56 | );
|
57 |
|
58 | const
|
59 | info = ModuleAnalyser.extractInfoFromAst( transpiled.ast ),
|
60 | dependencies = info.requires.map( function mapDependencies( itm ) {
|
61 | return `mod/${itm}`;
|
62 | } );
|
63 | console.log( `Dependent modules (${info.requires.length}): `, info.requires.join( ', ' ).grey );
|
64 |
|
65 | createMarkdownDoc( src, info );
|
66 | if ( options.transpilation ) {
|
67 | saveTags( src, transpiled.code, transpiled.zip, transpiled.map, dependencies );
|
68 | } else {
|
69 | console.log( "Transpilation: OFF".red );
|
70 | saveTags( src, code, transpiled.zip, null, dependencies );
|
71 | }
|
72 |
|
73 | return src;
|
74 | }
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 | function saveTags( src, code, zip, map, dependencies ) {
|
86 | src.tag( 'src', code );
|
87 | src.tag( 'zip', zip );
|
88 | src.tag( 'map', map );
|
89 | src.tag( 'dependencies', dependencies );
|
90 | src.save();
|
91 | }
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 | function ensureJavascriptFileExists( project, src ) {
|
99 | const srcXJS = src.clone( "xjs" );
|
100 | if ( !src.exists() ) {
|
101 | if ( !srcXJS.exists() ) {
|
102 |
|
103 | Fatal.fire( `Javascript file not found: "${src.name()}"!`, src.name() );
|
104 | } else {
|
105 |
|
106 | src.write( "// Code behind.\n\"use strict\";\n" );
|
107 | }
|
108 | }
|
109 | }
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 | function compileDEP( project, src, watch ) {
|
120 | const
|
121 | moduleName = src.name(),
|
122 | depFilename = Util.replaceExtension( moduleName, '.dep' );
|
123 |
|
124 | if ( !project.srcOrLibPath( depFilename ) ) return '';
|
125 |
|
126 | const depFile = new Source( project, depFilename );
|
127 | let code = '';
|
128 | try {
|
129 | const depJSON = ToloframeworkPermissiveJson.parse( depFile.read() );
|
130 | code = processAttributeVar( project, depJSON, watch, depFile.getAbsoluteFilePath() );
|
131 | } catch ( ex ) {
|
132 | Fatal.fire( `Unable to parse JSON dependency file!\n${ex}`, depFile.getAbsoluteFilePath() );
|
133 | }
|
134 |
|
135 | |
136 |
|
137 |
|
138 |
|
139 | src.tag( 'watch', watch );
|
140 | return code;
|
141 | }
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 | function processAttributeVar( project, depJSON, watch, depAbsPath ) {
|
154 | if ( typeof depJSON.var === 'undefined' ) return '';
|
155 |
|
156 | let
|
157 | head = 'const GLOBAL = {',
|
158 | firstItem = true;
|
159 |
|
160 | Object.keys( depJSON.var ).forEach( function forEachVarName( varName ) {
|
161 | const
|
162 | varFilename = depJSON.var[ varName ],
|
163 | folder = Path.dirname( depAbsPath ),
|
164 | srcVar = project.srcOrLibPath( Path.join( folder, varFilename ) ) ||
|
165 | project.srcOrLibPath( `mod/${varFilename}` ) ||
|
166 | project.srcOrLibPath( varFilename );
|
167 |
|
168 | if ( !srcVar ) {
|
169 | Fatal.fire(
|
170 | `Unable to find dendency file "${varFilename}" nor "mod/${varFilename}"!`,
|
171 | depAbsPath
|
172 | );
|
173 | }
|
174 | Util.pushUnique( watch, `mod/${varFilename}` );
|
175 | if ( firstItem ) {
|
176 | firstItem = false;
|
177 | } else {
|
178 | head += ',';
|
179 | }
|
180 | const source = new Source( project, srcVar );
|
181 | head += `\n ${JSON.stringify(varName)}: ${JSON.stringify(source.read())}`;
|
182 | } );
|
183 | head += "};\n";
|
184 |
|
185 | return head;
|
186 | }
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 | function compileINI( project, src ) {
|
196 |
|
197 | if ( src.name( "js" ) === 'mod/$.js' ) {
|
198 |
|
199 | src.tag( "intl", "" );
|
200 | } else {
|
201 | const
|
202 | iniName = src.name( "ini" ),
|
203 | iniPath = project.srcOrLibPath( iniName );
|
204 | if ( iniPath ) {
|
205 | src.tag( "intl", CompilerINI.parse( iniPath ) );
|
206 | } else {
|
207 | src.tag( "intl", "var _=function(){return ''};" );
|
208 | }
|
209 | }
|
210 | }
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 | function convertIntoModule( src, moduleShortName, head ) {
|
220 | const options = {
|
221 | name: moduleShortName,
|
222 | code: Xjs.generateCodeFrom( src ),
|
223 | intl: src.tag( 'intl' ),
|
224 | head: `${head} `,
|
225 | foot: ""
|
226 | };
|
227 | return Tpl.file( "module.js", options ).out;
|
228 | }
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 | function createMarkdownDoc( src, info ) {
|
237 | const
|
238 | publics = info.exports,
|
239 | prj = src.prj(),
|
240 | name = src.name(),
|
241 | dstPath = prj.docPath( Util.replaceExtension( name, '.md' ) );
|
242 | let output = `# ${name}\n`;
|
243 | publics.forEach( function ( item ) {
|
244 | const
|
245 | params = ( item.params || [] ).join( ', ' ),
|
246 | comments = item.comments;
|
247 | output += `## \`${item.id}(${params})\`\n\n${comments}\n\n`;
|
248 | } );
|
249 |
|
250 | if ( info.requires.length > 0 ) {
|
251 | output += "\n----\n\n## Dependencies\n";
|
252 | output += info.requires.map( req => `* [${req}](${req}.md)` ).join( "\n" );
|
253 | }
|
254 | PathUtils.file( dstPath, output );
|
255 | }
|