inital
This commit is contained in:
551
node_modules/@11ty/eleventy/src/TemplateConfig.js
generated
vendored
Normal file
551
node_modules/@11ty/eleventy/src/TemplateConfig.js
generated
vendored
Normal file
@@ -0,0 +1,551 @@
|
||||
import fs from "node:fs";
|
||||
import chalk from "kleur";
|
||||
import { Merge, TemplatePath, isPlainObject } from "@11ty/eleventy-utils";
|
||||
import debugUtil from "debug";
|
||||
|
||||
import { EleventyImportRaw, EleventyImportRawFromEleventy } from "./Util/Require.js";
|
||||
import EleventyBaseError from "./Errors/EleventyBaseError.js";
|
||||
import UserConfig from "./UserConfig.js";
|
||||
import GlobalDependencyMap from "./GlobalDependencyMap.js";
|
||||
import ExistsCache from "./Util/ExistsCache.js";
|
||||
import eventBus from "./EventBus.js";
|
||||
import ProjectTemplateFormats from "./Util/ProjectTemplateFormats.js";
|
||||
|
||||
const debug = debugUtil("Eleventy:TemplateConfig");
|
||||
const debugDev = debugUtil("Dev:Eleventy:TemplateConfig");
|
||||
|
||||
/**
|
||||
* @module 11ty/eleventy/TemplateConfig
|
||||
*/
|
||||
|
||||
/**
|
||||
* Config as used by the template.
|
||||
* @typedef {object} module:11ty/eleventy/TemplateConfig~TemplateConfig~config
|
||||
* @property {String} [pathPrefix] - The path prefix.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Errors in eleventy config.
|
||||
* @ignore
|
||||
*/
|
||||
class EleventyConfigError extends EleventyBaseError {}
|
||||
|
||||
/**
|
||||
* Errors in eleventy plugins.
|
||||
* @ignore
|
||||
*/
|
||||
class EleventyPluginError extends EleventyBaseError {}
|
||||
|
||||
/**
|
||||
* Config for a template.
|
||||
* @ignore
|
||||
* @param {{}} customRootConfig - tbd.
|
||||
* @param {String} projectConfigPath - Path to local project config.
|
||||
*/
|
||||
class TemplateConfig {
|
||||
#templateFormats;
|
||||
#runMode;
|
||||
#configManuallyDefined = false;
|
||||
/** @type {UserConfig} */
|
||||
#userConfig = new UserConfig();
|
||||
|
||||
constructor(customRootConfig, projectConfigPath) {
|
||||
/** @type {object} */
|
||||
this.overrides = {};
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
* @description Path to local project config.
|
||||
* @default .eleventy.js
|
||||
*/
|
||||
if (projectConfigPath !== undefined) {
|
||||
this.#configManuallyDefined = true;
|
||||
|
||||
if (!projectConfigPath) {
|
||||
// falsy skips config files
|
||||
this.projectConfigPaths = [];
|
||||
} else {
|
||||
this.projectConfigPaths = [projectConfigPath];
|
||||
}
|
||||
} else {
|
||||
this.projectConfigPaths = [
|
||||
".eleventy.js",
|
||||
"eleventy.config.js",
|
||||
"eleventy.config.mjs",
|
||||
"eleventy.config.cjs",
|
||||
];
|
||||
}
|
||||
|
||||
if (customRootConfig) {
|
||||
/**
|
||||
* @type {object}
|
||||
* @description Custom root config.
|
||||
*/
|
||||
this.customRootConfig = customRootConfig;
|
||||
debug("Warning: Using custom root config!");
|
||||
} else {
|
||||
this.customRootConfig = null;
|
||||
}
|
||||
|
||||
this.hasConfigMerged = false;
|
||||
this.isEsm = false;
|
||||
}
|
||||
|
||||
get userConfig() {
|
||||
return this.#userConfig;
|
||||
}
|
||||
|
||||
get aggregateBenchmark() {
|
||||
return this.userConfig.benchmarks.aggregate;
|
||||
}
|
||||
|
||||
/* Setter for Logger */
|
||||
setLogger(logger) {
|
||||
this.logger = logger;
|
||||
this.userConfig.logger = this.logger;
|
||||
}
|
||||
|
||||
/* Setter for Directories instance */
|
||||
setDirectories(directories) {
|
||||
this.directories = directories;
|
||||
this.userConfig.directories = directories.getUserspaceInstance();
|
||||
}
|
||||
|
||||
/* Setter for TemplateFormats instance */
|
||||
setTemplateFormats(templateFormats) {
|
||||
this.#templateFormats = templateFormats;
|
||||
}
|
||||
|
||||
get templateFormats() {
|
||||
if (!this.#templateFormats) {
|
||||
this.#templateFormats = new ProjectTemplateFormats();
|
||||
}
|
||||
return this.#templateFormats;
|
||||
}
|
||||
|
||||
/* Backwards compat */
|
||||
get inputDir() {
|
||||
return this.directories.input;
|
||||
}
|
||||
|
||||
setRunMode(runMode) {
|
||||
this.#runMode = runMode;
|
||||
}
|
||||
|
||||
shouldSpiderJavaScriptDependencies() {
|
||||
// not for a standard build
|
||||
return (
|
||||
(this.#runMode === "watch" || this.#runMode === "serve") &&
|
||||
this.userConfig.watchJavaScriptDependencies
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalises local project config file path.
|
||||
*
|
||||
* @method
|
||||
* @returns {String|undefined} - The normalised local project config file path.
|
||||
*/
|
||||
getLocalProjectConfigFile() {
|
||||
let configFiles = this.getLocalProjectConfigFiles();
|
||||
// Add the configFiles[0] in case of a test, where no file exists on the file system
|
||||
let configFile = configFiles.find((path) => path && fs.existsSync(path)) || configFiles[0];
|
||||
if (configFile) {
|
||||
return configFile;
|
||||
}
|
||||
}
|
||||
|
||||
getLocalProjectConfigFiles() {
|
||||
if (this.projectConfigPaths?.length > 0) {
|
||||
return TemplatePath.addLeadingDotSlashArray(this.projectConfigPaths.filter((path) => path));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
setProjectUsingEsm(isEsmProject) {
|
||||
this.isEsm = !!isEsmProject;
|
||||
this.usesGraph.setIsEsm(isEsmProject);
|
||||
}
|
||||
|
||||
getIsProjectUsingEsm() {
|
||||
return this.isEsm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the configuration.
|
||||
*/
|
||||
async reset() {
|
||||
debugDev("Resetting configuration: TemplateConfig and UserConfig.");
|
||||
this.userConfig.reset();
|
||||
// await this.initializeRootConfig();
|
||||
await this.forceReloadConfig();
|
||||
this.usesGraph.reset();
|
||||
|
||||
// Clear the compile cache
|
||||
eventBus.emit("eleventy.compileCacheReset");
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the configuration while in watch mode.
|
||||
*
|
||||
* @todo Add implementation.
|
||||
*/
|
||||
resetOnWatch() {
|
||||
// nothing yet
|
||||
}
|
||||
|
||||
hasInitialized() {
|
||||
return this.hasConfigMerged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Async-friendly init method
|
||||
*/
|
||||
async init(overrides) {
|
||||
await this.initializeRootConfig();
|
||||
|
||||
if (overrides) {
|
||||
this.appendToRootConfig(overrides);
|
||||
}
|
||||
|
||||
this.config = await this.mergeConfig();
|
||||
this.hasConfigMerged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a reload of the configuration object.
|
||||
*/
|
||||
async forceReloadConfig() {
|
||||
this.hasConfigMerged = false;
|
||||
await this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the config object.
|
||||
*
|
||||
* @returns {{}} - The config object.
|
||||
*/
|
||||
getConfig() {
|
||||
if (!this.hasConfigMerged) {
|
||||
throw new Error("Invalid call to .getConfig(). Needs an .init() first.");
|
||||
}
|
||||
|
||||
return this.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites the config path.
|
||||
*
|
||||
* @param {String} path - The new config path.
|
||||
*/
|
||||
async setProjectConfigPath(path) {
|
||||
this.#configManuallyDefined = true;
|
||||
|
||||
if (path !== undefined) {
|
||||
this.projectConfigPaths = [path];
|
||||
} else {
|
||||
this.projectConfigPaths = [];
|
||||
}
|
||||
|
||||
if (this.hasConfigMerged) {
|
||||
// merge it again
|
||||
debugDev("Merging in getConfig again after setting the local project config path.");
|
||||
await this.forceReloadConfig();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites the path prefix.
|
||||
*
|
||||
* @param {String} pathPrefix - The new path prefix.
|
||||
*/
|
||||
setPathPrefix(pathPrefix) {
|
||||
if (pathPrefix && pathPrefix !== "/") {
|
||||
debug("Setting pathPrefix to %o", pathPrefix);
|
||||
this.overrides.pathPrefix = pathPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current path prefix denoting the root folder the output will be deployed to
|
||||
*
|
||||
* @returns {String} - The path prefix string
|
||||
*/
|
||||
getPathPrefix() {
|
||||
if (this.overrides.pathPrefix) {
|
||||
return this.overrides.pathPrefix;
|
||||
}
|
||||
|
||||
if (!this.hasConfigMerged) {
|
||||
throw new Error("Config has not yet merged. Needs `init()`.");
|
||||
}
|
||||
|
||||
return this.config?.pathPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstraps the config object.
|
||||
*/
|
||||
async initializeRootConfig() {
|
||||
this.rootConfig = this.customRootConfig;
|
||||
if (!this.rootConfig) {
|
||||
let { default: cfg } = await EleventyImportRawFromEleventy("./src/defaultConfig.js");
|
||||
this.rootConfig = cfg;
|
||||
}
|
||||
|
||||
if (typeof this.rootConfig === "function") {
|
||||
// Not yet using async in defaultConfig.js
|
||||
this.rootConfig = this.rootConfig.call(this, this.userConfig);
|
||||
}
|
||||
|
||||
debug("Default Eleventy config %o", this.rootConfig);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add additional overrides to the root config object, used for testing
|
||||
*
|
||||
* @param {object} - a subset of the return Object from the user’s config file.
|
||||
*/
|
||||
appendToRootConfig(obj) {
|
||||
Object.assign(this.rootConfig, obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the userland plugins from the Config
|
||||
*
|
||||
* @param {object} - the return Object from the user’s config file.
|
||||
*/
|
||||
async processPlugins({ dir, pathPrefix }) {
|
||||
this.userConfig.dir = dir;
|
||||
this.userConfig.pathPrefix = pathPrefix;
|
||||
|
||||
// for Nested addPlugin calls, Issue #1925
|
||||
this.userConfig._enablePluginExecution();
|
||||
|
||||
let storedActiveNamespace = this.userConfig.activeNamespace;
|
||||
for (let { plugin, options, pluginNamespace } of this.userConfig.plugins) {
|
||||
try {
|
||||
this.userConfig.activeNamespace = pluginNamespace;
|
||||
await this.userConfig._executePlugin(plugin, options);
|
||||
} catch (e) {
|
||||
let name = this.userConfig._getPluginName(plugin);
|
||||
let namespaces = [storedActiveNamespace, pluginNamespace].filter((entry) => !!entry);
|
||||
|
||||
let namespaceStr = "";
|
||||
if (namespaces.length) {
|
||||
namespaceStr = ` (namespace: ${namespaces.join(".")})`;
|
||||
}
|
||||
|
||||
throw new EleventyPluginError(
|
||||
`Error processing ${name ? `the \`${name}\`` : "a"} plugin${namespaceStr}`,
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.userConfig.activeNamespace = storedActiveNamespace;
|
||||
|
||||
this.userConfig._disablePluginExecution();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches and executes the local configuration file
|
||||
*
|
||||
* @returns {Promise<object>} merged - The merged config file object.
|
||||
*/
|
||||
async requireLocalConfigFile() {
|
||||
let localConfig = {};
|
||||
let exportedConfig = {};
|
||||
|
||||
let path = this.projectConfigPaths.filter((path) => path).find((path) => fs.existsSync(path));
|
||||
|
||||
if (this.projectConfigPaths.length > 0 && this.#configManuallyDefined && !path) {
|
||||
throw new EleventyConfigError(
|
||||
"A configuration file was specified but not found: " + this.projectConfigPaths.join(", "),
|
||||
);
|
||||
}
|
||||
|
||||
debug(`Merging default config with ${path}`);
|
||||
if (path) {
|
||||
try {
|
||||
let { default: configDefaultReturn, config: exportedConfigObject } =
|
||||
await EleventyImportRaw(path, this.isEsm ? "esm" : "cjs");
|
||||
|
||||
exportedConfig = exportedConfigObject || {};
|
||||
|
||||
if (this.directories && Object.keys(exportedConfigObject?.dir || {}).length > 0) {
|
||||
debug(
|
||||
"Setting directories via `config.dir` export from config file: %o",
|
||||
exportedConfigObject.dir,
|
||||
);
|
||||
this.directories.setViaConfigObject(exportedConfigObject.dir);
|
||||
}
|
||||
|
||||
if (typeof configDefaultReturn === "function") {
|
||||
localConfig = await configDefaultReturn(this.userConfig);
|
||||
} else {
|
||||
localConfig = configDefaultReturn;
|
||||
}
|
||||
|
||||
// Removed a check for `filters` in 3.0.0-alpha.6 (now using addTransform instead) https://v3.11ty.dev/docs/config/#transforms
|
||||
} catch (err) {
|
||||
let isModuleError =
|
||||
err instanceof Error && (err?.message || "").includes("Cannot find module");
|
||||
|
||||
// TODO the error message here is bad and I feel bad (needs more accurate info)
|
||||
return Promise.reject(
|
||||
new EleventyConfigError(
|
||||
`Error in your Eleventy config file '${path}'.` +
|
||||
(isModuleError ? chalk.cyan(" You may need to run `npm install`.") : ""),
|
||||
err,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
debug(
|
||||
"Project config file not found (not an error—skipping). Looked in: %o",
|
||||
this.projectConfigPaths,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
localConfig,
|
||||
exportedConfig,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges different config files together.
|
||||
*
|
||||
* @returns {Promise<object>} merged - The merged config file.
|
||||
*/
|
||||
async mergeConfig() {
|
||||
let { localConfig, exportedConfig } = await this.requireLocalConfigFile();
|
||||
|
||||
// Merge `export const config = {}` with `return {}` in config callback
|
||||
if (isPlainObject(exportedConfig)) {
|
||||
localConfig = Merge(localConfig || {}, exportedConfig);
|
||||
}
|
||||
|
||||
if (this.directories) {
|
||||
if (Object.keys(this.userConfig.directoryAssignments || {}).length > 0) {
|
||||
debug(
|
||||
"Setting directories via set*Directory configuration APIs %o",
|
||||
this.userConfig.directoryAssignments,
|
||||
);
|
||||
this.directories.setViaConfigObject(this.userConfig.directoryAssignments);
|
||||
}
|
||||
|
||||
if (localConfig && Object.keys(localConfig?.dir || {}).length > 0) {
|
||||
debug(
|
||||
"Setting directories via `dir` object return from configuration file: %o",
|
||||
localConfig.dir,
|
||||
);
|
||||
this.directories.setViaConfigObject(localConfig.dir);
|
||||
}
|
||||
}
|
||||
|
||||
// `templateFormats` is an override via `setTemplateFormats`
|
||||
if (this.userConfig?.templateFormats) {
|
||||
this.templateFormats.setViaConfig(this.userConfig.templateFormats);
|
||||
} else if (localConfig?.templateFormats || this.rootConfig?.templateFormats) {
|
||||
// Local project config or defaultConfig.js
|
||||
this.templateFormats.setViaConfig(
|
||||
localConfig.templateFormats || this.rootConfig?.templateFormats,
|
||||
);
|
||||
}
|
||||
|
||||
// `templateFormatsAdded` is additive via `addTemplateFormats`
|
||||
if (this.userConfig?.templateFormatsAdded) {
|
||||
this.templateFormats.addViaConfig(this.userConfig.templateFormatsAdded);
|
||||
}
|
||||
|
||||
let mergedConfig = Merge({}, this.rootConfig, localConfig);
|
||||
|
||||
// Setup a few properties for plugins:
|
||||
|
||||
// Set frozen templateFormats
|
||||
mergedConfig.templateFormats = Object.freeze(this.templateFormats.getTemplateFormats());
|
||||
|
||||
// Setup pathPrefix set via command line for plugin consumption
|
||||
if (this.overrides.pathPrefix) {
|
||||
mergedConfig.pathPrefix = this.overrides.pathPrefix;
|
||||
}
|
||||
|
||||
// Returning a falsy value (e.g. "") from user config should reset to the default value.
|
||||
if (!mergedConfig.pathPrefix) {
|
||||
mergedConfig.pathPrefix = this.rootConfig.pathPrefix;
|
||||
}
|
||||
|
||||
// This is not set in UserConfig.js so that getters aren’t converted to strings
|
||||
// We want to error if someone attempts to use a setter there.
|
||||
if (this.directories) {
|
||||
mergedConfig.directories = this.directories.getUserspaceInstance();
|
||||
}
|
||||
|
||||
// Delay processing plugins until after the result of localConfig is returned
|
||||
// But BEFORE the rest of the config options are merged
|
||||
// this way we can pass directories and other template information to plugins
|
||||
|
||||
await this.userConfig.events.emit("eleventy.beforeConfig", this.userConfig);
|
||||
|
||||
let pluginsBench = this.aggregateBenchmark.get("Processing plugins in config");
|
||||
pluginsBench.before();
|
||||
await this.processPlugins(mergedConfig);
|
||||
pluginsBench.after();
|
||||
|
||||
// Template formats added via plugins
|
||||
if (this.userConfig?.templateFormatsAdded) {
|
||||
this.templateFormats.addViaConfig(this.userConfig.templateFormatsAdded);
|
||||
mergedConfig.templateFormats = Object.freeze(this.templateFormats.getTemplateFormats());
|
||||
}
|
||||
|
||||
let eleventyConfigApiMergingObject = this.userConfig.getMergingConfigObject();
|
||||
|
||||
if ("templateFormats" in eleventyConfigApiMergingObject) {
|
||||
throw new Error(
|
||||
"Internal error: templateFormats should not return from `getMergingConfigObject`",
|
||||
);
|
||||
}
|
||||
|
||||
// Overrides are only used by pathPrefix
|
||||
debug("Configuration overrides: %o", this.overrides);
|
||||
Merge(mergedConfig, eleventyConfigApiMergingObject, this.overrides);
|
||||
|
||||
debug("Current configuration: %o", mergedConfig);
|
||||
|
||||
// Add to the merged config too
|
||||
mergedConfig.uses = this.usesGraph;
|
||||
|
||||
// this is used for the layouts event
|
||||
this.usesGraph.setConfig(mergedConfig);
|
||||
|
||||
return mergedConfig;
|
||||
}
|
||||
|
||||
get usesGraph() {
|
||||
if (!this._usesGraph) {
|
||||
this._usesGraph = new GlobalDependencyMap();
|
||||
this._usesGraph.setIsEsm(this.isEsm);
|
||||
this._usesGraph.setTemplateConfig(this);
|
||||
}
|
||||
return this._usesGraph;
|
||||
}
|
||||
|
||||
get uses() {
|
||||
if (!this.usesGraph) {
|
||||
throw new Error("The Eleventy Global Dependency Graph has not yet been initialized.");
|
||||
}
|
||||
return this.usesGraph;
|
||||
}
|
||||
|
||||
get existsCache() {
|
||||
if (!this._existsCache) {
|
||||
this._existsCache = new ExistsCache();
|
||||
this._existsCache.setDirectoryCheck(true);
|
||||
}
|
||||
return this._existsCache;
|
||||
}
|
||||
}
|
||||
|
||||
export default TemplateConfig;
|
Reference in New Issue
Block a user