1 | "use strict";
|
2 |
|
3 | const assert = require("assert");
|
4 | const getOption = require("./options.js").get;
|
5 | const utils = require("./utils.js");
|
6 |
|
7 | const MagicString = require("magic-string");
|
8 | const Visitor = require("./visitor.js");
|
9 |
|
10 | const codeOfCR = "\r".charCodeAt(0);
|
11 | const codeOfDoubleQuote = '"'.charCodeAt(0);
|
12 | const codeOfSingleQuote = "'".charCodeAt(0);
|
13 |
|
14 | class ImportExportVisitor extends Visitor {
|
15 | finalizeHoisting() {
|
16 | const infoCount = this.bodyInfos.length;
|
17 | const isModule = this.sourceType === "module";
|
18 |
|
19 | for (let i = 0; i < infoCount; ++i) {
|
20 | const bodyInfo = this.bodyInfos[i];
|
21 | let codeToInsert = "";
|
22 |
|
23 |
|
24 |
|
25 | if (bodyInfo.needToAddUseStrictDirective &&
|
26 | (this.madeChanges || isModule)) {
|
27 | this.madeChanges = true;
|
28 | codeToInsert += '"use strict";';
|
29 |
|
30 | } else if (this.code &&
|
31 | bodyInfo.insertCharIndex > 0) {
|
32 | const charCode = this.code.charCodeAt(bodyInfo.insertCharIndex - 1)
|
33 | if (charCode === codeOfDoubleQuote ||
|
34 | charCode === codeOfSingleQuote) {
|
35 |
|
36 |
|
37 | codeToInsert += ";";
|
38 | }
|
39 | }
|
40 |
|
41 | const addExportsMap = (map, constant) => {
|
42 | const namedExports = toModuleExport(this, map, constant);
|
43 | if (namedExports) {
|
44 | codeToInsert += namedExports;
|
45 | }
|
46 | };
|
47 |
|
48 | addExportsMap(bodyInfo.hoistedExportsMap, false);
|
49 | addExportsMap(bodyInfo.hoistedConstExportsMap, true);
|
50 |
|
51 | if (bodyInfo.hoistedExportsString) {
|
52 | codeToInsert += bodyInfo.hoistedExportsString;
|
53 | }
|
54 |
|
55 | if (bodyInfo.hoistedImportsString) {
|
56 | codeToInsert += bodyInfo.hoistedImportsString;
|
57 | }
|
58 |
|
59 | if (codeToInsert) {
|
60 | if (this.magicString !== null) {
|
61 | this.magicString.prependRight(
|
62 | bodyInfo.insertCharIndex,
|
63 | codeToInsert
|
64 | );
|
65 | }
|
66 |
|
67 | if (this.modifyAST) {
|
68 | let ast = this.parse(codeToInsert);
|
69 |
|
70 | if (ast.type === "File") {
|
71 | ast = ast.program;
|
72 | }
|
73 | assert.strictEqual(ast.type, "Program");
|
74 |
|
75 | const spliceArgs = ast.body;
|
76 | spliceArgs.unshift(bodyInfo.insertNodeIndex, 0);
|
77 |
|
78 | const body = bodyInfo.body;
|
79 | body.splice.apply(body, spliceArgs);
|
80 |
|
81 | const parsedDirectives = ast.directives;
|
82 | const parentDirectives = bodyInfo.parent.directives;
|
83 |
|
84 | if (parsedDirectives && parentDirectives) {
|
85 | parentDirectives.push.apply(parentDirectives, parsedDirectives);
|
86 | }
|
87 | }
|
88 | }
|
89 |
|
90 | delete bodyInfo.parent._bodyInfoByName;
|
91 | }
|
92 |
|
93 |
|
94 | this.bodyInfos.length = 0;
|
95 |
|
96 | if (this.modifyAST) {
|
97 | this.removals.forEach(processRemoval);
|
98 |
|
99 |
|
100 | this.removals.length = 0;
|
101 | }
|
102 |
|
103 | this.maybeWrapModuleWithFunction();
|
104 | }
|
105 |
|
106 | maybeWrapModuleWithFunction() {
|
107 | if (
|
108 | this.finalCompilationPass &&
|
109 | this.moduleAlias !== "module"
|
110 | ) {
|
111 | if (this.magicString !== null) {
|
112 | this.magicString.prepend("!function(" + this.moduleAlias + "){");
|
113 | this.magicString.append("//*/\n}.call(this,module);");
|
114 | }
|
115 |
|
116 | if (this.modifyAST) {
|
117 | const id = name => ({ type: "Identifier", name });
|
118 |
|
119 | let ast = this.rootPath.getValue();
|
120 | if (ast.type !== "Program") {
|
121 | assert.strictEqual(ast.type, "File");
|
122 | ast = ast.program;
|
123 | }
|
124 |
|
125 | ast.body = [{
|
126 | type: "ExpressionStatement",
|
127 | expression: {
|
128 | type: "UnaryExpression",
|
129 | operator: "!",
|
130 | argument: {
|
131 | type: "CallExpression",
|
132 | callee: {
|
133 | type: "MemberExpression",
|
134 | object: {
|
135 | type: "FunctionExpression",
|
136 | id: null,
|
137 | params: [id(this.moduleAlias)],
|
138 | body: {
|
139 | type: "BlockStatement",
|
140 | body: ast.body
|
141 | }
|
142 | },
|
143 | property: id("call")
|
144 | },
|
145 | arguments: [
|
146 | { type: "ThisExpression" },
|
147 | id("module"),
|
148 | ]
|
149 | }
|
150 | }
|
151 | }];
|
152 | }
|
153 | }
|
154 | }
|
155 |
|
156 | reset(rootPath, codeOrNull, options) {
|
157 | if (typeof codeOrNull === "string") {
|
158 | this.code = codeOrNull;
|
159 | this.magicString = new MagicString(codeOrNull);
|
160 | } else {
|
161 | this.code = this.magicString = null;
|
162 | }
|
163 |
|
164 | this.rootPath = rootPath;
|
165 | this.bodyInfos = [];
|
166 | this.enforceStrictMode = getOption(options, "enforceStrictMode");
|
167 | this.exportedLocalNames = Object.create(null);
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 | this.avoidModernSyntax = !! getOption(options, "avoidModernSyntax");
|
175 | if (this.avoidModernSyntax) {
|
176 | this.generateLetDeclarations = false;
|
177 | } else {
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 | this.generateLetDeclarations =
|
184 | !! getOption(options, "generateLetDeclarations");
|
185 | }
|
186 |
|
187 |
|
188 |
|
189 | this.finalCompilationPass = getOption(options, "finalCompilationPass");
|
190 |
|
191 | this.madeChanges = false;
|
192 | this.modifyAST = !! getOption(options, "ast");
|
193 | this.nextKey = 0;
|
194 | this.parse = getOption(options, "parse");
|
195 | this.removals = [];
|
196 | this.sourceType = getOption(options, "sourceType");
|
197 | this.moduleAlias = getOption(options, "moduleAlias");
|
198 | this.possibleIndexes = getOption(options, "possibleIndexes");
|
199 | const di = this.dynamicImport = getOption(options, "dynamicImport");
|
200 | if (di && typeof di !== "string") {
|
201 |
|
202 |
|
203 | this.dynamicImport = "dynamicImport";
|
204 | }
|
205 | }
|
206 |
|
207 | visitProgram(path) {
|
208 | this.visitChildren(path);
|
209 | const program = path.getNode();
|
210 | if (program.body.length) {
|
211 | path.call(
|
212 | firstStmtPath => getBlockBodyInfo(this, firstStmtPath),
|
213 | "body", 0
|
214 | );
|
215 | } else {
|
216 | getBlockBodyInfo(this, path);
|
217 | }
|
218 | }
|
219 |
|
220 | visitImportDeclaration(path) {
|
221 | const decl = path.getValue();
|
222 | const specifierCount = decl.specifiers.length;
|
223 | let hoistedCode = "";
|
224 |
|
225 | if (specifierCount) {
|
226 | const identifiers = [];
|
227 | for (let i = 0; i < specifierCount; ++i) {
|
228 | identifiers.push(decl.specifiers[i].local.name);
|
229 | }
|
230 |
|
231 | const identifierCount = identifiers.length;
|
232 | if (identifierCount) {
|
233 | const lastIndex = identifierCount - 1;
|
234 | hoistedCode += this.generateLetDeclarations ? "let " : "var ";
|
235 |
|
236 | for (let i = 0; i < identifierCount; ++i) {
|
237 | const isLast = i === lastIndex;
|
238 | hoistedCode +=
|
239 | identifiers[i] +
|
240 | (isLast ? ";" : ",");
|
241 | }
|
242 | }
|
243 | }
|
244 |
|
245 | hoistedCode += toModuleImport(
|
246 | this,
|
247 | getSourceString(this, decl),
|
248 | computeSpecifierMap(decl.specifiers)
|
249 | );
|
250 |
|
251 | hoistImports(this, path, hoistedCode);
|
252 | }
|
253 |
|
254 | visitImport(path) {
|
255 | if (this.dynamicImport) {
|
256 | const importCallee = path.getValue();
|
257 | const replacement = this.moduleAlias + "." + this.dynamicImport;
|
258 | overwrite(this, importCallee.start, importCallee.end, replacement);
|
259 | if (this.modifyAST) {
|
260 | path.replace({
|
261 | type: "MemberExpression",
|
262 | object: {
|
263 | type: "Identifier",
|
264 | name: this.moduleAlias
|
265 | },
|
266 | property: {
|
267 | type: "Identifier",
|
268 | name: this.dynamicImport
|
269 | },
|
270 | computed: false
|
271 | });
|
272 | }
|
273 | this.madeChanges = true;
|
274 | }
|
275 | }
|
276 |
|
277 | visitExportAllDeclaration(path) {
|
278 | const decl = path.getValue();
|
279 | const hoistedCode = pad(
|
280 | this,
|
281 | this.moduleAlias + ".link(" + getSourceString(this, decl),
|
282 | decl.start,
|
283 | decl.source.start
|
284 | ) + pad(
|
285 | this,
|
286 | ',{"*":"*"},' + makeUniqueKey(this) + ");",
|
287 | decl.source.end,
|
288 | decl.end
|
289 | );
|
290 |
|
291 |
|
292 |
|
293 |
|
294 | hoistImports(this, path, hoistedCode);
|
295 | }
|
296 |
|
297 | visitExportDefaultDeclaration(path) {
|
298 | const decl = path.getValue();
|
299 | const dd = decl.declaration;
|
300 |
|
301 | if (dd.id && (dd.type === "FunctionDeclaration" ||
|
302 | dd.type === "FunctionExpression" ||
|
303 | dd.type === "ClassDeclaration")) {
|
304 |
|
305 |
|
306 |
|
307 |
|
308 | if (this.modifyAST && dd.type === "FunctionExpression") {
|
309 | dd.type = "FunctionDeclaration";
|
310 | }
|
311 |
|
312 | hoistExports(this, path, {
|
313 | "default": [dd.id.name]
|
314 | }, "declaration");
|
315 |
|
316 | } else {
|
317 |
|
318 |
|
319 |
|
320 | path.call(this.visitWithoutReset, "declaration");
|
321 | assert.strictEqual(decl.declaration, dd);
|
322 |
|
323 | let prefix = this.moduleAlias + ".exportDefault(";
|
324 | let suffix = ");";
|
325 |
|
326 | if (dd.type === "SequenceExpression") {
|
327 |
|
328 |
|
329 |
|
330 |
|
331 |
|
332 |
|
333 | prefix += "(";
|
334 | suffix = ")" + suffix;
|
335 | }
|
336 |
|
337 | overwrite(this, decl.start, dd.start, prefix);
|
338 | overwrite(this, dd.end, decl.end, suffix, true);
|
339 |
|
340 | if (this.modifyAST) {
|
341 |
|
342 |
|
343 |
|
344 | if (dd.type === "FunctionDeclaration") {
|
345 | dd.type = "FunctionExpression";
|
346 | } else if (dd.type === "ClassDeclaration") {
|
347 | dd.type = "ClassExpression";
|
348 | }
|
349 |
|
350 |
|
351 |
|
352 | let ast = this.parse(this.moduleAlias + ".exportDefault(ARG);");
|
353 | if (ast.type === "File") ast = ast.program;
|
354 | assert.strictEqual(ast.type, "Program");
|
355 |
|
356 | const callExprStmt = ast.body[0];
|
357 | assert.strictEqual(callExprStmt.type, "ExpressionStatement");
|
358 |
|
359 | const callExpr = callExprStmt.expression;
|
360 | assert.strictEqual(callExpr.type, "CallExpression");
|
361 |
|
362 |
|
363 | callExpr.arguments[0] = dd;
|
364 |
|
365 | path.replace(callExprStmt);
|
366 | }
|
367 |
|
368 | this.madeChanges = true;
|
369 | }
|
370 | }
|
371 |
|
372 | visitExportNamedDeclaration(path) {
|
373 | const decl = path.getValue();
|
374 | const dd = decl.declaration;
|
375 |
|
376 | if (dd) {
|
377 | const specifierMap = Object.create(null);
|
378 | const type = dd.type;
|
379 |
|
380 | if (dd.id && (type === "ClassDeclaration" ||
|
381 | type === "FunctionExpression" ||
|
382 | type === "FunctionDeclaration")) {
|
383 | addNameToMap(specifierMap, dd.id.name);
|
384 | } else if (type === "VariableDeclaration") {
|
385 | const ddCount = dd.declarations.length;
|
386 |
|
387 | for (let i = 0; i < ddCount; ++i) {
|
388 | const names = utils.getNamesFromPattern(dd.declarations[i].id);
|
389 | const nameCount = names.length;
|
390 |
|
391 | for (let j = 0; j < nameCount; ++j) {
|
392 | addNameToMap(specifierMap, names[j]);
|
393 | }
|
394 | }
|
395 | }
|
396 |
|
397 | hoistExports(this, path, specifierMap, "declaration");
|
398 |
|
399 | if (canExportedValuesChange(decl)) {
|
400 |
|
401 |
|
402 |
|
403 |
|
404 | addExportedLocalNames(this, specifierMap);
|
405 | }
|
406 |
|
407 | return;
|
408 | }
|
409 |
|
410 | if (decl.specifiers) {
|
411 | let specifierMap = computeSpecifierMap(decl.specifiers);
|
412 |
|
413 | if (decl.source) {
|
414 | if (specifierMap) {
|
415 | const newMap = Object.create(null);
|
416 | const keys = Object.keys(specifierMap);
|
417 | const keyCount = keys.length;
|
418 |
|
419 | for (let i = 0; i < keyCount; ++i) {
|
420 | const exported = keys[i];
|
421 | const locals = specifierMap[exported];
|
422 | const localCount = locals.length;
|
423 |
|
424 | for (let j = 0; j < localCount; ++j) {
|
425 | addToSpecifierMap(newMap, locals[j], "exports." + exported);
|
426 | }
|
427 | }
|
428 |
|
429 | specifierMap = newMap;
|
430 | }
|
431 |
|
432 |
|
433 |
|
434 |
|
435 | hoistImports(this, path, toModuleImport(
|
436 | this,
|
437 | getSourceString(this, decl),
|
438 | specifierMap
|
439 | ));
|
440 |
|
441 | } else {
|
442 | hoistExports(this, path, specifierMap);
|
443 | addExportedLocalNames(this, specifierMap);
|
444 | }
|
445 | }
|
446 | }
|
447 | };
|
448 |
|
449 | function addExportedLocalNames(visitor, specifierMap) {
|
450 | const exportedLocalNames = visitor.exportedLocalNames;
|
451 | const keys = Object.keys(specifierMap);
|
452 | const keyCount = keys.length;
|
453 |
|
454 | for (let i = 0; i < keyCount; ++i) {
|
455 | const exported = keys[i];
|
456 | const locals = specifierMap[exported];
|
457 | const localCount = locals.length;
|
458 |
|
459 | for (let j = 0; j < localCount; ++j) {
|
460 |
|
461 |
|
462 |
|
463 |
|
464 |
|
465 | exportedLocalNames[locals[j]] = true;
|
466 | }
|
467 | }
|
468 | }
|
469 |
|
470 | function addNameToMap(map, name) {
|
471 | addToSpecifierMap(map, name, name);
|
472 | }
|
473 |
|
474 | function addToSpecifierMap(map, __ported, local) {
|
475 | assert.strictEqual(typeof __ported, "string");
|
476 | assert.strictEqual(typeof local, "string");
|
477 |
|
478 | const locals = __ported in map ? map[__ported] : [];
|
479 |
|
480 | if (locals.indexOf(local) < 0) {
|
481 | locals.push(local);
|
482 | }
|
483 |
|
484 | map[__ported] = locals;
|
485 |
|
486 | return map;
|
487 | }
|
488 |
|
489 |
|
490 |
|
491 | function computeSpecifierMap(specifiers) {
|
492 | const specifierCount = specifiers.length;
|
493 | const specifierMap = Object.create(null);
|
494 |
|
495 | for (let i = 0; i < specifierCount; ++i) {
|
496 | const s = specifiers[i];
|
497 |
|
498 | const local =
|
499 | s.type === "ExportDefaultSpecifier" ? "default" :
|
500 | s.type === "ExportNamespaceSpecifier" ? "*" :
|
501 | s.local.name;
|
502 |
|
503 | const __ported =
|
504 | s.type === "ImportSpecifier" ? s.imported.name :
|
505 | s.type === "ImportDefaultSpecifier" ? "default" :
|
506 | s.type === "ImportNamespaceSpecifier" ? "*" :
|
507 | (s.type === "ExportSpecifier" ||
|
508 | s.type === "ExportDefaultSpecifier" ||
|
509 | s.type === "ExportNamespaceSpecifier") ? s.exported.name :
|
510 | null;
|
511 |
|
512 | if (typeof local === "string" && typeof __ported === "string") {
|
513 | addToSpecifierMap(specifierMap, __ported, local);
|
514 | }
|
515 | }
|
516 |
|
517 | return specifierMap;
|
518 | }
|
519 |
|
520 | function getBlockBodyInfo(visitor, path) {
|
521 | const node = path.getNode();
|
522 | let parent = path.getParentNode();
|
523 |
|
524 | if (parent === null) {
|
525 | parent = node;
|
526 | }
|
527 |
|
528 | let body = parent.body;
|
529 | let bodyName = "body";
|
530 | let insertCharIndex = node.start;
|
531 | let needToAddUseStrictDirective = false;
|
532 |
|
533 | switch (parent.type) {
|
534 | case "Program":
|
535 | insertCharIndex = parent.start;
|
536 |
|
537 |
|
538 |
|
539 | needToAddUseStrictDirective = visitor.enforceStrictMode;
|
540 | break;
|
541 |
|
542 | case "BlockStatement":
|
543 | insertCharIndex = parent.start + 1;
|
544 | break;
|
545 |
|
546 | case "SwitchCase":
|
547 | body = parent.consequent;
|
548 | bodyName = "consequent";
|
549 | insertCharIndex = body[0].start;
|
550 | break;
|
551 |
|
552 | default:
|
553 | const block = {
|
554 | type: "BlockStatement",
|
555 | body: [],
|
556 | start: node.start,
|
557 | end: node.end + 2
|
558 | };
|
559 |
|
560 | body = block.body;
|
561 | bodyName = path.getName();
|
562 | insertCharIndex = node.start;
|
563 |
|
564 | if (visitor.magicString !== null) {
|
565 | visitor.magicString
|
566 | .appendLeft(insertCharIndex, "{")
|
567 | .prependRight(node.end, "}");
|
568 | }
|
569 |
|
570 | if (visitor.modifyAST) {
|
571 | path.replace(block);
|
572 | }
|
573 | }
|
574 |
|
575 | assert.ok(Array.isArray(body), body);
|
576 |
|
577 |
|
578 |
|
579 |
|
580 | let insertNodeIndex = 0;
|
581 | const stmtCount = body.length;
|
582 |
|
583 | for (let i = 0; i < stmtCount; ++i) {
|
584 | const stmt = body[i];
|
585 | if (stmt.type === "ExpressionStatement") {
|
586 | const expr = stmt.expression;
|
587 | if (expr.type === "StringLiteral" ||
|
588 | (expr.type === "Literal" &&
|
589 | typeof expr.value === "string")) {
|
590 | insertCharIndex = stmt.end;
|
591 | insertNodeIndex = i + 1;
|
592 | if (expr.value === "use strict") {
|
593 |
|
594 |
|
595 | needToAddUseStrictDirective = false;
|
596 | }
|
597 | continue;
|
598 | }
|
599 | }
|
600 | break;
|
601 | }
|
602 |
|
603 |
|
604 |
|
605 | const directives = parent.directives;
|
606 | const directiveCount = directives ? directives.length : 0;
|
607 |
|
608 | for (let i = 0; i < directiveCount; ++i) {
|
609 | const d = directives[i];
|
610 | insertCharIndex = Math.max(d.end, insertCharIndex);
|
611 | if (d.value.value === "use strict") {
|
612 |
|
613 |
|
614 | needToAddUseStrictDirective = false;
|
615 | }
|
616 | }
|
617 |
|
618 | let bibn = parent._bodyInfoByName;
|
619 | if (bibn === void 0) {
|
620 | bibn = parent._bodyInfoByName = Object.create(null);
|
621 | }
|
622 |
|
623 | let bodyInfo = bibn[bodyName];
|
624 | if (bodyInfo === void 0) {
|
625 | bodyInfo = bibn[bodyName] = Object.create(null);
|
626 | bodyInfo.body = body;
|
627 | bodyInfo.parent = parent;
|
628 | bodyInfo.insertCharIndex = insertCharIndex;
|
629 | bodyInfo.insertNodeIndex = insertNodeIndex;
|
630 | bodyInfo.hoistedExportsMap = Object.create(null);
|
631 | bodyInfo.hoistedConstExportsMap = Object.create(null);
|
632 | bodyInfo.hoistedExportsString = "";
|
633 | bodyInfo.hoistedImportsString = "";
|
634 | } else {
|
635 | assert.strictEqual(bodyInfo.body, body);
|
636 | }
|
637 |
|
638 | if (visitor.bodyInfos.indexOf(bodyInfo) < 0) {
|
639 | visitor.bodyInfos.push(bodyInfo);
|
640 | }
|
641 |
|
642 | if (needToAddUseStrictDirective) {
|
643 | bodyInfo.needToAddUseStrictDirective = needToAddUseStrictDirective;
|
644 | }
|
645 |
|
646 | return bodyInfo;
|
647 | }
|
648 |
|
649 |
|
650 |
|
651 | function getSourceString(visitor, decl) {
|
652 | const code = visitor.code;
|
653 | if (code) {
|
654 | return code.slice(
|
655 | decl.source.start,
|
656 | decl.source.end
|
657 | );
|
658 | }
|
659 |
|
660 | assert.strictEqual(typeof decl.source.value, "string");
|
661 |
|
662 | return JSON.stringify(decl.source.value);
|
663 | }
|
664 |
|
665 | function hoistImports(visitor, importDeclPath, hoistedCode) {
|
666 | preserveLine(visitor, importDeclPath);
|
667 | const bodyInfo = getBlockBodyInfo(visitor, importDeclPath);
|
668 | bodyInfo.hoistedImportsString += hoistedCode;
|
669 | visitor.madeChanges = true;
|
670 | }
|
671 |
|
672 | function hoistExports(visitor, exportDeclPath, mapOrString, childName) {
|
673 | if (childName) {
|
674 | preserveChild(visitor, exportDeclPath, childName);
|
675 | } else {
|
676 | preserveLine(visitor, exportDeclPath);
|
677 | }
|
678 |
|
679 | const bodyInfo = getBlockBodyInfo(visitor, exportDeclPath);
|
680 | const constant = ! canExportedValuesChange(exportDeclPath.getValue());
|
681 |
|
682 | if (typeof mapOrString !== "string") {
|
683 | const keys = Object.keys(mapOrString);
|
684 | const keyCount = keys.length;
|
685 |
|
686 | for (let i = 0; i < keyCount; ++i) {
|
687 | const exported = keys[i];
|
688 | const locals = mapOrString[exported];
|
689 | const localCount = locals.length;
|
690 |
|
691 | for (let j = 0; j < localCount; ++j) {
|
692 | addToSpecifierMap(
|
693 | constant
|
694 | ? bodyInfo.hoistedConstExportsMap
|
695 | : bodyInfo.hoistedExportsMap,
|
696 | exported,
|
697 | locals[j]
|
698 | );
|
699 | }
|
700 | }
|
701 |
|
702 | } else {
|
703 | bodyInfo.hoistedExportsString += mapOrString;
|
704 | }
|
705 |
|
706 | visitor.madeChanges = true;
|
707 | }
|
708 |
|
709 | function canExportedValuesChange(exportDecl) {
|
710 | if (exportDecl) {
|
711 | if (exportDecl.type === "ExportDefaultDeclaration") {
|
712 | const dd = exportDecl.declaration;
|
713 | return (dd.type === "FunctionDeclaration" ||
|
714 | dd.type === "FunctionExpression" ||
|
715 | dd.type === "ClassDeclaration");
|
716 | }
|
717 |
|
718 | if (exportDecl.type === "ExportNamedDeclaration") {
|
719 | const dd = exportDecl.declaration;
|
720 | if (dd &&
|
721 | dd.type === "VariableDeclaration" &&
|
722 | dd.kind === "const") {
|
723 | return false;
|
724 | }
|
725 | }
|
726 | }
|
727 |
|
728 | return true;
|
729 | }
|
730 |
|
731 | function makeUniqueKey(visitor) {
|
732 | return visitor.nextKey++;
|
733 | }
|
734 |
|
735 | function overwrite(visitor, oldStart, oldEnd, newCode, trailing) {
|
736 | if (! visitor.code) {
|
737 | return;
|
738 | }
|
739 |
|
740 | assert.strictEqual(typeof oldStart, "number");
|
741 | assert.strictEqual(typeof oldEnd, "number");
|
742 | assert.strictEqual(typeof newCode, "string");
|
743 |
|
744 | const padded = pad(visitor, newCode, oldStart, oldEnd);
|
745 |
|
746 | if (oldStart === oldEnd) {
|
747 | if (padded === "") {
|
748 | return;
|
749 | }
|
750 |
|
751 | if (trailing) {
|
752 | visitor.magicString.appendLeft(oldStart, padded);
|
753 | } else {
|
754 | visitor.magicString.prependRight(oldStart, padded);
|
755 | }
|
756 |
|
757 | } else {
|
758 | visitor.magicString.overwrite(oldStart, oldEnd, padded);
|
759 | }
|
760 | }
|
761 |
|
762 | function pad(visitor, newCode, oldStart, oldEnd) {
|
763 | const code = visitor.code;
|
764 | if (code) {
|
765 | const oldLines = code.slice(oldStart, oldEnd).split("\n");
|
766 | const oldLineCount = oldLines.length;
|
767 | const newLines = newCode.split("\n");
|
768 | const lastIndex = newLines.length - 1;
|
769 |
|
770 | for (let i = lastIndex; i < oldLineCount; ++i) {
|
771 | const oldLine = oldLines[i];
|
772 | const lastCharCode = oldLine.charCodeAt(oldLine.length - 1);
|
773 | if (i > lastIndex) {
|
774 | newLines[i] = "";
|
775 | }
|
776 | if (lastCharCode === codeOfCR) {
|
777 | newLines[i] += "\r";
|
778 | }
|
779 | }
|
780 |
|
781 | newCode = newLines.join("\n");
|
782 | }
|
783 |
|
784 | return newCode;
|
785 | }
|
786 |
|
787 | function preserveChild(visitor, path, childName) {
|
788 | const value = path.getValue();
|
789 | const child = value ? value[childName] : null;
|
790 |
|
791 | if (child && visitor.code) {
|
792 | overwrite(
|
793 | visitor,
|
794 | value.start,
|
795 | child.start,
|
796 | ""
|
797 | );
|
798 | overwrite(
|
799 | visitor,
|
800 | child.end,
|
801 | value.end,
|
802 | ""
|
803 | );
|
804 | }
|
805 |
|
806 | path.call(visitor.visitWithoutReset, childName);
|
807 |
|
808 | if (visitor.modifyAST) {
|
809 |
|
810 | path.replace(child);
|
811 | }
|
812 | }
|
813 |
|
814 | function preserveLine(visitor, path) {
|
815 | const value = path.getValue();
|
816 |
|
817 | if (visitor.code) {
|
818 | overwrite(visitor, value.start, value.end, "");
|
819 | }
|
820 |
|
821 | if (visitor.modifyAST) {
|
822 | visitor.removals.push({
|
823 | container: path.getContainer(),
|
824 | name: path.getName(),
|
825 | value
|
826 | });
|
827 | }
|
828 | }
|
829 |
|
830 | function processRemoval(removal) {
|
831 | if (Array.isArray(removal.container)) {
|
832 | const index = removal.container.indexOf(removal.value);
|
833 | if (index >= 0) {
|
834 | removal.container.splice(index, 1);
|
835 | }
|
836 |
|
837 | } else if (removal.value ===
|
838 | removal.container[removal.name]) {
|
839 |
|
840 | removal.container[removal.name] = null;
|
841 |
|
842 | } else {
|
843 | const newValue = removal.container[removal.name];
|
844 |
|
845 | if (newValue.type === "BlockStatement") {
|
846 |
|
847 |
|
848 |
|
849 | processRemoval({
|
850 | container: newValue.body,
|
851 | value: removal.value
|
852 | });
|
853 | }
|
854 | }
|
855 | }
|
856 |
|
857 | function safeKey(key) {
|
858 | if (/^[_$a-zA-Z]\w*$/.test(key)) {
|
859 | return key;
|
860 | }
|
861 | return JSON.stringify(key);
|
862 | }
|
863 |
|
864 | function safeParam(param, locals) {
|
865 | if (locals.indexOf(param) < 0) {
|
866 | return param;
|
867 | }
|
868 | return safeParam("_" + param, locals);
|
869 | }
|
870 |
|
871 | function toModuleImport(visitor, source, specifierMap) {
|
872 | let code = visitor.moduleAlias + ".link(" + source;
|
873 | const importedNames = Object.keys(specifierMap);
|
874 | const nameCount = importedNames.length;
|
875 |
|
876 | if (! nameCount) {
|
877 | code += ");";
|
878 | return code;
|
879 | }
|
880 |
|
881 | const lastIndex = nameCount - 1;
|
882 | code += ",{";
|
883 |
|
884 | const properties = [];
|
885 |
|
886 | for (let i = 0; i < nameCount; ++i) {
|
887 | const imported = importedNames[i];
|
888 | const reexports = [];
|
889 | const locals = [];
|
890 |
|
891 | specifierMap[imported].forEach(local => {
|
892 | if (local.startsWith("exports.")) {
|
893 | var parts = local.split(".");
|
894 | parts.shift();
|
895 | reexports.push(parts.join("."));
|
896 | } else {
|
897 | locals.push(local);
|
898 | }
|
899 | });
|
900 |
|
901 | if (locals.length > 0) {
|
902 | let property = safeKey(imported);
|
903 |
|
904 | if (visitor.avoidModernSyntax) {
|
905 | property += ":function";
|
906 | }
|
907 |
|
908 | const valueParam = safeParam("v", locals);
|
909 | property += "(" + valueParam + "){";
|
910 |
|
911 | property += locals.join("=") + "=" + valueParam,
|
912 | property += "}"
|
913 |
|
914 | properties.push(property);
|
915 | }
|
916 |
|
917 | if (reexports.length > 0) {
|
918 | properties.push(
|
919 | safeKey(imported) + ":" + JSON.stringify(
|
920 |
|
921 | reexports.length === 1 ? reexports[0] : reexports
|
922 | )
|
923 | );
|
924 | }
|
925 | }
|
926 |
|
927 | code += properties.join(",") + "}," + makeUniqueKey(visitor) + ");";
|
928 |
|
929 | return code;
|
930 | }
|
931 |
|
932 | function toModuleExport(visitor, specifierMap, constant) {
|
933 | let code = "";
|
934 | const exportedKeys = Object.keys(specifierMap);
|
935 | const keyCount = exportedKeys.length;
|
936 |
|
937 | if (! keyCount) {
|
938 | return code;
|
939 | }
|
940 |
|
941 | const lastIndex = keyCount - 1;
|
942 | code += visitor.moduleAlias + ".export({";
|
943 |
|
944 | for (let i = 0; i < keyCount; ++i) {
|
945 | const exported = exportedKeys[i];
|
946 | const isLast = i === lastIndex;
|
947 | const locals = specifierMap[exported];
|
948 |
|
949 | assert.strictEqual(locals.length, 1);
|
950 |
|
951 | if (visitor.avoidModernSyntax) {
|
952 | code += exported + ":function(){return " + locals[0] + "}";
|
953 | } else {
|
954 | code += exported + ":()=>" + locals[0];
|
955 | }
|
956 |
|
957 | if (! isLast) {
|
958 | code += ",";
|
959 | }
|
960 | }
|
961 |
|
962 |
|
963 |
|
964 | code += constant ? "},true);" : "});";
|
965 |
|
966 | return code;
|
967 | }
|
968 |
|
969 | module.exports = ImportExportVisitor;
|