GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — doc: clarify module system selection by aduh95 · Pull Request #41383 · nodejs/node

Conversation

@aduh95

@aduh95 aduh95 commented Jan 2, 2022

Copy link
Copy Markdown
Contributor

Refs: #41345 (comment)

//cc @nodejs/modules

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@aduh95
doc: clarify module system selection
1b1159a
Refs: nodejs#41345 (comment)
@nodejs-github-bot

nodejs-github-bot commented Jan 2, 2022

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/modules
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot nodejs-github-bot added the doc Issues and PRs related to Node.js documentation. label Jan 2, 2022
@aduh95 aduh95 added the module Issues and PRs related to the module subsystem. label Jan 2, 2022
ljharb
ljharb reviewed Jan 2, 2022
View reviewed changes
Comment thread doc/api/esm.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

guybedford
guybedford approved these changes Jan 3, 2022
View reviewed changes
Comment thread doc/api/modules.md Outdated
Authors can tell Node.js to use the ECMAScript modules loader
via the `.mjs` file extension, the `package.json` [`"type"`][] field, or the
[`--input-type`][] flag. Outside of those cases, Node.js will use the CommonJS
module loader. See [Determining module system][] for more details.

@guybedford guybedford Jan 3, 2022

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add a note again that the ES module loader can be accessed in any context via dynamic import().

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ljharb and aduh95 reacted with thumbs up emoji
All reactions
  • 👍 2 reactions
Comment thread doc/api/packages.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

GeoffreyBooth
GeoffreyBooth reviewed Jan 3, 2022
View reviewed changes
Comment thread doc/api/esm.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/modules.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/packages.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/packages.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/packages.md Outdated
Comment on lines +87 to +91
Node.js will refuse to load the following when passed to `node` as the
initial input and the nearest parent `package.json` file contains a top-level
[`"type"`][] field with a value of `"module"`:

* Files whose name doesn't end in `.js`, `.mjs`, `.cjs`, or `.wasm`.

@GeoffreyBooth GeoffreyBooth Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than saying what Node won’t do, why not say what it will? I think it’s easier to understand the positive case than the negative:

Suggested change
Node.js will refuse to load the following when passed to `node` as the
initial input and the nearest parent `package.json` file contains a top-level
[`"type"`][] field with a value of `"module"`:
* Files whose name doesn't end in `.js`, `.mjs`, `.cjs`, or `.wasm`.
When the nearest parent `package.json` file contains a top-level
[`"type"`][] field with a value of `"module"`, the `node` command will
accept as input only files with `.js`, `.mjs`, or `.cjs` extensions;
and with `.wasm` extensions when `--experimental-wasm-modules` is enabled.

(And please add a link to --experimental-wasm-modules; and is a wasm entry point actually supported?)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ljharb ljharb Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type field has no bearing on what node accepts - it accepts a .js file regardless, and has no effect on the others.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@bmeck bmeck Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That isn't true, type affects if the ESM module loader is used for the entry point:

node/lib/internal/modules/run_main.js

Line 41 in 2cc7a91

return pkg && pkg.data.type === 'module';
. Also it makes extension-less files fail to load and that should probably be called out.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ljharb ljharb Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the latter, that’s good to know, and should indeed be called out.

For the former, are you saying node foo won’t do any extension lookup on foo.cjs,foo.js, or foo.mjs? If so, i understand why - but that’s not enabling ESM, that’s disabling the standard/default CJS loader, and that should definitely be explicitly called out as well (as a potential hazard/impact of using type module)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@bmeck bmeck Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

under type:"module" the regular ESM semantics are applied against the preresolved filepath argv[1] absolute file: URL; so, no searching occurs. For type:"commonjs" the CJS semantics are applied against the preresolved argv[1] absolute file path.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ljharb reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

@bmeck bmeck Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so it looks like CJS resolver is always hit prior to the the ESM resolver, but things are weird and spaghetti

node/lib/internal/modules/run_main.js

Line 70 in e46c680

