@samitouri / QOS-React-2 / commits / 82e99e1b02

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',