]>
git.proxmox.com Git - mirror_novnc.git/blob - utils/use_require.js
3 const path
= require ( 'path' );
4 const program
= require ( 'commander' );
5 const fs
= require ( 'fs' );
6 const fse
= require ( 'fs-extra' );
7 const babel
= require ( '@babel/core' );
9 const SUPPORTED_FORMATS
= new Set ([ 'amd' , 'commonjs' , 'systemjs' , 'umd' ]);
12 . option ( '--as [format]' , `output files using various import formats instead of ES6 import and export. Supports ${Array.from(SUPPORTED_FORMATS)} .` )
13 . option ( '-m, --with-source-maps [type]' , 'output source maps when not generating a bundled app (type may be empty for external source maps, inline for inline source maps, or both) ' )
14 . option ( '--with-app' , 'process app files as well as core files' )
15 . option ( '--only-legacy' , 'only output legacy files (no ES6 modules) for the app' )
16 . option ( '--clean' , 'clear the lib folder before building' )
19 // the various important paths
21 main
: path
. resolve ( __dirname
, '..' ),
22 core
: path
. resolve ( __dirname
, '..' , 'core' ),
23 app
: path
. resolve ( __dirname
, '..' , 'app' ),
24 vendor
: path
. resolve ( __dirname
, '..' , 'vendor' ),
25 out_dir_base
: path
. resolve ( __dirname
, '..' , 'build' ),
26 lib_dir_base
: path
. resolve ( __dirname
, '..' , 'lib' ),
29 const no_copy_files
= new Set ([
30 // skip these -- they don't belong in the processed application
31 path
. join ( paths
. vendor
, 'sinon.js' ),
32 path
. join ( paths
. vendor
, 'browser-es-module-loader' ),
33 path
. join ( paths
. app
, 'images' , 'icons' , 'Makefile' ),
36 const only_legacy_scripts
= new Set ([
37 path
. join ( paths
. vendor
, 'promise.js' ),
40 const no_transform_files
= new Set ([
41 // don't transform this -- we want it imported as-is to properly catch loading errors
42 path
. join ( paths
. app
, 'error-handler.js' ),
45 no_copy_files
. forEach ( file
=> no_transform_files
. add ( file
));
47 // util.promisify requires Node.js 8.x, so we have our own
48 function promisify ( original
) {
49 return function promise_wrap () {
50 const args
= Array
. prototype . slice
. call ( arguments
);
51 return new Promise (( resolve
, reject
) => {
52 original
. apply ( this , args
. concat (( err
, value
) => {
53 if ( err
) return reject ( err
);
60 const readFile
= promisify ( fs
. readFile
);
61 const writeFile
= promisify ( fs
. writeFile
);
63 const readdir
= promisify ( fs
. readdir
);
64 const lstat
= promisify ( fs
. lstat
);
66 const copy
= promisify ( fse
. copy
);
67 const unlink
= promisify ( fse
. unlink
);
68 const ensureDir
= promisify ( fse
. ensureDir
);
69 const rmdir
= promisify ( fse
. rmdir
);
71 const babelTransformFile
= promisify ( babel
. transformFile
);
73 // walkDir *recursively* walks directories trees,
74 // calling the callback for all normal files found.
75 function walkDir ( base_path
, cb
, filter
) {
76 return readdir ( base_path
)
78 const paths
= files
. map ( filename
=> path
. join ( base_path
, filename
));
79 return Promise
. all ( paths
. map ( filepath
=> lstat ( filepath
)
81 if ( filter
!== undefined && ! filter ( filepath
, stats
)) return ;
83 if ( stats
. isSymbolicLink ()) return ;
84 if ( stats
. isFile ()) return cb ( filepath
);
85 if ( stats
. isDirectory ()) return walkDir ( filepath
, cb
, filter
);
90 function transform_html ( legacy_scripts
, only_legacy
) {
91 // write out the modified vnc.html file that works with the bundle
92 const src_html_path
= path
. resolve ( __dirname
, '..' , 'vnc.html' );
93 const out_html_path
= path
. resolve ( paths
. out_dir_base
, 'vnc.html' );
94 return readFile ( src_html_path
)
95 . then (( contents_raw
) => {
96 let contents
= contents_raw
. toString ();
98 const start_marker
= '<!-- begin scripts --> \n ' ;
99 const end_marker
= '<!-- end scripts -->' ;
100 const start_ind
= contents
. indexOf ( start_marker
) + start_marker
. length
;
101 const end_ind
= contents
. indexOf ( end_marker
, start_ind
);
106 // Only legacy version, so include things directly
107 for ( let i
= 0 ; i
< legacy_scripts
. length
; i
++) {
108 new_script
+= ` <script src=" ${legacy_scripts[i]} "></script> \n ` ;
111 // Otherwise include both modules and legacy fallbacks
112 new_script
+= ' <script type="module" crossorigin="anonymous" src="app/ui.js"></script> \n ' ;
113 for ( let i
= 0 ; i
< legacy_scripts
. length
; i
++) {
114 new_script
+= ` <script nomodule src=" ${legacy_scripts[i]} "></script> \n ` ;
118 contents
= contents
. slice ( 0 , start_ind
) + ` ${new_script} \n ` + contents
. slice ( end_ind
);
122 . then (( contents
) => {
123 console
. log ( `Writing ${out_html_path} ` );
124 return writeFile ( out_html_path
, contents
);
128 function make_lib_files ( import_format
, source_maps
, with_app_dir
, only_legacy
) {
129 if (! import_format
) {
130 throw new Error ( "you must specify an import format to generate compiled noVNC libraries" );
131 } else if (! SUPPORTED_FORMATS
. has ( import_format
)) {
132 throw new Error ( `unsupported output format " ${import_format} " for import/export -- only ${Array.from(SUPPORTED_FORMATS)} are supported` );
135 // NB: we need to make a copy of babel_opts, since babel sets some defaults on it
136 const babel_opts
= () => ({
139 [ '@babel/preset-env' ,
140 { targets
: 'ie >= 11' ,
141 modules
: import_format
} ]
144 sourceMaps
: source_maps
,
147 // No point in duplicate files without the app, so force only converted files
155 out_path_base
= paths
. out_dir_base
;
156 in_path
= paths
. main
;
158 out_path_base
= paths
. lib_dir_base
;
160 const legacy_path_base
= only_legacy
? out_path_base
: path
. join ( out_path_base
, 'legacy' );
162 fse
. ensureDirSync ( out_path_base
);
164 const helpers
= require ( './use_require_helpers' );
165 const helper
= helpers
[ import_format
];
168 const legacyFiles
= [];
170 const handleDir
= ( js_only
, vendor_rewrite
, in_path_base
, filename
) => Promise
. resolve ()
172 const out_path
= path
. join ( out_path_base
, path
. relative ( in_path_base
, filename
));
173 const legacy_path
= path
. join ( legacy_path_base
, path
. relative ( in_path_base
, filename
));
175 if ( path
. extname ( filename
) !== '.js' ) {
177 console
. log ( `Writing ${out_path} ` );
178 return copy ( filename
, out_path
);
180 return ; // skip non-javascript files
183 if ( no_transform_files
. has ( filename
)) {
184 return ensureDir ( path
. dirname ( out_path
))
186 console
. log ( `Writing ${out_path} ` );
187 return copy ( filename
, out_path
);
191 if ( only_legacy_scripts
. has ( filename
)) {
192 legacyFiles
. push ( legacy_path
);
193 return ensureDir ( path
. dirname ( legacy_path
))
195 console
. log ( `Writing ${legacy_path} ` );
196 return copy ( filename
, legacy_path
);
200 return Promise
. resolve ()
205 return ensureDir ( path
. dirname ( out_path
))
207 console
. log ( `Writing ${out_path} ` );
208 return copy ( filename
, out_path
);
211 . then (() => ensureDir ( path
. dirname ( legacy_path
)))
213 const opts
= babel_opts ();
214 if ( helper
&& helpers
. optionsOverride
) {
215 helper
. optionsOverride ( opts
);
217 // Adjust for the fact that we move the core files relative
218 // to the vendor directory
219 if ( vendor_rewrite
) {
220 opts
. plugins
. push ([ "import-redirect" ,
221 { "root" : legacy_path_base
,
222 "redirect" : { "vendor/(.+)" : "./vendor/$1" }}]);
225 return babelTransformFile ( filename
, opts
)
227 console
. log ( `Writing ${legacy_path} ` );
230 if ( source_maps
=== true ) {
231 // append URL for external source map
232 code
+= ` \n //# sourceMappingURL= ${path.basename(legacy_path)} .map \n ` ;
234 outFiles
. push ( ` ${legacy_path} ` );
235 return writeFile ( legacy_path
, code
)
237 if ( source_maps
=== true || source_maps
=== 'both' ) {
238 console
. log ( ` and ${legacy_path} .map` );
239 outFiles
. push ( ` ${legacy_path} .map` );
240 return writeFile ( ` ${legacy_path} .map` , JSON
. stringify ( map
));
249 const handler
= handleDir
. bind ( null , true , false , in_path
|| paths
. main
);
250 const filter
= ( filename
, stats
) => ! no_copy_files
. has ( filename
);
251 return walkDir ( paths
. vendor
, handler
, filter
);
254 const handler
= handleDir
. bind ( null , true , ! in_path
, in_path
|| paths
. core
);
255 const filter
= ( filename
, stats
) => ! no_copy_files
. has ( filename
);
256 return walkDir ( paths
. core
, handler
, filter
);
259 if (! with_app_dir
) return ;
260 const handler
= handleDir
. bind ( null , false , false , in_path
);
261 const filter
= ( filename
, stats
) => ! no_copy_files
. has ( filename
);
262 return walkDir ( paths
. app
, handler
, filter
);
265 if (! with_app_dir
) return ;
267 if (! helper
|| ! helper
. appWriter
) {
268 throw new Error ( `Unable to generate app for the ${import_format} format!` );
271 const out_app_path
= path
. join ( legacy_path_base
, 'app.js' );
272 console
. log ( `Writing ${out_app_path} ` );
273 return helper
. appWriter ( out_path_base
, legacy_path_base
, out_app_path
)
274 . then (( extra_scripts
) => {
275 let legacy_scripts
= extra_scripts
;
277 legacyFiles
. forEach (( file
) => {
278 let rel_file_path
= path
. relative ( out_path_base
, file
);
279 legacy_scripts
. push ( rel_file_path
);
282 let rel_app_path
= path
. relative ( out_path_base
, out_app_path
);
283 legacy_scripts
. push ( rel_app_path
);
285 transform_html ( legacy_scripts
, only_legacy
);
288 if (! helper
. removeModules
) return ;
289 console
. log ( `Cleaning up temporary files...` );
290 return Promise
. all ( outFiles
. map (( filepath
) => {
293 // Try to clean up any empty directories if this
294 // was the last file in there
295 const rmdir_r
= dir
=>
297 . then (() => rmdir_r ( path
. dirname ( dir
)))
299 // Assume the error was ENOTEMPTY and ignore it
301 return rmdir_r ( path
. dirname ( filepath
));
307 console
. error ( `Failure converting modules: ${err} ` );
313 console
. log ( `Removing ${paths.lib_dir_base} ` );
314 fse
. removeSync ( paths
. lib_dir_base
);
316 console
. log ( `Removing ${paths.out_dir_base} ` );
317 fse
. removeSync ( paths
. out_dir_base
);
320 make_lib_files ( program
. as
, program
. withSourceMaps
, program
. withApp
, program
. onlyLegacy
);