@samitouri / QOS-React-2 / commits / 41c5d00fc9

[Flight] Minimal webpack plugin (#20228)

Dan Abramov committed Dec 3, 2020 at 21:21 UTC 41c5d00fc90734822e915a6a268a24c9726828a5
6 files changed +52 -44
fixtures/flight/.gitignore
+1
@@ -10,6 +10,7 @@
10
11 # production
12 /build
13 +/dist
14
15 # misc
16 .DS_Store
fixtures/flight/server/handler.server.js
+10 -41
@@ -1,51 +1,20 @@
1 'use strict';
2
3 import {pipeToNodeWritable} from 'react-transport-dom-webpack/server';
4 +import {readFileSync} from 'fs';
5 +import {resolve} from 'path';
6 import * as React from 'react';
7
6 -import url from 'url';
7 -
8 -function resolve(path) {
9 - return url.pathToFileURL(require.resolve(path)).href;
10 -}
11 -
8 module.exports = async function(req, res) {
13 - res.setHeader('Access-Control-Allow-Origin', '*');
9 const m = await import('../src/App.server.js');
10 // const m = require('../src/App.server.js');
11 const App = m.default.default || m.default;
17 - pipeToNodeWritable(<App />, res, {
18 - // TODO: Read from a map on the disk.
19 - [resolve('../src/Counter.client.js')]: {
20 - Counter: {
21 - id: './src/Counter.client.js',
22 - chunks: ['2'],
23 - name: 'Counter',
24 - },
25 - },
26 - [resolve('../src/Counter2.client.js')]: {
27 - Counter: {
28 - id: './src/Counter2.client.js',
29 - chunks: ['1'],
30 - name: 'Counter',
31 - },
32 - },
33 - [resolve('../src/ShowMore.client.js')]: {
34 - default: {
35 - id: './src/ShowMore.client.js',
36 - chunks: ['3'],
37 - name: 'default',
38 - },
39 - '': {
40 - id: './src/ShowMore.client.js',
41 - chunks: ['3'],
42 - name: '',
43 - },
44 - '*': {
45 - id: './src/ShowMore.client.js',
46 - chunks: ['3'],
47 - name: '*',
48 - },
49 - },
50 - });
12 + res.setHeader('Access-Control-Allow-Origin', '*');
13 + const moduleMap = JSON.parse(
14 + readFileSync(
15 + resolve(__dirname, '../dist/react-transport-manifest.json'),
16 + 'utf8'
17 + )
18 + );
19 + pipeToNodeWritable(<App />, res, moduleMap);
20 };
fixtures/flight/src/index.js
+1 -1
@@ -19,5 +19,5 @@ ReactDOM.render(
19 );
20
21 // Create entry points for Client Components.
22 -// TODO: Webpack plugin should do this and write a map to disk.
22 +// TODO: Webpack plugin should do this.
23 require.context('./', true, /\.client\.js$/, 'lazy');
packages/react-transport-dom-webpack/src/ReactFlightWebpackPlugin.js
+36 -1
@@ -7,7 +7,42 @@
7 * @flow
8 */
9
10 +import {mkdirSync, writeFileSync} from 'fs';
11 +import {dirname, resolve} from 'path';
12 +import {pathToFileURL} from 'url';
13 +
14 export default class ReactFlightWebpackPlugin {
15 constructor(options: {isServer: boolean}) {}
12 - apply(compiler: any) {}
16 +
17 + apply(compiler: any) {
18 + compiler.hooks.emit.tap('React Transport Plugin', compilation => {
19 + const json = {};
20 + compilation.chunks.forEach(chunk => {
21 + chunk.getModules().forEach(mod => {
22 + if (!/\.client\.js$/.test(mod.resource)) {
23 + return;
24 + }
25 + const moduleExports = {};
26 + ['', '*'].concat(mod.buildMeta.providedExports).forEach(name => {
27 + moduleExports[name] = {
28 + id: mod.id,
29 + chunks: chunk.ids,
30 + name: name,
31 + };
32 + });
33 + const href = pathToFileURL(mod.resource).href;
34 + if (href !== undefined) {
35 + json[href] = moduleExports;
36 + }
37 + });
38 + });
39 + const output = JSON.stringify(json, null, 2);
40 + const filename = resolve(
41 + compiler.options.output.path,
42 + 'react-transport-manifest.json',
43 + );
44 + mkdirSync(dirname(filename), {recursive: true});
45 + writeFileSync(filename, output);
46 + });
47 + }
48 }
scripts/rollup/bundles.js
+1 -1
@@ -283,7 +283,7 @@ const bundles = [
283 moduleType: RENDERER_UTILS,
284 entry: 'react-transport-dom-webpack/plugin',
285 global: 'ReactFlightWebpackPlugin',
286 - externals: [],
286 + externals: ['fs', 'path', 'url'],
287 },
288
289 /******* React Transport DOM Webpack Node.js Loader *******/
scripts/rollup/modules.js
+3
@@ -9,6 +9,8 @@ const {UMD_DEV, UMD_PROD, UMD_PROFILING} = require('./bundles').bundleTypes;
9 const HAS_NO_SIDE_EFFECTS_ON_IMPORT = false;
10 // const HAS_SIDE_EFFECTS_ON_IMPORT = true;
11 const importSideEffects = Object.freeze({
12 + fs: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
13 + path: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
14 'prop-types/checkPropTypes': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
15 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
16 scheduler: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
@@ -17,6 +19,7 @@ const importSideEffects = Object.freeze({
19 'react/jsx-dev-runtime': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
20 'react-fetch/node': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
21 'react-dom': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
22 + url: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
23 });
24
25 // Bundles exporting globals that other modules rely on.