Add Node ESM Loader and Register Entrypoints (#20274)
* Add Node ESM loader build This adds a loader build as a first-class export. This will grow in complexity so it deserves its own module. * Add Node CommonJS regiter build This adds a build as a first-class export for legacy CommonJS registration in Node.js. This will grow in complexity so it deserves its own module. * Simplify fixture a bit to easier show usage with or without esm * Bump es version We leave async function in here which are newer than ES2015.
Sebastian Markbåge committed
Nov 16, 2020 at 23:46 UTC
82e99e1b027636fd55937a3b5633064cbcb2ffe1
18 files changed
+181
-84
fixtures/flight/loader/index.js
new
+24
@@ -0,0 +1,24 @@
1
+import {resolve, getSource} from 'react-transport-dom-webpack/node-loader';
2
+
3
+export {resolve, getSource};
4
+
5
+import babel from '@babel/core';
6
+
7
+const babelOptions = {
8
+ babelrc: false,
9
+ ignore: [/\/(build|node_modules)\//],
10
+ plugins: [
11
+ '@babel/plugin-syntax-import-meta',
12
+ '@babel/plugin-transform-react-jsx',
13
+ ],
14
+};
15
+
16
+export async function transformSource(source, context, defaultTransformSource) {
17
+ const {format} = context;
18
+ if (format === 'module') {
19
+ const opt = Object.assign({filename: context.url}, babelOptions);
20
+ const {code} = await babel.transformAsync(source, opt);
21
+ return {source: code};
22
+ }
23
+ return defaultTransformSource(source, context, defaultTransformSource);
24
+}
fixtures/flight/loader/package.json
new
+3
@@ -0,0 +1,3 @@
1
+{
2
+ "type": "module"
3
+}
fixtures/flight/package.json
+1
-1
@@ -67,7 +67,7 @@
67
"prebuild": "cp -r ../../build/node_modules/* ./node_modules/",
68
"start": "concurrently \"npm run start:server\" \"npm run start:client\"",
69
"start:client": "node scripts/start.js",
70
- "start:server": "NODE_ENV=development node --experimental-loader ./server/loader.mjs server",
70
+ "start:server": "NODE_ENV=development node --experimental-loader ./loader/index.js server",
71
"start:prod": "node scripts/build.js && NODE_ENV=production node server",
72
"build": "node scripts/build.js",
73
"test": "node scripts/test.js --env=jsdom"
fixtures/flight/server/handler.server.js
+12
-4
@@ -2,18 +2,26 @@
2
3
import {pipeToNodeWritable} from 'react-transport-dom-webpack/server';
4
import * as React from 'react';
5
-import App from '../src/App.server';
5
7
-module.exports = function(req, res) {
6
+import url from 'url';
7
+
8
+function resolve(path) {
9
+ return url.pathToFileURL(require.resolve(path)).href;
10
+}
11
+
12
+module.exports = async function(req, res) {
13
res.setHeader('Access-Control-Allow-Origin', '*');
14
+ const m = await import('../src/App.server.js');
15
+ // const m = require('../src/App.server.js');
16
+ const App = m.default.default || m.default;
17
pipeToNodeWritable(<App />, res, {
18
// TODO: Read from a map on the disk.
11
- [require.resolve('../src/Counter.client.js')]: {
19
+ [resolve('../src/Counter.client.js')]: {
20
id: './src/Counter.client.js',
21
chunks: ['1'],
22
name: 'default',
23
},
16
- [require.resolve('../src/ShowMore.client.js')]: {
24
+ [resolve('../src/ShowMore.client.js')]: {
25
id: './src/ShowMore.client.js',
26
chunks: ['2'],
27
name: 'default',
fixtures/flight/server/handler.server.mjs
deleted
-27
@@ -1,27 +0,0 @@
1
-import {pipeToNodeWritable} from 'react-transport-dom-webpack/server.js';
2
-import * as React from 'react';
3
-import App from '../src/App.server.js';
4
-
5
-import {URL} from 'url';
6
-
7
-const rootPath = import.meta.url;
8
-function resolve(relative) {
9
- return new URL(relative, rootPath).href;
10
-}
11
-
12
-export default function(req, res) {
13
- res.setHeader('Access-Control-Allow-Origin', '*');
14
- pipeToNodeWritable(<App />, res, {
15
- // TODO: Read from a map on the disk.
16
- [resolve('../src/Counter.client.js')]: {
17
- id: './src/Counter.client.js',
18
- chunks: ['1'],
19
- name: 'default',
20
- },
21
- [resolve('../src/ShowMore.client.js')]: {
22
- id: './src/ShowMore.client.js',
23
- chunks: ['2'],
24
- name: 'default',
25
- },
26
- });
27
-};
fixtures/flight/server/index.js
+3
-8
@@ -1,11 +1,7 @@
1
'use strict';
2
3
-require.extensions['.client.js'] = function(module, path) {
4
- module.exports = {
5
- $$typeof: Symbol.for('react.module.reference'),
6
- name: path,
7
- };
8
-};
3
+const register = require('react-transport-dom-webpack/node-register');
4
+register();
5
6
const babelRegister = require('@babel/register');
7
@@ -25,8 +21,7 @@ app.get('/', function(req, res) {
21
delete require.cache[key];
22
}
23
}
28
- import('./handler.server.mjs').then(m => m.default(req, res));
29
- // require('./handler.server.js')(req, res);
24
+ require('./handler.server.js')(req, res);
25
});
26
27
app.listen(3001, () => {
fixtures/flight/server/loader.mjs
deleted
-42
@@ -1,42 +0,0 @@
1
-import babel from '@babel/core';
2
-
3
-const options = {
4
- babelrc: false,
5
- ignore: [/\/(build|node_modules)\//],
6
- plugins: [
7
- '@babel/plugin-syntax-import-meta',
8
- '@babel/plugin-transform-react-jsx',
9
- ],
10
-};
11
-
12
-const optionsCommonJS = {
13
- ignore: [/\/(build|node_modules)\//],
14
- presets: ['react-app'],
15
- plugins: ['@babel/transform-modules-commonjs'],
16
-};
17
-
18
-export async function transformSource(source, context, defaultTransformSource) {
19
- const {format} = context;
20
- if (format === 'module' || format === 'commonjs') {
21
- const opt = Object.assign(
22
- {filename: context.url},
23
- format === 'commonjs' ? optionsCommonJS : options
24
- );
25
- const {code} = await babel.transformAsync(source, opt);
26
- return {source: code};
27
- }
28
- return defaultTransformSource(source, context);
29
-}
30
-
31
-export async function getSource(url, context, defaultGetSource) {
32
- if (url.endsWith('.client.js')) {
33
- const name = url;
34
- return {
35
- source:
36
- "export default { $$typeof: Symbol.for('react.module.reference'), name: " +
37
- JSON.stringify(name) +
38
- '}',
39
- };
40
- }
41
- return defaultGetSource(url, context, defaultGetSource);
42
-}
packages/react-transport-dom-webpack/esm/package.json
new
+3
@@ -0,0 +1,3 @@
1
+{
2
+ "type": "module"
3
+}
packages/react-transport-dom-webpack/esm/react-transport-dom-webpack-node-loader.js
new
+10
@@ -0,0 +1,10 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+export * from '../src/ReactFlightWebpackNodeLoader.js';
packages/react-transport-dom-webpack/node-register.js
new
+10
@@ -0,0 +1,10 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+export * from './src/ReactFlightWebpackNodeRegister';
packages/react-transport-dom-webpack/npm/esm/package.json
new
+3
@@ -0,0 +1,3 @@
1
+{
2
+ "type": "module"
3
+}
packages/react-transport-dom-webpack/npm/node-register.js
new
+3
@@ -0,0 +1,3 @@
1
+'use strict';
2
+
3
+module.exports = require('./cjs/react-transport-dom-webpack-node-register.js');
packages/react-transport-dom-webpack/package.json
+13
-1
@@ -17,9 +17,21 @@
17
"server.js",
18
"server.browser.js",
19
"server.node.js",
20
+ "node-register.js",
21
"cjs/",
21
- "umd/"
22
+ "umd/",
23
+ "esm/"
24
],
25
+ "exports": {
26
+ ".": "./index.js",
27
+ "./plugin": "./plugin.js",
28
+ "./server": "./server.js",
29
+ "./server.browser": "./server.browser.js",
30
+ "./server.node": "./server.node.js",
31
+ "./node-loader": "./esm/react-transport-dom-webpack-node-loader.js",
32
+ "./node-register": "./node-register.js",
33
+ "./package.json": "./package.json"
34
+ },
35
"browser": {
36
"./server.js": "./server.browser.js"
37
},
packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeLoader.js
new
+57
@@ -0,0 +1,57 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+type ResolveContext = {
11
+ conditions: Array<string>,
12
+ parentURL: string | void,
13
+};
14
+
15
+type ResolveFunction = (
16
+ string,
17
+ ResolveContext,
18
+ ResolveFunction,
19
+) => Promise<string>;
20
+
21
+type GetSourceContext = {
22
+ format: string,
23
+ url: string,
24
+};
25
+
26
+type GetSourceFunction = (
27
+ string,
28
+ GetSourceContext,
29
+ GetSourceFunction,
30
+) => Promise<{source: Source}>;
31
+
32
+type Source = string | ArrayBuffer | Uint8Array;
33
+
34
+export async function resolve(
35
+ specifier: string,
36
+ context: ResolveContext,
37
+ defaultResolve: ResolveFunction,
38
+): Promise<string> {
39
+ // TODO: Resolve server-only files.
40
+ return defaultResolve(specifier, context, defaultResolve);
41
+}
42
+
43
+export async function getSource(
44
+ url: string,
45
+ context: GetSourceContext,
46
+ defaultGetSource: GetSourceFunction,
47
+): Promise<{source: Source}> {
48
+ if (url.endsWith('.client.js')) {
49
+ // TODO: Named exports.
50
+ const src =
51
+ "export default { $$typeof: Symbol.for('react.module.reference'), name: " +
52
+ JSON.stringify(url) +
53
+ '}';
54
+ return {source: src};
55
+ }
56
+ return defaultGetSource(url, context, defaultGetSource);
57
+}
packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeRegister.js
new
+19
@@ -0,0 +1,19 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+const url = require('url');
11
+
12
+module.exports = function register() {
13
+ (require: any).extensions['.client.js'] = function(module, path) {
14
+ module.exports = {
15
+ $$typeof: Symbol.for('react.module.reference'),
16
+ name: url.pathToFileURL(path).href,
17
+ };
18
+ };
19
+};
scripts/rollup/bundles.js
+18
@@ -286,6 +286,24 @@ const bundles = [
286
externals: [],
287
},
288
289
+ /******* React Transport DOM Webpack Node.js Loader *******/
290
+ {
291
+ bundleTypes: [NODE_ESM],
292
+ moduleType: RENDERER_UTILS,
293
+ entry: 'react-transport-dom-webpack/node-loader',
294
+ global: 'ReactFlightWebpackNodeLoader',
295
+ externals: [],
296
+ },
297
+
298
+ /******* React Transport DOM Webpack Node.js CommonJS Loader *******/
299
+ {
300
+ bundleTypes: [NODE_ES2015],
301
+ moduleType: RENDERER_UTILS,
302
+ entry: 'react-transport-dom-webpack/node-register',
303
+ global: 'ReactFlightWebpackNodeRegister',
304
+ externals: ['url'],
305
+ },
306
+
307
/******* React Transport DOM Server Relay *******/
308
{
309
bundleTypes: [FB_WWW_DEV, FB_WWW_PROD],
scripts/rollup/validate/eslintrc.esm.js
+1
-1
@@ -45,7 +45,7 @@ module.exports = {
45
jest: true,
46
},
47
parserOptions: {
48
- ecmaVersion: 2015,
48
+ ecmaVersion: 2017,
49
sourceType: 'module',
50
},
51
rules: {
scripts/shared/pathsByLanguageVersion.js
+1
@@ -10,6 +10,7 @@
10
const esNextPaths = [
11
// Internal forwarding modules
12
'packages/*/*.js',
13
+ 'packages/*/esm/*.js',
14
// Source files
15
'packages/*/src/**/*.js',
16
'packages/dom-event-testing-library/**/*.js',