UNPKG

@netlify/content-engine

Version:
251 lines 11 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.initialize = initialize; const core_utils_1 = require("../core-utils"); const fs = __importStar(require("fs-extra")); const mutex_1 = require("../core-utils/mutex"); const core_utils_2 = require("../core-utils"); // import telemetry from "gatsby-telemetry" const globby_1 = __importDefault(require("globby")); const api_runner_node_1 = __importDefault(require("../utils/api-runner-node")); const plugin_runner_1 = require("../redux/plugin-runner"); const redux_1 = require("../redux"); const reporter_1 = __importDefault(require("../reporter")); const remove_stale_jobs_1 = require("../bootstrap/remove-stale-jobs"); const load_config_1 = require("../bootstrap/load-config"); const load_plugins_1 = require("../bootstrap/load-plugins"); const detect_node_mutations_1 = require("../utils/detect-node-mutations"); // Show stack trace on unhandled promises. process.on(`unhandledRejection`, (reason) => { // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/33636 reporter_1.default.panic(reason || `Unhandled rejection`); }); async function initialize({ program: args, parentSpan, engineConfig, }) { if (!args) { reporter_1.default.panic(`Missing program args`); } if (reporter_1.default._registerAdditionalDiagnosticOutputHandler) { reporter_1.default._registerAdditionalDiagnosticOutputHandler(function logPendingJobs() { const outputs = []; for (const [, { job }] of redux_1.store.getState().jobsV2.incomplete) { outputs.push(job); if (outputs.length >= 5) { // 5 not finished jobs should be enough to track down issues // this is just limiting output "spam" break; } } return outputs.length ? `Unfinished jobs (showing ${outputs.length} of ${redux_1.store.getState().jobsV2.incomplete.size} jobs total):\n\n` + JSON.stringify(outputs, null, 2) : ``; }); } const directory = (0, core_utils_1.slash)(args.directory); const program = { ...args, extensions: [], // Fix program directory path for windows env. directory, }; redux_1.store.dispatch({ type: `SET_PROGRAM`, payload: program, }); let activityForJobs; redux_1.emitter.on(`CREATE_JOB`, () => { if (!activityForJobs) { activityForJobs = reporter_1.default.phantomActivity(`Running jobs`); activityForJobs.start(); } }); const onEndJob = () => { if (activityForJobs && redux_1.store.getState().jobs.active.length === 0) { activityForJobs.end(); activityForJobs = null; } }; redux_1.emitter.on(`END_JOB`, onEndJob); const siteDirectory = program.directory; const activity = reporter_1.default.activityTimer(`Loading plugins`); activity.start(); const config = await (0, load_config_1.loadConfig)({ siteDirectory, processFlags: true, providedConfig: engineConfig, }); const flattenedPlugins = await (0, load_plugins_1.loadPlugins)(config, siteDirectory); if (process.env.GATSBY_DETECT_NODE_MUTATIONS) { (0, detect_node_mutations_1.enableNodeMutationsDetection)(); } // run stale jobs // @ts-ignore we'll need to fix redux typings https://redux.js.org/usage/usage-with-typescript redux_1.store.dispatch((0, remove_stale_jobs_1.removeStaleJobs)(redux_1.store.getState().jobsV2)); // Multiple occurrences of the same name-version-pair can occur, // so we report an array of unique pairs // const pluginsStr = _.uniq(flattenedPlugins.map(p => `${p.name}@${p.version}`)) // telemetry.decorateEvent(`BUILD_END`, { // plugins: pluginsStr, // }) // telemetry.decorateEvent(`DEVELOP_STOP`, { // plugins: pluginsStr, // }) // Start plugin runner which listens to the store // and invokes Gatsby API based on actions. (0, plugin_runner_1.startPluginRunner)(); await (0, api_runner_node_1.default)(`onPreInit`, { parentSpan: activity.span }); const lmdbCacheDirectoryName = `caches-lmdb`; const cacheDirectory = `${program.directory}/.cache`; // We do this by creating a hash of all the version numbers of installed // plugins, the site's package.json, gatsby-config.js, and gatsby-node.js. // The last, gatsby-node.js, is important as many gatsby sites put important // logic in there e.g. generating slugs for custom pages. const pluginVersions = flattenedPlugins.map((p) => p.version); // we should include gatsby version as well pluginVersions.push(require(`../../package.json`).version); const optionalFiles = [ `${program.directory}/gatsby-node.js`, `${program.directory}/gatsby-node.ts`, ]; const state = redux_1.store.getState(); const hashes = await Promise.all( // Ignore optional files with .catch() as these are not required [(0, core_utils_2.md5File)(`package.json`)].concat(optionalFiles.map((f) => (0, core_utils_2.md5File)(f).catch(() => ``)))); const pluginsHash = await (0, core_utils_2.md5)(JSON.stringify(pluginVersions.concat(hashes))); const oldPluginsHash = state && state.status ? state.status.PLUGINS_HASH : ``; // Check if anything has changed. If it has, delete the site's .cache // directory and tell reducers to empty themselves. // // Also if the hash isn't there, then delete things just in case something // is weird. if (oldPluginsHash && pluginsHash !== oldPluginsHash) { reporter_1.default.info(reporter_1.default.stripIndent ` One or more of your plugins have changed since the last time you ran Content Engine. As a precaution, we're deleting your site's cache to ensure there's no stale data. `); } if (oldPluginsHash && pluginsHash !== oldPluginsHash) { try { const deleteGlobs = [ // By default delete all files & subdirectories `.cache/**`, `.cache/data/**`, `!.cache/data/core-utils/**`, `!.cache/compiled`, ]; if (process.env.GATSBY_EXPERIMENTAL_PRESERVE_FILE_DOWNLOAD_CACHE) { // Stop the caches directory from being deleted, add all sub directories, // but remove gatsby-source-filesystem deleteGlobs.push(`!.cache/caches`); deleteGlobs.push(`.cache/caches/*`); deleteGlobs.push(`!.cache/caches/gatsby-source-filesystem`); } const files = await (0, globby_1.default)(deleteGlobs, { cwd: program.directory, }); await Promise.all(files.map((file) => fs.remove(file))); } catch (e) { reporter_1.default.error(`Failed to remove .cache files.`, e); } // Tell reducers to delete their data (the store will already have // been loaded from the file system cache). redux_1.store.dispatch({ type: `DELETE_CACHE`, cacheIsCorrupt: false, }); // make sure all previous mutexes are released await (0, mutex_1.releaseAllMutexes)(); // in future this should show which plugin's caches are purged // possibly should also have which plugins had caches // telemetry.decorateEvent(`BUILD_END`, { // pluginCachesPurged: [`*`], // }) // telemetry.decorateEvent(`DEVELOP_STOP`, { // pluginCachesPurged: [`*`], // }) } // Update the store with the new plugins hash. redux_1.store.dispatch({ type: `UPDATE_PLUGINS_HASH`, payload: pluginsHash, }); // Now that we know the .cache directory is safe, initialize the cache // directory. await fs.ensureDir(cacheDirectory); // Init plugins once cache is initialized await (0, api_runner_node_1.default)(`onPluginInit`, { parentSpan: activity.span, }); try { await fs.ensureDir(`${cacheDirectory}/${lmdbCacheDirectoryName}`); } catch (err) { reporter_1.default.panic(`Unable to copy site files to .cache`, err); } /** * Start the main bootstrap processes. */ await (0, api_runner_node_1.default)(`onPreBootstrap`, { parentSpan: activity.span, }); activity.end(); // Collect resolvable extensions and attach to program. const extensions = [`.mjs`, `.js`, `.jsx`, `.wasm`, `.json`]; // Change to this being an action and plugins implement `onPreBootstrap` // for adding extensions. const apiResults = await (0, api_runner_node_1.default)(`resolvableExtensions`, { traceId: `initial-resolvableExtensions`, parentSpan, }); redux_1.store.dispatch({ type: `SET_PROGRAM_EXTENSIONS`, payload: [extensions, apiResults].flat(Infinity), }); // const siteDirectoryFiles = await fs.readdir(siteDirectory) // const gatsbyFilesIsInESM = siteDirectoryFiles.some(file => // file.match(/gatsby-(node|config)\.mjs/) // ) // if (gatsbyFilesIsInESM) { // telemetry.trackFeatureIsUsed(`ESMInGatsbyFiles`) // } let initialWebhookBody = undefined; if (process.env.GATSBY_INITIAL_WEBHOOK_BODY) { try { initialWebhookBody = JSON.parse(process.env.GATSBY_INITIAL_WEBHOOK_BODY); } catch (e) { reporter_1.default.error(`Failed to parse GATSBY_INITIAL_WEBHOOK_BODY as JSON:\n"${e.message}"`); } } return { store: redux_1.store, webhookBody: initialWebhookBody, }; } //# sourceMappingURL=initialize.js.map