diff --git a/fixtures/flight/loader/index.js b/fixtures/flight/loader/index.js
new file mode 100644
index 0000000000000..b9cfa5b73eee0
--- /dev/null
+++ b/fixtures/flight/loader/index.js
@@ -0,0 +1,24 @@
+import {resolve, getSource} from 'react-transport-dom-webpack/node-loader';
+
+export {resolve, getSource};
+
+import babel from '@babel/core';
+
+const babelOptions = {
+ babelrc: false,
+ ignore: [/\/(build|node_modules)\//],
+ plugins: [
+ '@babel/plugin-syntax-import-meta',
+ '@babel/plugin-transform-react-jsx',
+ ],
+};
+
+export async function transformSource(source, context, defaultTransformSource) {
+ const {format} = context;
+ if (format === 'module') {
+ const opt = Object.assign({filename: context.url}, babelOptions);
+ const {code} = await babel.transformAsync(source, opt);
+ return {source: code};
+ }
+ return defaultTransformSource(source, context, defaultTransformSource);
+}
diff --git a/fixtures/flight/loader/package.json b/fixtures/flight/loader/package.json
new file mode 100644
index 0000000000000..3dbc1ca591c05
--- /dev/null
+++ b/fixtures/flight/loader/package.json
@@ -0,0 +1,3 @@
+{
+ "type": "module"
+}
diff --git a/fixtures/flight/package.json b/fixtures/flight/package.json
index 92c41ebfe47a8..e8e30736f3fb2 100644
--- a/fixtures/flight/package.json
+++ b/fixtures/flight/package.json
@@ -67,7 +67,7 @@
"prebuild": "cp -r ../../build/node_modules/* ./node_modules/",
"start": "concurrently \"npm run start:server\" \"npm run start:client\"",
"start:client": "node scripts/start.js",
- "start:server": "NODE_ENV=development node --experimental-loader ./server/loader.mjs server",
+ "start:server": "NODE_ENV=development node --experimental-loader ./loader/index.js server",
"start:prod": "node scripts/build.js && NODE_ENV=production node server",
"build": "node scripts/build.js",
"test": "node scripts/test.js --env=jsdom"
diff --git a/fixtures/flight/server/handler.server.js b/fixtures/flight/server/handler.server.js
index f3b899d498d79..d7715af9cc757 100644
--- a/fixtures/flight/server/handler.server.js
+++ b/fixtures/flight/server/handler.server.js
@@ -2,18 +2,26 @@
import {pipeToNodeWritable} from 'react-transport-dom-webpack/server';
import * as React from 'react';
-import App from '../src/App.server';
-module.exports = function(req, res) {
+import url from 'url';
+
+function resolve(path) {
+ return url.pathToFileURL(require.resolve(path)).href;
+}
+
+module.exports = async function(req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
+ const m = await import('../src/App.server.js');
+ // const m = require('../src/App.server.js');
+ const App = m.default.default || m.default;
pipeToNodeWritable(, res, {
// TODO: Read from a map on the disk.
- [require.resolve('../src/Counter.client.js')]: {
+ [resolve('../src/Counter.client.js')]: {
id: './src/Counter.client.js',
chunks: ['1'],
name: 'default',
},
- [require.resolve('../src/ShowMore.client.js')]: {
+ [resolve('../src/ShowMore.client.js')]: {
id: './src/ShowMore.client.js',
chunks: ['2'],
name: 'default',
diff --git a/fixtures/flight/server/handler.server.mjs b/fixtures/flight/server/handler.server.mjs
deleted file mode 100644
index 0d21c80eebfd0..0000000000000
--- a/fixtures/flight/server/handler.server.mjs
+++ /dev/null
@@ -1,27 +0,0 @@
-import {pipeToNodeWritable} from 'react-transport-dom-webpack/server.js';
-import * as React from 'react';
-import App from '../src/App.server.js';
-
-import {URL} from 'url';
-
-const rootPath = import.meta.url;
-function resolve(relative) {
- return new URL(relative, rootPath).href;
-}
-
-export default function(req, res) {
- res.setHeader('Access-Control-Allow-Origin', '*');
- pipeToNodeWritable(, res, {
- // TODO: Read from a map on the disk.
- [resolve('../src/Counter.client.js')]: {
- id: './src/Counter.client.js',
- chunks: ['1'],
- name: 'default',
- },
- [resolve('../src/ShowMore.client.js')]: {
- id: './src/ShowMore.client.js',
- chunks: ['2'],
- name: 'default',
- },
- });
-};
diff --git a/fixtures/flight/server/index.js b/fixtures/flight/server/index.js
index ffa1a8628b8ec..00dc4815b7287 100644
--- a/fixtures/flight/server/index.js
+++ b/fixtures/flight/server/index.js
@@ -1,11 +1,7 @@
'use strict';
-require.extensions['.client.js'] = function(module, path) {
- module.exports = {
- $$typeof: Symbol.for('react.module.reference'),
- name: path,
- };
-};
+const register = require('react-transport-dom-webpack/node-register');
+register();
const babelRegister = require('@babel/register');
@@ -25,8 +21,7 @@ app.get('/', function(req, res) {
delete require.cache[key];
}
}
- import('./handler.server.mjs').then(m => m.default(req, res));
- // require('./handler.server.js')(req, res);
+ require('./handler.server.js')(req, res);
});
app.listen(3001, () => {
diff --git a/fixtures/flight/server/loader.mjs b/fixtures/flight/server/loader.mjs
deleted file mode 100644
index f2d109ce8179d..0000000000000
--- a/fixtures/flight/server/loader.mjs
+++ /dev/null
@@ -1,42 +0,0 @@
-import babel from '@babel/core';
-
-const options = {
- babelrc: false,
- ignore: [/\/(build|node_modules)\//],
- plugins: [
- '@babel/plugin-syntax-import-meta',
- '@babel/plugin-transform-react-jsx',
- ],
-};
-
-const optionsCommonJS = {
- ignore: [/\/(build|node_modules)\//],
- presets: ['react-app'],
- plugins: ['@babel/transform-modules-commonjs'],
-};
-
-export async function transformSource(source, context, defaultTransformSource) {
- const {format} = context;
- if (format === 'module' || format === 'commonjs') {
- const opt = Object.assign(
- {filename: context.url},
- format === 'commonjs' ? optionsCommonJS : options
- );
- const {code} = await babel.transformAsync(source, opt);
- return {source: code};
- }
- return defaultTransformSource(source, context);
-}
-
-export async function getSource(url, context, defaultGetSource) {
- if (url.endsWith('.client.js')) {
- const name = url;
- return {
- source:
- "export default { $$typeof: Symbol.for('react.module.reference'), name: " +
- JSON.stringify(name) +
- '}',
- };
- }
- return defaultGetSource(url, context, defaultGetSource);
-}
diff --git a/packages/react-transport-dom-webpack/esm/package.json b/packages/react-transport-dom-webpack/esm/package.json
new file mode 100644
index 0000000000000..3dbc1ca591c05
--- /dev/null
+++ b/packages/react-transport-dom-webpack/esm/package.json
@@ -0,0 +1,3 @@
+{
+ "type": "module"
+}
diff --git a/packages/react-transport-dom-webpack/esm/react-transport-dom-webpack-node-loader.js b/packages/react-transport-dom-webpack/esm/react-transport-dom-webpack-node-loader.js
new file mode 100644
index 0000000000000..d7a01f6f221d9
--- /dev/null
+++ b/packages/react-transport-dom-webpack/esm/react-transport-dom-webpack-node-loader.js
@@ -0,0 +1,10 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+export * from '../src/ReactFlightWebpackNodeLoader.js';
diff --git a/packages/react-transport-dom-webpack/node-register.js b/packages/react-transport-dom-webpack/node-register.js
new file mode 100644
index 0000000000000..03754438bf338
--- /dev/null
+++ b/packages/react-transport-dom-webpack/node-register.js
@@ -0,0 +1,10 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+export * from './src/ReactFlightWebpackNodeRegister';
diff --git a/packages/react-transport-dom-webpack/npm/esm/package.json b/packages/react-transport-dom-webpack/npm/esm/package.json
new file mode 100644
index 0000000000000..3dbc1ca591c05
--- /dev/null
+++ b/packages/react-transport-dom-webpack/npm/esm/package.json
@@ -0,0 +1,3 @@
+{
+ "type": "module"
+}
diff --git a/packages/react-transport-dom-webpack/npm/node-register.js b/packages/react-transport-dom-webpack/npm/node-register.js
new file mode 100644
index 0000000000000..87a431afe1989
--- /dev/null
+++ b/packages/react-transport-dom-webpack/npm/node-register.js
@@ -0,0 +1,3 @@
+'use strict';
+
+module.exports = require('./cjs/react-transport-dom-webpack-node-register.js');
diff --git a/packages/react-transport-dom-webpack/package.json b/packages/react-transport-dom-webpack/package.json
index b362d0847afa4..a71a558f5160f 100644
--- a/packages/react-transport-dom-webpack/package.json
+++ b/packages/react-transport-dom-webpack/package.json
@@ -17,9 +17,21 @@
"server.js",
"server.browser.js",
"server.node.js",
+ "node-register.js",
"cjs/",
- "umd/"
+ "umd/",
+ "esm/"
],
+ "exports": {
+ ".": "./index.js",
+ "./plugin": "./plugin.js",
+ "./server": "./server.js",
+ "./server.browser": "./server.browser.js",
+ "./server.node": "./server.node.js",
+ "./node-loader": "./esm/react-transport-dom-webpack-node-loader.js",
+ "./node-register": "./node-register.js",
+ "./package.json": "./package.json"
+ },
"browser": {
"./server.js": "./server.browser.js"
},
diff --git a/packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeLoader.js b/packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeLoader.js
new file mode 100644
index 0000000000000..f9fd2a54e24f6
--- /dev/null
+++ b/packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeLoader.js
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+type ResolveContext = {
+ conditions: Array,
+ parentURL: string | void,
+};
+
+type ResolveFunction = (
+ string,
+ ResolveContext,
+ ResolveFunction,
+) => Promise;
+
+type GetSourceContext = {
+ format: string,
+ url: string,
+};
+
+type GetSourceFunction = (
+ string,
+ GetSourceContext,
+ GetSourceFunction,
+) => Promise<{source: Source}>;
+
+type Source = string | ArrayBuffer | Uint8Array;
+
+export async function resolve(
+ specifier: string,
+ context: ResolveContext,
+ defaultResolve: ResolveFunction,
+): Promise {
+ // TODO: Resolve server-only files.
+ return defaultResolve(specifier, context, defaultResolve);
+}
+
+export async function getSource(
+ url: string,
+ context: GetSourceContext,
+ defaultGetSource: GetSourceFunction,
+): Promise<{source: Source}> {
+ if (url.endsWith('.client.js')) {
+ // TODO: Named exports.
+ const src =
+ "export default { $$typeof: Symbol.for('react.module.reference'), name: " +
+ JSON.stringify(url) +
+ '}';
+ return {source: src};
+ }
+ return defaultGetSource(url, context, defaultGetSource);
+}
diff --git a/packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeRegister.js b/packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeRegister.js
new file mode 100644
index 0000000000000..f5149be1c42c4
--- /dev/null
+++ b/packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeRegister.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+const url = require('url');
+
+module.exports = function register() {
+ (require: any).extensions['.client.js'] = function(module, path) {
+ module.exports = {
+ $$typeof: Symbol.for('react.module.reference'),
+ name: url.pathToFileURL(path).href,
+ };
+ };
+};
diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js
index 6a04d6aa4b066..15bbc68d364da 100644
--- a/scripts/rollup/bundles.js
+++ b/scripts/rollup/bundles.js
@@ -286,6 +286,24 @@ const bundles = [
externals: [],
},
+ /******* React Transport DOM Webpack Node.js Loader *******/
+ {
+ bundleTypes: [NODE_ESM],
+ moduleType: RENDERER_UTILS,
+ entry: 'react-transport-dom-webpack/node-loader',
+ global: 'ReactFlightWebpackNodeLoader',
+ externals: [],
+ },
+
+ /******* React Transport DOM Webpack Node.js CommonJS Loader *******/
+ {
+ bundleTypes: [NODE_ES2015],
+ moduleType: RENDERER_UTILS,
+ entry: 'react-transport-dom-webpack/node-register',
+ global: 'ReactFlightWebpackNodeRegister',
+ externals: ['url'],
+ },
+
/******* React Transport DOM Server Relay *******/
{
bundleTypes: [FB_WWW_DEV, FB_WWW_PROD],
diff --git a/scripts/rollup/validate/eslintrc.esm.js b/scripts/rollup/validate/eslintrc.esm.js
index e7f8d1ca09550..3b644b6274e44 100644
--- a/scripts/rollup/validate/eslintrc.esm.js
+++ b/scripts/rollup/validate/eslintrc.esm.js
@@ -45,7 +45,7 @@ module.exports = {
jest: true,
},
parserOptions: {
- ecmaVersion: 2015,
+ ecmaVersion: 2017,
sourceType: 'module',
},
rules: {
diff --git a/scripts/shared/pathsByLanguageVersion.js b/scripts/shared/pathsByLanguageVersion.js
index f1b19a8893a35..af6963f78949e 100644
--- a/scripts/shared/pathsByLanguageVersion.js
+++ b/scripts/shared/pathsByLanguageVersion.js
@@ -10,6 +10,7 @@
const esNextPaths = [
// Internal forwarding modules
'packages/*/*.js',
+ 'packages/*/esm/*.js',
// Source files
'packages/*/src/**/*.js',
'packages/dom-event-testing-library/**/*.js',