[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.