@netlify/content-engine
Version:
251 lines • 11 kB
JavaScript
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
;