768 lines
19 KiB
JavaScript
768 lines
19 KiB
JavaScript
const pkg = require("../package.json");
|
||
const TemplatePath = require("./TemplatePath");
|
||
const TemplateData = require("./TemplateData");
|
||
const TemplateWriter = require("./TemplateWriter");
|
||
const EleventyExtensionMap = require("./EleventyExtensionMap");
|
||
const EleventyErrorHandler = require("./EleventyErrorHandler");
|
||
const EleventyServe = require("./EleventyServe");
|
||
const EleventyWatch = require("./EleventyWatch");
|
||
const EleventyWatchTargets = require("./EleventyWatchTargets");
|
||
const EleventyFiles = require("./EleventyFiles");
|
||
const { performance } = require("perf_hooks");
|
||
|
||
const templateCache = require("./TemplateCache");
|
||
const simplePlural = require("./Util/Pluralize");
|
||
const deleteRequireCache = require("./Util/DeleteRequireCache");
|
||
const config = require("./Config");
|
||
const bench = require("./BenchmarkManager");
|
||
const debug = require("debug")("Eleventy");
|
||
|
||
/**
|
||
* @module @11ty/eleventy/Eleventy
|
||
*/
|
||
|
||
/**
|
||
* Runtime of eleventy.
|
||
*
|
||
* @param {String} input - Where to read files from.
|
||
* @param {String} output - Where to write rendered files to.
|
||
* @returns {undefined}
|
||
*/
|
||
class Eleventy {
|
||
constructor(input, output) {
|
||
/** @member {Object} - tbd. */
|
||
this.config = config.getConfig();
|
||
|
||
/**
|
||
* @member {String} - The path to Eleventy's config file.
|
||
* @default null
|
||
*/
|
||
this.configPath = null;
|
||
|
||
/**
|
||
* @member {Boolean} - Is Eleventy running in verbose mode?
|
||
* @default true
|
||
*/
|
||
this.isVerbose = process.env.DEBUG ? false : !this.config.quietMode;
|
||
|
||
/**
|
||
* @member {Boolean} - Was verbose mode overridden manually?
|
||
* @default false
|
||
*/
|
||
this.isVerboseOverride = false;
|
||
|
||
/**
|
||
* @member {Boolean} - Is Eleventy running in debug mode?
|
||
* @default false
|
||
*/
|
||
this.isDebug = false;
|
||
|
||
/**
|
||
* @member {Boolean} - Is Eleventy running in dry mode?
|
||
* @default false
|
||
*/
|
||
this.isDryRun = false;
|
||
|
||
if (performance) {
|
||
debug("Eleventy warm up time (in ms) %o", performance.now());
|
||
}
|
||
|
||
/** @member {Number} - The timestamp of Eleventy start. */
|
||
this.start = this.getNewTimestamp();
|
||
|
||
/**
|
||
* @member {Array<String>} - Subset of template types.
|
||
* @default null
|
||
*/
|
||
this.formatsOverride = null;
|
||
|
||
/** @member {Object} - tbd. */
|
||
this.eleventyServe = new EleventyServe();
|
||
|
||
/** @member {String} - Holds the path to the input directory. */
|
||
this.rawInput = input;
|
||
|
||
/** @member {String} - Holds the path to the output directory. */
|
||
this.rawOutput = output;
|
||
|
||
/** @member {Object} - tbd. */
|
||
this.watchManager = new EleventyWatch();
|
||
|
||
/** @member {Object} - tbd. */
|
||
this.watchTargets = new EleventyWatchTargets();
|
||
this.watchTargets.addAndMakeGlob(this.config.additionalWatchTargets);
|
||
this.watchTargets.watchJavaScriptDependencies = this.config.watchJavaScriptDependencies;
|
||
}
|
||
|
||
getNewTimestamp() {
|
||
if (performance) {
|
||
return performance.now();
|
||
}
|
||
return new Date().getTime();
|
||
}
|
||
|
||
/** @type {String} */
|
||
get input() {
|
||
return this.rawInput || this.config.dir.input;
|
||
}
|
||
|
||
/** @type {String} */
|
||
get inputDir() {
|
||
return TemplatePath.getDir(this.input);
|
||
}
|
||
|
||
/** @type {String} */
|
||
get outputDir() {
|
||
let dir = this.rawOutput || this.config.dir.output;
|
||
if (dir !== this._savedOutputDir) {
|
||
this.eleventyServe.setOutputDir(dir);
|
||
}
|
||
this._savedOutputDir = dir;
|
||
|
||
return dir;
|
||
}
|
||
|
||
/**
|
||
* Updates the dry-run mode of Eleventy.
|
||
*
|
||
* @method
|
||
* @param {Boolean} isDryRun - Shall Eleventy run in dry mode?
|
||
*/
|
||
setDryRun(isDryRun) {
|
||
this.isDryRun = !!isDryRun;
|
||
}
|
||
|
||
/**
|
||
* Sets the incremental build mode.
|
||
*
|
||
* @method
|
||
* @param {Boolean} isIncremental - Shall Eleventy run in incremental build mode and only write the files that trigger watch updates
|
||
*/
|
||
setIncrementalBuild(isIncremental) {
|
||
this.isIncremental = !!isIncremental;
|
||
this.watchManager.incremental = !!isIncremental;
|
||
}
|
||
|
||
/**
|
||
* Updates the passthrough mode of Eleventy.
|
||
*
|
||
* @method
|
||
* @param {Boolean} isPassthroughAll - Shall Eleventy passthrough everything?
|
||
*/
|
||
setPassthroughAll(isPassthroughAll) {
|
||
this.isPassthroughAll = !!isPassthroughAll;
|
||
}
|
||
|
||
/**
|
||
* Updates the path prefix used in the config.
|
||
*
|
||
* @method
|
||
* @param {String} pathPrefix - The new path prefix.
|
||
*/
|
||
setPathPrefix(pathPrefix) {
|
||
if (pathPrefix || pathPrefix === "") {
|
||
config.setPathPrefix(pathPrefix);
|
||
this.config = config.getConfig();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Updates the watch targets.
|
||
*
|
||
* @method
|
||
* @param {} watchTargets - The new watch targets.
|
||
*/
|
||
setWatchTargets(watchTargets) {
|
||
this.watchTargets = watchTargets;
|
||
}
|
||
|
||
/**
|
||
* Updates the config path.
|
||
*
|
||
* @method
|
||
* @param {String} configPath - The new config path.
|
||
*/
|
||
setConfigPathOverride(configPath) {
|
||
if (configPath) {
|
||
this.configPath = configPath;
|
||
|
||
config.setProjectConfigPath(configPath);
|
||
this.config = config.getConfig();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Restarts Eleventy.
|
||
*
|
||
* @async
|
||
* @method
|
||
*/
|
||
async restart() {
|
||
debug("Restarting");
|
||
this.start = this.getNewTimestamp();
|
||
templateCache.clear();
|
||
bench.reset();
|
||
this.eleventyFiles.restart();
|
||
|
||
// reload package.json values (if applicable)
|
||
// TODO only reset this if it changed
|
||
deleteRequireCache(TemplatePath.absolutePath("package.json"));
|
||
|
||
await this.init();
|
||
}
|
||
|
||
/**
|
||
* Marks the finish of a run of Eleventy.
|
||
*
|
||
* @method
|
||
*/
|
||
finish() {
|
||
bench.finish();
|
||
|
||
(this.logger || console).log(this.logFinished());
|
||
debug("Finished writing templates.");
|
||
}
|
||
|
||
/**
|
||
* Logs some statistics after a complete run of Eleventy.
|
||
*
|
||
* @method
|
||
* @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 writeCount = this.writer.getWriteCount();
|
||
let skippedCount = this.writer.getSkippedCount();
|
||
let copyCount = this.writer.getCopyCount();
|
||
|
||
let slashRet = [];
|
||
|
||
if (copyCount) {
|
||
slashRet.push(
|
||
`Copied ${copyCount} ${simplePlural(copyCount, "file", "files")}`
|
||
);
|
||
}
|
||
|
||
slashRet.push(
|
||
`Wrote ${writeCount} ${simplePlural(writeCount, "file", "files")}${
|
||
skippedCount ? ` (skipped ${skippedCount})` : ""
|
||
}`
|
||
);
|
||
|
||
if (slashRet.length) {
|
||
ret.push(slashRet.join(" / "));
|
||
}
|
||
|
||
let versionStr = `v${pkg.version}`;
|
||
let time = ((this.getNewTimestamp() - this.start) / 1000).toFixed(2);
|
||
ret.push(`in ${time} ${simplePlural(time, "second", "seconds")}`);
|
||
|
||
if (writeCount >= 10) {
|
||
ret.push(
|
||
`(${((time * 1000) / writeCount).toFixed(1)}ms each, ${versionStr})`
|
||
);
|
||
} else {
|
||
ret.push(`(${versionStr})`);
|
||
}
|
||
|
||
let pathPrefix = this.config.pathPrefix;
|
||
if (pathPrefix && pathPrefix !== "/") {
|
||
return `Using pathPrefix: ${pathPrefix}\n${ret.join(" ")}`;
|
||
}
|
||
|
||
return ret.join(" ");
|
||
}
|
||
|
||
/**
|
||
* Starts Eleventy.
|
||
*
|
||
* @async
|
||
* @method
|
||
* @returns {} - tbd.
|
||
*/
|
||
async init() {
|
||
this.config.inputDir = this.inputDir;
|
||
|
||
let formats = this.formatsOverride || this.config.templateFormats;
|
||
this.extensionMap = new EleventyExtensionMap(formats);
|
||
|
||
this.eleventyFiles = new EleventyFiles(
|
||
this.input,
|
||
this.outputDir,
|
||
formats,
|
||
this.isPassthroughAll
|
||
);
|
||
this.eleventyFiles.extensionMap = this.extensionMap;
|
||
this.eleventyFiles.init();
|
||
|
||
this.templateData = new TemplateData(this.inputDir);
|
||
this.templateData.extensionMap = this.extensionMap;
|
||
this.eleventyFiles.setTemplateData(this.templateData);
|
||
|
||
this.writer = new TemplateWriter(
|
||
this.input,
|
||
this.outputDir,
|
||
formats,
|
||
this.templateData,
|
||
this.isPassthroughAll
|
||
);
|
||
|
||
this.writer.extensionMap = this.extensionMap;
|
||
this.writer.setEleventyFiles(this.eleventyFiles);
|
||
|
||
debug(`Directories:
|
||
Input: ${this.inputDir}
|
||
Data: ${this.templateData.getDataDir()}
|
||
Includes: ${this.eleventyFiles.getIncludesDir()}
|
||
Layouts: ${this.eleventyFiles.getLayoutsDir()}
|
||
Output: ${this.outputDir}
|
||
Template Formats: ${formats.join(",")}
|
||
Verbose Output: ${this.isVerbose}`);
|
||
|
||
this.writer.setVerboseOutput(this.isVerbose);
|
||
this.writer.setDryRun(this.isDryRun);
|
||
|
||
return this.templateData.cacheData();
|
||
}
|
||
|
||
/**
|
||
* Updates the debug mode of Eleventy.
|
||
*
|
||
* @method
|
||
* @param {Boolean} isDebug - Shall Eleventy run in debug mode?
|
||
*/
|
||
setIsDebug(isDebug) {
|
||
this.isDebug = !!isDebug;
|
||
}
|
||
|
||
/**
|
||
* Updates the verbose mode of Eleventy.
|
||
*
|
||
* @method
|
||
* @param {Boolean} isVerbose - Shall Eleventy run in verbose mode?
|
||
*/
|
||
setIsVerbose(isVerbose) {
|
||
this.isVerbose = !!isVerbose;
|
||
|
||
// mark that this was changed from the default (probably from --quiet)
|
||
// this is used when we reset the config (only applies if not overridden)
|
||
this.isVerboseOverride = true;
|
||
|
||
if (this.writer) {
|
||
this.writer.setVerboseOutput(this.isVerbose);
|
||
}
|
||
if (bench) {
|
||
bench.setVerboseOutput(this.isVerbose);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Updates the template formats of Eleventy.
|
||
*
|
||
* @method
|
||
* @param {String} formats - The new template formats.
|
||
*/
|
||
setFormats(formats) {
|
||
if (formats && formats !== "*") {
|
||
this.formatsOverride = formats.split(",");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Reads the version of Eleventy.
|
||
*
|
||
* @method
|
||
* @returns {String} - The version of Eleventy.
|
||
*/
|
||
getVersion() {
|
||
return require("../package.json").version;
|
||
}
|
||
|
||
/**
|
||
* Shows a help message including usage.
|
||
*
|
||
* @method
|
||
* @returns {String} - The help mesage.
|
||
*/
|
||
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
|
||
--watch
|
||
Wait for files to change and automatically rewrite (no web server)
|
||
--formats=liquid,md
|
||
Whitelist 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 with \`DEBUG=Eleventy* npx eleventy\`
|
||
--help`;
|
||
}
|
||
|
||
/**
|
||
* Resets the config of Eleventy.
|
||
*
|
||
* @method
|
||
*/
|
||
resetConfig() {
|
||
config.reset();
|
||
|
||
this.config = config.getConfig();
|
||
this.eleventyServe.config = this.config;
|
||
|
||
if (!this.isVerboseOverride && !process.env.DEBUG) {
|
||
this.isVerbose = !this.config.quietMode;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* tbd.
|
||
*
|
||
* @private
|
||
* @method
|
||
* @param {String} changedFilePath - File that triggered a re-run (added or modified)
|
||
*/
|
||
async _addFileToWatchQueue(changedFilePath) {
|
||
this.watchManager.addToPendingQueue(changedFilePath);
|
||
}
|
||
|
||
/**
|
||
* tbd.
|
||
*
|
||
* @private
|
||
* @method
|
||
*/
|
||
async _watch() {
|
||
if (this.watchManager.isBuildRunning()) {
|
||
return;
|
||
}
|
||
|
||
this.watchManager.setBuildRunning();
|
||
|
||
this.config.events.emit("beforeWatch", this.watchManager.getActiveQueue());
|
||
|
||
// reset and reload global configuration :O
|
||
if (this.watchManager.hasQueuedFile(config.getLocalProjectConfigFile())) {
|
||
this.resetConfig();
|
||
}
|
||
|
||
await this.restart();
|
||
|
||
this.watchTargets.clearDependencyRequireCache();
|
||
|
||
let incrementalFile = this.watchManager.getIncrementalFile();
|
||
if (incrementalFile) {
|
||
// TODO remove these and delegate to the template dependency graph
|
||
let isInclude = TemplatePath.startsWithSubPath(
|
||
incrementalFile,
|
||
this.eleventyFiles.getIncludesDir()
|
||
);
|
||
let isJSDependency = this.watchTargets.isJavaScriptDependency(
|
||
incrementalFile
|
||
);
|
||
if (!isInclude && !isJSDependency) {
|
||
this.writer.setIncrementalFile(incrementalFile);
|
||
}
|
||
}
|
||
|
||
await this.write();
|
||
|
||
this.writer.resetIncrementalFile();
|
||
|
||
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()
|
||
)
|
||
);
|
||
});
|
||
|
||
if (onlyCssChanges) {
|
||
this.eleventyServe.reload("*.css");
|
||
} else {
|
||
this.eleventyServe.reload();
|
||
}
|
||
|
||
this.watchManager.setBuildFinished();
|
||
|
||
if (this.watchManager.getPendingQueueSize() > 0) {
|
||
console.log(
|
||
`You saved while Eleventy was running, let’s run again. (${this.watchManager.getPendingQueueSize()} remain)`
|
||
);
|
||
await this._watch();
|
||
} else {
|
||
console.log("Watching…");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* tbd.
|
||
*
|
||
* @returns {} - tbd.
|
||
*/
|
||
get watcherBench() {
|
||
return bench.get("Watcher");
|
||
}
|
||
|
||
/**
|
||
* Set up watchers and benchmarks.
|
||
*
|
||
* @async
|
||
* @method
|
||
*/
|
||
async initWatch() {
|
||
this.watchManager = new EleventyWatch();
|
||
this.watchManager.incremental = this.isIncremental;
|
||
|
||
this.watchTargets.add(this.eleventyFiles.getGlobWatcherFiles());
|
||
|
||
// Watch the local project config file
|
||
this.watchTargets.add(config.getLocalProjectConfigFile());
|
||
|
||
// 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();
|
||
}
|
||
|
||
/**
|
||
* Starts watching dependencies.
|
||
*
|
||
* @private
|
||
* @async
|
||
* @method
|
||
*/
|
||
async _initWatchDependencies() {
|
||
if (!this.watchTargets.watchJavaScriptDependencies) {
|
||
return;
|
||
}
|
||
|
||
let dataDir = this.templateData.getDataDir();
|
||
function filterOutGlobalDataFiles(path) {
|
||
return !dataDir || path.indexOf(dataDir) === -1;
|
||
}
|
||
|
||
// Template files .11ty.js
|
||
this.watchTargets.addDependencies(this.eleventyFiles.getWatchPathCache());
|
||
|
||
// Config file dependencies
|
||
this.watchTargets.addDependencies(
|
||
config.getLocalProjectConfigFile(),
|
||
filterOutGlobalDataFiles.bind(this)
|
||
);
|
||
|
||
// Deps from Global Data (that aren’t in the global data directory, everything is watched there)
|
||
this.watchTargets.addDependencies(
|
||
this.templateData.getWatchPathCache(),
|
||
filterOutGlobalDataFiles.bind(this)
|
||
);
|
||
|
||
this.watchTargets.addDependencies(
|
||
await this.eleventyFiles.getWatcherTemplateJavaScriptDataFiles()
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Returns all watched files.
|
||
*
|
||
* @async
|
||
* @method
|
||
* @returns {} 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,
|
||
// also interesting: awaitWriteFinish
|
||
},
|
||
configOptions
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Start the watching of files.
|
||
*
|
||
* @async
|
||
* @method
|
||
*/
|
||
async watch() {
|
||
this.watcherBench.setMinimumThresholdMs(500);
|
||
this.watcherBench.reset();
|
||
|
||
const chokidar = require("chokidar");
|
||
|
||
// 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 watcher = chokidar.watch(rawFiles, this.getChokidarConfig());
|
||
|
||
initWatchBench.after();
|
||
|
||
this.watcherBench.setIsVerbose(true);
|
||
this.watcherBench.finish("Watch");
|
||
|
||
console.log("Watching…");
|
||
|
||
this.watcher = watcher;
|
||
|
||
let watchDelay;
|
||
async function watchRun(path) {
|
||
try {
|
||
this._addFileToWatchQueue(path);
|
||
clearTimeout(watchDelay);
|
||
watchDelay = setTimeout(async () => {
|
||
await this._watch();
|
||
}, this.config.watchThrottleWaitTime);
|
||
} catch (e) {
|
||
EleventyErrorHandler.fatal(e, "Eleventy fatal watch error");
|
||
this.stopWatch();
|
||
}
|
||
}
|
||
|
||
watcher.on("change", async (path) => {
|
||
console.log("File changed:", path);
|
||
await watchRun.call(this, path);
|
||
});
|
||
|
||
watcher.on("add", async (path) => {
|
||
console.log("File added:", path);
|
||
await watchRun.call(this, path);
|
||
});
|
||
|
||
process.on("SIGINT", () => this.stopWatch());
|
||
}
|
||
|
||
stopWatch() {
|
||
debug("Cleaning up chokidar and browsersync (if exists) instances.");
|
||
this.eleventyServe.close();
|
||
this.watcher.close();
|
||
process.exit();
|
||
}
|
||
|
||
/**
|
||
* Serve Eleventy on this port.
|
||
*
|
||
* @param {Number} port - The HTTP port to serve Eleventy from.
|
||
*/
|
||
serve(port) {
|
||
this.eleventyServe.serve(port);
|
||
}
|
||
|
||
/* For testing */
|
||
/**
|
||
* Updates the logger.
|
||
*
|
||
* @param {} logger - The new logger.
|
||
*/
|
||
setLogger(logger) {
|
||
this.logger = logger;
|
||
}
|
||
|
||
/**
|
||
* tbd.
|
||
*
|
||
* @async
|
||
* @method
|
||
* @returns {Promise<{}>} ret - tbd.
|
||
*/
|
||
async write() {
|
||
let ret;
|
||
if (this.logger) {
|
||
EleventyErrorHandler.logger = this.logger;
|
||
}
|
||
|
||
this.config.events.emit("beforeBuild");
|
||
|
||
try {
|
||
let promise = this.writer.write();
|
||
|
||
ret = await promise;
|
||
this.config.events.emit("afterBuild");
|
||
} catch (e) {
|
||
EleventyErrorHandler.initialMessage(
|
||
"Problem writing Eleventy templates",
|
||
"error",
|
||
"red"
|
||
);
|
||
EleventyErrorHandler.fatal(e);
|
||
}
|
||
|
||
this.finish();
|
||
|
||
debug(`
|
||
Getting frustrated? Have a suggestion/feature request/feedback?
|
||
I want to hear it! Open an issue: https://github.com/11ty/eleventy/issues/new`);
|
||
|
||
// unset the logger
|
||
EleventyErrorHandler.logger = undefined;
|
||
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
module.exports = Eleventy;
|