UNPKG

next

Version:

The React Framework

243 lines (242 loc) • 9.42 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { detectChangedEntries: null, hasShuttle: null }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { detectChangedEntries: function() { return detectChangedEntries; }, hasShuttle: function() { return hasShuttle; } }); const _fs = /*#__PURE__*/ _interop_require_default(require("fs")); const _path = /*#__PURE__*/ _interop_require_default(require("path")); const _crypto = /*#__PURE__*/ _interop_require_default(require("crypto")); const _child_process = require("child_process"); const _entries = require("../entries"); const _debug = /*#__PURE__*/ _interop_require_default(require("next/dist/compiled/debug")); const _asyncsema = require("next/dist/compiled/async-sema"); const _storeshuttle = require("./store-shuttle"); const _builddiagnostics = require("../../diagnostics/build-diagnostics"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const debug = (0, _debug.default)('next:build:flying-shuttle'); function deepEqual(obj1, obj2) { if (obj1 === obj2) return true; if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) { return false; } let keys1 = Object.keys(obj1); let keys2 = Object.keys(obj2); if (keys1.length !== keys2.length) return false; for (let key of keys1){ if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) { return false; } } return true; } let _hasShuttle = undefined; async function hasShuttle(config, shuttleDir) { if (typeof _hasShuttle === 'boolean') { return _hasShuttle; } let foundShuttleManifest; async function pruneCache() { await _fs.default.promises.rm(shuttleDir, { force: true, recursive: true }); await _fs.default.promises.mkdir(shuttleDir, { recursive: true }); } try { foundShuttleManifest = JSON.parse(await _fs.default.promises.readFile(_path.default.join(shuttleDir, 'shuttle-manifest.json'), 'utf8')); _hasShuttle = true; } catch (err) { _hasShuttle = false; console.log(`Failed to read shuttle manifest`); // prune potentially corrupted cache await pruneCache(); return _hasShuttle; } const currentShuttleManifest = JSON.parse((0, _storeshuttle.generateShuttleManifest)(config)); if (currentShuttleManifest.nextVersion !== foundShuttleManifest.nextVersion) { // we don't allow using shuttle from differing Next.js version // as it could have internal changes console.log(`shuttle has differing Next.js version ${foundShuttleManifest.nextVersion} versus current ${currentShuttleManifest.nextVersion}, skipping.`); _hasShuttle = false; } if (!deepEqual(currentShuttleManifest.config, foundShuttleManifest.config)) { _hasShuttle = false; console.log(`Mismatching shuttle configs`, currentShuttleManifest.config, foundShuttleManifest.config); } if (_hasShuttle) { let currentGitSha = ''; try { currentGitSha = (0, _child_process.execSync)('git rev-parse HEAD').toString().trim(); } catch (_) {} await (0, _builddiagnostics.updateIncrementalBuildMetrics)({ currentGitSha, shuttleGitSha: currentShuttleManifest.gitSha }); } else { // prune mis-matching cache await pruneCache(); } return _hasShuttle; } async function detectChangedEntries({ appPaths, pagesPaths, pageExtensions, distDir, shuttleDir, config }) { const changedEntries = { app: [], pages: [] }; const unchangedEntries = { app: [], pages: [] }; if (!await hasShuttle(config, shuttleDir)) { // no shuttle so consider everything changed console.log(`no shuttle. can't detect changes`); return { changed: { pages: pagesPaths || [], app: appPaths || [] }, unchanged: { pages: [], app: [] } }; } const hashCache = new Map(); async function computeHash(p) { let hash = hashCache.get(p); if (hash) { return hash; } return new Promise((resolve, reject)=>{ const hashInst = _crypto.default.createHash('sha1'); const stream = _fs.default.createReadStream(p); stream.on('error', (err)=>reject(err)); stream.on('data', (chunk)=>hashInst.update(chunk)); stream.on('end', ()=>{ const digest = hashInst.digest('hex'); resolve(digest); hashCache.set(p, digest); }); }); } const hashSema = new _asyncsema.Sema(16); let globalEntryChanged = false; const changedDependencies = {}; async function detectChange({ normalizedEntry, entry, type }) { const traceFile = _path.default.join(shuttleDir, 'server', type, `${normalizedEntry}.js.nft.json`); let changed = true; // we don't need to check any further entry's dependencies if // a global entry changed since that invalidates everything if (!globalEntryChanged) { try { const traceData = JSON.parse(await _fs.default.promises.readFile(traceFile, 'utf8')); if (traceData) { let changedDependency = false; await Promise.all(Object.keys(traceData.fileHashes).map(async (file)=>{ try { if (changedDependency) return; await hashSema.acquire(); const relativeTraceFile = _path.default.relative(_path.default.join(shuttleDir, 'server', type), traceFile); const originalTraceFile = _path.default.join(distDir, 'server', type, relativeTraceFile); const absoluteFile = _path.default.join(_path.default.dirname(originalTraceFile), file); if (absoluteFile.startsWith(distDir)) { return; } const prevHash = traceData.fileHashes[file]; const curHash = await computeHash(absoluteFile); if (prevHash !== curHash) { changedDependencies[normalizedEntry] = file; debug('detected change on', { prevHash, curHash, file, entry: normalizedEntry }); changedDependency = true; } } finally{ hashSema.release(); } })); if (!changedDependency) { changed = false; } } else { console.error('missing trace data', traceFile, normalizedEntry); } } catch (err) { console.error(`Failed to detect change for ${entry} ${normalizedEntry}`, err); } } // we always rebuild global entries so we have a version // that matches the newest build/runtime const isGlobalEntry = /(_app|_document|_error)/.test(entry); if (changed || isGlobalEntry) { // if a global entry changed all entries are changed if (changed && !globalEntryChanged && isGlobalEntry) { console.log(`global entry ${entry} changed invalidating all entries`); globalEntryChanged = true; // move unchanged to changed changedEntries[type].push(...unchangedEntries[type]); } changedEntries[type].push(entry); } else { unchangedEntries[type].push(entry); } } // loop over entries and their dependency's hashes // to detect which changed for (const entry of pagesPaths || []){ let normalizedEntry = (0, _entries.getPageFromPath)(entry, pageExtensions); if (normalizedEntry === '/') { normalizedEntry = '/index'; } await detectChange({ entry, normalizedEntry, type: 'pages' }); } for (const entry of appPaths || []){ let normalizedEntry = (0, _entries.getPageFromPath)(entry, pageExtensions); if (normalizedEntry === '/not-found') { normalizedEntry = '/_not-found/page'; } await detectChange({ entry, normalizedEntry, type: 'app' }); } await (0, _builddiagnostics.updateIncrementalBuildMetrics)({ changedDependencies }); return { changed: changedEntries, unchanged: unchangedEntries }; } //# sourceMappingURL=detect-changed-entries.js.map