UNPKG

gatsby

Version:
238 lines (189 loc) • 9.84 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.PartialHydrationPlugin = void 0; var path = _interopRequireWildcard(require("path")); var _Template = _interopRequireDefault(require("webpack/lib/Template")); var _ModuleDependency = _interopRequireDefault(require("webpack/lib/dependencies/ModuleDependency")); var _NullDependency = _interopRequireDefault(require("webpack/lib/dependencies/NullDependency")); var _url = _interopRequireDefault(require("url")); var _webpack = _interopRequireDefault(require("webpack")); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /** * @see https://github.com/facebook/react/blob/3f70e68cea8d2ed0f53d35420105ae20e22ce428/packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js#L27-L35 */ class ClientReferenceDependency extends _ModuleDependency.default { constructor(request) { super(request); } get type() { return `client-reference`; } } /** * inspiration and code mostly comes from https://github.com/facebook/react/blob/3f70e68cea8d2ed0f53d35420105ae20e22ce428/packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js */ class PartialHydrationPlugin { name = `PartialHydrationPlugin`; _references = []; _clientModules = new Set(); constructor(manifestPath, rootFilePath) { this._manifestPath = manifestPath; this._rootFilePath = rootFilePath; } _generateClientReferenceChunk(reference, module, rootContext) { const chunkName = _Template.default.toPath( // @ts-ignore - types are incorrect path.relative(rootContext, reference.userRequest)); const dep = new ClientReferenceDependency(reference.rawRequest); const block = new _webpack.default.AsyncDependenciesBlock({ name: chunkName }, undefined, // @ts-ignore - types are incorrect reference.request); block.addDependency(dep); module.addBlock(block); } _generateManifest(_chunkGroups, moduleGraph, chunkGraph, rootContext) { const json = {}; // @see https://github.com/facebook/react/blob/3f70e68cea8d2ed0f53d35420105ae20e22ce428/packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js#L220-L252 const recordModule = (id, module, exports, chunkIds) => { if ( // @ts-ignore - types are incorrect !module.resource) { return; } const normalModule = module; const moduleExports = {}; exports.forEach(({ originalExport, resolvedExport }) => { moduleExports[originalExport] = { id: id, chunks: chunkIds, name: resolvedExport }; }); const href = _url.default.pathToFileURL(normalModule.resource).href.replace(rootContext.replace(/\\/g, `/`), ``).replace(/file:\/{3,4}/g, `file://`); if (href !== undefined) { json[href] = moduleExports; } }; const toRecord = new Map(); for (const clientModule of this._clientModules) { for (const connection of moduleGraph.getIncomingConnections(clientModule)) { if (connection.dependency) { if (toRecord.has(connection.module)) { continue; } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const childMap = new Map(); toRecord.set(connection.module, childMap); for (const exportInfo of moduleGraph.getExportsInfo(connection.module).exports) { if (exportInfo.isReexport()) { var _childMap$get; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const targetInfo = exportInfo.getTarget(moduleGraph); if (!childMap.has(targetInfo.module)) { childMap.set(targetInfo.module, []); } (_childMap$get = childMap.get(targetInfo.module)) === null || _childMap$get === void 0 ? void 0 : _childMap$get.push({ originalExport: exportInfo.name, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion resolvedExport: targetInfo.export[0] }); } else { var _childMap$get2; if (!childMap.has(connection.module)) { childMap.set(connection.module, []); } (_childMap$get2 = childMap.get(connection.module)) === null || _childMap$get2 === void 0 ? void 0 : _childMap$get2.push({ originalExport: exportInfo.name, resolvedExport: exportInfo.name }); } } } } } for (const [originalModule, resolvedMap] of toRecord) { for (const [resolvedModule, exports] of resolvedMap) { const chunkIds = new Set(); for (const chunk of chunkGraph.getModuleChunksIterable(resolvedModule)) { for (const group of chunk.groupsIterable) { for (const chunkInGroup of group.chunks) { if (chunkInGroup.id) { chunkIds.add(chunkInGroup.id); } } } } const moduleId = chunkGraph.getModuleId(resolvedModule); recordModule(moduleId, originalModule, exports, Array.from(chunkIds)); } } toRecord.clear(); return json; } apply(compiler) { compiler.hooks.thisCompilation.tap(this.name, (compilation, { normalModuleFactory }) => { // tell webpack that this is a regular javascript module compilation.dependencyFactories.set(ClientReferenceDependency, normalModuleFactory); // don't add extra code to the source file compilation.dependencyTemplates.set(ClientReferenceDependency, new _NullDependency.default.Template()); // const entryModule: webpack.NormalModule | null = null const handler = parser => { parser.hooks.program.tap(this.name, ast => { const hasClientExportDirective = ast.body.find(statement => statement.type === `ExpressionStatement` && statement.directive === `client export`); const module = parser.state.module; if (hasClientExportDirective) { this._clientModules.add(module); // if (entryModule) { // console.log(`parse`, module.resource) // this._generateClientReferenceChunk( // module, // entryModule, // compilation.options.context as string // ) // entryModule.invalidateBuild() // } } // if (module.resource.includes(`production-app`) && !entryModule) { // entryModule = module // for (const clientModule of this._clientModules) { // this._generateClientReferenceChunk( // clientModule, // entryModule, // compilation.options.context as string // ) // } // } }); }; compilation.hooks.optimizeChunkModules.tap(this.name, // eslint-disable-next-line @typescript-eslint/no-unused-vars () => { // 1. move clientModules into their own chunk // 2. disconnect module from original chunk for (const clientModule of this._clientModules) { const chunkName = _Template.default.toPath( // @ts-ignore - types are incorrect path.relative(compilation.options.context, clientModule.userRequest)); const selectedChunks = Array.from(compilation.chunkGraph.getModuleChunksIterable(clientModule)); const chunk = compilation.addChunk(chunkName); chunk.chunkReason = `PartialHydration client module`; compilation.chunkGraph.connectChunkAndModule(chunk, clientModule); for (const connectedChunk of selectedChunks) { compilation.chunkGraph.disconnectChunkAndModule(connectedChunk, clientModule); connectedChunk.split(chunk); } } }); normalModuleFactory.hooks.parser.for(`javascript/auto`).tap(this.name, handler); normalModuleFactory.hooks.parser.for(`javascript/esm`).tap(this.name, handler); normalModuleFactory.hooks.parser.for(`javascript/dynamic`).tap(this.name, handler); compilation.hooks.processAssets.tap({ name: this.name, stage: _webpack.default.Compilation.PROCESS_ASSETS_STAGE_REPORT }, () => { const manifest = this._generateManifest(compilation.chunkGroups, compilation.moduleGraph, compilation.chunkGraph, compilation.options.context); compilation.emitAsset(this._manifestPath, new _webpack.default.sources.RawSource(JSON.stringify(manifest, null, 2), false)); }); }); } } exports.PartialHydrationPlugin = PartialHydrationPlugin; //# sourceMappingURL=partial-hydration.js.map