next
Version:
The React Framework
250 lines (249 loc) • 12.4 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getEntrypointInfo = getEntrypointInfo;
exports.collectAssets = collectAssets;
exports.default = exports.ssrEntries = void 0;
var _webpack = require("next/dist/compiled/webpack/webpack");
var _utils = require("../../../shared/lib/router/utils");
var _constants = require("../../../shared/lib/constants");
var _nonNullable = require("../../../lib/non-nullable");
const PLUGIN_NAME = 'MiddlewarePlugin';
const MIDDLEWARE_FULL_ROUTE_REGEX = /^pages[/\\]?(.*)\/_middleware$/;
const ssrEntries = new Map();
exports.ssrEntries = ssrEntries;
const middlewareManifest = {
sortedMiddleware: [],
clientInfo: [],
middleware: {},
version: 1
};
function getPageFromEntrypointName(pagePath) {
const ssrEntryInfo = ssrEntries.get(pagePath);
const result = MIDDLEWARE_FULL_ROUTE_REGEX.exec(pagePath);
const page = result ? `/${result[1]}` : ssrEntryInfo ? pagePath.slice('pages'.length).replace(/\/index$/, '') || '/' : null;
return page;
}
function getEntrypointInfo(compilation, { envPerRoute , wasmPerRoute }, isEdgeRuntime) {
const entrypoints = compilation.entrypoints;
const infos = [];
for (const entrypoint of entrypoints.values()){
if (!entrypoint.name) continue;
const ssrEntryInfo = ssrEntries.get(entrypoint.name);
if (ssrEntryInfo && !isEdgeRuntime) continue;
if (!ssrEntryInfo && isEdgeRuntime) continue;
const page = getPageFromEntrypointName(entrypoint.name);
if (!page) {
continue;
}
const entryFiles = entrypoint.getFiles().filter((file)=>!file.endsWith('.hot-update.js')
);
const files = ssrEntryInfo ? [
ssrEntryInfo.requireFlightManifest ? `server/${_constants.MIDDLEWARE_FLIGHT_MANIFEST}.js` : null,
`server/${_constants.MIDDLEWARE_BUILD_MANIFEST}.js`,
`server/${_constants.MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js`,
...entryFiles.map((file)=>'server/' + file
),
].filter(_nonNullable.nonNullable) : entryFiles.map((file)=>file
);
infos.push({
env: envPerRoute.get(entrypoint.name) || [],
wasm: wasmPerRoute.get(entrypoint.name) || [],
files,
name: entrypoint.name,
page,
regexp: (0, _utils).getMiddlewareRegex(page, !ssrEntryInfo).namedRegex
});
}
return infos;
}
class MiddlewarePlugin {
constructor({ dev , isEdgeRuntime }){
this.dev = dev;
this.isEdgeRuntime = isEdgeRuntime;
}
createAssets(compilation, assets, { envPerRoute , wasmPerRoute }, isEdgeRuntime) {
const infos = getEntrypointInfo(compilation, {
envPerRoute,
wasmPerRoute
}, isEdgeRuntime);
infos.forEach((info)=>{
middlewareManifest.middleware[info.page] = info;
});
middlewareManifest.sortedMiddleware = (0, _utils).getSortedRoutes(Object.keys(middlewareManifest.middleware));
middlewareManifest.clientInfo = middlewareManifest.sortedMiddleware.map((key)=>{
const middleware = middlewareManifest.middleware[key];
const ssrEntryInfo = ssrEntries.get(middleware.name);
return [
key,
!!ssrEntryInfo
];
});
assets[this.isEdgeRuntime ? _constants.MIDDLEWARE_MANIFEST : `server/${_constants.MIDDLEWARE_MANIFEST}`] = new _webpack.sources.RawSource(JSON.stringify(middlewareManifest, null, 2));
}
apply(compiler) {
collectAssets(compiler, this.createAssets.bind(this), {
dev: this.dev,
pluginName: PLUGIN_NAME,
isEdgeRuntime: this.isEdgeRuntime
});
}
}
exports.default = MiddlewarePlugin;
function collectAssets(compiler, createAssets, options) {
const wp = compiler.webpack;
compiler.hooks.compilation.tap(options.pluginName, (compilation, { normalModuleFactory })=>{
compilation.hooks.afterChunks.tap(options.pluginName, ()=>{
const middlewareRuntimeChunk = compilation.namedChunks.get(_constants.MIDDLEWARE_RUNTIME_WEBPACK);
if (middlewareRuntimeChunk) {
middlewareRuntimeChunk.filenameTemplate = 'server/[name].js';
}
});
const envPerRoute = new Map();
const wasmPerRoute = new Map();
compilation.hooks.afterOptimizeModules.tap(PLUGIN_NAME, ()=>{
const { moduleGraph } = compilation;
envPerRoute.clear();
for (const [name, info] of compilation.entries){
if (info.options.runtime === _constants.MIDDLEWARE_SSR_RUNTIME_WEBPACK || info.options.runtime === _constants.MIDDLEWARE_RUNTIME_WEBPACK) {
const middlewareEntries = new Set();
const env = new Set();
const wasm = new Set();
const addEntriesFromDependency = (dep)=>{
const module = moduleGraph.getModule(dep);
if (module) {
middlewareEntries.add(module);
}
};
const runtime = wp.util.runtime.getEntryRuntime(compilation, name);
info.dependencies.forEach(addEntriesFromDependency);
info.includeDependencies.forEach(addEntriesFromDependency);
const queue = new Set(middlewareEntries);
for (const module1 of queue){
const { buildInfo } = module1;
if (buildInfo.nextWasmMiddlewareBinding) {
wasm.add(buildInfo.nextWasmMiddlewareBinding);
}
if (!options.dev && buildInfo && isUsedByExports({
module: module1,
moduleGraph,
runtime,
usedByExports: buildInfo.usingIndirectEval
})) {
if (/node_modules[\\/]regenerator-runtime[\\/]runtime\.js/.test(module1.identifier())) continue;
const error = new wp.WebpackError(`Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Middleware ${name}${typeof buildInfo.usingIndirectEval !== 'boolean' ? `\nUsed by ${Array.from(buildInfo.usingIndirectEval).join(', ')}` : ''}`);
error.module = module1;
compilation.errors.push(error);
}
if ((buildInfo === null || buildInfo === void 0 ? void 0 : buildInfo.nextUsedEnvVars) !== undefined) {
for (const envName of buildInfo.nextUsedEnvVars){
env.add(envName);
}
}
const connections = moduleGraph.getOutgoingConnections(module1);
for (const connection of connections){
if (connection.module) {
queue.add(connection.module);
}
}
}
envPerRoute.set(name, Array.from(env));
wasmPerRoute.set(name, Array.from(wasm));
}
}
});
const handler = (parser)=>{
const isMiddlewareModule = ()=>parser.state.module && parser.state.module.layer === 'middleware'
;
const wrapExpression = (expr)=>{
if (!isMiddlewareModule()) return;
if (options.dev) {
const dep1 = new wp.dependencies.ConstDependency('__next_eval__(function() { return ', expr.range[0]);
dep1.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep1);
const dep2 = new wp.dependencies.ConstDependency('})', expr.range[1]);
dep2.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep2);
}
expressionHandler();
return true;
};
const flagModule = (usedByExports)=>{
if (usedByExports === undefined) usedByExports = true;
const old = parser.state.module.buildInfo.usingIndirectEval;
if (old === true || usedByExports === false) return;
if (!old || usedByExports === true) {
parser.state.module.buildInfo.usingIndirectEval = usedByExports;
return;
}
const set = new Set(old);
for (const item of usedByExports){
set.add(item);
}
parser.state.module.buildInfo.usingIndirectEval = set;
};
const expressionHandler = ()=>{
if (!isMiddlewareModule()) return;
wp.optimize.InnerGraph.onUsage(parser.state, flagModule);
};
const ignore = ()=>{
if (!isMiddlewareModule()) return;
return true;
};
// wrapping
parser.hooks.call.for('eval').tap(PLUGIN_NAME, wrapExpression);
parser.hooks.call.for('global.eval').tap(PLUGIN_NAME, wrapExpression);
parser.hooks.call.for('Function').tap(PLUGIN_NAME, wrapExpression);
parser.hooks.call.for('global.Function').tap(PLUGIN_NAME, wrapExpression);
parser.hooks.new.for('Function').tap(PLUGIN_NAME, wrapExpression);
parser.hooks.new.for('global.Function').tap(PLUGIN_NAME, wrapExpression);
// fallbacks
parser.hooks.expression.for('eval').tap(PLUGIN_NAME, expressionHandler);
parser.hooks.expression.for('Function').tap(PLUGIN_NAME, expressionHandler);
parser.hooks.expression.for('Function.prototype').tap(PLUGIN_NAME, ignore);
parser.hooks.expression.for('global.eval').tap(PLUGIN_NAME, expressionHandler);
parser.hooks.expression.for('global.Function').tap(PLUGIN_NAME, expressionHandler);
parser.hooks.expression.for('global.Function.prototype').tap(PLUGIN_NAME, ignore);
const memberChainHandler = (_expr, members)=>{
if (members.length >= 2 && members[0] === 'env') {
const envName = members[1];
const { buildInfo } = parser.state.module;
if (buildInfo.nextUsedEnvVars === undefined) {
buildInfo.nextUsedEnvVars = new Set();
}
buildInfo.nextUsedEnvVars.add(envName);
if (isMiddlewareModule()) return true;
}
};
parser.hooks.callMemberChain.for('process').tap(PLUGIN_NAME, memberChainHandler);
parser.hooks.expressionMemberChain.for('process').tap(PLUGIN_NAME, memberChainHandler);
};
normalModuleFactory.hooks.parser.for('javascript/auto').tap(PLUGIN_NAME, handler);
normalModuleFactory.hooks.parser.for('javascript/dynamic').tap(PLUGIN_NAME, handler);
normalModuleFactory.hooks.parser.for('javascript/esm').tap(PLUGIN_NAME, handler);
// @ts-ignore TODO: Remove ignore when webpack 5 is stable
compilation.hooks.processAssets.tap({
name: 'NextJsMiddlewareManifest',
// @ts-ignore TODO: Remove ignore when webpack 5 is stable
stage: _webpack.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
}, (assets)=>{
createAssets(compilation, assets, {
envPerRoute,
wasmPerRoute
}, options.isEdgeRuntime);
});
});
}
function isUsedByExports(args) {
const { moduleGraph , runtime , module , usedByExports } = args;
if (usedByExports === undefined) return false;
if (typeof usedByExports === 'boolean') return usedByExports;
const exportsInfo = moduleGraph.getExportsInfo(module);
const wp = _webpack.webpack;
for (const exportName of usedByExports){
if (exportsInfo.getUsed(exportName, runtime) !== wp.UsageState.Unused) return true;
}
return false;
}
//# sourceMappingURL=middleware-plugin.js.map
;