1 | #!/usr/bin/env node
|
2 |
|
3 | const Path = require('path');
|
4 | const Json5 = require('json5');
|
5 | const Util = require('util');
|
6 | const FS = require('./fs2');
|
7 | const Rollup = require('rollup');
|
8 | const nodeResolve = require('rollup-plugin-node-resolve');
|
9 | const commonjs = require('rollup-plugin-commonjs');
|
10 | const babel = require('rollup-plugin-babel');
|
11 | const del = require('del');
|
12 | const builtinModules = require('builtin-modules');
|
13 | const {ArgumentParser} = require('argparse');
|
14 | const npm = require('npm');
|
15 | const {codeFrameColumns} = require('@babel/code-frame');
|
16 | const Chalk = require('chalk');
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | process.on('unhandledRejection', dump);
|
22 |
|
23 |
|
24 | function* findPlugins(searchString, obj, path) {
|
25 | if(obj[searchString]) {
|
26 | for(let i=0; i<obj[searchString].length; ++i) {
|
27 | if(Array.isArray(obj[searchString][i])) {
|
28 | yield [[...path, searchString, i, 0], obj[searchString][i][0]];
|
29 | } else {
|
30 | yield [[...path, searchString, i], obj[searchString][i]];
|
31 | }
|
32 | }
|
33 | }
|
34 | for(let key of Object.keys(obj)) {
|
35 | if(key !== searchString && typeof obj[key] === 'object') {
|
36 | let newPath = [...path, key];
|
37 | for(let x of findPlugins(searchString, obj[key], newPath)) {
|
38 | yield x;
|
39 | }
|
40 | }
|
41 | }
|
42 | }
|
43 |
|
44 |
|
45 | const normalizePluginName = (plugin) => plugin.replace(/^babel-plugin-/, "");
|
46 |
|
47 | (async () => {
|
48 | const argparse = new ArgumentParser({
|
49 |
|
50 |
|
51 | addHelp: true,
|
52 | });
|
53 |
|
54 |
|
55 | argparse.addArgument(['-w', '--watch'], {
|
56 | help: "Watch source files for changes",
|
57 | dest: 'watch',
|
58 | action: 'storeTrue',
|
59 | });
|
60 |
|
61 | const cliArgs = argparse.parseArgs();
|
62 |
|
63 | const pkgFile = await FS.closest('package.json');
|
64 | const pkgJson = await FS.readText(pkgFile);
|
65 | const pkg = Json5.parse(pkgJson);
|
66 | const mainFields = ['browser', 'module', 'main'];
|
67 | const pkgDeps = objectKeys(pkg.dependencies).concat(objectKeys(pkg.peerDependencies));
|
68 | const pkgDir = Path.dirname(pkgFile);
|
69 | const distDir = Path.join(pkgDir, 'dist');
|
70 | const babelRcFile = Path.join(pkgDir, '.babelrc');
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | let babelOptions;
|
76 | let useBabel = await FS.exists(babelRcFile);
|
77 | if(useBabel) {
|
78 | babelOptions = Json5.parse(await FS.readText(babelRcFile));
|
79 |
|
80 | let pluginPaths = findPlugins('plugins', babelOptions, []);
|
81 |
|
82 | for(let [path,plug] of pluginPaths) {
|
83 | let plugMod;
|
84 |
|
85 | try {
|
86 | plugMod = require.resolve(plug)
|
87 | } catch(_) {
|
88 | try {
|
89 | plugMod = require.resolve(`babel-plugin-${plug}`);
|
90 | } catch(_) {
|
91 | plugMod = plug;
|
92 | }
|
93 | }
|
94 | setValue(babelOptions, path, plugMod);
|
95 | }
|
96 |
|
97 | let presetPaths = findPlugins('presets', babelOptions, []);
|
98 |
|
99 |
|
100 | for(let [path,preset] of presetPaths) {
|
101 | let plugMod;
|
102 |
|
103 | try {
|
104 | plugMod = require.resolve(preset)
|
105 | } catch(_) {
|
106 | try {
|
107 | plugMod = require.resolve(`babel-preset-${preset}`);
|
108 | } catch(_) {
|
109 | plugMod = preset;
|
110 | }
|
111 | }
|
112 | setValue(babelOptions, path, plugMod);
|
113 | }
|
114 | }
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 | let rollupConfigs = [];
|
121 |
|
122 | await forAll(mainFields, field => forAll(toArray(pkg[field]), async filename => {
|
123 | const srcFile = Path.resolve(pkgDir, filename);
|
124 |
|
125 | if(!(await FS.exists(srcFile))) {
|
126 | console.log(`Entry file does not exist: ${srcFile}`);
|
127 | delete pkg[field];
|
128 | return;
|
129 | }
|
130 |
|
131 | const distFile = `${field}.js`;
|
132 | const distPath = Path.join(distDir, distFile);
|
133 | pkg[field] = distFile;
|
134 |
|
135 | let rollupOptions = {
|
136 | input: srcFile,
|
137 | external: field === 'main' ? [...pkgDeps, ...builtinModules] : pkgDeps,
|
138 | name: pkg.name,
|
139 | plugins: [
|
140 | nodeResolve({
|
141 | jail: pkgDir,
|
142 |
|
143 | }),
|
144 | commonjs(),
|
145 | ],
|
146 | output: {
|
147 | file: distPath,
|
148 | format: field === 'main' ? 'cjs' : 'es',
|
149 | sourcemap: true,
|
150 | },
|
151 | watch: {
|
152 | chokidar: true,
|
153 | }
|
154 | };
|
155 |
|
156 | if(useBabel) {
|
157 | rollupOptions.plugins.unshift(babel({
|
158 | ...babelOptions,
|
159 | babelrc: false,
|
160 | }));
|
161 | }
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 | rollupConfigs.push(rollupOptions);
|
169 | }));
|
170 |
|
171 |
|
172 |
|
173 | delete pkg.devDependencies;
|
174 | delete pkg.scripts;
|
175 | delete pkg.eslintConfig;
|
176 | delete pkg.babel;
|
177 |
|
178 | await FS.writeText(Path.join(distDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
179 |
|
180 |
|
181 |
|
182 |
|
183 | if(cliArgs.watch) {
|
184 |
|
185 |
|
186 | let watcher = Rollup.watch(rollupConfigs);
|
187 |
|
188 | watcher.on('event', ev => {
|
189 |
|
190 | switch(ev.code) {
|
191 | case 'START':
|
192 | break;
|
193 | case 'END':
|
194 | break;
|
195 | case 'BUNDLE_START':
|
196 |
|
197 |
|
198 | break;
|
199 | case 'BUNDLE_END':
|
200 |
|
201 | break;
|
202 | default:
|
203 | dump(ev);
|
204 | break;
|
205 | }
|
206 | });
|
207 | } else {
|
208 | try {
|
209 |
|
210 |
|
211 |
|
212 |
|
213 | await Promise.all(rollupConfigs.map(({output, ...input}) => Rollup.rollup(input).then(bundle => bundle.write(output))));
|
214 | } catch(err) {
|
215 |
|
216 |
|
217 | console.log(Chalk.red(err.message));
|
218 | if(err.id && err.loc) {
|
219 | let srcCode = await FS.readText(err.id);
|
220 | console.log(codeFrameColumns(srcCode, {
|
221 | start: {
|
222 | line: err.loc.line,
|
223 | column: err.loc.column + 1,
|
224 | }
|
225 | }, {forceColor: true}));
|
226 | }
|
227 | dump(err);
|
228 |
|
229 | }
|
230 | }
|
231 | })();
|
232 |
|
233 | function objectKeys(obj) {
|
234 | return obj ? Object.keys(obj) : [];
|
235 | }
|
236 |
|
237 | function toArray(x) {
|
238 | if(x == null) return [];
|
239 | if(Array.isArray(x)) return x;
|
240 | return [x];
|
241 | }
|
242 |
|
243 | function dump(...args) {
|
244 | console.log(...args.map(o => Util.inspect(o, {colors: true, depth: 10})));
|
245 | }
|
246 |
|
247 | function forAll(iterable, callback) {
|
248 | let promises = [];
|
249 | for(let x of iterable) {
|
250 | promises.push(callback(x));
|
251 | }
|
252 | return Promise.all(promises);
|
253 | }
|
254 |
|
255 | function setValue(obj, path, value) {
|
256 | const end = path.length - 1;
|
257 | for(let i=0; i<end; ++i) {
|
258 | obj = obj[path[i]];
|
259 | }
|
260 | obj[path[end]] = value;
|
261 | }
|