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

Clarify recommendations regarding .mjs #228

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/features/dynamic-import.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Here’s how to statically import and use the `./utils.mjs` module:
```

:::note
**Note:** The previous example uses the `.mjs` extension to signal that it’s a module rather than a regular script. On the web, file extensions don’t really matter, as long as the files are served with the correct MIME type (e.g. `text/javascript` for JavaScript files) in the `Content-Type` HTTP header. The `.mjs` extension is [especially useful](https://github.com/nodejs/node-eps/blob/master/002-es-modules.md#32-determining-if-source-is-an-es-module) on other platforms such as Node.js, where there’s no concept of MIME types or other mandatory hooks such as `type="module"` to determine whether something is a module or a regular script. We’re using the same extension here for consistency across platforms and to clearly make the distinction between modules and regular scripts.
Copy link
Member

Choose a reason for hiding this comment

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

Seems like we should just keep this, and update the link to https://nodejs.org/api/esm.html#esm_enabling. We could also point out that d8 recognizes .mjs, in case we want to add more examples than just Node.js.

Copy link
Contributor Author

@GeoffreyBooth GeoffreyBooth Aug 24, 2019

Choose a reason for hiding this comment

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

I’ve updated my revision per your note.

The “no concept of MIME types or other mandatory hooks such as type="module"” language isn’t quite correct. As you know, Node has "type": "module" now, which in fairness didn’t exist back when this sentence was written; but before you argue that the current text is still correct because of the “mandatory” qualifier that was added in response to Node’s "type": "module", keep in mind that HTML’s type="module" isn’t mandatory either: a regular script can load a module via import(), with no type="module" in sight.

But that’s just one clause, and the paragraph is stronger without it. .mjs’s utility is that it indicates a file’s module-ness for all non-browser environments and tools, rather than Node.js’ "type": "module" or Spidermonkey’s -m or the various proprietary configuration options in Babel and Webpack and so on. I think that point comes across more clearly without the digression into .mjs’s competitors.

Copy link
Member

@mathiasbynens mathiasbynens Aug 24, 2019

Choose a reason for hiding this comment

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

The “no concept of MIME types or other mandatory hooks such as type="module"” language isn’t quite correct. As you know, Node has "type": "module" now, which in fairness didn’t exist back when this sentence was written; but before you argue that the current text is still correct because of the “mandatory” qualifier, keep in mind that HTML’s type="module" isn’t mandatory either: a regular script can load a module via import(), with no type="module" in sight.

Yeah, but the script doing the dynamic import() would be a classic script, not a module. The current phrasing is precise and correct, and I am not in favor of removing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Allow me to be blunt, if you don’t mind. The original sentence was this:

The .mjs extension is especially useful on other platforms such as Node.js, where there’s no concept of MIME types or other hooks such as type="module" to determine whether something is a module or a regular script.

Then "type": "module" came to Node, and so that sentence was no longer true. So you added “mandatory” before “hooks.”

As currently written, “mandatory” is a weasel word. It makes the sentence true again (arguably) but it is misleading to the reader in that it implies that .mjs is the only way to use ESM in Node. No reasonable reader would read this sentence and assume that there was any way to use ESM in Node other than via .mjs. No one would read this and think, “oh, but does Node have any non-mandatory hooks?”

If you want to recommend .mjs, that’s fine; my revised text makes that case for you, without deceptive language.

Copy link
Contributor Author

@GeoffreyBooth GeoffreyBooth Aug 24, 2019

Choose a reason for hiding this comment

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

P.S. to my last comment: I didn’t mean to imply that you were being deceptive in your edit. I only meant that it comes across that way to me, and I’m hardly unbiased. Apologies if I came across otherwise.

We’re debating the wrong thing. The question isn’t whether the current text is wrong or incorrect; the question should be whether the new text is better. So let’s compare:

Old: The .mjs extension is especially useful on other platforms such as Node.js, where there’s no concept of MIME types or other mandatory hooks such as type="module" to determine whether something is a module or a regular script.

New: The .mjs extension is especially useful on other platforms such as Node.js and d8, where it’s a convenient way to indicate to the runtime that your code is a module.

Both sentences offer a reason why .mjs is especially useful. The old sentence’s reason is why .mjs is useful for platforms: .mjs is useful for such platforms to have a way to be able to recognize modules. The new sentence’s reason is why .mjs is useful for users: this is a convenient way to signal to platforms that a file is a module.

As this article is written for users, I think the new sentence is more appropriate.

**Note:** The previous example uses the `.mjs` extension to signal that it’s a module rather than a regular script. On the web, file extensions don’t really matter, as long as the files are served with the correct MIME type (e.g. `text/javascript` for JavaScript files) in the `Content-Type` HTTP header. The `.mjs` extension is especially useful on other platforms such as [Node.js](https://nodejs.org/api/esm.html#esm_enabling) and [`d8`](/docs/d8), where it’s a convenient way to indicate to the runtime that your code is a module. We’re using the same extension here for consistency across platforms and to clearly make the distinction between modules and regular scripts.
:::

This syntactic form for importing modules is a *static* declaration: it only accepts a string literal as the module specifier, and introduces bindings into the local scope via a pre-runtime “linking” process. The static `import` syntax can only be used at the top-level of the file.
Expand Down
11 changes: 7 additions & 4 deletions src/features/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,16 @@ Another difference relates to the `async` attribute, which causes the script to

You may have noticed we’re using the `.mjs` file extension for modules. On the Web, the file extension doesn’t really matter, as long as the file is served with [the JavaScript MIME type `text/javascript`](https://html.spec.whatwg.org/multipage/scripting.html#scriptingLanguages:javascript-mime-type). The browser knows it’s a module because of the `type` attribute on the script element.

Still, we recommend using the `.mjs` extension for modules, for two reasons:
In production, you should name your files with an `.mjs` extension only if you are sure that they will be served with the required `Content-Type: text/javascript` header. If your host lacks such support, or if you are publishing open source JavaScript for public use by others, it’s safer to use the `.js` extension.

1. During development, it makes it crystal clear that the file is a module as opposed to a regular script. (It’s not always possible to tell just by looking at the code.) As mentioned before, modules are treated differently than regular scripts, so the difference is hugely important!
1. It’s consistent with Node.js, where [the experimental modules implementation](https://nodejs.org/api/esm.html) only supports files with the `.mjs` extension by default.
GeoffreyBooth marked this conversation as resolved.
Show resolved Hide resolved
GeoffreyBooth marked this conversation as resolved.
Show resolved Hide resolved
During development, we recommend using the `.mjs` extension for modules, for two reasons:

1. The `.mjs` extension makes it crystal clear to you and anyone else looking at your project that the file is a module as opposed to a regular script. (It’s not always possible to tell just by looking at the code.) As mentioned before, modules are treated differently than regular scripts, so the difference is hugely important!

1. It ensures that your file is parsed as a module by runtimes such as [Node.js](https://nodejs.org/api/esm.html#esm_enabling) and [`d8`](/docs/d8), and build tools such as [Babel](https://babeljs.io/docs/en/options#sourcetype). While these environments and tools each have proprietary ways via configuration to interpret `.js` files as modules, the `.mjs` extension is the cross-compatible way to ensure that files are treated as modules.

:::note
**Note:** To deploy `.mjs` on the web, your web server needs to be configured to serve files with this extension using the appropriate `Content-Type: text/javascript` header, as mentioned above. Additionally, you may want to configure your editor to treat `.mjs` files as `.js` files to get syntax highlighting. Most modern editors already do this by default.
GeoffreyBooth marked this conversation as resolved.
Show resolved Hide resolved
**Note:** You may want to configure your editor to treat `.mjs` files as `.js` files to get syntax highlighting. Most modern editors already do this by default.
Copy link
Contributor Author

@GeoffreyBooth GeoffreyBooth Aug 24, 2019

Choose a reason for hiding this comment

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

I’ve revised this to be much closer to your original text. I still think it weakens the article to include a discussion of non-Web platforms, but if you want that included, I think this language is a fairer and stronger explanation of .mjs’s benefits than the previous text. Now that all ESM-supporting runtimes (both browsers and Node.js and d8 and Deno and Spidermonkey and JSC) support ESM in either .js or .mjs files, I think it’s more accurate to say that the benefit of .mjs is that it’s a consistent way of declaring your code to be a module, in contrast to each runtime’s proprietary configuration. .mjs is metadata that ships with the file itself, rather than nearby such as in a package.json.

The previous previous text (“It’s consistent with Node.js, where the experimental modules implementation only supports files with the .mjs extension.” before you added “by default” in response to "type": "module") seems like it was written back when it seemed like .mjs was the only way that ESM was going to be supported in Node.js, and therefore it was really important to persuade the world to adopt .mjs in order to help Node join the ESM bandwagon (and vice versa, to help prevent ESM adoption from being slowed by incompatibility with Node). I know you say that something like "type": "module" was always the plan, and I’ve heard that from others in the Node modules group, but I’ve scoured the Internet for any record of such history and I can’t find any. I would be very curious to read any if you have any links you could share.

:::

### Module specifiers { #specifiers }
Expand Down