import: fix functor generation wrt import
MODIFY
src/babelPlugin.js
src/babelPlugin.js
@@ -2,13 +2,19 @@ import * as h from './hidden';
export default options =>
function rewriteModules({ types: t }) {
- const { fixedExports, imports, importDecls, liveExportMap } = options;
+ const {
+ fixedExports,
+ imports,
+ importDecls,
+ importSources,
+ liveExportMap,
+ } = options;
const hiddenIdentifier = hi => {
const ident = t.identifier(hi);
ident.allowedInternalHidden = true;
return ident;
};
- const updaters = {};
+ const updaterSources = {};
const visitor = {
Identifier(path) {
if (options.allowHidden || path.node.allowedInternalHidden) {
@@ -35,8 +41,14 @@ export default options =>
const moduleVisitor = {
ImportDeclaration(path) {
const specs = path.node.specifiers;
- const myImports = [];
- imports[path.node.source.value] = myImports;
+ const myImportSources = [];
+ const specifier = path.node.source.value;
+ importSources[specifier] = myImportSources;
+ let myImports = imports[specifier];
+ if (!myImports) {
+ myImports = [];
+ imports[specifier] = myImports;
+ }
if (!specs) {
return;
}
@@ -45,12 +57,15 @@ export default options =>
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;
@@ -59,14 +74,23 @@ export default options =>
`Unrecognized import specifier type ${spec.type}`,
);
}
+ if (myImports.indexOf(importFrom) < 0) {
+ myImports.push(importFrom);
+ }
- const myUpdaters = myImports[importFrom] || [];
- if (myUpdaters.length === 0) {
- myImports[importFrom] = myUpdaters;
+ let myUpdaterSources = myImportSources[importFrom];
+ if (!myUpdaterSources) {
+ myUpdaterSources = [];
+ myImportSources[importFrom] = myUpdaterSources;
}
- myUpdaters.push(`${h.HIDDEN_A} => (${importTo} = ${h.HIDDEN_A})`);
- updaters[importTo] = myUpdaters;
+
+ 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)
@@ -82,6 +106,7 @@ export default options =>
const specs = path.node.specifiers;
const replace = [];
if (decl) {
+ // export const pattern = ...;
const collectPatternIdentifiers = pattern => {
switch (pattern.type) {
case 'Identifier':
@@ -211,16 +236,16 @@ export default options =>
...specs.map(spec => {
const { local, exported, source } = spec;
// If local.name is reexported we omit it.
- const myUpdaters = updaters[local.name];
- if (myUpdaters) {
+ const myUpdaterSources = updaterSources[local.name];
+ if (myUpdaterSources) {
// If there are updaters, we must have a local
// name, so update it with this export.
- myUpdaters.push(`${h.HIDDEN_LIVE}.${local.name}`);
+ myUpdaterSources.push(`${h.HIDDEN_LIVE}.${local.name}`);
}
// 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 || myUpdaters ? [] : [local.name];
+ source || myUpdaterSources ? [] : [local.name];
const callee = t.memberExpression(
hiddenIdentifier(h.HIDDEN_LIVE),
exported,

MODIFY
src/index.js
src/index.js
@@ -29,8 +29,15 @@ const makeModuleTransformer = (babelCore, makeImporter) => {
// Transform the Module source code.
const sourceOptions = {
sourceType: 'module',
+ // exportNames of variables that are only initialized and used, but
+ // never assigned to. The exportName 'default' has no localName.
fixedExports: [],
+ // Record of imported module specifier names to list of importNames.
+ // The importName '*' is that module's module namespace object.
imports: {},
+ // exportNames of variables that are assigned to, or reexported and
+ // therefore assumed live. A reexported variable might not have any
+ // localName.
liveExportMap: {},
hoistedDecls: [],
importSources: {},
@@ -47,9 +54,9 @@ const makeModuleTransformer = (babelCore, makeImporter) => {
preamble += `${h.HIDDEN_IMPORTS}({${Object.keys(isrc)
.map(
src =>
- `${js(src)}: ${Object.entries(isrc[src])
+ `${js(src)}:{${Object.entries(isrc[src])
.map(([exp, upds]) => `${js(exp)}: [${upds.join(',')}]`)
- .join(',')}`,
+ .join(',')}}`,
)
.join(',')}});`;
preamble += sourceOptions.hoistedDecls
@@ -64,7 +71,11 @@ const makeModuleTransformer = (babelCore, makeImporter) => {
// We use destructuring parameters, so 'use strict' is not allowed
// but the function actually is strict.
const functorSource = `\
-(({${h.HIDDEN_IMPORT}, ${h.HIDDEN_IMPORTS}, ${h.HIDDEN_ONCE}, ${h.HIDDEN_LIVE}}) => { \
+(({ \
+ imports: ${h.HIDDEN_IMPORTS}, \
+ constVar: ${h.HIDDEN_ONCE}, \
+ letVar: ${h.HIDDEN_LIVE}, \
+ }) => { \
${preamble} \
${scriptSource}
})`;

ADD
test/test-import.js
+import { test } from 'tape-promise/tape';
+import { makeEvaluators } from '@agoric/evaluate';
+
+import * as babelCore from '@babel/core';
+
+import makeModuleTransformer from '../src/index';
+
+const makeMakeImporter = () => (
+ srcSpec,
+ createStaticRecord,
+ evaluateProgram,
+) => {
+ const { spec, source } = srcSpec;
+ let actualSource;
+ const doImport = async () => {
+ const staticRecord = createStaticRecord(actualSource);
+ const ret = { staticRecord };
+ const functorArg = {
+ imports(imp) {
+ ret.imports = imp;
+ },
+ };
+ // console.log(staticRecord.functorSource);
+ evaluateProgram(staticRecord.functorSource)(functorArg);
+ return ret;
+ };
+
+ if (spec === undefined && source !== undefined) {
+ actualSource = source;
+ return doImport;
+ }
+
+ throw Error(`Not expecting import expression`);
+};
+
+test('import', async t => {
+ try {
+ const makeImporter = makeMakeImporter();
+ const transforms = [makeModuleTransformer(babelCore, makeImporter)];
+ const { evaluateModule } = makeEvaluators({
+ transforms,
+ });
+
+ const srcNS = `import * as ns from 'module';`;
+ const importNS = await evaluateModule(srcNS);
+ const fsrcNS = importNS.staticRecord.functorSource;
+ t.equal(typeof fsrcNS, 'string', 'namespace functor source is string');
+ t.doesNotThrow(() => {
+ importNS.imports.module['*'].forEach(updater => updater(123));
+ }, 'import namespace works');
+ t.deepEquals(
+ importNS.staticRecord,
+ {
+ moduleSource: srcNS,
+ imports: { module: ['*'] },
+ liveExportMap: {},
+ fixedExports: [],
+ functorSource: fsrcNS,
+ },
+ 'namespace static record',
+ );
+
+ const srcNames = `import { foo, bar } from 'module';`;
+ const importNames = await evaluateModule(srcNames);
+ const fsrcNames = importNames.staticRecord.functorSource;
+ t.equal(typeof fsrcNames, 'string', 'names functor source is string');
+ t.doesNotThrow(() => {
+ importNames.imports.module.foo.forEach(updater => updater(123));
+ }, 'import foo works');
+ t.doesNotThrow(() => {
+ importNames.imports.module.bar.forEach(updater => updater(123));
+ }, 'import bar works');
+ t.deepEquals(
+ importNames.staticRecord,
+ {
+ moduleSource: srcNames,
+ imports: { module: ['foo', 'bar'] },
+ liveExportMap: {},
+ fixedExports: [],
+ functorSource: fsrcNames,
+ },
+ 'names static record',
+ );
+
+ const srcDefault = `import myName from 'module';`;
+ const importDefault = await evaluateModule(srcDefault);
+ const fsrcDefault = importDefault.staticRecord.functorSource;
+ t.equal(typeof fsrcDefault, 'string', 'default functor source is string');
+ t.doesNotThrow(() => {
+ importDefault.imports.module.default.forEach(updater => updater(123));
+ }, 'import default works');
+ t.deepEquals(
+ importDefault.staticRecord,
+ {
+ moduleSource: srcDefault,
+ imports: { module: ['default'] },
+ liveExportMap: {},
+ fixedExports: [],
+ functorSource: fsrcDefault,
+ },
+ 'default static record',
+ );
+
+ const importDefaultAndNamed = await evaluateModule(`\
+import myName, { otherName as other } from 'module';
+`);
+ t.doesNotThrow(() => {
+ importDefaultAndNamed.imports.module.default.forEach(upd => upd({}));
+ importDefaultAndNamed.imports.module.otherName.forEach(upd => upd('def'));
+ }, 'import default and named works');
+
+ const importNothing = await evaluateModule(`\
+import 'module';
+`);
+ t.deepEquals(importNothing.imports, { module: {} }, 'import nothing works');
+ } catch (e) {
+ t.isNot(e, e, 'unexpected exception');
+ } finally {
+ t.end();
+ }
+});

import: fix functor generation wrt import