const resolvedMain = resolveMainPath(main);
, so it does both, not one or the other when it thinks it is ESM. there is an odd comment about back compat, but I have no memory of a compat issue.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@bmeck bmeck Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ljharb the comments in the file that are causing it explicitly state that removing extension searching can be done in the future.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@bmeck bmeck Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, this seems like it would break if someone mucked with require.extensions in weird ways and passed it to ESM

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ljharb ljharb Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comments were probably from when it was still experimental. Now that it no longer is, it doesn't seem like something that can be removed without a semver-major.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@bmeck bmeck Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely would be semver-major yes.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread doc/api/packages.md Outdated
[`"type"`][] field with a value of `"module"`:

* Files whose name doesn't end in `.js`, `.mjs`, `.cjs`, or `.wasm`.

@GeoffreyBooth GeoffreyBooth Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is perhaps where we should mention that ESM entry points need to be URLs.

Suggested change
When the initial input is ES module JavaScript, the reference to it must be
given as a URL, not a file path. For example: `node entry.mjs`;
`node ./app/entry.mjs`; `node file:///c:/path/to/app/entry.mjs`.

I feel like others can come up with better wording; my point is just that something along these lines should be part of the docs, either in the ESM or CLI docs or probably both.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 3, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's.. not true? AFAIK you can only specify a path as CLI entry point, using a URL only works if it happens to coincide with a path.

$ cd /tmp
$ touch file.mjs
$ node file:///tmp/file.mjs
node:internal/modules/cjs/loader:936
  throw err;
  ^

Error: Cannot find module '/private/tmp/file:/file/path'
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
    at Function.Module._load (node:internal/modules/cjs/loader:778:27)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47 {
  code: 'MODULE_NOT_FOUND',
  requireStack: []
}

Node.js v17.3.0
$ node ./%66ile.mjs
node:internal/modules/cjs/loader:936
  throw err;
  ^

Error: Cannot find module '/private/tmp/%66ile.mjs'
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
    at Function.Module._load (node:internal/modules/cjs/loader:778:27)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47 {
  code: 'MODULE_NOT_FOUND',
  requireStack: []
}

Node.js v17.3.0

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@bmeck bmeck Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Takes a deep breath the CLI never accepts a filepath or a url. It is always a string specifier resolved using file semantics against the cwd PRIOR to doing any loading stuff

node/lib/internal/bootstrap/pre_execution.js

Line 105 in e46c680

process.argv[1] = path.resolve(process.argv[1]);
. It would resolve the same as a URL or filepath semantically. This causes confusions since it uses file resolution, but that is something that would be too dangerous to change last I talked with others about it (percent encoding problems for example). The increased usage of URL in various places may change that, but even if the CLI was to move to be URL based it likely would need a flag, but it does use URLs when it goes into the ESM Loader.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ljharb reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

@GeoffreyBooth GeoffreyBooth Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in other words, it is true?

This was what tripped me up in the tests PR, where the tests were failing only in Windows and it took me a long time to figure out why.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 3, 2022 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would resolve the same as a URL or filepath semantically.

My understanding is it only accepts a CJS specifier (with the notable difference that if you don't specify ./, it still tries to resolve it as a relative path, but it still does extension searching), because of this Module._findPath+path.resolve call:

node/lib/internal/modules/run_main.js

Line 16 in 2cc7a91

let mainPath = Module._findPath(path.resolve(main), null, true);

At least this discussion shows that this part of the docs definitely needs some clarification, because I'm quite confused right now ^^

This was what tripped me up in the tests PR, where the tests were failing only in Windows and it took me a long time to figure out why.

I think there's a confusion with --experimental-loader that takes a URL and never a path (even if the loader is a .cjs file) – or maybe a more correct way of saying it would be: it accepts any specifier that can be resolved by ESM loader relative to cwd?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@bmeck bmeck Jan 3, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--loader differs greatly reading through edge case carved out code. It was always intended to be a URL for forwards compatibility and to avoid some questions about passing in an entire loader via ARGV without a file via data:.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@aduh95
Address review comments
dd7140f
@GeoffreyBooth

This comment has been minimized.

Sign in to view
GeoffreyBooth
GeoffreyBooth reviewed Jan 7, 2022
View reviewed changes
Comment thread doc/api/cli.md Outdated

The program entry point is a specifier-like string. That string is first passed
through `path.resolve()` and the [CommonJS][] modules loader. If no
corresponding module, an error is thrown.

@GeoffreyBooth GeoffreyBooth Jan 7, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
corresponding module, an error is thrown.
corresponding module is found, an error is thrown.

A user so new to Node that they’re reading this page is highly unlikely to know what the CommonJS modules loader is, or even path.resolve. Is there a way we can describe what it means for the string to be parsed by those things, that doesn’t assume any prior Node knowledge?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 7, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm all for simplifying what can be simplified, but I also strongly think docs should not omit details on the only basis that new users won't be familiar. I think it's fair to assume no one that has participated in the discussion in this PR is new to Node.js (euphemism), yet folks have expressed surprise at node behavior regarding entry point specifier: to me, that indicates that it should be documented.

Maybe instead we should try to define what is the CommonJS module loader and link to that?

Regarding path.resolve, should we replace that with if it's not an absolute path, it's resolved as a relative path from the current working directory?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@GeoffreyBooth GeoffreyBooth Jan 7, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re path.resolve, sure, that sounds good.

Re the loaders, if you want to mention them then I think you need to introduce them. Something like:

Node.js has two module systems: CommonJS and ECMAScript (ES) Modules. A set of rules define whether a particular specifier (the string passed to require or import) should be interpreted as a CommonJS or ES module. Once this determination is made, either the CommonJS or ES module loader Node.js subsystem then resolves the specifier into a file path or URL, and loads the source for that resource.

The last sentence is the key: I’m defining what either of these loaders is by describing what it does. (What it “is” is a Node.js subsystem, but that’s not very informative.)

And within this brief overview, you can link to the detailed sections describing each loader.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 7, 2022 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A set of rules define whether a particular specifier (the string passed to require or import) should be interpreted as a CommonJS or ES module. Once this determination is made, either the CommonJS or ES module loader Node.js subsystem then resolves the specifier into a file path or URL, and loads the source for that resource.

A specifier passed to import is always resolved and loaded by the ES module loader (which may in turn pass the resolved URL to the CJS loader), and a specifier passed to require is always resolved and loaded by the CJS loader. I must be missing something, I don't understand what you mean in this last sentence...

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@GeoffreyBooth GeoffreyBooth Jan 8, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the user's perspective, saying that import is always handled by the ESM loader implies that the reference would always be treated as ESM. What matters is that Node can treat some imports as CommonJS and some as ESM. As in, what Node is doing is more important than what Node subsystem is doing it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 8, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will depend if the user is a loader author (or maybe even a loader user) imho. If loaders were not public, I would agree that's just an implementation detail that would not belong in the docs. Unless we want to make separate docs for loaders, I think we should have this information somewhere.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@GeoffreyBooth GeoffreyBooth Jan 8, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can say that one loader or the other is responsible, that's fine. My point is to go further and explain what it means that a particular loader handles something: what does it do as a result?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 8, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to address that in ce3edd0, PTAL.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread doc/api/cli.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/cli.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/cli.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/cli.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/esm.md
Authors can tell Node.js to treat JavaScript code as ECMAScript modules
Node.js has two module systems: [CommonJS][] modules and ECMAScript modules.

Authors can tell Node.js to use the ECMAScript modules loader

@GeoffreyBooth GeoffreyBooth Jan 7, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Authors can tell Node.js to use the ECMAScript modules loader
Authors can tell Node.js to treat JavaScript code as ECMAScript modules

I just don’t think the docs should have any references to “the CommonJS modules loader” or the “ECMAScript modules loader”—no average user knows what those are. There’s just Node, and how it interprets source code.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 7, 2022 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK but using the ES module loader has more effect than on simply JS code (it no longer accepts .node and .json file as entry point, it no longer treats extensionless / unknown extension as JS files), which was the nuance I was trying to communicate here.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@GeoffreyBooth GeoffreyBooth Jan 8, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add those other implications here, then. Most people reading these docs wouldn't know about those nuances just because the loader is mentioned.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 8, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to address that in ce3edd0, PTAL.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread doc/api/modules.md Outdated
Comment on lines +90 to +91
Calling `require()` always use the CommonJS loader, calling `import()` always
use the [ECMAScript modules][] loader.

@GeoffreyBooth GeoffreyBooth Jan 7, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Calling `require()` always use the CommonJS loader, calling `import()` always
use the [ECMAScript modules][] loader.
Calls to `require()` will always try to load the referenced resource as a
CommonJS module. `import()` expressions will load _either_ an
ES module or a CommonJS module, depending on how
the file being imported should be interpreted.
`import()` can be used to reference ES modules from
CommonJS code.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 7, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think your suggestion is correct, require('./file.json') will first try to load a JSON file before trying to load it as CJS.
I mean, I get that using jargon and expect the reader to know what we mean by "loader" is not a low-bar, but this is a technical documentation, I'd rather use the technical terms than write a potentially mis-leading or incomplete documentation.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@GeoffreyBooth GeoffreyBooth Jan 8, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If require is loading json, it's loading it as a CommonJS module. Does a CommonJS module need to be JavaScript?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 8, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, that was my understanding but maybe it's wrong and a JSON file can count as a CommonJS module. The lack of official spec makes the definition blurry so it's probably not worth debating it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread doc/api/packages.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/packages.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/synopsis.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

aduh95 added 2 commits January 7, 2022 19:45
@aduh95
fixup!
97515fd
@aduh95
fixup!
c19d161
ljharb
ljharb reviewed Jan 7, 2022
View reviewed changes
Comment thread doc/api/cli.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

GeoffreyBooth
GeoffreyBooth approved these changes Jan 8, 2022
View reviewed changes

@GeoffreyBooth GeoffreyBooth left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The grammar is addressed so I'll approve this. I think we can still do a better job making the loaders more explained in introductory terms, if you'd like to keep iterating on the other notes, but this is already good enough to merge I think.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
aduh95 added 2 commits January 8, 2022 17:24
@aduh95
fixup!
ce3edd0
@aduh95
fixup!
be6f791
ljharb
ljharb reviewed Jan 10, 2022
View reviewed changes
Comment thread doc/api/modules.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

@aduh95
fixup!
e9c0b15
GeoffreyBooth
GeoffreyBooth reviewed Jan 12, 2022
View reviewed changes
Comment thread doc/api/modules.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/packages.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/packages.md Outdated
Comment on lines +96 to +97
* When resolving a specifier, if no exact match is found, it will try to add
extensions (`.js`, `.json`, and finally `.node`).

@GeoffreyBooth GeoffreyBooth Jan 12, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* When resolving a specifier, if no exact match is found, it will try to add
extensions (`.js`, `.json`, and finally `.node`).
* When resolving a specifier, if no exact match is found, it will try to add
extensions (`.js`, `.json`, and finally `.node`) and index files
(`/index.js`, `/index.json`, and finally `/index.node`).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 13, 2022 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's true, if the specifier points to an existing directory, then an exact match is found – we have a Folders as modules section that explains it. Otherwise, we would have to specify it first tries to load the "main" field if the directory contains a package.json file, we might as well just link to that section (EDIT: which I've done in the very next item, I think we should leave it as is)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@GeoffreyBooth GeoffreyBooth Jan 14, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then maybe mention the folder resolution without spelling it out?

