Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"True" (non-statically-analyzable) dynamic imports #433

Open
lionel-rowe opened this issue Jul 12, 2023 · 4 comments
Open

"True" (non-statically-analyzable) dynamic imports #433

lionel-rowe opened this issue Jul 12, 2023 · 4 comments
Labels
enhancement New feature or request

Comments

@lionel-rowe
Copy link

lionel-rowe commented Jul 12, 2023

What problem are you trying to solve?

Separate tracking issue for non-statically-analyzable dynamic imports, as the fix for #1 Dynamic imports only solves statically-analyzable use cases.

Among other use cases, this would be useful for framework authors wishing to skip build steps entirely; without this feature, at a minimum, a file containing literal paths for routes/components has to be generated.

Example (currently possible in Deno, but not on Deno Deploy):

import { serve } from 'https://deno.land/std@0.190.0/http/server.ts'
import { resolve } from 'https://deno.land/std@0.190.0/path/mod.ts'
import html from 'https://deno.land/x/htm@0.2.1/mod.ts'

serve(async (req: Request) => {
	try {
		const { default: route } = await import(
			resolve('./routes', `.${new URL(req.url).pathname}.tsx`),
		)

		return html({ body: route({ req }) })
	} catch (e) {
		if (e.code === 'ERR_MODULE_NOT_FOUND') {
			return new Response(null, { status: 404 })
		} else throw e
	}
})

Describe the solution you'd like

  • True dynamic local path/URL imports to be available in Deploy, supporting js/ts/jsx/tsx/json (via { assert: { type: 'json' } }) file types (is this list exhaustive?)
  • Dynamic non-local URL imports — not sure how much demand there is for this, but it'd be nice to have full feature parity with Deno.
  • On-the-fly SWC transpilation of dynamically imported ts/tsx/jsx files, falling back to the current eszip-based solution for statically analyzable paths/URLs for better performance.

Describe alternatives you've considered

A partial solution could involve only supporting js/json files, with transpilation not supported. However, my guess is that wouldn't support most use cases, as most developers using Deno primarily author in TypeScript.

Documentation, Adoption, Migration Strategy

No response

@lionel-rowe lionel-rowe added the enhancement New feature or request label Jul 12, 2023
@vfssantos
Copy link

vfssantos commented Aug 12, 2023

For anyone looking for a workaround to this issue in Deno Deploy, check out https://github.com/ayoreis/import.

With it, you can dynamically import non-statically-analyzable modules; import code from string and even a code string that imports another module dynamically inside it.

import { importString, importModule } from "https://deno.land/x/import/mod.ts";

let { default: renderer } = await importString(`
    const renderer = async ()=>{

      const { render } = await modules.importModule('https://deno.land/x/mustache_ts/mustache.ts');

      const template = '{{foo}}, {{bar}}!'
      const view = {
          foo: 'Hello',
          bar: 'World!'
      }
      const output = render(template, view)
      return output;
    };
    export default renderer;
  `,
    { modules: { importModule } },
)
  console.log(await renderer()) // expected: "Hello, World!"

@lowlighter
Copy link

Another workaround inspired by a nice trick someone posted on the discord

Assuming you want to achieve something similar to this:

const {mod} = await import(`./routes/${url.pathname}`)

You can make something that has a similar behavior to the --include flags from deno compile (i.e. include additional modules you know you may use later ahead of time) by doing this:

(async () => await import("./routes/a.ts"));
(async () => await import("./routes/b.ts"));
(async () => await import("./routes/c.ts"));

Basically these are async functions which are not called (so not executed meaning they are no-op) but the analyzer will still resolve these imports during deployment, making them loadable dynamically later on

If you want to automate a bit things, you can even generate these definitions using something like an expandGlob("**/*.ts") and run this as a build step or pre-commit

This alternative has the advantage of being somewhat "native" as it uses deno deploy own resolution/loading system

@revgum
Copy link

revgum commented Jan 21, 2024

Thanks for the comment @lowlighter .. I wrote a simple shell script, run it as a step in Github Actions to find all of the files and append the await import lines to the code which is performing the dynamic imports. Deno Deploy static analysis sees the imports, the files are kept, and the dynamic imports are working! 🚀

@lionel-rowe
Copy link
Author

lionel-rowe commented Oct 23, 2024

As things stand, importing from dynamically created data: URIs does work, including if they import other (statically analyzable) packages, e.g.

// Import in a statically analyzable way
import('npm:<some-npm-package>@1.2.3')

// then, later on...
await import(
    `data:text/javascript;base64,${
        encodeBase64(`
            import * as mod from 'npm:<some-npm-package>@1.2.3'
            console.log(mod)
            console.log(${Math.random()})
        `)
    }`
)

But it doesn't work if you try to dedupe the package version by un-hard-coding it, even if it's a simple string declared with const in the same file:

const PACKAGE_VERSION = 'npm:<some-npm-package>@1.2.3'
// next line fails
import(PACKAGE_VERSION)

await import(
    `data:text/javascript;base64,${
        encodeBase64(`
            import * as mod from '${PACKAGE_VERSION}'
            console.log(mod)
        `)
    }`
)

Not sure if it'd be feasible to enable relatively simple cases like this as an intermediate step toward full dynamic imports, but I have a use case where it'd be useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants