UNPKG

5.97 kBJavaScriptView Raw
1'use strict';
2const parser = require('@babel/parser');
3const defaultOptions = {
4 allowAwaitOutsideFunction: true,
5 sourceType: 'module',
6 plugins: [
7 // 'estree',
8 'jsx',
9 'typescript',
10 'exportExtensions',
11 'exportDefaultFrom',
12 'exportNamespaceFrom',
13 'dynamicImport',
14 'importMeta',
15 'asyncGenerators',
16 'bigInt',
17 'classProperties',
18 'classPrivateProperties',
19 'classPrivateMethods',
20 ['decorators', {decoratorsBeforeExport: true}],
21 'doExpressions',
22 'functionBind',
23 'functionSent',
24 'logicalAssignment',
25 'nullishCoalescingOperator',
26 'numericSeparator',
27 'objectRestSpread',
28 'optionalCatchBinding',
29 'optionalChaining',
30 'partialApplication',
31 ['pipelineOperator', {proposal: 'minimal'}],
32 'throwExpressions',
33 'topLevelAwait'
34 ]
35};
36
37const EXPORT = `Object.defineProperty(exports, '__esModule', {value: true})`;
38const IMPORT = `(m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)`;
39const asDefault = name => name === 'default' ? `${parse.info.EXPORT}.default` : `exports.${name}`;
40const fromDefault = defaultImport => `${parse.info.IMPORT}(${defaultImport})`;
41
42const slice = (code, info) => code.slice(info.start, info.end);
43const chunk = (info, esm, cjs) => ({
44 start: info.start,
45 end: info.end,
46 esm, cjs
47});
48
49const replace = {
50
51 ImportDeclaration(code, item) {
52 const source = item.source;
53 const name = withoutCDN(slice(code, source));
54 const esm = slice(code, item);
55 const SEPS = /\{(\s+)/.test(esm) ? RegExp.$1 : '';
56 const SEPE = /(\s+)\}/.test(esm) ? RegExp.$1 : '';
57 const SEP = /(,\s+)[^{]/.test(esm) ? RegExp.$1 : ', ';
58 const EOL = /;$/.test(esm) ? ';' : '';
59 const imported = [];
60 const specifiers = [];
61 let defaultImport = `require(${name})`;
62 if (item.specifiers.length) {
63 item.specifiers.forEach(specifier => {
64 switch(specifier.type) {
65 case 'ImportDefaultSpecifier':
66 imported.push(
67 `const ${specifier.local.name} = ${fromDefault(defaultImport)}${EOL}`
68 );
69 break;
70 case 'ImportNamespaceSpecifier':
71 imported.push(
72 `const ${specifier.local.name} = ${defaultImport}${EOL}`
73 );
74 break;
75 case 'ImportSpecifier':
76 specifiers.push(
77 specifier.local.name === specifier.imported.name ?
78 specifier.local.name :
79 `${specifier.imported.name}: ${specifier.local.name}`
80 );
81 break;
82 }
83 });
84 if (specifiers.length) {
85 imported.push(
86 `const {${SEPS}${specifiers.join(SEP)}${SEPE}} = ${defaultImport}${EOL}`
87 );
88 }
89 } else {
90 imported.push(`${defaultImport}${EOL}`);
91 }
92 return chunk(item, esm, imported.join('\n'));
93 },
94
95 ExportAllDeclaration(code, item) {
96 const source = item.source;
97 const esm = slice(code, item);
98 const cjs = `(m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k])))\n(require(${
99 withoutCDN(slice(code, source))
100 }));`;
101 return chunk(item, esm, cjs);
102 },
103
104 ExportDefaultDeclaration(code, item) {
105 const declaration = item.declaration;
106 const esm = slice(code, item);
107 let cjs;
108 switch (declaration.type) {
109 case 'AssignmentExpression':
110 case 'FunctionDeclaration':
111 if (declaration.id) {
112 cjs = `${esm.replace(/^export\s+default\s+/, '')}\n${parse.info.EXPORT}.default = ${declaration.id.name}`;
113 } else {
114 cjs = esm.replace(/^export\s+default\s+/, `${parse.info.EXPORT}.default = `);
115 }
116 break;
117 case 'Identifier':
118 case 'ObjectExpression':
119 default:
120 cjs = esm.replace(/^export\s+default\s+/, `${parse.info.EXPORT}.default = `);
121 break;
122 }
123 return chunk(item, esm, cjs);
124 },
125
126 ExportNamedDeclaration(code, item) {
127 const declaration = item.declaration;
128 const source = item.source;
129 const esm = slice(code, item);
130 const EOL = /;$/.test(esm) ? ';\n' : '\n';
131 let cjs = source ? '(m => {\n' : '';
132 item.specifiers.forEach(specifier => {
133 cjs += `${
134 source ? ' ' : ''
135 }${asDefault(specifier.exported.name)} = ${
136 source ? 'm.' : ''
137 }${specifier.local.name}${EOL}`;
138 });
139 if (declaration) {
140 cjs += esm.replace(/^export\s+/, '') + '\n';
141 (declaration.declarations || [declaration]).forEach(specifier => {
142 cjs += `${asDefault(specifier.id.name)} = ${specifier.id.name}${EOL}`;
143 });
144 }
145 if (source) cjs += `})(require(${
146 withoutCDN(slice(code, source))
147 }));\n`;
148 return chunk(item, esm, cjs.trim());
149 }
150};
151
152const parse = (code, options) => {
153 if (!options) options = {};
154 const parserOptions = Object.assign(
155 {},
156 defaultOptions,
157 options
158 );
159 parse.info = {
160 EXPORT: options.EXPORT || EXPORT,
161 IMPORT: options.IMPORT || IMPORT
162 };
163 delete options.EXPORT;
164 delete options.IMPORT;
165 code = code.toString();
166 const out = [];
167 const chunks = [];
168 const parsed = parser.parse(code, parserOptions);
169 parsed.program.body.forEach(item => {
170 if (replace.hasOwnProperty(item.type)) {
171 chunks.push(replace[item.type](code, item));
172 }
173 });
174 const length = chunks.length;
175 let c = 0;
176 for (let i = 0; i < length; i++) {
177 out.push(
178 code.slice(c, chunks[i].start),
179 chunks[i].cjs
180 );
181 c = chunks[i].end;
182 }
183 out.push(length ? code.slice(c) : code);
184 let result = out.join('').replace(
185 /\bimport\.meta\b/g,
186 "({url: require('url').pathToFileURL(__filename).href})"
187 );
188 return /^(?:#!|['"]use strict['"])/.test(result.trim()) ?
189 result :
190 ("'use strict';\n" + result);
191};
192
193const withoutCDN = name =>
194 /^(['"`])https?:\/\/(?:unpkg\.com)\/([^@/]+)\S*?\1$/.test(name) ?
195 `${RegExp.$1}${RegExp.$2}${RegExp.$1}` : name;
196
197parse.EXPORT = EXPORT;
198parse.IMPORT = IMPORT;
199module.exports = parse;