]>
Commit | Line | Data |
---|---|---|
ae510306 SR |
1 | #!/usr/bin/env node |
2 | ||
3 | var path = require('path'); | |
4 | var program = require('commander'); | |
5 | var fs = require('fs'); | |
6 | var fse = require('fs-extra'); | |
ae510306 | 7 | |
6cae7b58 | 8 | const SUPPORTED_FORMATS = new Set(['amd', 'commonjs', 'systemjs', 'umd']); |
ae510306 SR |
9 | |
10 | program | |
6cae7b58 SR |
11 | .option('--as [format]', `output files using various import formats instead of ES6 import and export. Supports ${Array.from(SUPPORTED_FORMATS)}.`) |
12 | .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) ') | |
13 | .option('--with-app', 'process app files as well as core files') | |
ae510306 SR |
14 | .parse(process.argv); |
15 | ||
16 | // the various important paths | |
152c3995 SR |
17 | const paths = { |
18 | main: path.resolve(__dirname, '..'), | |
19 | core: path.resolve(__dirname, '..', 'core'), | |
20 | app: path.resolve(__dirname, '..', 'app'), | |
21 | vendor: path.resolve(__dirname, '..', 'vendor'), | |
22 | out_dir_base: path.resolve(__dirname, '..', 'build'), | |
23 | lib_dir_base: path.resolve(__dirname, '..', 'lib'), | |
24 | }; | |
ae510306 | 25 | |
adfc9d3f SR |
26 | const no_copy_files = new Set([ |
27 | // skip these -- they don't belong in the processed application | |
152c3995 SR |
28 | path.join(paths.vendor, 'sinon.js'), |
29 | path.join(paths.vendor, 'browser-es-module-loader'), | |
30 | path.join(paths.vendor, 'promise.js'), | |
adfc9d3f SR |
31 | ]); |
32 | ||
33 | const no_transform_files = new Set([ | |
34 | // don't transform this -- we want it imported as-is to properly catch loading errors | |
152c3995 | 35 | path.join(paths.app, 'error-handler.js'), |
adfc9d3f SR |
36 | ]); |
37 | ||
152c3995 SR |
38 | no_copy_files.forEach((file) => no_transform_files.add(file)); |
39 | ||
6cae7b58 SR |
40 | // walkDir *recursively* walks directories trees, |
41 | // calling the callback for all normal files found. | |
399fa2ee | 42 | var walkDir = function (base_path, cb, filter) { |
6cae7b58 SR |
43 | fs.readdir(base_path, (err, files) => { |
44 | if (err) throw err; | |
ae510306 | 45 | |
6cae7b58 SR |
46 | files.map((filename) => path.join(base_path, filename)).forEach((filepath) => { |
47 | fs.lstat(filepath, (err, stats) => { | |
48 | if (err) throw err; | |
ae510306 | 49 | |
399fa2ee SR |
50 | if (filter !== undefined && !filter(filepath, stats)) return; |
51 | ||
6cae7b58 SR |
52 | if (stats.isSymbolicLink()) return; |
53 | if (stats.isFile()) cb(filepath); | |
399fa2ee | 54 | if (stats.isDirectory()) walkDir(filepath, cb, filter); |
ae510306 SR |
55 | }); |
56 | }); | |
57 | }); | |
6cae7b58 | 58 | }; |
ae510306 | 59 | |
6cae7b58 | 60 | var transform_html = function (new_script) { |
ae510306 SR |
61 | // write out the modified vnc.html file that works with the bundle |
62 | var src_html_path = path.resolve(__dirname, '..', 'vnc.html'); | |
152c3995 | 63 | var out_html_path = path.resolve(paths.out_dir_base, 'vnc.html'); |
6cae7b58 | 64 | fs.readFile(src_html_path, (err, contents_raw) => { |
ae510306 SR |
65 | if (err) { throw err; } |
66 | ||
67 | var contents = contents_raw.toString(); | |
ae510306 SR |
68 | |
69 | var start_marker = '<!-- begin scripts -->\n'; | |
70 | var end_marker = '<!-- end scripts -->'; | |
71 | var start_ind = contents.indexOf(start_marker) + start_marker.length; | |
72 | var end_ind = contents.indexOf(end_marker, start_ind); | |
73 | ||
6cae7b58 | 74 | contents = contents.slice(0, start_ind) + `${new_script}\n` + contents.slice(end_ind); |
ae510306 | 75 | |
6cae7b58 | 76 | console.log(`Writing ${out_html_path}`); |
ae510306 SR |
77 | fs.writeFile(out_html_path, contents, function (err) { |
78 | if (err) { throw err; } | |
ae510306 SR |
79 | }); |
80 | }); | |
6cae7b58 | 81 | } |
ae510306 | 82 | |
6cae7b58 SR |
83 | var make_lib_files = function (import_format, source_maps, with_app_dir) { |
84 | if (!import_format) { | |
85 | throw new Error("you must specify an import format to generate compiled noVNC libraries"); | |
86 | } else if (!SUPPORTED_FORMATS.has(import_format)) { | |
87 | throw new Error(`unsupported output format "${import_format}" for import/export -- only ${Array.from(SUPPORTED_FORMATS)} are supported`); | |
88 | } | |
89 | ||
90 | // NB: we need to make a copy of babel_opts, since babel sets some defaults on it | |
91 | const babel_opts = () => ({ | |
92 | plugins: [`transform-es2015-modules-${import_format}`], | |
93 | ast: false, | |
94 | sourceMaps: source_maps, | |
95 | }); | |
96 | const babel = require('babel-core'); | |
97 | ||
98 | var in_path; | |
99 | if (with_app_dir) { | |
152c3995 SR |
100 | var out_path_base = paths.out_dir_base; |
101 | in_path = paths.main; | |
6cae7b58 | 102 | } else { |
152c3995 | 103 | var out_path_base = paths.lib_dir_base; |
6cae7b58 SR |
104 | } |
105 | ||
106 | fse.ensureDirSync(out_path_base); | |
107 | ||
108 | const helpers = require('./use_require_helpers'); | |
109 | const helper = helpers[import_format]; | |
110 | ||
111 | var handleDir = (js_only, in_path_base, filename) => { | |
adfc9d3f SR |
112 | if (no_copy_files.has(filename)) return; |
113 | ||
6cae7b58 SR |
114 | const out_path = path.join(out_path_base, path.relative(in_path_base, filename)); |
115 | if(path.extname(filename) !== '.js') { | |
116 | if (!js_only) { | |
117 | console.log(`Writing ${out_path}`); | |
118 | fse.copy(filename, out_path, (err) => { if (err) throw err; }); | |
ae510306 | 119 | } |
6cae7b58 | 120 | return; // skip non-javascript files |
ae510306 | 121 | } |
ae510306 | 122 | |
6cae7b58 | 123 | fse.ensureDir(path.dirname(out_path), () => { |
adfc9d3f SR |
124 | if (no_transform_files.has(filename)) { |
125 | console.log(`Writing ${out_path}`); | |
126 | fse.copy(filename, out_path, (err) => { if (err) throw err; }); | |
127 | return; | |
128 | } | |
129 | ||
6cae7b58 SR |
130 | const opts = babel_opts(); |
131 | if (helper && helpers.optionsOverride) { | |
132 | helper.optionsOverride(opts); | |
133 | } | |
adfc9d3f | 134 | |
6cae7b58 SR |
135 | babel.transformFile(filename, babel_opts(), (err, res) => { |
136 | console.log(`Writing ${out_path}`); | |
137 | if (err) throw err; | |
138 | var {code, map, ast} = res; | |
139 | if (source_maps === true) { | |
140 | // append URL for external source map | |
141 | code += `\n//# sourceMappingURL=${path.basename(out_path)}.map\n`; | |
142 | } | |
143 | fs.writeFile(out_path, code, (err) => { if (err) throw err; }); | |
144 | if (source_maps === true || source_maps === 'both') { | |
145 | console.log(` and ${out_path}.map`); | |
146 | fs.writeFile(`${out_path}.map`, JSON.stringify(map), (err) => { if (err) throw err; }); | |
147 | } | |
148 | }); | |
149 | }); | |
150 | }; | |
151 | ||
152c3995 SR |
152 | if (with_app_dir && helper && helper.noCopyOverride) { |
153 | helper.noCopyOverride(paths, no_copy_files); | |
154 | } | |
155 | ||
156 | walkDir(paths.core, handleDir.bind(null, true, in_path || paths.core), (filename, stats) => !no_copy_files.has(filename)); | |
157 | walkDir(paths.vendor, handleDir.bind(null, true, in_path || paths.main), (filename, stats) => !no_copy_files.has(filename)); | |
6cae7b58 SR |
158 | |
159 | if (with_app_dir) { | |
152c3995 | 160 | walkDir(paths.app, handleDir.bind(null, false, in_path || paths.app), (filename, stats) => !no_copy_files.has(filename)); |
6cae7b58 SR |
161 | |
162 | const out_app_path = path.join(out_path_base, 'app.js'); | |
163 | if (helper && helper.appWriter) { | |
164 | console.log(`Writing ${out_app_path}`); | |
165 | let out_script = helper.appWriter(out_path_base, out_app_path); | |
166 | transform_html(out_script); | |
167 | } else { | |
168 | console.error(`Unable to generate app for the ${import_format} format!`); | |
169 | } | |
170 | } | |
ae510306 SR |
171 | }; |
172 | ||
6cae7b58 | 173 | make_lib_files(program.as, program.withSourceMaps, program.withApp); |