gatsby
Version:
Blazing fast modern site generator for React
238 lines (189 loc) • 9.84 kB
JavaScript
;
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