* When resolving a specifier, if no exact match is found, it will try to add
  extensions (`.js`, `.json`, and finally `.node`) and then attempt to resolve
  [folders as modules][].

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 14, 2022 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that work?

node/doc/api/packages.md

Lines 97 to 99 in 93d6a23

* When resolving a specifier, if no exact match is found, it will try to add
extensions (`.js`, `.json`, and finally `.node`).
* It supports [folders as modules][].

EDIT: no you're right, we should document the order here.

node/doc/api/packages.md

Lines 97 to 100 in fe05b72

* It supports [folders as modules][].
* When resolving a specifier, if no exact match is found, it will try to add
extensions (`.js`, `.json`, and finally `.node`) and then attempt to resolve
[folders as modules][].

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread doc/api/packages.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/packages.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/packages.md Outdated
Comment on lines +104 to +106
* It cannot be used to load ECMAScript modules. Attempting to do so will result
in a [`ERR_REQUIRE_ESM`][] error.
* It can be accessed using `require` function.

@GeoffreyBooth GeoffreyBooth Jan 12, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* It cannot be used to load ECMAScript modules. Attempting to do so will result
in a [`ERR_REQUIRE_ESM`][] error.
* It can be accessed using `require` function.
* It allows one CommonJS module to load another using the `require` function.
* It allows CommonJS modules to asynchronously import ECMAScript modules,
using `import()` expressions.
* `require` cannot be used to load ECMAScript modules. Attempting to do so
will result in a [`ERR_REQUIRE_ESM`][] error.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 13, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if you noticed, but I tried to list the same features in both lists, so user can compare the difference between CJS and ESM loaders. If we add an item here, can we add it to the ESM list as well?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@GeoffreyBooth GeoffreyBooth Jan 13, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could combine the two require-related ones, maybe?

