UNPKG

next

Version:

The React Framework

319 lines (318 loc) • 16.5 kB
"use strict"; 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