next
Version:
The React Framework
319 lines (318 loc) • 16.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "stitchBuilds", {
enumerable: true,
get: function() {
return stitchBuilds;
}
});
const _fs = /*#__PURE__*/ _interop_require_default(require("fs"));
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
const _entries = require("../entries");
const _asyncsema = require("next/dist/compiled/async-sema");
const _recursivecopy = require("../../lib/recursive-copy");
const _utils = require("../../shared/lib/router/utils");
const _buildmanifestplugin = require("../webpack/plugins/build-manifest-plugin");
const _createclientrouterfilter = require("../../lib/create-client-router-filter");
const _detectchangedentries = require("./detect-changed-entries");
const _constants = require("../../shared/lib/constants");
const _apppaths = require("../../shared/lib/router/utils/app-paths");
const _ismetadataroute = require("../../lib/metadata/is-metadata-route");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
async function stitchBuilds({ config, distDir, shuttleDir, buildId, rewrites, redirects, allowedErrorRate, encryptionKey, edgePreviewProps }, entries) {
var _entries_unchanged, _entries_changed, _entries_changed1;
if (!await (0, _detectchangedentries.hasShuttle)(config, shuttleDir)) {
// no shuttle directory nothing to stitch
return {};
}
// if a manifest is needed in the rest of the build
// we return it from here so it can be used without
// re-reading from disk after changing
const updatedManifests = {};
// copy static chunks (this includes stale chunks but won't be loaded)
// unless referenced
await (0, _recursivecopy.recursiveCopy)(_path.default.join(shuttleDir, 'static'), _path.default.join(distDir, 'static'), {
overwrite: true
});
async function copyIfNoDestFile(srcFile, destFile) {
const content = await _fs.default.promises.readFile(srcFile);
await _fs.default.promises.writeFile(destFile, content, {
flag: 'wx'
}).catch((err)=>{
if (err.code !== 'EEXIST') {
throw err;
}
});
}
async function copyPageChunk(entry, type) {
const entryFile = _path.default.join('server', type, `${entry}.js`);
await _fs.default.promises.mkdir(_path.default.join(distDir, _path.default.dirname(entryFile)), {
recursive: true
});
await copyIfNoDestFile(_path.default.join(shuttleDir, entryFile + '.nft.json'), _path.default.join(distDir, entryFile + '.nft.json'));
if (type === 'app' && !(0, _ismetadataroute.isMetadataRoute)(entry)) {
const clientRefManifestFile = _path.default.join('server', type, `${entry}_${_constants.CLIENT_REFERENCE_MANIFEST}.js`);
await copyIfNoDestFile(_path.default.join(shuttleDir, clientRefManifestFile), _path.default.join(distDir, clientRefManifestFile));
}
await copyIfNoDestFile(_path.default.join(shuttleDir, entryFile), _path.default.join(distDir, entryFile));
// copy map file as well if it exists
await copyIfNoDestFile(_path.default.join(shuttleDir, `${entryFile}.map`), _path.default.join(distDir, `${entryFile}.map`)).catch((err)=>{
if (err.code !== 'ENOENT') {
throw err;
}
});
}
const copySema = new _asyncsema.Sema(8);
// restore unchanged entries avoiding copying stale
// entries from the shuttle/previous build
for (const { type, curEntries } of [
{
type: 'app',
curEntries: entries.unchanged.app
},
{
type: 'pages',
curEntries: entries.unchanged.pages
}
]){
await Promise.all(curEntries.map(async (entry)=>{
try {
await copySema.acquire();
let normalizedEntry = (0, _entries.getPageFromPath)(entry, entries.pageExtensions);
if (normalizedEntry === '/') {
normalizedEntry = '/index';
}
if (normalizedEntry === '/not-found') {
normalizedEntry = '/_not-found/page';
}
await copyPageChunk(normalizedEntry, type);
} finally{
copySema.release();
}
}));
}
// merge dynamic/static routes in routes-manifest
const [restoreRoutesManifest, currentRoutesManifest] = await Promise.all([
_path.default.join(shuttleDir, 'manifests', _constants.ROUTES_MANIFEST),
_path.default.join(distDir, _constants.ROUTES_MANIFEST)
].map(async (f)=>JSON.parse(await _fs.default.promises.readFile(f, 'utf8'))));
const dynamicRouteMap = {};
const combinedDynamicRoutes = [
...restoreRoutesManifest.dynamicRoutes,
...currentRoutesManifest.dynamicRoutes
];
for (const route of combinedDynamicRoutes){
dynamicRouteMap[route.page] = route;
}
const mergedRoutesManifest = {
...restoreRoutesManifest,
...currentRoutesManifest,
dynamicRoutes: (0, _utils.getSortedRoutes)(combinedDynamicRoutes.map((item)=>item.page)).map((page)=>dynamicRouteMap[page]),
staticRoutes: [
...restoreRoutesManifest.staticRoutes,
...currentRoutesManifest.staticRoutes
]
};
await _fs.default.promises.writeFile(_path.default.join(distDir, _constants.ROUTES_MANIFEST), JSON.stringify(mergedRoutesManifest, null, 2));
// for build-manifest we use latest runtime files
// and only merge previous page chunk entries
// middleware-build-manifest.js (needs to be regenerated)
const [restoreBuildManifest, currentBuildManifest] = await Promise.all([
_path.default.join(shuttleDir, 'manifests', _constants.BUILD_MANIFEST),
_path.default.join(distDir, _constants.BUILD_MANIFEST)
].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
const mergedBuildManifest = {
// we want to re-use original runtime
// chunks so we favor restored version
// over new
...currentBuildManifest,
pages: {
...restoreBuildManifest.pages,
...currentBuildManifest.pages
}
};
// _app and _error is unique per runtime
// so nest under each specific entry in build-manifest
const internalEntries = [
'/_error',
'/_app'
];
for (const entry of Object.keys(restoreBuildManifest.pages)){
if (currentBuildManifest.pages[entry]) {
continue;
}
for (const internalEntry of internalEntries){
for (const chunk of restoreBuildManifest.pages[internalEntry]){
if (!restoreBuildManifest.pages[entry].includes(chunk)) {
mergedBuildManifest.pages[entry].unshift(chunk);
}
}
}
}
for (const entry of Object.keys(currentBuildManifest.pages)){
for (const internalEntry of internalEntries){
for (const chunk of currentBuildManifest.pages[internalEntry]){
if (!currentBuildManifest.pages[entry].includes(chunk)) {
mergedBuildManifest.pages[entry].unshift(chunk);
}
}
}
}
for (const key of internalEntries){
mergedBuildManifest.pages[key] = [];
}
for (const entry of entries.unchanged.app || []){
const normalizedEntry = (0, _entries.getPageFromPath)(entry, entries.pageExtensions);
mergedBuildManifest.rootMainFilesTree[normalizedEntry] = restoreBuildManifest.rootMainFilesTree[normalizedEntry] || restoreBuildManifest.rootMainFiles;
}
await _fs.default.promises.writeFile(_path.default.join(distDir, _constants.BUILD_MANIFEST), JSON.stringify(mergedBuildManifest, null, 2));
await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.MIDDLEWARE_BUILD_MANIFEST}.js`), `self.__BUILD_MANIFEST=${JSON.stringify(mergedBuildManifest)}`);
await _fs.default.promises.writeFile(_path.default.join(distDir, 'static', buildId, `_buildManifest.js`), `self.__BUILD_MANIFEST = ${(0, _buildmanifestplugin.generateClientManifest)(mergedBuildManifest, rewrites, (0, _createclientrouterfilter.createClientRouterFilter)([
...[
// client filter always has all app paths
...((_entries_unchanged = entries.unchanged) == null ? void 0 : _entries_unchanged.app) || [],
...((_entries_changed = entries.changed) == null ? void 0 : _entries_changed.app) || []
].map((entry)=>(0, _apppaths.normalizeAppPath)((0, _entries.getPageFromPath)(entry, entries.pageExtensions))),
...(entries.unchanged.pages.length ? ((_entries_changed1 = entries.changed) == null ? void 0 : _entries_changed1.pages) || [] : []).map((item)=>(0, _entries.getPageFromPath)(item, entries.pageExtensions))
], redirects, allowedErrorRate))};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()`);
// for react-loadable-manifest we just merge directly
// prioritizing current manifest over previous,
// middleware-react-loadable-manifest (needs to be regenerated)
const [restoreLoadableManifest, currentLoadableManifest] = await Promise.all([
_path.default.join(shuttleDir, 'manifests', _constants.REACT_LOADABLE_MANIFEST),
_path.default.join(distDir, _constants.REACT_LOADABLE_MANIFEST)
].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
const mergedLoadableManifest = {
...restoreLoadableManifest,
...currentLoadableManifest
};
await _fs.default.promises.writeFile(_path.default.join(distDir, _constants.REACT_LOADABLE_MANIFEST), JSON.stringify(mergedLoadableManifest, null, 2));
await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js`), `self.__REACT_LOADABLE_MANIFEST=${JSON.stringify(JSON.stringify(mergedLoadableManifest))}`);
// for server/middleware-manifest we just merge the functions
// and middleware fields
const [restoreMiddlewareManifest, currentMiddlewareManifest] = await Promise.all([
_path.default.join(shuttleDir, 'server', _constants.MIDDLEWARE_MANIFEST),
_path.default.join(distDir, 'server', _constants.MIDDLEWARE_MANIFEST)
].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
const mergedMiddlewareManifest = {
...restoreMiddlewareManifest,
...currentMiddlewareManifest,
functions: {
...restoreMiddlewareManifest.functions,
...currentMiddlewareManifest.functions
}
};
// update edge function env
const updatedEdgeEnv = {
__NEXT_BUILD_ID: buildId,
NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: encryptionKey,
...edgePreviewProps
};
if (mergedMiddlewareManifest.middleware['/']) {
Object.assign(mergedMiddlewareManifest.middleware['/'].env, updatedEdgeEnv);
}
for (const key of Object.keys(mergedMiddlewareManifest.functions)){
Object.assign(mergedMiddlewareManifest.functions[key].env, updatedEdgeEnv);
}
await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', _constants.MIDDLEWARE_MANIFEST), JSON.stringify(mergedMiddlewareManifest, null, 2));
// for server/next-font-manifest we just merge nested
// page/app fields and regenerate server/next-font-manifest.js
const [restoreNextFontManifest, currentNextFontManifest] = await Promise.all([
_path.default.join(shuttleDir, 'server', `${_constants.NEXT_FONT_MANIFEST}.json`),
_path.default.join(distDir, 'server', `${_constants.NEXT_FONT_MANIFEST}.json`)
].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
const mergedNextFontManifest = {
...restoreNextFontManifest,
...currentNextFontManifest,
pages: {
...restoreNextFontManifest.pages,
...currentNextFontManifest.pages
},
app: {
...restoreNextFontManifest.app,
...currentNextFontManifest.app
}
};
await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.NEXT_FONT_MANIFEST}.json`), JSON.stringify(mergedNextFontManifest, null, 2));
await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.NEXT_FONT_MANIFEST}.js`), `self.__NEXT_FONT_MANIFEST=${JSON.stringify(JSON.stringify(mergedNextFontManifest))}`);
// for server/functions-config-manifest.json we just merge
// the functions field
const [restoreFunctionsConfigManifest, currentFunctionsConfigManifest] = await Promise.all([
_path.default.join(shuttleDir, 'server', _constants.FUNCTIONS_CONFIG_MANIFEST),
_path.default.join(distDir, 'server', _constants.FUNCTIONS_CONFIG_MANIFEST)
].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
const mergedFunctionsConfigManifest = {
...restoreFunctionsConfigManifest,
...currentFunctionsConfigManifest,
functions: {
...restoreFunctionsConfigManifest.functions,
...currentFunctionsConfigManifest.functions
}
};
await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', _constants.FUNCTIONS_CONFIG_MANIFEST), JSON.stringify(mergedFunctionsConfigManifest, null, 2));
for (const file of [
_constants.APP_BUILD_MANIFEST,
_constants.APP_PATH_ROUTES_MANIFEST
]){
const [restorePagesManifest, currentPagesManifest] = await Promise.all([
_path.default.join(shuttleDir, 'manifests', file),
_path.default.join(distDir, file)
].map(async (f)=>JSON.parse(await _fs.default.promises.readFile(f, 'utf8'))));
const mergedPagesManifest = {
...restorePagesManifest,
...currentPagesManifest,
...file === _constants.APP_BUILD_MANIFEST ? {
pages: {
...restorePagesManifest.pages,
...currentPagesManifest.pages
}
} : {}
};
await _fs.default.promises.writeFile(_path.default.join(distDir, file), JSON.stringify(mergedPagesManifest, null, 2));
}
for (const file of [
_constants.PAGES_MANIFEST,
_constants.APP_PATHS_MANIFEST
]){
const [restoreAppManifest, currentAppManifest] = await Promise.all([
_path.default.join(shuttleDir, 'server', file),
_path.default.join(distDir, 'server', file)
].map(async (f)=>JSON.parse(await _fs.default.promises.readFile(f, 'utf8'))));
const mergedManifest = {
...restoreAppManifest,
...currentAppManifest
};
await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', file), JSON.stringify(mergedManifest, null, 2));
if (file === _constants.PAGES_MANIFEST) {
updatedManifests.pagesManifest = mergedManifest;
}
}
// for server/server-reference-manifest.json we merge
// and regenerate server/server-reference-manifest.js
const [restoreServerRefManifest, currentServerRefManifest] = await Promise.all([
_path.default.join(shuttleDir, 'server', `${_constants.SERVER_REFERENCE_MANIFEST}.json`),
_path.default.join(distDir, 'server', `${_constants.SERVER_REFERENCE_MANIFEST}.json`)
].map(async (file)=>JSON.parse(await _fs.default.promises.readFile(file, 'utf8'))));
const mergedServerRefManifest = {
...restoreServerRefManifest,
...currentServerRefManifest,
node: {
...restoreServerRefManifest.node,
...currentServerRefManifest.node
},
edge: {
...restoreServerRefManifest.edge,
...currentServerRefManifest.edge
}
};
await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.SERVER_REFERENCE_MANIFEST}.json`), JSON.stringify(mergedServerRefManifest, null, 2));
await _fs.default.promises.writeFile(_path.default.join(distDir, 'server', `${_constants.SERVER_REFERENCE_MANIFEST}.js`), `self.__RSC_SERVER_MANIFEST=${JSON.stringify(JSON.stringify(mergedServerRefManifest))}`);
return updatedManifests;
}
//# sourceMappingURL=stitch-builds.js.map