* It allows one CommonJS module to load another using the `require` function.
  `require` cannot be used to load ECMAScript modules. Attempting to do so
  will result in a [`ERR_REQUIRE_ESM`][] error.
* It allows CommonJS modules to asynchronously import ECMAScript modules,
  using `import()` expressions.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 13, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It allows one CommonJS module to load another using the `require` function.

It also allows ES module to load a CJS module using require function. I'm not sure this information is useful tbh. Is it correct that the information you are trying to convey is that all JS files are treated as CJS? If so sure I can add that.

It allows CommonJS modules to asynchronously import ECMAScript modules,
  using `import()` expressions.

I don't see how this is related to the CJS loader. import() is exposed in CJS modules, it is explicitly called out is several places, but I think it would add too much confusion to list import() as a CJS loader feature.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 13, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with

node/doc/api/packages.md

Lines 103 to 107 in 21b45bf

* It treats all files that lack `.json` or `.node` extensions as JavaScript
text files.
* It cannot be used to load ECMAScript modules. Attempting to do so will result
in a [`ERR_REQUIRE_ESM`][] error. When used to load a JavaScript text file
that is not an ECMAScript module, it loads it as a CommonJS module.
wdyt?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 14, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I don't "like" with this last version is it gives the impression that if an ESM file do not use any ESM specific syntax, CJS loader would (try to) parse it as CJS. Can we have a version without using tries to?

Do we really need to document here that ERR_REQUIRE_ESM is thrown? It's already documented in modules.md, I'd prefer if we leave it out to simplify that list item.

what’s the value of stating that the CommonJS loader only loads CommonJS?

ESM loader can load more than just ESM, so it may not be obvious that CJS only loads CJS?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@GeoffreyBooth GeoffreyBooth Jan 16, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESM loader can load more than just ESM, so it may not be obvious that CJS only loads CJS?

Can it though? Doesn’t it just call the CommonJS loader to handle cases of import of CJS? Just like how the CommonJS loader calls the ESM loader to handle cases of import() of ESM?

We can leave out the error message, sure. But then I don’t think there’s anything left of this bullet point, and so we should just remove it. Both loaders use the other to handle the other system’s modules. The only thing worth mentioning is how import/import() can handle either module system while require only supports ESM. That’s a really important point that we should make if we’re not doing so already, and is much more valuable to users than a description of how the internals of the loaders work.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ljharb ljharb Jan 17, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(require only supports CJS)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@aduh95 aduh95 Jan 17, 2022 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO there's a clear difference: with Module._load there is no way to load a file as ESM; with esmLoader.load, you can load a file as CJS. That's what I'm talking about, maybe it's not an information relevant for end users, but I believe it is for loader hooks authors.
require and import are user facing features, and are already documented elsewhere, it's easier if we define require => CJS loader and import => ESM loader (and it's mostly true AFAIK), and I think it would be detrimental to imply that import and require behave differently depending on the parse goal of the file.

I'm going to land as is, hopefully we can improve this part of the docs in future PRs.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@GeoffreyBooth GeoffreyBooth Jan 17, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESM loader can load more than just ESM, so it may not be obvious that CJS only loads CJS?

But can it really? If you import a CommonJS file, isn’t it just passing that off to the CommonJS loader for loading? So in that sense, each loader only handles its domain.

CommonJS modules load more than just CommonJS, and the inverse for ES modules, and that’s what matters to end users. Stating that the loader can only handle CommonJS just adds confusion.

I think that matters to users is that require can only take CommonJS, while import/import() can take either. And that import doesn’t exist in CommonJS, but import() does.

