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

process.mainModule is undefined with --experimental-modules #15760

Closed
generalov opened this issue Oct 3, 2017 · 13 comments
Closed

process.mainModule is undefined with --experimental-modules #15760

generalov opened this issue Oct 3, 2017 · 13 comments
Labels
esm Issues and PRs related to the ECMAScript Modules implementation. known limitation Issues that are identified as known limitations. process Issues and PRs related to the process subsystem.

Comments

@generalov
Copy link

  • 8.6.0:
  • Ubuntu 17.04:
  • ES Modules:

Create index.mjs file having the contents:

console.log(process.mainModule)

Run node from command line:

node --experimental-modules index.mjs
(node:24852) ExperimentalWarning: The ESM module loader is experimental.
undefined

There is an entry script detection line in .js file that used as an entry point if (process.mainModule === module) { start(); }. I'm trying to convert it to .mjs and I'd like know if my script is being run directly or being imported by another module.

@mscdex mscdex added the process Issues and PRs related to the process subsystem. label Oct 3, 2017
@mscdex
Copy link
Contributor

mscdex commented Oct 3, 2017

I've always just checked module.parent. If it's null, then it's the main script, otherwise it's been loaded by another module.

@mscdex mscdex added the esm Issues and PRs related to the ECMAScript Modules implementation. label Oct 3, 2017
@generalov
Copy link
Author

@mscdex the module is not defined too when --experimental-modules argument is using.

@Fishrock123
Copy link
Member

I'm really not sure how to support this for ES Modules without having something special like import isMainModule from runtime - something like this was previously discussed.

It would be necessary to have it be special because it need to be able to change based on the file importing it. Something similar is required for __dirname and __filename.

cc @bmeck

@Fishrock123 Fishrock123 added the known limitation Issues that are identified as known limitations. label Oct 5, 2017
@bmeck
Copy link
Member

bmeck commented Oct 5, 2017

wait on import.meta. I'd say we can set it to the module namespace of the ESM that is the main since we don't have a module like variable for it. It won't be API compatible though since ESM doesn't match up with the Module constructor.

@Fishrock123
Copy link
Member

Perhaps we could expose some identifier in import.meta and then we could check against that.

@danactive
Copy link

danactive commented Oct 16, 2017

On a related note, when running node with --experimental-modules flag the __dirname or __filename commands are no longer available. I was thinking the path core module would be extended to support this root absolute lookup such as

const rootPath = path.dirname('.'); // ESM
const rootPath = __dirname; // CommonJS

const favPath = path.dirname('./favicon.ico'); // ESM
const favPath = `${__dirname}/favicon.ico`; // CommonJS

@Jamesernator
Copy link

@danactive I asked about that the other day and it looks like node will support the import.meta.url that browsers will support so that files can be more cross-environment.

However I think import.meta.dirname/import.meta.filename would still be worthwhile additions given that often you'll want to use fs/path with them to read data files.

One criticism I have of import.meta.url is in order to use it in a browser-compatible way you basically have to use strings directly:

// Not browser compatible
import url from "url";
const file = url.resolve(import.meta.url, '../../foo.bar');

// Not Node compatible (as URL isn't global in Node)
const file = new URL('../../foo.bar', import.meta.url).href;

// Also not browser compatible
import url from "url";
const file = new url.URL('../../foo.bar', import.meta.url).href;

// Compatible with both but sort've defeats the point of having
// url.resolve/path.join in the first place
const file = import.meta.url + '/../../foo.bar'

@SMotaal
Copy link

SMotaal commented Dec 4, 2017

@danactive I think import.meta is something that will make it's way through the standards body eventually and node soon after (I hope 10 LTS will have it) until then you might want to explore using the --loader flag with a custom loader.

I have played around with this and tried many different designs, but all lead to the same question of how to ensure that your modules are consumed correctly downstream.

I eventually found this to be a bit of an overkill to manage if all you want is __filename. Long story short, a less elegant but temporary solution that can easily be written in a single line is to use Error().stack with a regex at the top of modules that require such metadata like:

//#region META 

const FILENAME = typeof __filename !== 'undefined' ? __filename : (/^ +at (?:file:\/*(?=\/)|)(.*?):\d+:\d+$/m.exec(Error().stack) || '')[1];
const DIRNAME = typeof __dirname !== 'undefined' ? __dirname : FILENAME.replace(/[\/\\].*?$/, '');

console.log(process.argv[1], { FILENAME, DIRNAME });

//#endregion

@Jamesernator I think that import.meta is actually going to be more like an open singleton per module that may be used to keep runtime populated information in a more open fashion.

With a custom loader, it is possible to make a similar mechanism (I can dig out some examples if you need). I think I could get it to work reliably using two different approaches.

  1. injecting a prologue to each file that contained import.meta. in a separate temp file which replaced those statements with import_meta. then resolving to the temp file instead.

  2. used a import meta from 'meta' and had the loader intercept and remap the meta specifier to a dynamic module with a url meta?referrer=${referrer}. The former is potentially future-compatible (but history proves otherwise).

In the end, I really recommend waiting for import.meta to settle before taking such risks. Meanwhile, my two cents would be to try to structure your code so that you only need __filename in as few files as possible and use the less elegant solution (which might need very little tweaking for browsers).

@matthewp
Copy link

Will module.parent in a CommonJS module be null if the parent is an ES module?

@devsnek
Copy link
Member

devsnek commented Jun 27, 2018

@matthewp yes

@matthewp
Copy link

matthewp commented Jun 27, 2018

@devsnek Thanks, this issue focuses on import.meta as a solution, but presumably there will not be an import.meta in CommonJS modules (am I wrong?), in which case what is the replacement for module.parent?

@devsnek
Copy link
Member

devsnek commented Jun 27, 2018

@matthewp a cjs imported by an esm has no parent.

@guybedford
Copy link
Contributor

Closing as this is by design.

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. known limitation Issues that are identified as known limitations. process Issues and PRs related to the process subsystem.
Projects
None yet
Development

No branches or pull requests

10 participants