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

Link modules with paths in module.link(linker) #35848

Open
lacmuch opened this issue Oct 28, 2020 · 12 comments
Open

Link modules with paths in module.link(linker) #35848

lacmuch opened this issue Oct 28, 2020 · 12 comments
Labels
esm Issues and PRs related to the ECMAScript Modules implementation.

Comments

@lacmuch
Copy link

lacmuch commented Oct 28, 2020

https://nodejs.org/api/vm.html#vm_module_link_linker

Now we can only link "standard" modules with functions combined with SyntheticModules, like this:

await module.link( function( spec ) {
	
    return new Promise( async function( resolve, reject ) {
	
	const mod = await import( spec );
        resolve( new vm.SyntheticModule( [ 'default' ], function() {

            this.setExport( 'default', mod.default );
        }, { context } ) );
    } )
} );
@devsnek
Copy link
Member

devsnek commented Oct 28, 2020

You have to set up your own system to load the source text modules.

@lacmuch
Copy link
Author

lacmuch commented Oct 28, 2020

I think it would be easier to pass back the standard import() function in the linker function, like:

module.link( spec => import( spec ) )

@devsnek
Copy link
Member

devsnek commented Oct 28, 2020

that's not really how that's meant to work though. If you want to import a module that node has imported, synthetic modules would be the correct choice. If you're just trying to match how node's resolution works, you will have to do that yourself and plug it into source text modules.

@lacmuch lacmuch closed this as completed Oct 28, 2020
@lacmuch lacmuch reopened this Oct 28, 2020
@lacmuch
Copy link
Author

lacmuch commented Oct 28, 2020

Here is an example, and it works fine now:

const soruce = `import express from 'express';
const app = express();
const port = 3000;
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(port, () => console.log('Example app listening on port ' + port +'!'));`;

const module = new vm.SourceTextModule( source );
const bin = module.createCachedData();
await module.link( function( spec ) {

    return new Promise( async function( resolve, reject ) {
	
	const mod = await import( spec );
        resolve( new vm.SyntheticModule( [ 'default' ], function() {

            this.setExport( 'default', mod.default );
        } ) );
    } )
} );
await module.evaluate();

I think: In the future, it would be better if the module.link() could eat :-) the Module Class what ES6 import() returns
(its differs from the vm.Module class)

const soruce = `import express from 'express';
const app = express();
const port = 3000;
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(port, () => console.log('Example app listening on port ' + port +'!'));`;

const module = new vm.SourceTextModule( source );
const bin = module.createCachedData();
await module.link( spec => import( spec ) ); //<---here
await module.evaluate();

@devsnek
Copy link
Member

devsnek commented Oct 28, 2020

@lacmuch It seems like maybe you just want this:

function importSource(source) {
  return import(`data:text/javascript,${source}`);
}

@lacmuch
Copy link
Author

lacmuch commented Oct 28, 2020

I want to use module.createCachedData();

@devsnek
Copy link
Member

devsnek commented Oct 28, 2020

do you just want node to cache the startup of your app?

@lacmuch
Copy link
Author

lacmuch commented Oct 28, 2020

I want to use it for code protection... but there is an other issue:

#35847

...and i dont want to compile the node_modules folder :-)

@targos targos added the esm Issues and PRs related to the ECMAScript Modules implementation. label Dec 27, 2020
@aral
Copy link

aral commented Jan 29, 2022

@lacmuch Just wanted to say thanks for documenting this; it helped me today. PS. In case it helps anyone else, I’m iterating over the module keys and copying them over in my tests to ensure they work with any module:

await module.link(async (specifier, referencingModule) => {
  return new Promise(async (resolve, reject) => {
    const module = await import(specifier)
    const exportNames = Object.keys(module)

    const syntheticModule = new vm.SyntheticModule(
      exportNames,
      function () {
        exportNames.forEach(key => {
          this.setExport(key, module[key])
      })
    }, { context })

    resolve(syntheticModule)
  })
})

@axkibe
Copy link
Contributor

axkibe commented Mar 3, 2023

Hmm, the thing is, when using "await import" this way means it will do the import within the context of the link implementator, not within the context of possibly another package using the implementator (meaning it should look into that node_modules folder, of said package instead that of the linker)

I'm using this for a code generator, that generates some JS code on the fly matching definitions in a file, and the code generator is in another package than the code taking use of this. (https://gitlab.com/timberdoodle/tim I generate code for immutables)

In CommonJS world, I solved this by passing the callees module object as paramater to the generator, so it call that "module.require" from the callees package context to get additional packages from there... but how do I this when doing modules?

Yes completely reimplementing node.js module loading is an option, but thats IMO asking for incompatiblity problems and nasty supprises down the road.

What would be needed would the node.js linking function as parameter, as object to extend or so, the linker if it would natively link that file in that directory if node would do it by itself (where my generator would e.g. decide to link something itself (say the specifier starts with "tim:") or forward it do the default linker otherwise.

@axkibe
Copy link
Contributor

axkibe commented Mar 3, 2023

I guess this #31234 is asking for the same thing.

@axkibe
Copy link
Contributor

axkibe commented Mar 4, 2023

Found it, this way you can load modules from another path.

import { createRequire } from 'node:module';
const require = createRequire('file:///a/path/somewhere/script.mjs');
await import(require.resolve("apackage"));

Wasn't obvious in any way tough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
esm Issues and PRs related to the ECMAScript Modules implementation.
Projects
None yet
Development

No branches or pull requests

5 participants