Skip to content

ES6 -> JavaScript module with "module": "esmodule" #838

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

Merged
merged 1 commit into from
Apr 13, 2024
Merged
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
20 changes: 10 additions & 10 deletions data/api/latest/js.json
Original file line number Diff line number Diff line change
Expand Up @@ -9057,8 +9057,8 @@
"id": "Js.WeakMap",
"name": "WeakMap",
"docstrings": [
"Provides bindings for ES6 WeakMap",
"ES6 WeakMap API"
"Provides bindings for JavaScript WeakMap",
"WeakMap API"
],
"items": [
{
Expand All @@ -9074,8 +9074,8 @@
"id": "Js.Map",
"name": "Map",
"docstrings": [
"Provides bindings for ES6 Map",
"ES6 Map API"
"Provides bindings for JavaScript Map",
"Map API"
],
"items": [
{
Expand All @@ -9091,8 +9091,8 @@
"id": "Js.WeakSet",
"name": "WeakSet",
"docstrings": [
"Provides bindings for ES6 WeakSet",
"ES6 WeakSet API"
"Provides bindings for JavaScript WeakSet",
"WeakSet API"
],
"items": [
{
Expand All @@ -9108,8 +9108,8 @@
"id": "Js.Set",
"name": "Set",
"docstrings": [
"Provides bindings for ES6 Set",
"ES6 Set API"
"Provides bindings for JavaScript Set",
"Set API"
],
"items": [
{
Expand Down Expand Up @@ -10138,7 +10138,7 @@
"kind": "type",
"name": "symbol",
"docstrings": [
"Js symbol type (only available in ES6)"
"Js symbol type"
],
"signature": "type symbol"
},
Expand Down Expand Up @@ -14984,4 +14984,4 @@
}
]
}
}
}
2 changes: 1 addition & 1 deletion misc_docs/syntax/decorator_module.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var root = Path.dirname("/User/github");

`@module` also supports [import attributes](https://github.com/tc39/proposal-import-attributes). It looks like this:

<CodeTab labels={["ReScript", "JS Output (ES6)"]}>
<CodeTab labels={["ReScript", "JS Output (Module)"]}>
```rescript
@module({from: "./myJson.json", with: {type_: "json", \"some-exotic-identifier": "someValue"}})
external myJson: Js.Json.t = "default"
Expand Down
2 changes: 1 addition & 1 deletion pages/docs/manual/latest/array-and-list.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ var x3 = Belt_Array.concatMany([y]);
```
</CodeTab>

> Note that array spreads compiles to `Belt.Array.concatMany` right now. This is likely to change to native ES6 array spreads in the future.
> Note that array spreads compiles to `Belt.Array.concatMany` right now. This is likely to change to native array spreads in the future.

## List

Expand Down
9 changes: 5 additions & 4 deletions pages/docs/manual/latest/build-configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ The command itself is called from inside `lib/bs`.

## package-specs

Output to either CommonJS (the default) or ES6 modules. Example:
Output to either CommonJS (the default) or JavaScript module. Example:

```json
{
Expand All @@ -133,7 +133,8 @@ Output to either CommonJS (the default) or ES6 modules. Example:
}
```

- `"module": "es6-global"` resolves `node_modules` using relative paths. Good for development-time usage of ES6 in conjunction with browsers like Safari and Firefox that support ES6 modules today. **No more dev-time bundling**!
- `"module": "commonjs"` generates output as CommonJS format.
- `"module": "esmodule"` generates output as JavaScript module format. Will be default value in next major.
- `"in-source": true` generates output alongside source files. If you omit it, it'll generate the artifacts into `lib/js`. The output directory is not configurable otherwise.

This configuration only applies to you, when you develop the project. When the project is used as a third-party library, the consumer's own `rescript.json` `package-specs` overrides the configuration here, logically.
Expand Down Expand Up @@ -198,7 +199,7 @@ To enable genType, set `"gentypeconfig"` at top level in the project's `rescript
```json
{
"gentypeconfig": {
"module": "es6",
"module": "esmodule",
"moduleResolution": "node",
"generatedFileExtension": ".gen.tsx",
"debug": {
Expand All @@ -211,7 +212,7 @@ To enable genType, set `"gentypeconfig"` at top level in the project's `rescript

`generatedFileExtension`: File extension used for genType generated files (defaults to `".gen.tsx"`)

`module`: Module format used for the generated `*.gen.tsx` files (supports `"es6"` and `"commonjs"`)
`module`: Module format used for the generated `*.gen.tsx` files (supports `"esmodule"` and `"commonjs"`)

`moduleResolution`: Module resolution strategy used in genType outputs. This may be required for compatibility with TypeScript projects. Specify the value as the same in `tsconfig.json`.

Expand Down
2 changes: 1 addition & 1 deletion pages/docs/manual/latest/build-pinned-dependencies.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The build system respects the following rules for each package type:
- Builds dev dependencies
- Builds pinned dependencies
- Runs custom rules
- Package-specs like ES6/CommonJS overrides all its dependencies
- Package-specs like JavaScript module or CommonJS overrides all its dependencies

**Pinned dependencies**
- Warnings reported
Expand Down
42 changes: 21 additions & 21 deletions pages/docs/manual/latest/import-from-export-to-js.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ canonical: "/docs/manual/latest/import-from-export-to-js"

You've seen how ReScript's idiomatic [Import & Export](import-export.md) works. This section describes how we work with importing stuff from JavaScript and exporting stuff for JavaScript consumption.

**Note**: due to JS ecosystem's module compatibility issues, our advice of keeping your ReScript file's compiled JS output open in a tab applies here **more than ever**, as you don't want to subtly output the wrong JS module import/export code, on top of having to deal with Babel/Webpack/Jest/Node's CommonJS \<-> ES6 compatibility shims.
**Note**: due to JS ecosystem's module compatibility issues, our advice of keeping your ReScript file's compiled JS output open in a tab applies here **more than ever**, as you don't want to subtly output the wrong JS module import/export code, on top of having to deal with Babel/Webpack/Jest/Node's CommonJS \<-> JavaScript module compatibility shims.

In short: **make sure your bindings below output what you'd have manually written in JS**.

## Output Format

We support 2 JavaScript import/export formats:

- JavaScript module: `import * from 'MyReScriptFile'` and `export let ...`.
- CommonJS: `require('myFile')` and `module.exports = ...`.
- ES6 modules: `import * from 'MyReScriptFile'` and `export let ...`.

The format is [configurable in via `rescript.json`](build-configuration.md#package-specs).

Expand All @@ -27,19 +27,19 @@ The format is [configurable in via `rescript.json`](build-configuration.md#packa

Use the `module` [external](external.md):

<CodeTab labels={["ReScript", "JS Output (CommonJS)", "JS Output (ES6)"]}>
<CodeTab labels={["ReScript", "JS Output (Module)", "JS Output (CommonJS)"]}>

```res example
// Import nodejs' path.dirname
@module("path") external dirname: string => string = "dirname"
let root = dirname("/User/github") // returns "User"
```
```js
var Path = require("path");
import * as Path from "path";
var root = Path.dirname("/User/github");
```
```js
import * as Path from "path";
var Path = require("path");
var root = Path.dirname("/User/github");
```

Expand All @@ -57,30 +57,30 @@ Here's what the `external` does:

By omitting the string argument to `module`, you bind to the whole JS module:

<CodeTab labels={["ReScript", "JS Output (CommonJS)", "JS Output (ES6)"]}>
<CodeTab labels={["ReScript", "JS Output (Module)", "JS Output (CommonJS)"]}>

```res example
@module external leftPad: (string, int) => string = "./leftPad"
let paddedResult = leftPad("hi", 5)
```
```js
var LeftPad = require("./leftPad");
import * as LeftPad from "./leftPad";
var paddedResult = LeftPad("hi", 5);
Comment on lines +67 to 68
Copy link
Member Author

Choose a reason for hiding this comment

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

@module external leftPad: (string, int) => string = "./leftPad"
let paddedResult = leftPad("hi", 5)

btw, this is a problematic example that only works with transpilers such as Babel or TypeScript (a.k.a synthetic default) and does not work with standard modules.

```
```js
import * as LeftPad from "./leftPad";
var LeftPad = require("./leftPad");
var paddedResult = LeftPad("hi", 5);
```

</CodeTab>

Depending on whether you're compiling ReScript to CommonJS or ES6 module, **this feature will generate subtly different code**. Please check both output tabs to see the difference. The ES6 output here would be wrong!
Depending on whether you're compiling ReScript to JavaScript module or CommonJS, **this feature will generate subtly different code**. Please check both output tabs to see the difference. The JavaScript module output here would be wrong!

### Import an ES6 Default Export
### Import an `default` Export

Use the value `"default"` on the right hand side:
Use the value `default` on the right hand side:

<CodeTab labels={["ReScript", "JS Output (ES6)"]}>
<CodeTab labels={["ReScript", "JS Output (Module)"]}>

```res example
@module("./student") external studentName: string = "default"
Expand All @@ -97,9 +97,9 @@ var studentName = Student;

**Since 11.1**

[Import attributes](https://github.com/tc39/proposal-import-attributes) can be used in ReScript, as long as ReScript is configured to output ES6. You do that by passing configuration to the `@module` attribute:
[Import attributes](https://github.com/tc39/proposal-import-attributes) can be used in ReScript, as long as ReScript is configured to output JavaScript module. You do that by passing configuration to the `@module` attribute:

<CodeTab labels={["ReScript", "JS Output (ES6)"]}>
<CodeTab labels={["ReScript", "JS Output (Module)"]}>
```rescript
@module({from: "./myJson.json", with: {type_: "json", \"some-exotic-identifier": "someValue"}})
external myJson: Js.Json.t = "default"
Expand Down Expand Up @@ -141,7 +141,7 @@ let sub = (a, b) => a - b

Now let's dynamically import the add function in another module, e.g. `App.res`:

<CodeTab labels={["ReScript", "JS Output (ES6)"]}>
<CodeTab labels={["ReScript", "JS Output (Module)"]}>
```rescript
// App.res
let main = async () => {
Expand All @@ -165,7 +165,7 @@ async function main() {

### Dynamically Importing an Entire Module
The syntax for importing a whole module looks a little different, since we are operating on the module syntax level; instead of using `Js.import`, you may simply `await` the module itself:
<CodeTab labels={["ReScript", "JS Output (ES6)"]}>
<CodeTab labels={["ReScript", "JS Output (Module)"]}>
```rescript
// App.res
let main = async () => {
Expand All @@ -191,9 +191,9 @@ async function main() {

As mentioned in ReScript's idiomatic [Import & Export](import-export.md), every let binding and module is exported by default to other ReScript modules (unless you use a `.resi` [interface file](module#signatures)). If you open up the compiled JS file, you'll see that these values can also directly be used by a _JavaScript_ file too.

### Export an ES6 Default Value
### Export a `default` Value

If your JS project uses ES6 modules, you're likely exporting & importing some default values:
If your JS project uses JavaScript module, you're likely exporting & importing some default values:

```js
// student.js
Expand All @@ -207,7 +207,7 @@ import studentName from 'student.js';

A JavaScript default export is really just syntax sugar for a named export implicitly called `default` (now you know!). So to export a default value from ReScript, you can just do:

<CodeTab labels={["ReScript", "JS Output (CommonJS)", "JS Output (ES6)"]}>
<CodeTab labels={["ReScript", "JS Output (Module)", "JS Output (CommonJS)"]}>

```res example
// ReScriptStudent.res
Expand All @@ -218,7 +218,7 @@ var $$default = "Bob";

exports.$$default = $$default;
exports.default = $$default;
// informal transpiler-compatible marker of a default export compiled from ES6
// informal transpiler-compatible marker of a default export compiled from JavaScript module
exports.__esModule = true;
```
```js
Expand All @@ -239,4 +239,4 @@ You can then import this default export as usual on the JS side:
import studentName from 'ReScriptStudent.js';
```

If your JavaScript's ES6 default import is transpiled by Babel/Webpack/Jest into CommonJS `require`s, we've taken care of that too! See the CommonJS output tab for `__esModule`.
If your JavaScript's default import is transpiled by Babel/Webpack/Jest into CommonJS `require`s, we've taken care of that too! See the CommonJS output tab for `__esModule`.
2 changes: 1 addition & 1 deletion pages/docs/manual/latest/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ bun create rescript-app
],
"package-specs": [
{
"module": "es6",
"module": "esmodule",
"in-source": true
}
],
Expand Down
4 changes: 2 additions & 2 deletions pages/docs/manual/latest/typescript-integration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ The minimal configuration of genType is following:
```json
{
"gentypeconfig": {
"module": "es6",
"module": "esmodule",
"moduleResolution": "node",
"generatedFileExtension": ".gen.tsx"
}
Expand All @@ -153,7 +153,7 @@ And don't forget to make sure `allowJs` is set to `true` in the project's `tscon

Make sure to set the same `moduleResolution` value in both `rescript.json` and `tsconfig.json`, so that the output of genType is done with the preferred module resolution.

For example if the TypeScript project uses ES Modules with `Node16` / `NodeNext` module resolution:
For example if the TypeScript project uses JavaScript modules with `Node16` / `NodeNext` module resolution:

```json
// tsconfig.json
Expand Down
8 changes: 4 additions & 4 deletions pages/docs/react/latest/styling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ let style =
Use a `%%raw` expression to import CSS files within your ReScript / React component code:

```rescript
// in a CommonJS setup
%%raw("require('./styles/main.css')")

// or with ES6
// in a JS module setup
%%raw("import './styles/main.css'")

// or with CommonJS
%%raw("require('./styles/main.css')")
```

## CSS Modules
Expand Down
8 changes: 4 additions & 4 deletions pages/docs/react/v0.10.0/styling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ let style =
Use a `%%raw` expression to import CSS files within your ReScript / React component code:

```rescript
// in a CommonJS setup
%%raw("require('./styles/main.css')")

// or with ES6
// in a JS module setup
%%raw("import './styles/main.css'")

// or with CommonJS
%%raw("require('./styles/main.css')")
```

## CSS Modules
Expand Down
8 changes: 4 additions & 4 deletions pages/docs/react/v0.11.0/styling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ let style =
Use a `%%raw` expression to import CSS files within your ReScript / React component code:

```rescript
// in a CommonJS setup
%%raw("require('./styles/main.css')")

// or with ES6
// in a JS module setup
%%raw("import './styles/main.css'")

// or with CommonJS
%%raw("require('./styles/main.css')")
```

## CSS Modules
Expand Down
2 changes: 1 addition & 1 deletion src/Playground.res
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ module Settings = {
<div className="mt-6">
<div className=titleClass> {React.string("Module-System")} </div>
<ToggleSelection
values=["nodejs", "es6"]
values=["commonjs", "esmodule"]
toLabel={value => value}
selected=config.module_system
onChange=onModuleSystemUpdate
Expand Down
4 changes: 2 additions & 2 deletions src/bindings/RescriptCompilerApi.res
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,8 @@ module Compiler = {

let setConfig = (t: t, config: Config.t): unit => {
let moduleSystem = switch config.module_system {
| "nodejs" => #nodejs->Some
| "es6" => #es6->Some
| "commonjs" => #nodejs->Some
| "esmodule" => #es6->Some
| _ => None
}

Expand Down
4 changes: 2 additions & 2 deletions src/common/CompilerManagerHook.res
Original file line number Diff line number Diff line change
Expand Up @@ -407,12 +407,12 @@ let useCompilerManager = (

// Note: The compiler bundle currently defaults to
// commonjs when initiating the compiler, but our playground
// should default to ES6. So we override the config
// should default to esmodule. So we override the config
// and use the `setConfig` function to sync up the
// internal compiler state with our playground state.
let config = {
...instance->Compiler.getConfig,
module_system: "es6",
module_system: "esmodule",
?open_modules,
}
instance->Compiler.setConfig(config)
Expand Down