denoland / deno

A modern runtime for JavaScript and TypeScript.
https://deno.com
MIT License
97.83k stars 5.39k forks source link

Dynamically generating import maps #6782

Open mikeal opened 4 years ago

mikeal commented 4 years ago

There’s a pattern emerging for creating cross-platform ESM modules using the exports property in package.json. To be perfectly honest, I wish there were a conditional import syntax for this use case instead, but there isn’t so this is what we’ve got.

Rollup, webpack, and of course Node.js, all now support this property.

This means that internally in the module, and in the module’s tests, you can’t use relative imports but instead have to use the package name because relative imports skip the export map loader. And of course, tests all have to do the same.

The nice part of authoring this way is that my modules and all the tests can be run and loaded in the browser and Node.js without a compiler, and the module works as expected when people consume it with a compiler.

When I run tests in the browser I generate an export map and stick it in the page before loading my tests.

Similarly, I can generate an import map for Deno and pass it as a command line flag.

The thing is though, I have my test runner running in Deno natively. I’d like to generate an import map from package.json in Deno and then load it, rather than passing as a command line flag because that would require another subprocess launch and additional permissions.

There are security concerns here. The browser doesn’t allow dynamic import map loading, it requires that it’s in the page you load from the origin. Perhaps something similar could be done with Deno by passing a .js or .ts file instead of .json file as the importMap argument? There could be an API in Deno to set the import map and it would only be accessible to that module, creating a similar security pattern to what the browser uses. Or the default export of that file could be the import map?

Thoughts?

kitsonk commented 4 years ago

We sort of talked about some of this before (ref: #3585).

I get the utility of it, but as you mention, the security concerns would be the biggest "worry", but allowing a .js or .ts file to be run in an isolated worker, as I think you are implying, and allowing it to return an import map, might work. I guess though, you would want this dynamic import map to access the outside world though? What information would it need to complete its "dynamic" tasks? Because then allowing permissions like that become complicated as you would want your import map access flags and your application access flags.

I can't remember off hand if we support remote import maps (we should if we don't) which means that a server could generate an import map, potentially on the fly, with a given set of information. Would something like that sort of solve the problem?

mikeal commented 4 years ago

You could get away with --allow-read=package.json though. Whereas, the subprocess method requires --allow-run which basically means do anything you want.

bartlomieju commented 4 years ago

I can't remember off hand if we support remote import maps (we should if we don't)

We don't - #5849

There's another PR that adds support for loading import maps in workers - #6704 - maybe that would suite you use-case @mikeal?

mikeal commented 4 years ago

@bartlomieju that’s actually a very elegant solution to this problem, much better than what I was thinking.

caspervonb commented 4 years ago

Also related https://github.com/denoland/deno/issues/5791

I think workers would be ideal here, or something like node's fork that inherits the current permissions or less.