rewrite updates
MODIFY
src/babelPlugin.js
src/babelPlugin.js
import * as h from './hidden';
-export default options =>
- function rewriteModules({ types: t }) {
- const {
- fixedExportMap,
- imports,
- importDecls,
- importSources,
- liveExportMap,
- } = options;
- const hiddenIdentifier = hi => {
- const ident = t.identifier(hi);
- ident.allowedInternalHidden = true;
- return ident;
- };
- const updaterSources = {};
- const visitor = {
- Identifier(path) {
- if (options.allowHidden || path.node.allowedInternalHidden) {
- return;
- }
- // Ensure the parse doesn't already include our required hidden symbols.
- // console.log(`have identifier`, path.node);
- const i = h.HIDDEN_SYMBOLS.indexOf(path.node.name);
- if (i >= 0) {
- throw path.buildCodeFrameError(
- `The ${h.HIDDEN_SYMBOLS[i]} identifier is reserved`,
- );
- }
- },
- CallExpression(path) {
- // import(FOO) -> $h_import(FOO)
- if (path.node.callee.type === 'Import') {
- path.node.callee = hiddenIdentifier(h.HIDDEN_IMPORT);
- }
- },
- };
-
- // We handle all the import and export productions.
- const moduleVisitor = {
- ImportDeclaration(path) {
- const specs = path.node.specifiers;
- const specifier = path.node.source.value;
- let myImportSources = importSources[specifier];
- if (!myImportSources) {
- myImportSources = [];
- importSources[specifier] = myImportSources;
- }
- let myImports = imports[specifier];
- if (!myImports) {
- myImports = [];
- imports[specifier] = myImports;
- }
- if (!specs) {
- return;
- }
- specs.forEach(spec => {
- const importTo = spec.local.name;
- importDecls.push(importTo);
- let importFrom;
- switch (spec.type) {
- // import importTo from 'module';
- case 'ImportDefaultSpecifier':
- importFrom = 'default';
- break;
- // import * as importTo from 'module';
- case 'ImportNamespaceSpecifier':
- importFrom = '*';
- break;
- // import { importFrom as importTo } from 'module';
- case 'ImportSpecifier':
- importFrom = spec.imported.name;
- break;
- default:
- throw path.buildCodeFrameError(
- `Unrecognized import specifier type ${spec.type}`,
- );
- }
- if (myImports.indexOf(importFrom) < 0) {
- myImports.push(importFrom);
- }
-
- let myUpdaterSources = myImportSources[importFrom];
- if (!myUpdaterSources) {
- myUpdaterSources = [];
- myImportSources[importFrom] = myUpdaterSources;
- }
-
- myUpdaterSources.push(
- `${h.HIDDEN_A} => (${importTo} = ${h.HIDDEN_A})`,
- );
- updaterSources[importTo] = myUpdaterSources;
- });
- // Nullify the import declaration.
- path.replaceWithMultiple([]);
- },
- ExportDefaultDeclaration(path) {
- // export default FOO -> $h_once.default(FOO)
- fixedExportMap.default = [];
- const callee = t.memberExpression(
- hiddenIdentifier(h.HIDDEN_ONCE),
- t.identifier('default'),
- );
- path.replaceWith(t.callExpression(callee, [path.node.declaration]));
- },
- ExportNamedDeclaration(path) {
- const { declaration: decl, specifiers: specs, source } = path.node;
- const replace = [];
- if (decl) {
// export const pattern = ...;
- const collectPatternIdentifiers = pattern => {
+const collectPatternIdentifiers = (path, pattern) => {
switch (pattern.type) {
case 'Identifier':
return [pattern];
case 'RestElement':
- return collectPatternIdentifiers(pattern.argument);
+ return collectPatternIdentifiers(path, pattern.argument);
case 'ObjectProperty':
- return collectPatternIdentifiers(pattern.value);
+ return collectPatternIdentifiers(path, pattern.value);
case 'ObjectPattern':
return pattern.properties.reduce((prior, prop) => {
- prior.push(...collectPatternIdentifiers(prop));
+ prior.push(...collectPatternIdentifiers(path, prop));
return prior;
}, []);
case 'ArrayPattern':
return pattern.elements.reduce((prior, pat) => {
if (pat !== null) {
// Non-elided pattern.
- prior.push(...collectPatternIdentifiers(pat));
+ prior.push(...collectPatternIdentifiers(path, pat));
}
return prior;
}, []);
@@ -137,10 +29,29 @@ export default options =>
}
};
+export default options =>
+ function rewriteModules({ types: t }) {
+ const {
+ fixedExportMap,
+ imports,
+ importDecls,
+ importSources,
+ liveExportMap,
+ } = options;
+ const hiddenIdentifier = hi => {
+ const ident = t.identifier(hi);
+ ident.allowedInternalHidden = true;
+ return ident;
+ };
+ const updaterSources = {};
+ const topLevelDecls = {};
+
+ const rewriteExportDecl = (path, decl) => {
// Find all the declared identifiers.
+ const replace = [];
const vids = (decl.declarations || [decl]).reduce(
(prior, { id: pat }) => {
- prior.push(...collectPatternIdentifiers(pat));
+ prior.push(...collectPatternIdentifiers(path, pat));
return prior;
},
[],
@@ -233,9 +144,171 @@ export default options =>
`Unrecognized rewrite kind ${rewriteKind}`,
);
}
+ return replace;
+ };
+
+ const visitor = {
+ Identifier(path) {
+ if (options.allowHidden || path.node.allowedInternalHidden) {
+ return;
+ }
+ // Ensure the parse doesn't already include our required hidden symbols.
+ // console.log(`have identifier`, path.node);
+ const i = h.HIDDEN_SYMBOLS.indexOf(path.node.name);
+ if (i >= 0) {
+ throw path.buildCodeFrameError(
+ `The ${h.HIDDEN_SYMBOLS[i]} identifier is reserved`,
+ );
+ }
+ },
+ CallExpression(path) {
+ // import(FOO) -> $h_import(FOO)
+ if (path.node.callee.type === 'Import') {
+ path.node.callee = hiddenIdentifier(h.HIDDEN_IMPORT);
+ }
+ },
+ };
+
+ const moduleVisitor = {
+ Program: {
+ exit(_path) {
+ const rewriteMap = new Map();
+ [
+ [fixedExportMap, hiddenIdentifier(h.HIDDEN_ONCE)],
+ [liveExportMap, hiddenIdentifier(h.HIDDEN_LIVE)],
+ ].forEach(([exportMap, exportId]) => {
+ Object.entries(exportMap).forEach(([_exportName, [localName]]) => {
+ // Find all the traversal paths for the local names.
+ // console.error(`adding`, _exportName, localName, topLevelDecls);
+ if (!localName) {
+ return;
+ }
+ const path = topLevelDecls[localName];
+ if (!path) {
+ return;
+ }
+ let rewrites = rewriteMap.get(path);
+ if (!rewrites) {
+ // Ensure the local name will be rewritten.
+ rewrites = new Map();
+ rewriteMap.set(path, rewrites);
}
- replace.push(
- ...specs.reduce((prior, spec) => {
+ rewrites.set(localName, exportId);
+ });
+ });
+ rewriteMap.forEach((rewrites, path) => {
+ // console.log(`rewriteMap`, rewrites, path);
+ const body = [path.node];
+ // Rewrite the traversal paths for the export identifier.
+ rewrites.forEach((exportId, vname) => {
+ // console.error(`replacing`, vname, exportId);
+ const callee = t.memberExpression(exportId, t.identifier(vname));
+ body.push(
+ t.expressionStatement(
+ t.callExpression(callee, [t.identifier(vname)]),
+ ),
+ );
+ });
+ if (path.node.kind === 'const') {
+ path.replaceWithMultiple(body);
+ } else {
+ path.replaceWith(t.blockStatement(body));
+ }
+ });
+ },
+ },
+
+ // We handle all the import and export productions.
+ ImportDeclaration(path) {
+ const specs = path.node.specifiers;
+ const specifier = path.node.source.value;
+ let myImportSources = importSources[specifier];
+ if (!myImportSources) {
+ myImportSources = [];
+ importSources[specifier] = myImportSources;
+ }
+ let myImports = imports[specifier];
+ if (!myImports) {
+ myImports = [];
+ imports[specifier] = myImports;
+ }
+ if (!specs) {
+ return;
+ }
+ specs.forEach(spec => {
+ const importTo = spec.local.name;
+ importDecls.push(importTo);
+ let importFrom;
+ switch (spec.type) {
+ // import importTo from 'module';
+ case 'ImportDefaultSpecifier':
+ importFrom = 'default';
+ break;
+ // import * as importTo from 'module';
+ case 'ImportNamespaceSpecifier':
+ importFrom = '*';
+ break;
+ // import { importFrom as importTo } from 'module';
+ case 'ImportSpecifier':
+ importFrom = spec.imported.name;
+ break;
+ default:
+ throw path.buildCodeFrameError(
+ `Unrecognized import specifier type ${spec.type}`,
+ );
+ }
+ if (myImports.indexOf(importFrom) < 0) {
+ myImports.push(importFrom);
+ }
+
+ let myUpdaterSources = myImportSources[importFrom];
+ if (!myUpdaterSources) {
+ myUpdaterSources = [];
+ myImportSources[importFrom] = myUpdaterSources;
+ }
+
+ myUpdaterSources.push(
+ `${h.HIDDEN_A} => (${importTo} = ${h.HIDDEN_A})`,
+ );
+ updaterSources[importTo] = myUpdaterSources;
+ });
+ // Nullify the import declaration.
+ path.replaceWithMultiple([]);
+ },
+ ExportDefaultDeclaration(path) {
+ // export default FOO -> $h_once.default(FOO)
+ fixedExportMap.default = ['default'];
+ const callee = t.memberExpression(
+ hiddenIdentifier(h.HIDDEN_ONCE),
+ t.identifier('default'),
+ );
+ path.replaceWith(t.callExpression(callee, [path.node.declaration]));
+ },
+ VariableDeclaration(path) {
+ if (
+ path.parent.type !== 'Program' ||
+ path.node.ignoreForModuleTransform
+ ) {
+ return;
+ }
+
+ // We may need to rewrite this topLevelDecl later.
+ const vids = path.node.declarations.reduce((prior, { id: pat }) => {
+ prior.push(...collectPatternIdentifiers(path, pat));
+ return prior;
+ }, []);
+ vids.forEach(vid => {
+ topLevelDecls[vid.name] = path;
+ });
+ },
+ ExportNamedDeclaration(path) {
+ const { declaration: decl, specifiers: specs, source } = path.node;
+ const replace = [];
+ if (decl) {
+ decl.ignoreForModuleTransform = true;
+ replace.push(...rewriteExportDecl(path, decl));
+ }
+ specs.forEach(spec => {
const { local, exported } = spec;
// If local.name is reexported we omit it.
const myUpdaterSources = updaterSources[local.name];
@@ -247,22 +320,10 @@ export default options =>
// If it was imported directly (i.e. has a source or updaters)
// then don't put the local name in the liveExportMap.
- liveExportMap[exported.name] =
- source || myUpdaterSources ? [] : [local.name];
- if (!source && !myUpdaterSources) {
- const callee = t.memberExpression(
- hiddenIdentifier(h.HIDDEN_LIVE),
- local,
- );
- prior.push(
- t.expressionStatement(t.callExpression(callee, [local])),
- );
- }
- return prior;
- }, []),
- // and don't evaluate to anything.
- t.expressionStatement(t.identifier('undefined')),
- );
+ liveExportMap[exported.name] = source ? [] : [local.name];
+ // source || myUpdaterSources ? [] : [local.name];
+ });
+
path.replaceWithMultiple(replace);
},
};

rewrite updates