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

Use native dynamic imports #21

Closed
AStoker opened this issue Oct 30, 2020 · 13 comments
Closed

Use native dynamic imports #21

AStoker opened this issue Oct 30, 2020 · 13 comments

Comments

@AStoker
Copy link

AStoker commented Oct 30, 2020

I'm wanting to use native dynamic imports in my code, however, they get transpired to use requirejs (which in my case fails because I'm using a url to import, not a module id).
For example, this

this.dynamicComponent = await import('http://localhost:9000/external/test/test.js');

is being transpired to this

...
var imp0r_ = function(d){return requirejs([requirejs.resolveModuleId(module.id,d)]).then(function(r){return r[0];});};
...
this.dynamicComponent = await imp0r_('http://localhost:9000/external/test/test.js');

Is it possible to use the native dynamic import syntax with DumberJS? Am I missing a configuration somewhere, or is this a new feature request?

@3cp
Copy link
Member

3cp commented Oct 31, 2020

I will do static analysis, if the param looks like a remote url, skip transpiling.

@3cp
Copy link
Member

3cp commented Oct 31, 2020

Will implement in this weekend.

@3cp
Copy link
Member

3cp commented Oct 31, 2020

Wait, I recalled why I didn't do this. By spec, dynamic import loads an esm module. But the app here is running in AMD env, not ESM env. And I didn't implement transpiling from esm to amd at app runtime, which is required for this to work.

Alternatively you can use requirejs paths config to map some module if prefix to a remote url, then use requirejs() directly to load remote module.

@3cp
Copy link
Member

3cp commented Oct 31, 2020

Maybe I don't need to transpile esm to amd, because import will handle execution of the remote file.

@3cp
Copy link
Member

3cp commented Oct 31, 2020

I guess your remote file is in amd format?

Because otherwise I don't know how it can handle

import {computedFrom} from 'aurelia-framework';

From esm which the app is running in amd.

@3cp
Copy link
Member

3cp commented Oct 31, 2020

https://github.com/3cp/dr-paths
A demo to use requirejs config paths (you can set it in dumber config) to map external modules. You have to make sure external modules were transpiled to amd format as an anonymous module (with no module name).

In my production app, I am using special dumber-module-loader (an implementation of requirejs, used in every dumber app) api to load additional bundle (not just a module) at runtime. I use this architect to support plugins (plugin contains both backend logic and a frontend ui bundle) in Buttonwood app.

The special api not only loads the additional bundle, but also put it behind a namespace to prevent module id pollution. There is a simple demo with limited documentation. https://dumber.js.org/runtime-app-composition

@AStoker
Copy link
Author

AStoker commented Nov 2, 2020

So, a little clarification on what exactly I'm trying to do.
I have an app that's been compiled ahead of time, and yes, just so happens to be compiled into AMD (using Aurelia v2 as the framework here btw). That's all great. However, this app needs to be able to load in components that clients have written, and these components have not been precompiled into AMD, and are in fact ES Modules. They are in essence, source files.
These components need to be loaded in dynamically (hence the need for a dynamic import). I know that importing these components as modules will follow the normal tracing that happens with native modules, and that's fine.
In regards to loading ESM from an AMD module, as long as the exports are right from the ESM file, things should still work like normal... Unless I'm missing something. True, it won't be an AMD module, so single instances and module definitions are a bit squirrelly if you're expecting behavior like AMD. But if you're importing a module with dynamic import, I think you could assume that the person importing it know's what they're trying to do?

@AStoker
Copy link
Author

AStoker commented Nov 2, 2020

Totally just playing around with it, but in the cjs-to-amd.js transformer, when we're checking the node type, look at the node.source.value and simply check if it's a url. If it's a url then skip the replacing with imp0r_, otherwise continue on like you have been.
I think that's what you were talking about doing earlier. Maybe I'm not tracking with what error's you were coming up with, but I feel like that's the way to go?

@3cp
Copy link
Member

3cp commented Nov 2, 2020

You can try go into node_modules/dumber to comment out those code.

let hasDynamicImport = false;
traverse(parsed, node => {
if (node.type === 'Import' || node.type === 'ImportExpression') {
hasDynamicImport = true;
m.replace(node.start, node.start + 6, 'imp0r_');
}
});
if (hasDynamicImport) {
m.prepend("var imp0r_ = function(d){return requirejs([requirejs.resolveModuleId(module.id,d)]).then(function(r){return r[0];});};");
}

Then do gulp clear-cache to reset tracing cache, then build/run again.


To load esm source code, I think you know the limitations: client has to use JS syntax browser can understand. For example, decorator would not work.

Another limitation is import statement would not simply work. import {customElement} from 'aurelia'; will fail because browser will attempt to load ecm module 'aurelia'.
User can write import {customElement from 'https://jspm.dev/aurelia@dev';, it will load aurelia esm module. But again that's duplicated code, not the aurelia code already loaded in your AMD environment. It would not work unless you get ride of AMD, and use ecm from start to end.

In short, it could work as long as client code has no import statement. Import of 3rd party libs might work.

@AStoker
Copy link
Author

AStoker commented Nov 2, 2020

Yeah, this is what I've been playing with

let hasDynamicImport = false;
traverse(parsed, node => {
    if (node.type === 'Import' || node.type === 'ImportExpression') {
      hasDynamicImport = true;

      try {
        new URL(node.source.value);
        log.info('found dynamic import, not converting')
      } catch (e) {
        m.replace(node.start, node.start + 6, 'imp0r_');
      }

    }
  });

Seems to be working well so far

@3cp
Copy link
Member

3cp commented Nov 2, 2020

I agree. I will make the change.

BTW, URL doesn't know this one?

> new URL('//fds.com/fewf')
Uncaught TypeError [ERR_INVALID_URL]: Invalid URL: //fds.com/fewf

I can always use regex.

@AStoker
Copy link
Author

AStoker commented Nov 2, 2020

I was using the built in Node URL functionality: https://nodejs.org/api/url.html
In this case, sounds like Regex is safer

@3cp 3cp closed this as completed in f2205f0 Nov 2, 2020
@3cp
Copy link
Member

3cp commented Nov 2, 2020

v1.14.1 is released.

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

No branches or pull requests

2 participants