summaryrefslogtreecommitdiff
path: root/node_modules/@11ty/eleventy/src/Eleventy.js
diff options
context:
space:
mode:
authorShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
committerShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
commit7a52ddeba2a68388b544f529d2d92104420f77b0 (patch)
tree15ddd47457a2cb4a96060747437d36474e4f6b4e /node_modules/@11ty/eleventy/src/Eleventy.js
parent53d6ae2b5568437afa5e4995580a3fb679b7b91b (diff)
Changed from static to 11ty!
Diffstat (limited to 'node_modules/@11ty/eleventy/src/Eleventy.js')
-rw-r--r--node_modules/@11ty/eleventy/src/Eleventy.js1565
1 files changed, 1565 insertions, 0 deletions
diff --git a/node_modules/@11ty/eleventy/src/Eleventy.js b/node_modules/@11ty/eleventy/src/Eleventy.js
new file mode 100644
index 0000000..0568a3e
--- /dev/null
+++ b/node_modules/@11ty/eleventy/src/Eleventy.js
@@ -0,0 +1,1565 @@
+import chalk from "kleur";
+import { performance } from "node:perf_hooks";
+import debugUtil from "debug";
+import { filesize } from "filesize";
+import path from "node:path";
+
+/* Eleventy Deps */
+import { TemplatePath } from "@11ty/eleventy-utils";
+import BundlePlugin from "@11ty/eleventy-plugin-bundle";
+
+import TemplateData from "./Data/TemplateData.js";
+import TemplateWriter from "./TemplateWriter.js";
+import EleventyExtensionMap from "./EleventyExtensionMap.js";
+import { EleventyErrorHandler } from "./Errors/EleventyErrorHandler.js";
+import EleventyBaseError from "./Errors/EleventyBaseError.js";
+import EleventyServe from "./EleventyServe.js";
+import EleventyWatch from "./EleventyWatch.js";
+import EleventyWatchTargets from "./EleventyWatchTargets.js";
+import EleventyFiles from "./EleventyFiles.js";
+import TemplatePassthroughManager from "./TemplatePassthroughManager.js";
+import TemplateConfig from "./TemplateConfig.js";
+import FileSystemSearch from "./FileSystemSearch.js";
+import TemplateEngineManager from "./Engines/TemplateEngineManager.js";
+
+/* Utils */
+import ConsoleLogger from "./Util/ConsoleLogger.js";
+import PathPrefixer from "./Util/PathPrefixer.js";
+import ProjectDirectories from "./Util/ProjectDirectories.js";
+import PathNormalizer from "./Util/PathNormalizer.js";
+import { isGlobMatch } from "./Util/GlobMatcher.js";
+import simplePlural from "./Util/Pluralize.js";
+import checkPassthroughCopyBehavior from "./Util/PassthroughCopyBehaviorCheck.js";
+import eventBus from "./EventBus.js";
+import {
+ getEleventyPackageJson,
+ importJsonSync,
+ getWorkingProjectPackageJsonPath,
+} from "./Util/ImportJsonSync.js";
+import { EleventyImport } from "./Util/Require.js";
+import ProjectTemplateFormats from "./Util/ProjectTemplateFormats.js";
+import { withResolvers } from "./Util/PromiseUtil.js";
+
+/* Plugins */
+import RenderPlugin, * as RenderPluginExtras from "./Plugins/RenderPlugin.js";
+import I18nPlugin, * as I18nPluginExtras from "./Plugins/I18nPlugin.js";
+import HtmlBasePlugin, * as HtmlBasePluginExtras from "./Plugins/HtmlBasePlugin.js";
+import { TransformPlugin as InputPathToUrlTransformPlugin } from "./Plugins/InputPathToUrl.js";
+import { IdAttributePlugin } from "./Plugins/IdAttributePlugin.js";
+import FileSystemRemap from "./Util/GlobRemap.js";
+
+const pkg = getEleventyPackageJson();
+const debug = debugUtil("Eleventy");
+
+/**
+ * Eleventy’s programmatic API
+ * @module 11ty/eleventy/Eleventy
+ */
+
+class Eleventy {
+ /**
+ * Userspace package.json file contents
+ * @type {object|undefined}
+ */
+ #projectPackageJson;
+ /** @type {string} */
+ #projectPackageJsonPath;
+ /** @type {ProjectTemplateFormats|undefined} */
+ #templateFormats;
+ /** @type {ConsoleLogger|undefined} */
+ #logger;
+ /** @type {ProjectDirectories|undefined} */
+ #directories;
+ /** @type {boolean|undefined} */
+ #verboseOverride;
+ /** @type {boolean} */
+ #isVerboseMode = true;
+ /** @type {boolean|undefined} */
+ #preInitVerbose;
+ /** @type {boolean} */
+ #hasConfigInitialized = false;
+ /** @type {boolean} */
+ #needsInit = true;
+ /** @type {Promise|undefined} */
+ #initPromise;
+ /** @type {EleventyErrorHandler|undefined} */
+ #errorHandler;
+ /** @type {Map} */
+ #privateCaches = new Map();
+ /** @type {boolean} */
+ #isStopping = false;
+ /** @type {boolean|undefined} */
+ #isEsm;
+
+ /**
+ * @typedef {object} EleventyOptions
+ * @property {'cli'|'script'=} source
+ * @property {'build'|'serve'|'watch'=} runMode
+ * @property {boolean=} dryRun
+ * @property {string=} configPath
+ * @property {string=} pathPrefix
+ * @property {boolean=} quietMode
+ * @property {Function=} config
+ * @property {string=} inputDir
+
+ * @param {string} [input] - Directory or filename for input/sources files.
+ * @param {string} [output] - Directory serving as the target for writing the output files.
+ * @param {EleventyOptions} [options={}]
+ * @param {TemplateConfig} [eleventyConfig]
+ */
+ constructor(input, output, options = {}, eleventyConfig = null) {
+ /**
+ * @type {string|undefined}
+ * @description Holds the path to the input (might be a file or folder)
+ */
+ this.rawInput = input || undefined;
+
+ /**
+ * @type {string|undefined}
+ * @description holds the path to the output directory
+ */
+ this.rawOutput = output || undefined;
+
+ /**
+ * @type {module:11ty/eleventy/TemplateConfig}
+ * @description Override the config instance (for centralized config re-use)
+ */
+ this.eleventyConfig = eleventyConfig;
+
+ /**
+ * @type {EleventyOptions}
+ * @description Options object passed to the Eleventy constructor
+ * @default {}
+ */
+ this.options = options;
+
+ /**
+ * @type {'cli'|'script'}
+ * @description Called via CLI (`cli`) or Programmatically (`script`)
+ * @default "script"
+ */
+ this.source = options.source || "script";
+
+ /**
+ * @type {string}
+ * @description One of build, serve, or watch
+ * @default "build"
+ */
+ this.runMode = options.runMode || "build";
+
+ /**
+ * @type {boolean}
+ * @description Is Eleventy running in dry mode?
+ * @default false
+ */
+ this.isDryRun = options.dryRun ?? false;
+
+ /**
+ * @type {boolean}
+ * @description Is this an incremental build? (only operates on a subset of input files)
+ * @default false
+ */
+ this.isIncremental = false;
+
+ /**
+ * @type {string|undefined}
+ * @description If an incremental build, this is the file we’re operating on.
+ * @default null
+ */
+ this.programmaticApiIncrementalFile = undefined;
+
+ /**
+ * @type {boolean}
+ * @description Should we process files on first run? (The --ignore-initial feature)
+ * @default true
+ */
+ this.isRunInitialBuild = true;
+
+ /**
+ * @type {Number}
+ * @description Number of builds run on this instance.
+ * @default 0
+ */
+ this.buildCount = 0;
+
+ /**
+ * @member {String} - Force ESM or CJS mode instead of detecting from package.json. Either cjs, esm, or auto.
+ * @default "auto"
+ */
+ this.loader = this.options.loader ?? "auto";
+
+ /**
+ * @type {Number}
+ * @description The timestamp of Eleventy start.
+ */
+ this.start = this.getNewTimestamp();
+ }
+
+ /**
+ * @type {string|undefined}
+ * @description An override of Eleventy's default config file paths
+ * @default undefined
+ */
+ get configPath() {
+ return this.options.configPath;
+ }
+
+ /**
+ * @type {string}
+ * @description The top level directory the site pretends to reside in
+ * @default "/"
+ */
+ get pathPrefix() {
+ return this.options.pathPrefix || "/";
+ }
+
+ async initializeConfig(initOverrides) {
+ if (!this.eleventyConfig) {
+ this.eleventyConfig = new TemplateConfig(null, this.configPath);
+ } else if (this.configPath) {
+ await this.eleventyConfig.setProjectConfigPath(this.configPath);
+ }
+
+ this.eleventyConfig.setRunMode(this.runMode);
+ this.eleventyConfig.setProjectUsingEsm(this.isEsm);
+ this.eleventyConfig.setLogger(this.logger);
+ this.eleventyConfig.setDirectories(this.directories);
+ this.eleventyConfig.setTemplateFormats(this.templateFormats);
+
+ if (this.pathPrefix || this.pathPrefix === "") {
+ this.eleventyConfig.setPathPrefix(this.pathPrefix);
+ }
+
+ // Debug mode should always run quiet (all output goes to debug logger)
+ if (process.env.DEBUG) {
+ this.#verboseOverride = false;
+ } else if (this.options.quietMode === true || this.options.quietMode === false) {
+ this.#verboseOverride = !this.options.quietMode;
+ }
+
+ // Moved before config merges: https://github.com/11ty/eleventy/issues/3316
+ if (this.#verboseOverride === true || this.#verboseOverride === false) {
+ this.eleventyConfig.userConfig._setQuietModeOverride(!this.#verboseOverride);
+ }
+
+ this.eleventyConfig.userConfig.directories = this.directories;
+
+ /* Programmatic API config */
+ if (this.options.config && typeof this.options.config === "function") {
+ debug("Running options.config configuration callback (passed to Eleventy constructor)");
+ // TODO use return object here?
+ await this.options.config(this.eleventyConfig.userConfig);
+ }
+
+ /**
+ * @type {object}
+ * @description Initialize Eleventy environment variables
+ * @default null
+ */
+ // this.runMode need to be set before this
+ this.env = this.getEnvironmentVariableValues();
+ this.initializeEnvironmentVariables(this.env);
+
+ // Async initialization of configuration
+ await this.eleventyConfig.init(initOverrides);
+
+ /**
+ * @type {object}
+ * @description Initialize Eleventy’s configuration, including the user config file
+ */
+ this.config = this.eleventyConfig.getConfig();
+
+ /**
+ * @type {object}
+ * @description Singleton BenchmarkManager instance
+ */
+ this.bench = this.config.benchmarkManager;
+
+ if (performance) {
+ debug("Eleventy warm up time: %o (ms)", performance.now());
+ }
+
+ // Careful to make sure the previous server closes on SIGINT, issue #3873
+ if (!this.eleventyServe) {
+ /** @type {object} */
+ this.eleventyServe = new EleventyServe();
+ }
+ this.eleventyServe.eleventyConfig = this.eleventyConfig;
+
+ /** @type {object} */
+ this.watchManager = new EleventyWatch();
+
+ /** @type {object} */
+ this.watchTargets = new EleventyWatchTargets(this.eleventyConfig);
+ this.watchTargets.addAndMakeGlob(this.config.additionalWatchTargets);
+
+ /** @type {object} */
+ this.fileSystemSearch = new FileSystemSearch();
+
+ this.#hasConfigInitialized = true;
+
+ // after #hasConfigInitialized above
+ this.setIsVerbose(this.#preInitVerbose ?? !this.config.quietMode);
+ }
+
+ getNewTimestamp() {
+ if (performance) {
+ return performance.now();
+ }
+ return new Date().getTime();
+ }
+
+ /** @type {ProjectDirectories} */
+ get directories() {
+ if (!this.#directories) {
+ this.#directories = new ProjectDirectories();
+ this.#directories.setInput(this.rawInput, this.options.inputDir);
+ this.#directories.setOutput(this.rawOutput);
+
+ if (this.source == "cli" && (this.rawInput !== undefined || this.rawOutput !== undefined)) {
+ this.#directories.freeze();
+ }
+ }
+
+ return this.#directories;
+ }
+
+ /** @type {string} */
+ get input() {
+ return this.directories.inputFile || this.directories.input || this.config.dir.input;
+ }
+
+ /** @type {string} */
+ get inputFile() {
+ return this.directories.inputFile;
+ }
+
+ /** @type {string} */
+ get inputDir() {
+ return this.directories.input;
+ }
+
+ // Not used internally, removed in 3.0.
+ setInputDir() {
+ throw new Error(
+ "Eleventy->setInputDir was removed in 3.0. Use the inputDir option to the constructor",
+ );
+ }
+
+ /** @type {string} */
+ get outputDir() {
+ return this.directories.output || this.config.dir.output;
+ }
+
+ /**
+ * Updates the dry-run mode of Eleventy.
+ *
+ * @param {boolean} isDryRun - Shall Eleventy run in dry mode?
+ */
+ setDryRun(isDryRun) {
+ this.isDryRun = !!isDryRun;
+ }
+
+ /**
+ * Sets the incremental build mode.
+ *
+ * @param {boolean} isIncremental - Shall Eleventy run in incremental build mode and only write the files that trigger watch updates
+ */
+ setIncrementalBuild(isIncremental) {
+ this.isIncremental = !!isIncremental;
+
+ if (this.watchManager) {
+ this.watchManager.incremental = !!isIncremental;
+ }
+ if (this.writer) {
+ this.writer.setIncrementalBuild(this.isIncremental);
+ }
+ }
+
+ /**
+ * Set whether or not to do an initial build
+ *
+ * @param {boolean} ignoreInitialBuild - Shall Eleventy ignore the default initial build before watching in watch/serve mode?
+ * @default true
+ */
+ setIgnoreInitial(ignoreInitialBuild) {
+ this.isRunInitialBuild = !ignoreInitialBuild;
+
+ if (this.writer) {
+ this.writer.setRunInitialBuild(this.isRunInitialBuild);
+ }
+ }
+
+ /**
+ * Updates the path prefix used in the config.
+ *
+ * @param {string} pathPrefix - The new path prefix.
+ */
+ setPathPrefix(pathPrefix) {
+ if (pathPrefix || pathPrefix === "") {
+ this.eleventyConfig.setPathPrefix(pathPrefix);
+ // TODO reset config
+ // this.config = this.eleventyConfig.getConfig();
+ }
+ }
+
+ /**
+ * Restarts Eleventy.
+ */
+ async restart() {
+ debug("Restarting.");
+ this.start = this.getNewTimestamp();
+
+ this.extensionMap.reset();
+ this.bench.reset();
+ this.passthroughManager.reset();
+ this.eleventyFiles.restart();
+ }
+
+ /**
+ * Logs some statistics after a complete run of Eleventy.
+ *
+ * @returns {string} ret - The log message.
+ */
+ logFinished() {
+ if (!this.writer) {
+ throw new Error(
+ "Did you call Eleventy.init to create the TemplateWriter instance? Hint: you probably didn’t.",
+ );
+ }
+
+ let ret = [];
+
+ let {
+ copyCount,
+ copySize,
+ skipCount,
+ writeCount,
+ // renderCount, // files that render (costly) but may not write to disk
+ } = this.writer.getMetadata();
+
+ let slashRet = [];
+
+ if (copyCount) {
+ debug("Total passthrough copy aggregate size: %o", filesize(copySize));
+ slashRet.push(`Copied ${chalk.bold(copyCount)}`);
+ }
+
+ slashRet.push(
+ `Wrote ${chalk.bold(writeCount)} ${simplePlural(writeCount, "file", "files")}${
+ skipCount ? ` (skipped ${skipCount})` : ""
+ }`,
+ );
+
+ // slashRet.push(
+ // `${renderCount} rendered`
+ // )
+
+ if (slashRet.length) {
+ ret.push(slashRet.join(" "));
+ }
+
+ let time = (this.getNewTimestamp() - this.start) / 1000;
+ ret.push(
+ `in ${chalk.bold(time.toFixed(2))} ${simplePlural(time.toFixed(2), "second", "seconds")}`,
+ );
+
+ // More than 1 second total, show estimate of per-template time
+ if (time >= 1 && writeCount > 1) {
+ ret.push(`(${((time * 1000) / writeCount).toFixed(1)}ms each, v${pkg.version})`);
+ } else {
+ ret.push(`(v${pkg.version})`);
+ }
+
+ return ret.join(" ");
+ }
+
+ #cache(key, inst) {
+ if (!("caches" in inst)) {
+ throw new Error("To use #cache you need a `caches` getter object");
+ }
+
+ // Restore from cache
+ if (this.#privateCaches.has(key)) {
+ let c = this.#privateCaches.get(key);
+ for (let cacheKey in c) {
+ inst[cacheKey] = c[cacheKey];
+ }
+ } else {
+ // Set cache
+ let c = {};
+ for (let cacheKey of inst.caches || []) {
+ c[cacheKey] = inst[cacheKey];
+ }
+ this.#privateCaches.set(key, c);
+ }
+ }
+
+ /**
+ * Starts Eleventy.
+ */
+ async init(options = {}) {
+ let { viaConfigReset } = Object.assign({ viaConfigReset: false }, options);
+ if (!this.#hasConfigInitialized) {
+ await this.initializeConfig();
+ } else {
+ // Note: Global event bus is different from user config event bus
+ this.config.events.reset();
+ }
+
+ await this.config.events.emit("eleventy.config", this.eleventyConfig);
+
+ if (this.env) {
+ await this.config.events.emit("eleventy.env", this.env);
+ }
+
+ let formats = this.templateFormats.getTemplateFormats();
+ let engineManager = new TemplateEngineManager(this.eleventyConfig);
+ this.extensionMap = new EleventyExtensionMap(this.eleventyConfig);
+ this.extensionMap.setFormats(formats);
+ this.extensionMap.engineManager = engineManager;
+ await this.config.events.emit("eleventy.extensionmap", this.extensionMap);
+
+ // eleventyServe is always available, even when not in --serve mode
+ // TODO directorynorm
+ this.eleventyServe.setOutputDir(this.outputDir);
+
+ // TODO
+ // this.eleventyServe.setWatcherOptions(this.getChokidarConfig());
+
+ this.templateData = new TemplateData(this.eleventyConfig);
+ this.templateData.setProjectUsingEsm(this.isEsm);
+ this.templateData.extensionMap = this.extensionMap;
+ if (this.env) {
+ this.templateData.environmentVariables = this.env;
+ }
+ this.templateData.setFileSystemSearch(this.fileSystemSearch);
+
+ this.passthroughManager = new TemplatePassthroughManager(this.eleventyConfig);
+ this.passthroughManager.setRunMode(this.runMode);
+ this.passthroughManager.setDryRun(this.isDryRun);
+ this.passthroughManager.extensionMap = this.extensionMap;
+ this.passthroughManager.setFileSystemSearch(this.fileSystemSearch);
+
+ this.eleventyFiles = new EleventyFiles(formats, this.eleventyConfig);
+ this.eleventyFiles.setPassthroughManager(this.passthroughManager);
+ this.eleventyFiles.setFileSystemSearch(this.fileSystemSearch);
+ this.eleventyFiles.setRunMode(this.runMode);
+ this.eleventyFiles.extensionMap = this.extensionMap;
+ // This needs to be set before init or it’ll construct a new one
+ this.eleventyFiles.templateData = this.templateData;
+ this.eleventyFiles.init();
+
+ if (checkPassthroughCopyBehavior(this.config, this.runMode)) {
+ this.eleventyServe.watchPassthroughCopy(
+ this.eleventyFiles.getGlobWatcherFilesForPassthroughCopy(),
+ );
+ }
+
+ // Note these directories are all project root relative
+ this.config.events.emit("eleventy.directories", this.directories.getUserspaceInstance());
+
+ this.writer = new TemplateWriter(formats, this.templateData, this.eleventyConfig);
+
+ if (!viaConfigReset) {
+ // set or restore cache
+ this.#cache("TemplateWriter", this.writer);
+ }
+
+ this.writer.logger = this.logger;
+ this.writer.extensionMap = this.extensionMap;
+ this.writer.setEleventyFiles(this.eleventyFiles);
+ this.writer.setPassthroughManager(this.passthroughManager);
+ this.writer.setRunInitialBuild(this.isRunInitialBuild);
+ this.writer.setIncrementalBuild(this.isIncremental);
+
+ let debugStr = `Directories:
+ Input:
+ Directory: ${this.directories.input}
+ File: ${this.directories.inputFile || false}
+ Glob: ${this.directories.inputGlob || false}
+ Data: ${this.directories.data}
+ Includes: ${this.directories.includes}
+ Layouts: ${this.directories.layouts || false}
+ Output: ${this.directories.output}
+Template Formats: ${formats.join(",")}
+Verbose Output: ${this.verboseMode}`;
+ debug(debugStr);
+
+ this.writer.setVerboseOutput(this.verboseMode);
+ this.writer.setDryRun(this.isDryRun);
+
+ this.#needsInit = false;
+ }
+
+ // These are all set as initial global data under eleventy.env.* (see TemplateData->environmentVariables)
+ getEnvironmentVariableValues() {
+ let values = {
+ source: this.source,
+ runMode: this.runMode,
+ };
+
+ let configPath = this.eleventyConfig.getLocalProjectConfigFile();
+ if (configPath) {
+ values.config = TemplatePath.absolutePath(configPath);
+ }
+
+ // Fixed: instead of configuration directory, explicit root or working directory
+ values.root = TemplatePath.getWorkingDir();
+
+ values.source = this.source;
+
+ // Backwards compatibility
+ Object.defineProperty(values, "isServerless", {
+ enumerable: false,
+ value: false,
+ });
+
+ return values;
+ }
+
+ /**
+ * Set process.ENV variables for use in Eleventy projects
+ *
+ * @method
+ */
+ initializeEnvironmentVariables(env) {
+ // Recognize that global data `eleventy.version` is coerced to remove prerelease tags
+ // and this is the raw version (3.0.0 versus 3.0.0-alpha.6).
+ // `eleventy.env.version` does not yet exist (unnecessary)
+ process.env.ELEVENTY_VERSION = Eleventy.getVersion();
+
+ process.env.ELEVENTY_ROOT = env.root;
+ debug("Setting process.env.ELEVENTY_ROOT: %o", env.root);
+
+ process.env.ELEVENTY_SOURCE = env.source;
+ process.env.ELEVENTY_RUN_MODE = env.runMode;
+ }
+
+ /** @param {boolean} value */
+ set verboseMode(value) {
+ this.setIsVerbose(value);
+ }
+
+ /** @type {boolean} */
+ get verboseMode() {
+ return this.#isVerboseMode;
+ }
+
+ /** @type {ConsoleLogger} */
+ get logger() {
+ if (!this.#logger) {
+ this.#logger = new ConsoleLogger();
+ this.#logger.isVerbose = this.verboseMode;
+ }
+
+ return this.#logger;
+ }
+
+ /** @param {ConsoleLogger} logger */
+ set logger(logger) {
+ this.eleventyConfig.setLogger(logger);
+ this.#logger = logger;
+ }
+
+ disableLogger() {
+ this.logger.overrideLogger(false);
+ }
+
+ /** @type {EleventyErrorHandler} */
+ get errorHandler() {
+ if (!this.#errorHandler) {
+ this.#errorHandler = new EleventyErrorHandler();
+ this.#errorHandler.isVerbose = this.verboseMode;
+ this.#errorHandler.logger = this.logger;
+ }
+
+ return this.#errorHandler;
+ }
+
+ /**
+ * Updates the verbose mode of Eleventy.
+ *
+ * @method
+ * @param {boolean} isVerbose - Shall Eleventy run in verbose mode?
+ */
+ setIsVerbose(isVerbose) {
+ if (!this.#hasConfigInitialized) {
+ this.#preInitVerbose = !!isVerbose;
+ return;
+ }
+
+ // always defer to --quiet if override happened
+ isVerbose = this.#verboseOverride ?? !!isVerbose;
+
+ this.#isVerboseMode = isVerbose;
+
+ if (this.logger) {
+ this.logger.isVerbose = isVerbose;
+ }
+
+ this.bench.setVerboseOutput(isVerbose);
+
+ if (this.writer) {
+ this.writer.setVerboseOutput(isVerbose);
+ }
+
+ if (this.errorHandler) {
+ this.errorHandler.isVerbose = isVerbose;
+ }
+
+ // Set verbose mode in config file
+ this.eleventyConfig.verbose = isVerbose;
+ }
+
+ get templateFormats() {
+ if (!this.#templateFormats) {
+ let tf = new ProjectTemplateFormats();
+ this.#templateFormats = tf;
+ }
+
+ return this.#templateFormats;
+ }
+
+ /**
+ * Updates the template formats of Eleventy.
+ *
+ * @method
+ * @param {string} formats - The new template formats.
+ */
+ setFormats(formats) {
+ this.templateFormats.setViaCommandLine(formats);
+ }
+
+ /**
+ * Updates the run mode of Eleventy.
+ *
+ * @method
+ * @param {string} runMode - One of "build", "watch", or "serve"
+ */
+ setRunMode(runMode) {
+ this.runMode = runMode;
+ }
+
+ /**
+ * Set the file that needs to be rendered/compiled/written for an incremental build.
+ * This method is also wired up to the CLI --incremental=incrementalFile
+ *
+ * @method
+ * @param {string} incrementalFile - File path (added or modified in a project)
+ */
+ setIncrementalFile(incrementalFile) {
+ if (incrementalFile) {
+ // This used to also setIgnoreInitial(true) but was changed in 3.0.0-alpha.14
+ this.setIncrementalBuild(true);
+
+ this.programmaticApiIncrementalFile = TemplatePath.addLeadingDotSlash(incrementalFile);
+
+ this.eleventyConfig.setPreviousBuildModifiedFile(incrementalFile);
+ }
+ }
+
+ unsetIncrementalFile() {
+ // only applies to initial build, no re-runs (--watch/--serve)
+ if (this.programmaticApiIncrementalFile) {
+ // this.setIgnoreInitial(false);
+ this.programmaticApiIncrementalFile = undefined;
+ }
+
+ // reset back to false
+ this.setIgnoreInitial(false);
+ }
+
+ /**
+ * Reads the version of Eleventy.
+ *
+ * @static
+ * @returns {string} - The version of Eleventy.
+ */
+ static getVersion() {
+ return pkg.version;
+ }
+
+ /**
+ * @deprecated since 1.0.1, use static Eleventy.getVersion()
+ */
+ getVersion() {
+ return Eleventy.getVersion();
+ }
+
+ /**
+ * Shows a help message including usage.
+ *
+ * @static
+ * @returns {string} - The help message.
+ */
+ static getHelp() {
+ return `Usage: eleventy
+ eleventy --input=. --output=./_site
+ eleventy --serve
+
+Arguments:
+
+ --version
+
+ --input=.
+ Input template files (default: \`.\`)
+
+ --output=_site
+ Write HTML output to this folder (default: \`_site\`)
+
+ --serve
+ Run web server on --port (default 8080) and watch them too
+
+ --port
+ Run the --serve web server on this port (default 8080)
+
+ --watch
+ Wait for files to change and automatically rewrite (no web server)
+
+ --incremental
+ Only build the files that have changed. Best with watch/serve.
+
+ --incremental=filename.md
+ Does not require watch/serve. Run an incremental build targeting a single file.
+
+ --ignore-initial
+ Start without a build; build when files change. Works best with watch/serve/incremental.
+
+ --formats=liquid,md
+ Allow only certain template types (default: \`*\`)
+
+ --quiet
+ Don’t print all written files (off by default)
+
+ --config=filename.js
+ Override the eleventy config file path (default: \`.eleventy.js\`)
+
+ --pathprefix='/'
+ Change all url template filters to use this subdirectory.
+
+ --dryrun
+ Don’t write any files. Useful in DEBUG mode, for example: \`DEBUG=Eleventy* npx @11ty/eleventy --dryrun\`
+
+ --loader
+ Set to "esm" to force ESM mode, "cjs" to force CommonJS mode, or "auto" (default) to infer it from package.json.
+
+ --to=json
+ --to=ndjson
+ Change the output to JSON or NDJSON (default: \`fs\`)
+
+ --help`;
+ }
+
+ /**
+ * @deprecated since 1.0.1, use static Eleventy.getHelp()
+ */
+ getHelp() {
+ return Eleventy.getHelp();
+ }
+
+ /**
+ * Resets the config of Eleventy.
+ *
+ * @method
+ */
+ resetConfig() {
+ delete this.eleventyConfig;
+
+ // ensures `initializeConfig()` will run when `init()` is called next
+ this.#hasConfigInitialized = false;
+ }
+
+ /**
+ * @param {string} changedFilePath - File that triggered a re-run (added or modified)
+ * @param {boolean} [isResetConfig] - are we doing a config reset
+ */
+ async #addFileToWatchQueue(changedFilePath, isResetConfig) {
+ // Currently this is only for 11ty.js deps but should be extended with usesGraph
+ let usedByDependants = [];
+ if (this.watchTargets) {
+ usedByDependants = this.watchTargets.getDependantsOf(
+ TemplatePath.addLeadingDotSlash(changedFilePath),
+ );
+ }
+
+ let relevantLayouts = this.eleventyConfig.usesGraph.getLayoutsUsedBy(changedFilePath);
+
+ // `eleventy.templateModified` is no longer used internally, remove in a future major version.
+ eventBus.emit("eleventy.templateModified", changedFilePath, {
+ usedByDependants,
+ relevantLayouts,
+ });
+
+ // These listeners are *global*, not cleared even on config reset
+ eventBus.emit("eleventy.resourceModified", changedFilePath, usedByDependants, {
+ viaConfigReset: isResetConfig,
+ relevantLayouts,
+ });
+
+ this.config.events.emit("eleventy#templateModified", changedFilePath);
+
+ this.watchManager.addToPendingQueue(changedFilePath);
+ }
+
+ shouldTriggerConfigReset(changedFiles) {
+ let configFilePaths = new Set(this.eleventyConfig.getLocalProjectConfigFiles());
+ let resetConfigGlobs = EleventyWatchTargets.normalizeToGlobs(
+ Array.from(this.eleventyConfig.userConfig.watchTargetsConfigReset),
+ );
+ for (let filePath of changedFiles) {
+ if (configFilePaths.has(filePath)) {
+ return true;
+ }
+ if (isGlobMatch(filePath, resetConfigGlobs)) {
+ return true;
+ }
+ }
+
+ for (const configFilePath of configFilePaths) {
+ // Any dependencies of the config file changed
+ let configFileDependencies = new Set(this.watchTargets.getDependenciesOf(configFilePath));
+
+ for (let filePath of changedFiles) {
+ if (configFileDependencies.has(filePath)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Checks the build queue to see if any configuration related files have changed
+ #shouldResetConfig(activeQueue = []) {
+ if (!activeQueue.length) {
+ return false;
+ }
+
+ return this.shouldTriggerConfigReset(
+ activeQueue.map((path) => {
+ return PathNormalizer.normalizeSeperator(TemplatePath.addLeadingDotSlash(path));
+ }),
+ );
+ }
+
+ async #watch(isResetConfig = false) {
+ if (this.watchManager.isBuildRunning()) {
+ return;
+ }
+
+ this.watchManager.setBuildRunning();
+
+ let queue = this.watchManager.getActiveQueue();
+
+ await this.config.events.emit("beforeWatch", queue);
+ await this.config.events.emit("eleventy.beforeWatch", queue);
+
+ // Clear `import` cache for all files that triggered the rebuild (sync event)
+ this.watchTargets.clearImportCacheFor(queue);
+
+ // reset and reload global configuration
+ if (isResetConfig) {
+ // important: run this before config resets otherwise the handlers will disappear.
+ await this.config.events.emit("eleventy.reset");
+ this.resetConfig();
+ }
+
+ await this.restart();
+ await this.init({ viaConfigReset: isResetConfig });
+
+ try {
+ let [passthroughCopyResults, templateResults] = await this.write();
+
+ this.watchTargets.reset();
+
+ await this.#initWatchDependencies();
+
+ // Add new deps to chokidar
+ this.watcher.add(this.watchTargets.getNewTargetsSinceLastReset());
+
+ // Is a CSS input file and is not in the includes folder
+ // TODO check output path file extension of this template (not input path)
+ // TODO add additional API for this, maybe a config callback?
+ let onlyCssChanges = this.watchManager.hasAllQueueFiles((path) => {
+ return (
+ path.endsWith(".css") &&
+ // TODO how to make this work with relative includes?
+ !TemplatePath.startsWithSubPath(path, this.eleventyFiles.getIncludesDir())
+ );
+ });
+
+ let files = this.watchManager.getActiveQueue();
+
+ // Maps passthrough copy files to output URLs for CSS live reload
+ let stylesheetUrls = new Set();
+ for (let entry of passthroughCopyResults) {
+ for (let filepath in entry.map) {
+ if (
+ filepath.endsWith(".css") &&
+ files.includes(TemplatePath.addLeadingDotSlash(filepath))
+ ) {
+ stylesheetUrls.add(
+ "/" + TemplatePath.stripLeadingSubPath(entry.map[filepath], this.outputDir),
+ );
+ }
+ }
+ }
+
+ let normalizedPathPrefix = PathPrefixer.normalizePathPrefix(this.config.pathPrefix);
+ let matchingTemplates = templateResults
+ .flat()
+ .filter((entry) => Boolean(entry))
+ .map((entry) => {
+ // only `url`, `inputPath`, and `content` are used: https://github.com/11ty/eleventy-dev-server/blob/1c658605f75224fdc76f68aebe7a412eeb4f1bc9/client/reload-client.js#L140
+ entry.url = PathPrefixer.joinUrlParts(normalizedPathPrefix, entry.url);
+ delete entry.rawInput; // Issue #3481
+ return entry;
+ });
+
+ await this.eleventyServe.reload({
+ files,
+ subtype: onlyCssChanges ? "css" : undefined,
+ build: {
+ stylesheets: Array.from(stylesheetUrls),
+ templates: matchingTemplates,
+ },
+ });
+ } catch (error) {
+ this.eleventyServe.sendError({
+ error,
+ });
+ }
+
+ this.watchManager.setBuildFinished();
+
+ let queueSize = this.watchManager.getPendingQueueSize();
+ if (queueSize > 0) {
+ this.logger.log(
+ `You saved while Eleventy was running, let’s run again. (${queueSize} change${
+ queueSize !== 1 ? "s" : ""
+ })`,
+ );
+ await this.#watch();
+ } else {
+ this.logger.log("Watching…");
+ }
+ }
+
+ /**
+ * @returns {module:11ty/eleventy/src/Benchmark/BenchmarkGroup~BenchmarkGroup}
+ */
+ get watcherBench() {
+ return this.bench.get("Watcher");
+ }
+
+ /**
+ * Set up watchers and benchmarks.
+ *
+ * @async
+ * @method
+ */
+ async initWatch() {
+ this.watchManager = new EleventyWatch();
+ this.watchManager.incremental = this.isIncremental;
+
+ if (this.projectPackageJsonPath) {
+ this.watchTargets.add([
+ path.relative(TemplatePath.getWorkingDir(), this.projectPackageJsonPath),
+ ]);
+ }
+ this.watchTargets.add(this.eleventyFiles.getGlobWatcherFiles());
+ this.watchTargets.add(this.eleventyFiles.getIgnoreFiles());
+
+ // Watch the local project config file
+ this.watchTargets.add(this.eleventyConfig.getLocalProjectConfigFiles());
+
+ // Template and Directory Data Files
+ this.watchTargets.add(await this.eleventyFiles.getGlobWatcherTemplateDataFiles());
+
+ let benchmark = this.watcherBench.get(
+ "Watching JavaScript Dependencies (disable with `eleventyConfig.setWatchJavaScriptDependencies(false)`)",
+ );
+ benchmark.before();
+ await this.#initWatchDependencies();
+ benchmark.after();
+ }
+
+ // fetch from project’s package.json
+ get projectPackageJsonPath() {
+ if (this.#projectPackageJsonPath === undefined) {
+ this.#projectPackageJsonPath = getWorkingProjectPackageJsonPath() || false;
+ }
+ return this.#projectPackageJsonPath;
+ }
+
+ get projectPackageJson() {
+ if (!this.#projectPackageJson) {
+ let p = this.projectPackageJsonPath;
+ this.#projectPackageJson = p ? importJsonSync(p) : {};
+ }
+ return this.#projectPackageJson;
+ }
+
+ get isEsm() {
+ if (this.#isEsm !== undefined) {
+ return this.#isEsm;
+ }
+ if (this.loader == "esm") {
+ this.#isEsm = true;
+ } else if (this.loader == "cjs") {
+ this.#isEsm = false;
+ } else if (this.loader == "auto") {
+ this.#isEsm = this.projectPackageJson?.type === "module";
+ } else {
+ throw new Error("The 'loader' option must be one of 'esm', 'cjs', or 'auto'");
+ }
+ return this.#isEsm;
+ }
+
+ /**
+ * Starts watching dependencies.
+ */
+ async #initWatchDependencies() {
+ if (!this.eleventyConfig.shouldSpiderJavaScriptDependencies()) {
+ return;
+ }
+
+ // TODO use DirContains
+ let dataDir = TemplatePath.stripLeadingDotSlash(this.templateData.getDataDir());
+ function filterOutGlobalDataFiles(path) {
+ return !dataDir || !TemplatePath.stripLeadingDotSlash(path).startsWith(dataDir);
+ }
+
+ // Lazy resolve isEsm only for --watch
+ this.watchTargets.setProjectUsingEsm(this.isEsm);
+
+ // Template files .11ty.js
+ let templateFiles = await this.eleventyFiles.getWatchPathCache();
+ await this.watchTargets.addDependencies(templateFiles);
+
+ // Config file dependencies
+ await this.watchTargets.addDependencies(
+ this.eleventyConfig.getLocalProjectConfigFiles(),
+ filterOutGlobalDataFiles,
+ );
+
+ // Deps from Global Data (that aren’t in the global data directory, everything is watched there)
+ let globalDataDeps = this.templateData.getWatchPathCache();
+ await this.watchTargets.addDependencies(globalDataDeps, filterOutGlobalDataFiles);
+
+ await this.watchTargets.addDependencies(
+ await this.eleventyFiles.getWatcherTemplateJavaScriptDataFiles(),
+ );
+ }
+
+ /**
+ * Returns all watched files.
+ *
+ * @async
+ * @method
+ * @returns {Promise<Array>} targets - The watched files.
+ */
+ async getWatchedFiles() {
+ return this.watchTargets.getTargets();
+ }
+
+ getChokidarConfig() {
+ let ignores = this.eleventyFiles.getGlobWatcherIgnores();
+ debug("Ignoring watcher changes to: %o", ignores);
+
+ let configOptions = this.config.chokidarConfig;
+
+ // can’t override these yet
+ // TODO maybe if array, merge the array?
+ delete configOptions.ignored;
+
+ return Object.assign(
+ {
+ ignored: ignores,
+ ignoreInitial: true,
+ awaitWriteFinish: {
+ stabilityThreshold: 150,
+ pollInterval: 25,
+ },
+ },
+ configOptions,
+ );
+ }
+
+ /**
+ * Start the watching of files
+ *
+ * @async
+ * @method
+ */
+ async watch() {
+ this.watcherBench.setMinimumThresholdMs(500);
+ this.watcherBench.reset();
+
+ // We use a string module name and try/catch here to hide this from the zisi and esbuild serverless bundlers
+ let chokidar;
+ // eslint-disable-next-line no-useless-catch
+ try {
+ let moduleName = "chokidar";
+ let chokidarImport = await import(moduleName);
+ chokidar = chokidarImport.default;
+ } catch (e) {
+ throw e;
+ }
+
+ // Note that watching indirectly depends on this for fetching dependencies from JS files
+ // See: TemplateWriter:pathCache and EleventyWatchTargets
+ await this.write();
+
+ let initWatchBench = this.watcherBench.get("Start up --watch");
+ initWatchBench.before();
+
+ await this.initWatch();
+
+ // TODO improve unwatching if JS dependencies are removed (or files are deleted)
+ let rawFiles = await this.getWatchedFiles();
+ debug("Watching for changes to: %o", rawFiles);
+
+ let options = this.getChokidarConfig();
+
+ // Remap all paths to `cwd` if in play (Issue #3854)
+ let remapper = new FileSystemRemap(rawFiles);
+ let cwd = remapper.getCwd();
+
+ if (cwd) {
+ options.cwd = cwd;
+
+ rawFiles = remapper.getInput().map((entry) => {
+ return TemplatePath.stripLeadingDotSlash(entry);
+ });
+
+ options.ignored = remapper.getRemapped(options.ignored || []).map((entry) => {
+ return TemplatePath.stripLeadingDotSlash(entry);
+ });
+ }
+
+ let watcher = chokidar.watch(rawFiles, options);
+
+ initWatchBench.after();
+
+ this.watcherBench.finish("Watch");
+
+ this.logger.forceLog("Watching…");
+
+ this.watcher = watcher;
+
+ let watchDelay;
+ let watchRun = async (path) => {
+ path = TemplatePath.normalize(path);
+ try {
+ let isResetConfig = this.#shouldResetConfig([path]);
+ this.#addFileToWatchQueue(path, isResetConfig);
+
+ clearTimeout(watchDelay);
+
+ let { promise, resolve, reject } = withResolvers();
+
+ watchDelay = setTimeout(async () => {
+ this.#watch(isResetConfig).then(resolve, reject);
+ }, this.config.watchThrottleWaitTime);
+
+ await promise;
+ } catch (e) {
+ if (e instanceof EleventyBaseError) {
+ this.errorHandler.error(e, "Eleventy watch error");
+ this.watchManager.setBuildFinished();
+ } else {
+ this.errorHandler.fatal(e, "Eleventy fatal watch error");
+ await this.stopWatch();
+ }
+ }
+
+ this.config.events.emit("eleventy.afterwatch");
+ };
+
+ watcher.on("change", async (path) => {
+ // Emulated passthrough copy logs from the server
+ if (!this.eleventyServe.isEmulatedPassthroughCopyMatch(path)) {
+ this.logger.forceLog(`File changed: ${TemplatePath.standardizeFilePath(path)}`);
+ }
+
+ await watchRun(path);
+ });
+
+ watcher.on("add", async (path) => {
+ // Emulated passthrough copy logs from the server
+ if (!this.eleventyServe.isEmulatedPassthroughCopyMatch(path)) {
+ this.logger.forceLog(`File added: ${TemplatePath.standardizeFilePath(path)}`);
+ }
+
+ this.fileSystemSearch.add(path);
+ await watchRun(path);
+ });
+
+ watcher.on("unlink", (path) => {
+ this.logger.forceLog(`File deleted: ${TemplatePath.standardizeFilePath(path)}`);
+ this.fileSystemSearch.delete(path);
+ });
+
+ // wait for chokidar to be ready.
+ await new Promise((resolve) => {
+ watcher.on("ready", () => resolve());
+ });
+
+ // Returns for testability
+ return watchRun;
+ }
+
+ async stopWatch() {
+ // Prevent multiple invocations.
+ if (this.#isStopping) {
+ return this.#isStopping;
+ }
+
+ debug("Cleaning up chokidar and server instances, if they exist.");
+ this.#isStopping = Promise.all([this.eleventyServe.close(), this.watcher?.close()]).then(() => {
+ this.#isStopping = false;
+ });
+
+ return this.#isStopping;
+ }
+
+ /**
+ * Serve Eleventy on this port.
+ *
+ * @param {Number} port - The HTTP port to serve Eleventy from.
+ */
+ async serve(port) {
+ // Port is optional and in this case likely via --port on the command line
+ // May defer to configuration API options `port` property
+ return this.eleventyServe.serve(port);
+ }
+
+ /**
+ * Writes templates to the file system.
+ *
+ * @async
+ * @method
+ * @returns {Promise<{Array}>}
+ */
+ async write() {
+ return this.executeBuild("fs");
+ }
+
+ /**
+ * Renders templates to a JSON object.
+ *
+ * @async
+ * @method
+ * @returns {Promise<{Array}>}
+ */
+ async toJSON() {
+ return this.executeBuild("json");
+ }
+
+ /**
+ * Returns a stream of new line delimited (NDJSON) objects
+ *
+ * @async
+ * @method
+ * @returns {Promise<{ReadableStream}>}
+ */
+ async toNDJSON() {
+ return this.executeBuild("ndjson");
+ }
+
+ /**
+ * tbd.
+ *
+ * @async
+ * @method
+ * @returns {Promise<{Array,ReadableStream}>} ret - tbd.
+ */
+ async executeBuild(to = "fs") {
+ if (this.#needsInit) {
+ if (!this.#initPromise) {
+ this.#initPromise = this.init();
+ }
+ await this.#initPromise.then(() => {
+ // #needsInit also set to false at the end of `init()`
+ this.#needsInit = false;
+ this.#initPromise = undefined;
+ });
+ }
+
+ if (!this.writer) {
+ throw new Error(
+ "Internal error: Eleventy didn’t run init() properly and wasn’t able to create a TemplateWriter.",
+ );
+ }
+
+ let incrementalFile =
+ this.programmaticApiIncrementalFile || this.watchManager?.getIncrementalFile();
+ if (incrementalFile) {
+ this.writer.setIncrementalFile(incrementalFile);
+ }
+
+ let returnObj;
+ let hasError = false;
+
+ try {
+ let directories = this.directories.getUserspaceInstance();
+ let eventsArg = {
+ directories,
+
+ // v3.0.0-alpha.6, changed to use `directories` instead (this was only used by serverless plugin)
+ inputDir: directories.input,
+
+ // Deprecated (not normalized) use `directories` instead.
+ dir: this.config.dir,
+
+ runMode: this.runMode,
+ outputMode: to,
+ incremental: this.isIncremental,
+ };
+
+ await this.config.events.emit("beforeBuild", eventsArg);
+ await this.config.events.emit("eleventy.before", eventsArg);
+
+ let promise;
+ if (to === "fs") {
+ promise = this.writer.write();
+ } else if (to === "json") {
+ promise = this.writer.getJSON("json");
+ } else if (to === "ndjson") {
+ promise = this.writer.getJSON("ndjson");
+ } else {
+ throw new Error(
+ `Invalid argument for \`Eleventy->executeBuild(${to})\`, expected "json", "ndjson", or "fs".`,
+ );
+ }
+
+ let resolved = await promise;
+
+ // Passing the processed output to the eleventy.after event (2.0+)
+ eventsArg.results = resolved.templates;
+
+ if (to === "ndjson") {
+ // return a stream
+ // TODO this outputs all ndjson rows after all the templates have been written to the stream
+ returnObj = this.logger.closeStream();
+ } else if (to === "json") {
+ // Backwards compat
+ returnObj = resolved.templates;
+ } else {
+ // Backwards compat
+ returnObj = [resolved.passthroughCopy, resolved.templates];
+ }
+
+ this.unsetIncrementalFile();
+ this.writer.resetIncrementalFile();
+
+ eventsArg.uses = this.eleventyConfig.usesGraph.map;
+ await this.config.events.emit("afterBuild", eventsArg);
+ await this.config.events.emit("eleventy.after", eventsArg);
+
+ this.buildCount++;
+ } catch (error) {
+ hasError = true;
+
+ // Issue #2405: Don’t change the exitCode for programmatic scripts
+ let errorSeverity = this.source === "script" ? "error" : "fatal";
+ this.errorHandler.once(errorSeverity, error, "Problem writing Eleventy templates");
+
+ // TODO ndjson should stream the error but https://github.com/11ty/eleventy/issues/3382
+ throw error;
+ } finally {
+ this.bench.finish();
+
+ if (to === "fs") {
+ this.logger.logWithOptions({
+ message: this.logFinished(),
+ color: hasError ? "red" : "green",
+ force: true,
+ });
+ }
+
+ debug("Finished.");
+
+ debug(`
+Have a suggestion/feature request/feedback? Feeling frustrated? I want to hear it!
+Open an issue: https://github.com/11ty/eleventy/issues/new`);
+ }
+
+ return returnObj;
+ }
+}
+
+export default Eleventy;
+
+// extend for exporting to CJS
+Object.assign(RenderPlugin, RenderPluginExtras);
+Object.assign(I18nPlugin, I18nPluginExtras);
+Object.assign(HtmlBasePlugin, HtmlBasePluginExtras);
+
+// Removed plugins
+
+const EleventyServerlessBundlerPlugin = function () {
+ throw new Error(
+ "Following feedback from our Community Survey, low interest in this plugin prompted its removal from Eleventy core in 3.0 as we refocus on static sites. Learn more: https://v3.11ty.dev/docs/plugins/serverless/",
+ );
+};
+
+const EleventyEdgePlugin = function () {
+ throw new Error(
+ "Following feedback from our Community Survey, low interest in this plugin prompted its removal from Eleventy core in 3.0 as we refocus on static sites. Learn more: https://v3.11ty.dev/docs/plugins/edge/",
+ );
+};
+
+export {
+ Eleventy,
+ EleventyImport as ImportFile,
+
+ // Error messages for removed plugins
+ EleventyServerlessBundlerPlugin as EleventyServerless,
+ EleventyServerlessBundlerPlugin,
+ EleventyEdgePlugin,
+
+ /**
+ * @type {module:11ty/eleventy/Plugins/RenderPlugin}
+ */
+ RenderPlugin as EleventyRenderPlugin, // legacy name
+ /**
+ * @type {module:11ty/eleventy/Plugins/RenderPlugin}
+ */
+ RenderPlugin,
+
+ /**
+ * @type {module:11ty/eleventy/Plugins/I18nPlugin}
+ */
+ I18nPlugin as EleventyI18nPlugin, // legacy name
+ /**
+ * @type {module:11ty/eleventy/Plugins/I18nPlugin}
+ */
+ I18nPlugin,
+
+ /**
+ * @type {module:11ty/eleventy/Plugins/HtmlBasePlugin}
+ */
+ HtmlBasePlugin as EleventyHtmlBasePlugin, // legacy name
+ /**
+ * @type {module:11ty/eleventy/Plugins/HtmlBasePlugin}
+ */
+ HtmlBasePlugin,
+
+ /**
+ * @type {module:11ty/eleventy/Plugins/InputPathToUrlTransformPlugin}
+ */
+ InputPathToUrlTransformPlugin,
+
+ /**
+ * @type {module:11ty/eleventy-plugin-bundle}
+ */
+ BundlePlugin,
+
+ /**
+ * @type {module:11ty/eleventy/Plugins/IdAttributePlugin}
+ */
+ IdAttributePlugin,
+};