1 | #!/usr/bin/env node
|
2 | const FileSystem = require('fs');
|
3 | const Path = require('path');
|
4 | const {ArgumentParser} = require('argparse');
|
5 | const pkg = require('./package.json');
|
6 | const Babel = require('babel-core');
|
7 | const {promisify, readDirR, copyFile, log, splice, mapToObject, mergeDeep, getFiles, readDir, fileStat, readText, forAll} = require('./util');
|
8 | const mkdirp = promisify(require('mkdirp'));
|
9 | const writeFile = promisify(FileSystem.writeFile);
|
10 | const BabelTypes = require('babel-types');
|
11 | const traverseAst = require('babel-traverse').default;
|
12 | const babelGenerator = require('babel-generator').default;
|
13 | const babelTemplate = require('babel-template');
|
14 | const Babylon = require('babylon');
|
15 | const Chalk = require('chalk');
|
16 |
|
17 | let parser = new ArgumentParser({
|
18 | version: pkg.version,
|
19 | description: pkg.description,
|
20 | addHelp: true,
|
21 | });
|
22 |
|
23 | parser.addArgument('pkgDir', {
|
24 | help: "Directory with package.json",
|
25 | metavar: "package-dir",
|
26 | defaultValue: '.',
|
27 | });
|
28 |
|
29 | parser.addArgument(['-s', '--src-dir'], {
|
30 | help: "Source directory to bundle up",
|
31 | metavar: "src-dir",
|
32 | dest: 'srcDir',
|
33 | });
|
34 |
|
35 | parser.addArgument(['-d', '--out-dir'], {
|
36 | help: "Output directory",
|
37 | metavar: "out-dir",
|
38 | dest: 'outDir',
|
39 | });
|
40 |
|
41 | parser.addArgument(['-t', '--target'], {
|
42 | help: "Target environment",
|
43 | metavar: "target",
|
44 | dest: 'target',
|
45 | defaultValue: 'node:6,webpack:2'
|
46 | });
|
47 |
|
48 | parser.addArgument(['--index'], {
|
49 | help: "Generate an index file for each directory where it is missing",
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 | dest: 'index',
|
58 | defaultValue: 'shallow',
|
59 | });
|
60 |
|
61 |
|
62 | let args = parser.parseArgs();
|
63 |
|
64 |
|
65 | const SRC_DIR = args.srcDir || Path.join(args.pkgDir, 'src');
|
66 |
|
67 | const DIST_DIR = args.outDir || Path.join(SRC_DIR, '..', 'dist');
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 | let sourceFiles = readDirR(SRC_DIR);
|
75 | sourceFiles.sort();
|
76 |
|
77 |
|
78 | const transformFile = promisify(Babel.transformFile);
|
79 |
|
80 |
|
81 | const babelEnvs = {
|
82 | node: {
|
83 | plugins: [
|
84 | require.resolve('babel-plugin-dynamic-import-node'),
|
85 | ],
|
86 | presets: [
|
87 | [
|
88 | require.resolve('babel-preset-env'),
|
89 | {
|
90 | modules: 'commonjs',
|
91 | loose: true,
|
92 | targets: {
|
93 | node: 6
|
94 | }
|
95 | }
|
96 | ]
|
97 | ],
|
98 | },
|
99 | web: {
|
100 | plugins: [
|
101 |
|
102 | ],
|
103 | presets: [
|
104 | [
|
105 | require.resolve('babel-preset-env'),
|
106 | {
|
107 | modules: false,
|
108 | loose: true,
|
109 | targets: {
|
110 | browsers: [
|
111 | "> 1%",
|
112 | "last 2 Firefox versions",
|
113 | "last 2 Chrome versions",
|
114 | "last 2 Edge versions",
|
115 | "last 2 Safari versions",
|
116 | "Firefox ESR",
|
117 | "IE >= 8"
|
118 | ]
|
119 | }
|
120 | }
|
121 | ]
|
122 | ],
|
123 | },
|
124 | };
|
125 |
|
126 |
|
127 |
|
128 |
|
129 | async function processDir(inputDir) {
|
130 | let srcEntries = await readDir(inputDir);
|
131 |
|
132 | let hasIndex = srcEntries.map(p => Path.parse(p).name).includes('index');
|
133 | let indexLines = [];
|
134 |
|
135 |
|
136 | await forAll(srcEntries, async srcFile => {
|
137 | let srcStat = await fileStat(srcFile);
|
138 |
|
139 | if(srcStat.isDirectory()) {
|
140 | processDir(srcFile);
|
141 | } else {
|
142 | let srcParsed = Path.parse(srcFile);
|
143 | let destFileBase = srcParsed.base.replace(/\.(web|node)\./, '.');
|
144 |
|
145 | if(/\.jsx?/.test(srcFile)) {
|
146 | let fileContents = await readText(srcFile);
|
147 |
|
148 | if(!hasIndex) {
|
149 | let ast;
|
150 | try {
|
151 | ast = Babylon.parse(fileContents, {
|
152 | sourceType: 'module',
|
153 | plugins: [
|
154 |
|
155 | 'jsx',
|
156 | 'doExpressions',
|
157 | 'objectRestSpread',
|
158 | 'classProperties',
|
159 | 'exportExtensions',
|
160 | 'dynamicImport',
|
161 | 'functionBind',
|
162 | ]
|
163 | });
|
164 | } catch(parseErr) {
|
165 | console.log(`${Chalk.red(`Parse error in ${Chalk.underline(srcFile)}`)}`, parseErr.message);
|
166 | return;
|
167 | }
|
168 |
|
169 |
|
170 | let destParsed = Path.parse(destFileBase);
|
171 | let defaultId = destParsed.name;
|
172 | let srcString = JSON.stringify(`./${destParsed.name}`);
|
173 |
|
174 |
|
175 | let hasDefaultExport = ast.program.body.some(x => x.type === 'ExportDefaultDeclaration');
|
176 | let hasNamedExport = ast.program.body.some(x => x.type === 'ExportNamedDeclaration');
|
177 |
|
178 |
|
179 | if(hasDefaultExport) {
|
180 | indexLines.push(`export {default as ${defaultId}} from ${srcString};`);
|
181 | }
|
182 | if(hasNamedExport) {
|
183 |
|
184 | indexLines.push(`export * from ${srcString};`);
|
185 | }
|
186 | }
|
187 |
|
188 | ['node', 'web'].forEach(async target => {
|
189 |
|
190 | let result = transpile(target, fileContents, srcFile);
|
191 |
|
192 | let destDir = Path.join(DIST_DIR, target, Path.relative(SRC_DIR, inputDir));
|
193 | let destFile = Path.join(destDir, destFileBase);
|
194 |
|
195 | await mkdirp(destDir);
|
196 | await writeFile(destFile, result.code);
|
197 | console.log(`compiled ${srcFile} -> ${destFile}`);
|
198 | });
|
199 | } else {
|
200 | ['node', 'web'].forEach(async target => {
|
201 |
|
202 | let destDir = Path.join(DIST_DIR, target, Path.relative(SRC_DIR, inputDir));
|
203 | let destFile = Path.join(destDir, destFileBase);
|
204 |
|
205 | await mkdirp(destDir);
|
206 | await copyFile(srcFile, destFile);
|
207 | console.log(`copied ${srcFile} -> ${destFile}`);
|
208 | });
|
209 | }
|
210 | }
|
211 | });
|
212 |
|
213 |
|
214 | if(indexLines.length) {
|
215 | indexLines.push(`import * as __self__ from './index';`);
|
216 | indexLines.push(`export default __self__;`);
|
217 | let indexFileContents = indexLines.join('\n');
|
218 |
|
219 | ['node', 'web'].forEach(async target => {
|
220 | let destDir = Path.join(DIST_DIR, target, Path.relative(SRC_DIR, inputDir));
|
221 | let destFile = Path.join(destDir, 'index.js');
|
222 | let result = transpile(target, indexFileContents, destFile);
|
223 | await mkdirp(destDir);
|
224 | await writeFile(destFile, result.code);
|
225 | console.log(`wrote ${destFile}`);
|
226 | });
|
227 | }
|
228 | }
|
229 |
|
230 | function transpile(target, fileContents, filename) {
|
231 | const babelPlugins = [
|
232 | require.resolve('babel-plugin-transform-function-bind'),
|
233 | require.resolve('babel-plugin-syntax-do-expressions'),
|
234 | require.resolve('babel-plugin-syntax-dynamic-import'),
|
235 | require.resolve('babel-plugin-transform-class-properties'),
|
236 | require.resolve('babel-plugin-transform-do-expressions'),
|
237 | require.resolve('babel-plugin-transform-function-bind'),
|
238 | require.resolve('babel-plugin-transform-object-rest-spread'),
|
239 | require.resolve('babel-plugin-transform-react-jsx'),
|
240 |
|
241 | [require.resolve('babel-plugin-transform-define'), {'BUNDILIO_TARGET': target}],
|
242 | ];
|
243 |
|
244 | return Babel.transform(fileContents, mergeDeep({
|
245 | ast: false,
|
246 | code: true,
|
247 | babelrc: false,
|
248 | plugins: babelPlugins,
|
249 | filename,
|
250 | }, babelEnvs[target]));
|
251 | }
|
252 |
|
253 | processDir(SRC_DIR);
|
254 |
|
255 |
|
\ | No newline at end of file |