deploy_feedback icon indicating copy to clipboard operation
deploy_feedback copied to clipboard

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

Open lionel-rowe opened this issue 2 years ago • 3 comments

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/[email protected]/http/server.ts'
import { resolve } from 'https://deno.land/[email protected]/path/mod.ts'
import html from 'https://deno.land/x/[email protected]/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 avatar Jul 12 '23 02:07 lionel-rowe

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!"

vfssantos avatar Aug 12 '23 14:08 vfssantos

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

lowlighter avatar Oct 05 '23 19:10 lowlighter

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! 🚀

revgum avatar Jan 21 '24 14:01 revgum