From 340aaf6a78421e90f06f8a30a0927d09d3471b58 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Wed, 1 Apr 2020 21:50:14 +1100 Subject: [PATCH] Support dynamic import in bundles. Fixes #4062 --- cli/tests/integration_tests.rs | 32 ++++++++++++++++++++++ cli/tests/subdir/subdir2/dynamic_import.ts | 6 ++++ deno_typescript/README.md | 11 ++++++++ deno_typescript/system_loader.js | 21 +++++++++++++- 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 cli/tests/subdir/subdir2/dynamic_import.ts diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 8342da10864316..98c564b4d9a051 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -490,6 +490,38 @@ fn bundle_js() { assert_eq!(output.stderr, b""); } +#[test] +fn bundle_dynamic_import() { + let dynamic_import = + util::root_path().join("cli/tests/subdir/subdir2/dynamic_import.ts"); + assert!(dynamic_import.is_file()); + let t = TempDir::new().expect("tempdir fail"); + let bundle = t.path().join("dynamic_import.bundle.js"); + let mut deno = util::deno_cmd() + .current_dir(util::root_path()) + .arg("bundle") + .arg(dynamic_import) + .arg(&bundle) + .spawn() + .expect("failed to spawn script"); + let status = deno.wait().expect("failed to wait for the child process"); + assert!(status.success()); + assert!(bundle.is_file()); + + let output = util::deno_cmd() + .current_dir(util::root_path()) + .arg("run") + .arg(&bundle) + .output() + .expect("failed to spawn script"); + // check the output of the test.ts program. + assert!(std::str::from_utf8(&output.stdout) + .unwrap() + .trim() + .ends_with("Hello")); + assert_eq!(output.stderr, b""); +} + #[test] fn repl_test_console_log() { let (out, err) = util::run_and_collect_output( diff --git a/cli/tests/subdir/subdir2/dynamic_import.ts b/cli/tests/subdir/subdir2/dynamic_import.ts new file mode 100644 index 00000000000000..3dccaca04b5502 --- /dev/null +++ b/cli/tests/subdir/subdir2/dynamic_import.ts @@ -0,0 +1,6 @@ +(async () => { + const { printHello } = await import("../print_hello.ts"); + printHello(); +})(); + +export {}; diff --git a/deno_typescript/README.md b/deno_typescript/README.md index 220b04ae4d264a..8ab969c784f917 100644 --- a/deno_typescript/README.md +++ b/deno_typescript/README.md @@ -34,6 +34,17 @@ module specifier, a reference to the dynamic `import()` and the equivalent of and its value, or an object record of keys of the named exports and the values of the exports. +Currently, TypeScript does not re-write dynamic imports which resolve to static +strings (see +[microsoft/TypeScript#37429](https://github.com/microsoft/TypeScript/issues/37429)), +which means the import specifier for a dynamic import which has been +incorporated in the bundle does not automatically match a module specifier that +has been registered in the bundle. The `di()` function provides the capability +to try to identify relative import specifiers and resolve them to a specifier +inside the bundle. If it does this, it resolves with the exports of the module, +otherwise it simply passes the module specifier to `import()` and returns the +resulting promise. + The running of the factories is handled by `rF()`. When the factory is run, it returns an object with two keys, `execute` and `setters`. `execute` is a function which finalises that instantiation of the module, and `setters` which diff --git a/deno_typescript/system_loader.js b/deno_typescript/system_loader.js index b5b10af480c55c..537d852b5ea4f2 100644 --- a/deno_typescript/system_loader.js +++ b/deno_typescript/system_loader.js @@ -15,10 +15,29 @@ let System, __instantiateAsync, __instantiate; }, }; + async function dI(mid, src) { + let id = mid.replace(/\.\w+$/i, ""); + if (id.includes("./")) { + const [o, ...ia] = id.split("/").reverse(), + [, ...sa] = src.split("/").reverse(), + oa = [o]; + let s = 0, + i; + while ((i = ia.shift())) { + if (i === "..") s++; + else if (i === ".") break; + else oa.push(i); + } + if (s < sa.length) oa.push(...sa.slice(s)); + id = oa.reverse().join("/"); + } + return r.has(id) ? r.get(id).exp : import(mid); + } + function gC(id, main) { return { id, - import: async (id) => r.get(id)?.exp, + import: (m) => dI(m, id), meta: { url: id, main }, }; }