diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index bb4fede3..ff48b59f 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node-version: [10.x, 12.x, 14.x, 15.x, 16.x] + node-version: [14.x, 16.x, 18.x, 20.x, 22.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/lib/import-export-visitor.js b/lib/import-export-visitor.js index 8e2a2e98..65ae3a66 100644 --- a/lib/import-export-visitor.js +++ b/lib/import-export-visitor.js @@ -11,6 +11,13 @@ const codeOfCR = "\r".charCodeAt(0); const codeOfDoubleQuote = '"'.charCodeAt(0); const codeOfSingleQuote = "'".charCodeAt(0); +class AwaitDetector extends Visitor { + foundAwait = false; + visitAwaitExpression() { + this.foundAwait = true; + } +} + class ImportExportVisitor extends Visitor { finalizeHoisting() { const infoCount = this.bodyInfos.length; @@ -388,19 +395,41 @@ class ImportExportVisitor extends Visitor { visitAwaitExpression(path) { if (!this.hasTopLevelAwait) { - let parent = path.getParentNode(1); - if ( - parent.type === 'ExpressionStatement' && - path.getParentNode(2).type === 'Program' - ) { - this.hasTopLevelAwait = true; + const parent = path.getParentNode(1); + switch (parent.type) { + case "ExpressionStatement": + if (path.getParentNode(2).type === 'Program') { + this.hasTopLevelAwait = true; + } + break; + default: + break; } } - + // There could be children with import expressions this.visitChildren(path) } + containsAwaitExpression(path) { + const subVisitor = new AwaitDetector(); + subVisitor.visitChildren(path); + return subVisitor.foundAwait; + } + + visitVariableDeclaration(path) { + if (!this.hasTopLevelAwait) { + const parent2 = path.getParentNode(2); + if (parent2?.type !== "Program") { + // Only top level variable declaration(s) impact our TLA detection + return; + } + if (this.containsAwaitExpression(path)) { + this.hasTopLevelAwait = true; + } + } + } + // This is also aliased as visitImport for Babel's AST visitImportExpression(path) { if (this.dynamicImport) { diff --git a/node/version.js b/node/version.js index 255187cd..f3dd3623 100644 --- a/node/version.js +++ b/node/version.js @@ -5,6 +5,6 @@ const path = require("path"); const pkgPath = path.join(__dirname, "../package.json"); const SemVer = require("semver"); -module.exports = new SemVer( +module.exports = SemVer.parse( process.env.REIFY_VERSION || fs.readJSON(pkgPath).version ); diff --git a/test/.mocharc.js b/test/.mocharc.js new file mode 100644 index 00000000..0e0b9a44 --- /dev/null +++ b/test/.mocharc.js @@ -0,0 +1,3 @@ +module.exports = { + // grep: /test name to match/i +} \ No newline at end of file diff --git a/test/tla/export-awaited-init-deep.js b/test/tla/export-awaited-init-deep.js new file mode 100644 index 00000000..0673fc86 --- /dev/null +++ b/test/tla/export-awaited-init-deep.js @@ -0,0 +1,5 @@ +await 0 + +const asyncInitializer = async () => 12; + +export const value = Math.max(false ? 666 : await asyncInitializer(), 2); diff --git a/test/tla/export-awaited-init-shallow.js b/test/tla/export-awaited-init-shallow.js new file mode 100644 index 00000000..f0f7237d --- /dev/null +++ b/test/tla/export-awaited-init-shallow.js @@ -0,0 +1,4 @@ +const asyncInitializer = async () => 8; + +export const value = await asyncInitializer(); + diff --git a/test/top-level-await-tests.js b/test/top-level-await-tests.js index 2d1aa4d8..09dc8925 100644 --- a/test/top-level-await-tests.js +++ b/test/top-level-await-tests.js @@ -80,6 +80,16 @@ import { importSync, importAsync, importAsyncEvaluated } from './tla/nested/pare assert(exports.value === 5); }); + it('should await shallow variable initializers', async () => { + const exports = await require('./tla/export-awaited-init-shallow.js'); + assert(exports.value === 8); + }); + + it('should await deep variable initializers', async () => { + const exports = await require('./tla/export-awaited-init-deep.js'); + assert(exports.value === 12); + }); + it('should run setters after module with async dep finished', async () => { const exports = await require('./tla/export-sync-parent.js'); assert(exports.a === 1);