All this other stuff about what the loaders can do is unnecessary detail that can confuse people. I understand the desire to keep the whole list focused on the CommonJS loader, which is why I’m thinking it’s best to just leave out this detail unless you want to make this bullet about require vs import rather than about the loader itself.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ljharb reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread doc/api/packages.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/packages.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread doc/api/packages.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

aduh95 and others added 4 commits January 13, 2022 12:39
@aduh95 @GeoffreyBooth
Apply suggestions from code review
4c697b7
Co-authored-by: Geoffrey Booth <[email protected]>
@aduh95
fixup!
2cea6a8
@aduh95
fixup!
21b45bf
@aduh95
fixup!
93d6a23
@aduh95 aduh95 mentioned this pull request Jan 14, 2022
make experimental-specifier-resolution=node support extension-less files #41465
Closed
arcanis
arcanis reviewed Jan 14, 2022
View reviewed changes
Comment thread doc/api/cli.md Outdated
Comment on lines +37 to +38
* If the program was started with a command-line flag that forces the entry
point to be loaded with ECMAScript module loader.

@arcanis arcanis Jan 14, 2022

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should explicitly refer to --input-type. I know that --loader also currently makes the entry point esm, but that's a bug (cf #33226, cc @cspotcode).

Formalizing this behaviour on --loader is problematic because it means whoever sets must also be sure that the entry point is esm. This isn't always the case: NODE_OPTIONS is typically set long before node is ever called, and by processes which aren't necessarily aware of what the entry point will be (or even if there will only be one of them).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cspotcode reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

ghost Jan 14, 2022

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To save readers some time, this is the comment where we established that it is, indeed, a bug: #33226 (comment)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

ghost Jan 14, 2022

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is trying to describe the current behavior of Node.js, not its final or ideal state. I don't call out which CLI flags do trigger this behavior precisely because of that discussion you linked to. --experimental-specifier-resolution=node also triggers this behavior.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

ghost Jan 14, 2022

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nature of the fix isn't clear yet so idk if this PR needs to fix it right now. I wouldn't want to block this PR based upon #33226 .

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

ghost Jan 14, 2022

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only meant that documenting the interaction between --loader and entry points (even if --loader isn't explicitly referred to in this paragraph) may have a slight risk of suggesting to readers that it can be relied upon and that it'd be a regression to fix it. While loaders are experimental, I noticed people often expect experimental-induced breaking changes to be very visible in their API (like the hook refactoring) rather than subtle under-the-hood behavioural changes.

Note that this wasn't a blocker by any means, just a suggestion.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Antoine du Hamel added 3 commits January 14, 2022 20:47
Antoine du Hamel
fixup!
fe05b72
Antoine du Hamel
fixup!
5de6698
Antoine du Hamel
fixup!
b08ec87
@aduh95
aduh95 merged commit 8732591 into nodejs:master Jan 17, 2022
@aduh95

ghost commented Jan 17, 2022

Copy link
Copy Markdown
Contributor Author

Landed in 8732591

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aduh95
aduh95 deleted the module-system-selection branch January 17, 2022 10:36
@ruyadorno ruyadorno mentioned this pull request Feb 8, 2022
v17.5.0 release proposal #41897
Merged
@danielleadams danielleadams mentioned this pull request Mar 3, 2022
v16.14.1 release proposal #42200
Merged
@joyeecheung joyeecheung mentioned this pull request Mar 4, 2026
[25.x] module: fix extensionless CJS files in type:module packages #62083
Merged
@mcollina mcollina mentioned this pull request Mar 10, 2026
doc: remove extensionless CJS exception for type:module #62176
Merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@ljharb ljharb ljharb left review comments
@GeoffreyBooth GeoffreyBooth GeoffreyBooth approved these changes
@guybedford guybedford guybedford approved these changes
+3 more reviewers
@bmeck bmeck bmeck left review comments
@cspotcode cspotcode cspotcode left review comments
@arcanis arcanis arcanis left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

doc Issues and PRs related to Node.js documentation. module Issues and PRs related to the module subsystem.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

8 participants

@aduh95 @nodejs-github-bot @GeoffreyBooth @ljharb @bmeck @cspotcode @guybedford @arcanis
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.