mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
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.
This commit is contained in:
committed by
GitHub
parent
bf7b7aeb10
commit
82e99e1b02
24
fixtures/flight/loader/index.js
Normal file
24
fixtures/flight/loader/index.js
Normal file
@@ -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);
|
||||
}
|
||||
3
fixtures/flight/loader/package.json
Normal file
3
fixtures/flight/loader/package.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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(<App />, 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',
|
||||
|
||||
@@ -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(<App />, 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',
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -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, () => {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
3
packages/react-transport-dom-webpack/esm/package.json
Normal file
3
packages/react-transport-dom-webpack/esm/package.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
10
packages/react-transport-dom-webpack/esm/react-transport-dom-webpack-node-loader.js
vendored
Normal file
10
packages/react-transport-dom-webpack/esm/react-transport-dom-webpack-node-loader.js
vendored
Normal file
@@ -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';
|
||||
10
packages/react-transport-dom-webpack/node-register.js
vendored
Normal file
10
packages/react-transport-dom-webpack/node-register.js
vendored
Normal file
@@ -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';
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
3
packages/react-transport-dom-webpack/npm/node-register.js
vendored
Normal file
3
packages/react-transport-dom-webpack/npm/node-register.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./cjs/react-transport-dom-webpack-node-register.js');
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
57
packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeLoader.js
vendored
Normal file
57
packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeLoader.js
vendored
Normal file
@@ -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<string>,
|
||||
parentURL: string | void,
|
||||
};
|
||||
|
||||
type ResolveFunction = (
|
||||
string,
|
||||
ResolveContext,
|
||||
ResolveFunction,
|
||||
) => Promise<string>;
|
||||
|
||||
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<string> {
|
||||
// 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);
|
||||
}
|
||||
19
packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeRegister.js
vendored
Normal file
19
packages/react-transport-dom-webpack/src/ReactFlightWebpackNodeRegister.js
vendored
Normal file
@@ -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,
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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],
|
||||
|
||||
@@ -45,7 +45,7 @@ module.exports = {
|
||||
jest: true,
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2015,
|
||||
ecmaVersion: 2017,
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user