@11ty/eleventy
Advanced tools
| export class TemplatePreprocessors { | ||
| constructor(preprocessors) { | ||
| this.preprocessors = preprocessors || []; | ||
| } | ||
| async runAll(template, data) { | ||
| let { inputPath } = template; | ||
| let content = await template.getPreRender(); | ||
| let skippedVia = false; | ||
| for (let [name, preprocessor] of Object.entries(this.preprocessors)) { | ||
| let { filter, callback } = preprocessor; | ||
| let filters; | ||
| if (Array.isArray(filter)) { | ||
| filters = filter; | ||
| } else if (typeof filter === "string") { | ||
| filters = filter.split(","); | ||
| } else { | ||
| throw new Error( | ||
| `Expected file extensions passed to "${name}" content preprocessor to be a string or array. Received: ${filter}`, | ||
| ); | ||
| } | ||
| filters = filters.map((extension) => { | ||
| if (extension.startsWith(".") || extension === "*") { | ||
| return extension; | ||
| } | ||
| return `.${extension}`; | ||
| }); | ||
| if (!filters.some((extension) => extension === "*" || inputPath.endsWith(extension))) { | ||
| // skip | ||
| continue; | ||
| } | ||
| try { | ||
| let ret = await callback.call( | ||
| { | ||
| inputPath, | ||
| }, | ||
| data, | ||
| content, | ||
| ); | ||
| // Returning explicit false is the same as ignoring the template | ||
| if (ret === false) { | ||
| skippedVia = name; | ||
| continue; | ||
| } | ||
| // Different from transforms: returning falsy (not false) here does nothing (skips the preprocessor) | ||
| if (ret) { | ||
| content = ret; | ||
| } | ||
| } catch (e) { | ||
| throw new Error( | ||
| `Preprocessor \`${name}\` encountered an error when transforming ${inputPath}.`, | ||
| { cause: e }, | ||
| ); | ||
| } | ||
| } | ||
| return { | ||
| skippedVia, | ||
| content, | ||
| }; | ||
| } | ||
| } |
| import { spawnAsync } from "./spawn.js"; | ||
| import memoize from "./MemoizeFunction.js"; | ||
| const getCreatedTimestamp = memoize(async function (filePath) { | ||
| try { | ||
| let timestamp = await spawnAsync( | ||
| "git", | ||
| // Formats https://www.git-scm.com/docs/git-log#_pretty_formats | ||
| // %at author date, UNIX timestamp | ||
| ["log", "--diff-filter=A", "--follow", "-1", "--format=%at", filePath], | ||
| ); | ||
| // parseInt removes trailing \n | ||
| return parseInt(timestamp, 10) * 1000; | ||
| } catch (e) { | ||
| // do nothing | ||
| } | ||
| }); | ||
| const getUpdatedTimestamp = memoize(async function (filePath) { | ||
| try { | ||
| let timestamp = await spawnAsync( | ||
| "git", | ||
| // Formats https://www.git-scm.com/docs/git-log#_pretty_formats | ||
| // %at author date, UNIX timestamp | ||
| ["log", "-1", "--format=%at", filePath], | ||
| ); | ||
| return parseInt(timestamp, 10) * 1000; | ||
| } catch (e) { | ||
| // do nothing | ||
| } | ||
| }); | ||
| export { getCreatedTimestamp, getUpdatedTimestamp }; |
| export function union(...sets) { | ||
| let root = new Set(); | ||
| for (let set of sets) { | ||
| for (let entry of set) { | ||
| root.add(entry); | ||
| } | ||
| } | ||
| return root; | ||
| } |
| export function isValidUrl(url) { | ||
| try { | ||
| new URL(url); | ||
| return true; | ||
| } catch (e) { | ||
| // invalid url OR local path | ||
| return false; | ||
| } | ||
| } | ||
| export function getDirectoryFromUrl(url) { | ||
| if (url === false) { | ||
| return false; | ||
| } | ||
| // returns a url | ||
| if (url.endsWith("/")) { | ||
| return url; | ||
| } | ||
| let parts = url.split("/"); | ||
| parts.pop(); | ||
| return parts.join("/") + "/"; | ||
| } |
+46
-42
| { | ||
| "name": "@11ty/eleventy", | ||
| "version": "4.0.0-alpha.4", | ||
| "version": "4.0.0-alpha.5", | ||
| "description": "A simpler static site generator.", | ||
@@ -18,3 +18,4 @@ "publishConfig": { | ||
| "types": "./src/UserConfig.js" | ||
| } | ||
| }, | ||
| "./utils/git": "./src/Util/Git.js" | ||
| }, | ||
@@ -26,3 +27,3 @@ "bin": { | ||
| "engines": { | ||
| "node": ">=20" | ||
| "node": ">=20.19" | ||
| }, | ||
@@ -71,3 +72,3 @@ "workspaces": [ | ||
| "type": "git", | ||
| "url": "git://github.com/11ty/eleventy.git" | ||
| "url": "git+https://github.com/11ty/eleventy.git" | ||
| }, | ||
@@ -103,19 +104,19 @@ "bugs": "https://github.com/11ty/eleventy/issues", | ||
| "@11ty/eleventy-plugin-rss": "^2.0.4", | ||
| "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.1", | ||
| "@11ty/eleventy-plugin-webc": "^0.12.0-beta.3", | ||
| "@eslint/js": "^9.32.0", | ||
| "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2", | ||
| "@11ty/eleventy-plugin-webc": "^0.12.0-beta.7", | ||
| "@eslint/js": "^9.39.1", | ||
| "@iarna/toml": "^2.2.5", | ||
| "@mdx-js/node-loader": "^3.1.0", | ||
| "@mdx-js/node-loader": "^3.1.1", | ||
| "@stylistic/eslint-plugin-js": "^4.4.1", | ||
| "@types/node": "^22.17.0", | ||
| "@vue/server-renderer": "^3.5.18", | ||
| "@types/node": "^22.19.1", | ||
| "@vue/server-renderer": "^3.5.25", | ||
| "@zachleat/noop": "^1.0.6", | ||
| "ava": "^6.4.1", | ||
| "c8": "^10.1.3", | ||
| "cross-env": "^7.0.3", | ||
| "eslint": "^9.32.0", | ||
| "cross-env": "^10.1.0", | ||
| "eslint": "^9.39.1", | ||
| "eslint-config-prettier": "^10.1.8", | ||
| "globals": "^16.3.0", | ||
| "jsx-async-runtime": "^1.0.3", | ||
| "luxon": "^3.7.1", | ||
| "globals": "^16.5.0", | ||
| "jsx-async-runtime": "^1.0.4", | ||
| "luxon": "^3.7.2", | ||
| "markdown-it-abbr": "^2.0.0", | ||
@@ -125,36 +126,36 @@ "markdown-it-emoji": "^3.0.0", | ||
| "nano-staged": "^0.8.0", | ||
| "prettier": "^3.6.2", | ||
| "prettier": "^3.7.1", | ||
| "pretty": "^2.0.0", | ||
| "react": "^19.1.1", | ||
| "react-dom": "^19.1.1", | ||
| "rimraf": "^6.0.1", | ||
| "sass": "^1.89.2", | ||
| "react": "^19.2.1", | ||
| "react-dom": "^19.2.1", | ||
| "rimraf": "^6.1.2", | ||
| "sass": "^1.94.2", | ||
| "simple-git-hooks": "^2.13.1", | ||
| "tsx": "^4.20.3", | ||
| "typescript": "^5.9.2", | ||
| "vue": "^3.5.18", | ||
| "tsx": "^4.20.6", | ||
| "typescript": "^5.9.3", | ||
| "vue": "^3.5.25", | ||
| "zod": "^3.25.76", | ||
| "zod-validation-error": "^3.5.3" | ||
| "zod-validation-error": "^3.5.4" | ||
| }, | ||
| "dependencies": { | ||
| "@11ty/dependency-tree": "^4.0.0", | ||
| "@11ty/dependency-tree-esm": "^2.0.0", | ||
| "@11ty/eleventy-dev-server": "3.0.0-alpha.3", | ||
| "@11ty/eleventy-plugin-bundle": "^3.0.6", | ||
| "@11ty/dependency-tree": "^4.0.1", | ||
| "@11ty/dependency-tree-esm": "^2.0.3", | ||
| "@11ty/eleventy-dev-server": "^3.0.0-alpha.5", | ||
| "@11ty/eleventy-plugin-bundle": "^3.0.7", | ||
| "@11ty/eleventy-utils": "^2.0.7", | ||
| "@11ty/gray-matter": "^2.0.0", | ||
| "@11ty/gray-matter": "^2.0.1", | ||
| "@11ty/lodash-custom": "^4.17.21", | ||
| "@11ty/parse-date-strings": "^2.0.5", | ||
| "@11ty/posthtml-urls": "^1.0.1", | ||
| "@11ty/recursive-copy": "^4.0.2", | ||
| "@sindresorhus/slugify": "^2.2.1", | ||
| "@11ty/parse-date-strings": "^2.0.6", | ||
| "@11ty/posthtml-urls": "^1.0.2", | ||
| "@11ty/recursive-copy": "^4.0.3", | ||
| "@sindresorhus/slugify": "^3.0.0", | ||
| "bcp-47-normalize": "^2.3.0", | ||
| "chokidar": "^4.0.3", | ||
| "debug": "^4.4.1", | ||
| "chokidar": "^5.0.0", | ||
| "debug": "^4.4.3", | ||
| "dependency-graph": "^1.0.0", | ||
| "entities": "^6.0.1", | ||
| "import-module-string": "^2.0.1", | ||
| "entities": "^7.0.0", | ||
| "import-module-string": "^2.0.3", | ||
| "iso-639-1": "^3.1.5", | ||
| "kleur": "^4.1.5", | ||
| "liquidjs": "^10.21.1", | ||
| "liquidjs": "^10.24.0", | ||
| "markdown-it": "^14.1.0", | ||
@@ -167,10 +168,13 @@ "minimist": "^1.2.8", | ||
| "please-upgrade-node": "^3.2.0", | ||
| "posthtml": "^0.16.6", | ||
| "posthtml": "^0.16.7", | ||
| "posthtml-match-helper": "^2.0.3", | ||
| "semver": "^7.7.2", | ||
| "tinyglobby": "^0.2.14" | ||
| "semver": "^7.7.3", | ||
| "tinyglobby": "^0.2.15" | ||
| }, | ||
| "overrides": { | ||
| "chokidar": "$chokidar" | ||
| "commander": "^14", | ||
| "chokidar": "$chokidar", | ||
| "fdir": "6.4.6", | ||
| "posthtml-parser": "^0.12" | ||
| } | ||
| } |
+1
-1
@@ -48,3 +48,3 @@ <p align="center"><img src="https://www.11ty.dev/img/logo-github.svg" width="200" height="200" alt="eleventy Logo"></p> | ||
| - [Top Feature Requests](https://github.com/11ty/eleventy/issues?q=label%3Aneeds-votes+sort%3Areactions-%2B1-desc+label%3Aenhancement) (Add your own votes using the 👍 reaction) | ||
| - [Top Feature Requests](https://github.com/11ty/eleventy/discussions/categories/enhancement-queue?discussions_q=is%3Aopen+category%3A%22Enhancement+Queue%22+sort%3Atop) (Vote for your favorites!) | ||
| - [Top Bugs 😱](https://github.com/11ty/eleventy/issues?q=is%3Aissue+is%3Aopen+label%3Abug+sort%3Areactions) (Add your own votes using the 👍 reaction) | ||
@@ -51,0 +51,0 @@ |
@@ -308,3 +308,3 @@ import path from "node:path"; | ||
| let oldData = lodashGet(globalData, objectPathTarget); | ||
| data = TemplateData.mergeDeep(this.config.dataDeepMerge, oldData, data); | ||
| data = Merge(oldData, data); | ||
| } | ||
@@ -426,3 +426,3 @@ | ||
| } | ||
| TemplateData.mergeDeep(this.config.dataDeepMerge, localData, cleanedDataForPath); | ||
| Merge(localData, cleanedDataForPath); | ||
| } | ||
@@ -637,14 +637,2 @@ } | ||
| static mergeDeep(deepMerge, target, ...source) { | ||
| if (!deepMerge && deepMerge !== undefined) { | ||
| return Object.assign(target, ...source); | ||
| } else { | ||
| return TemplateData.merge(target, ...source); | ||
| } | ||
| } | ||
| static merge(target, ...source) { | ||
| return Merge(target, ...source); | ||
| } | ||
| /* Like cleanupData() but does not mutate */ | ||
@@ -651,0 +639,0 @@ static getCleanedTagsImmutable(data, options = {}) { |
+10
-9
@@ -129,2 +129,3 @@ import { relative } from "node:path"; | ||
| shouldTriggerConfigReset(changedFiles) { | ||
| // looks for all eligible config files (not just the active one, handles config file rename) | ||
| let configFilePaths = new Set(this.eleventyConfig.getLocalProjectConfigFiles()); | ||
@@ -146,3 +147,3 @@ | ||
| for (const configFilePath of configFilePaths) { | ||
| for (let configFilePath of configFilePaths) { | ||
| // Any dependencies of the config file changed | ||
@@ -302,3 +303,3 @@ let configFileDependencies = new Set(this.watchTargets.getDependenciesOf(configFilePath)); | ||
| // Watch the local project config file | ||
| this.watchTargets.add(this.eleventyConfig.getLocalProjectConfigFiles()); | ||
| this.watchTargets.add(this.eleventyConfig.getActiveConfigPath()); | ||
@@ -407,8 +408,2 @@ // Template and Directory Data Files | ||
| // 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 | ||
@@ -421,5 +416,11 @@ this.watchTargets.setProjectUsingEsm(this.isEsm); | ||
| // TODO use DirContains | ||
| let dataDir = TemplatePath.stripLeadingDotSlash(this.templateData.getDataDir()); | ||
| function filterOutGlobalDataFiles(path) { | ||
| return !dataDir || !TemplatePath.stripLeadingDotSlash(path).startsWith(dataDir); | ||
| } | ||
| // Config file dependencies | ||
| await this.watchTargets.addDependencies( | ||
| this.eleventyConfig.getLocalProjectConfigFiles(), | ||
| this.eleventyConfig.getActiveConfigPath(), | ||
| filterOutGlobalDataFiles, | ||
@@ -426,0 +427,0 @@ ); |
@@ -151,6 +151,2 @@ import { existsSync, statSync, readFileSync } from "node:fs"; | ||
| get templateData() { | ||
| if (!this._templateData) { | ||
| this._templateData = new TemplateData(this.templateConfig); | ||
| } | ||
| return this._templateData; | ||
@@ -157,0 +153,0 @@ } |
@@ -10,3 +10,5 @@ import { RetrieveGlobals } from "../../Util/RetrieveGlobals.js"; | ||
| if (trimmed.startsWith("{")) { | ||
| return RetrieveGlobals(`export default ${trimmed}`, filePath).then((res) => { | ||
| return RetrieveGlobals(`export default ${trimmed}`, filePath, { | ||
| isJavaScriptFrontMatterCompat: true, // turns off implicit exports | ||
| }).then((res) => { | ||
| return res.default; | ||
@@ -13,0 +15,0 @@ }); |
@@ -52,4 +52,4 @@ import moo from "moo"; | ||
| strictFilters: true, | ||
| // TODO? | ||
| // cache: true, | ||
| jsTruthy: true, // Breaking in v4 #3507 | ||
| }; | ||
@@ -329,2 +329,8 @@ | ||
| options.globals = { | ||
| page: data?.page, | ||
| eleventy: data?.eleventy, | ||
| collections: data?.collections, | ||
| }; | ||
| return engine.render(tmpl, data, options); | ||
@@ -331,0 +337,0 @@ }; |
+62
-44
@@ -117,6 +117,11 @@ import debugUtil from "debug"; | ||
| // TODO(zachleat): variableName should work with quotes or without quotes (same as {% set %}) | ||
| this.addPairedShortcode("setAsync", function (content, variableName) { | ||
| this.ctx[variableName] = content; | ||
| return ""; | ||
| }); | ||
| // This was changed to be an async function in v4 but notably previous versions of synchronous paired shortcodes used CallExtensionAsync | ||
| this.addPairedShortcode( | ||
| "setAsync", | ||
| async function (content, variableName) { | ||
| this.ctx[variableName] = content; | ||
| return ""; | ||
| }, | ||
| true, | ||
| ); | ||
@@ -287,30 +292,30 @@ this.addCustomTags(this.config.nunjucksTags); | ||
| this.parse = function (parser, nodes) { | ||
| var tok = parser.nextToken(); | ||
| if (isAsync) { | ||
| this.parse = function (parser, nodes) { | ||
| var tok = parser.nextToken(); | ||
| var args = parser.parseSignature(true, true); | ||
| parser.advanceAfterBlockEnd(tok.value); | ||
| var args = parser.parseSignature(true, true); | ||
| parser.advanceAfterBlockEnd(tok.value); | ||
| var body = parser.parseUntilBlocks("end" + shortcodeName); | ||
| parser.advanceAfterBlockEnd(); | ||
| var body = parser.parseUntilBlocks("end" + shortcodeName); | ||
| parser.advanceAfterBlockEnd(); | ||
| return new nodes.CallExtensionAsync(this, "run", args, [body]); | ||
| }; | ||
| return new nodes.CallExtensionAsync(this, "run", args, [body]); | ||
| }; | ||
| this.run = function (...args) { | ||
| let resolve = args.pop(); | ||
| let body = args.pop(); | ||
| let [context, ...argArray] = args; | ||
| this.run = function (...args) { | ||
| let resolve = args.pop(); | ||
| let body = args.pop(); | ||
| let [context, ...argArray] = args; | ||
| body(function (e, bodyContent) { | ||
| if (e) { | ||
| resolve( | ||
| new EleventyNunjucksError( | ||
| `Error with Nunjucks paired shortcode \`${shortcodeName}\``, | ||
| e, | ||
| ), | ||
| ); | ||
| } | ||
| body(function (e, bodyContent) { | ||
| if (e) { | ||
| resolve( | ||
| new EleventyNunjucksError( | ||
| `Error with Nunjucks paired shortcode \`${shortcodeName}\``, | ||
| e, | ||
| ), | ||
| ); | ||
| } | ||
| if (isAsync) { | ||
| let ret = shortcodeFn.call( | ||
@@ -342,21 +347,34 @@ Nunjucks.normalizeContext(context), | ||
| ); | ||
| } else { | ||
| try { | ||
| resolve( | ||
| null, | ||
| new NunjucksLib.runtime.SafeString( | ||
| shortcodeFn.call(Nunjucks.normalizeContext(context), bodyContent, ...argArray), | ||
| ), | ||
| ); | ||
| } catch (e) { | ||
| resolve( | ||
| new EleventyNunjucksError( | ||
| `Error with Nunjucks paired shortcode \`${shortcodeName}\``, | ||
| e, | ||
| ), | ||
| ); | ||
| } | ||
| }); | ||
| }; | ||
| } else { | ||
| this.parse = function (parser, nodes) { | ||
| var tok = parser.nextToken(); | ||
| var args = parser.parseSignature(true, true); | ||
| parser.advanceAfterBlockEnd(tok.value); | ||
| var body = parser.parseUntilBlocks("end" + shortcodeName); | ||
| parser.advanceAfterBlockEnd(); | ||
| return new nodes.CallExtension(this, "run", args, [body]); | ||
| }; | ||
| this.run = function (...args) { | ||
| let body = args.pop(); | ||
| let [context, ...argArray] = args; | ||
| let bodyContent = body(); | ||
| try { | ||
| return new NunjucksLib.runtime.SafeString( | ||
| shortcodeFn.call(Nunjucks.normalizeContext(context), bodyContent, ...argArray), | ||
| ); | ||
| } catch (e) { | ||
| throw new EleventyNunjucksError( | ||
| `Error with Nunjucks paired shortcode \`${shortcodeName}\``, | ||
| e, | ||
| ); | ||
| } | ||
| }); | ||
| }; | ||
| }; | ||
| } | ||
| }; | ||
@@ -363,0 +381,0 @@ } |
@@ -34,3 +34,5 @@ import { glob } from "tinyglobby"; | ||
| // Strip leading slashes from everything! | ||
| globs = globs.map((entry) => TemplatePath.stripLeadingDotSlash(entry)); | ||
| globs = globs.map((entry) => { | ||
| return TemplatePath.stripLeadingDotSlash(entry); | ||
| }); | ||
@@ -37,0 +39,0 @@ let cwd = GlobRemap.getCwd(globs); |
| import { TemplatePath } from "@11ty/eleventy-utils"; | ||
| import isValidUrl from "../Util/ValidUrl.js"; | ||
| import { isValidUrl } from "../Util/UrlUtil.js"; | ||
@@ -5,0 +5,0 @@ // Note: This filter is used in the Eleventy Navigation plugin in versions prior to 0.3.4 |
@@ -5,3 +5,3 @@ import { DeepCopy } from "@11ty/eleventy-utils"; | ||
| import { HtmlTransformer } from "../Util/HtmlTransformer.js"; | ||
| import isValidUrl from "../Util/ValidUrl.js"; | ||
| import { isValidUrl } from "../Util/UrlUtil.js"; | ||
@@ -8,0 +8,0 @@ function addPathPrefixToUrl(url, pathPrefix, base) { |
| import path from "node:path"; | ||
| import { TemplatePath } from "@11ty/eleventy-utils"; | ||
| import isValidUrl from "../Util/ValidUrl.js"; | ||
| import { isValidUrl } from "../Util/UrlUtil.js"; | ||
@@ -5,0 +5,0 @@ function getValidPath(contentMap, testPath) { |
@@ -340,3 +340,4 @@ import { isPlainObject } from "@11ty/eleventy-utils"; | ||
| let { /*linkInstance,*/ rawPath, path, href } = await cloned.getOutputLocations(clonedData); | ||
| let { /*linkInstance,*/ rawPath, path, href, dir } = | ||
| await cloned.getOutputLocations(clonedData); | ||
| // TODO subdirectory to links if the site doesn’t live at / | ||
@@ -352,2 +353,3 @@ if (rawPath) { | ||
| clonedData.page.outputPath = path; | ||
| clonedData.page.dir = dir; | ||
@@ -354,0 +356,0 @@ entries.push({ |
+61
-105
@@ -1,6 +0,6 @@ | ||
| import path from "node:path"; | ||
| import { parse } from "node:path"; | ||
| import { statSync } from "node:fs"; | ||
| import lodash from "@11ty/lodash-custom"; | ||
| import { TemplatePath, isPlainObject } from "@11ty/eleventy-utils"; | ||
| import { Merge, TemplatePath, isPlainObject } from "@11ty/eleventy-utils"; | ||
| import debugUtil from "debug"; | ||
@@ -10,5 +10,3 @@ | ||
| import ConsoleLogger from "./Util/ConsoleLogger.js"; | ||
| import getDateFromGitLastUpdated from "./Util/DateGitLastUpdated.js"; | ||
| import getDateFromGitFirstAdded from "./Util/DateGitFirstAdded.js"; | ||
| import TemplateData from "./Data/TemplateData.js"; | ||
| import { getCreatedTimestamp, getUpdatedTimestamp } from "./Util/Git.js"; | ||
| import TemplateContent from "./TemplateContent.js"; | ||
@@ -28,2 +26,5 @@ import TemplatePermalink from "./TemplatePermalink.js"; | ||
| import { FileSystemManager } from "./Util/FileSystemManager.js"; | ||
| import { TemplatePreprocessors } from "./TemplatePreprocessors.js"; | ||
| import PathNormalizer from "./Util/PathNormalizer.js"; | ||
| import { getDirectoryFromUrl } from "./Util/UrlUtil.js"; | ||
@@ -39,2 +40,3 @@ const { set: lodashSet, get: lodashGet } = lodash; | ||
| #stats; | ||
| #preprocessorCache; | ||
@@ -45,3 +47,3 @@ constructor(templatePath, templateData, extensionMap, config) { | ||
| this.parsed = path.parse(templatePath); | ||
| this.parsed = parse(templatePath); | ||
@@ -70,2 +72,4 @@ // for pagination | ||
| this.behavior.setOutputFormat(this.outputFormat); | ||
| this.templatePreprocessor = new TemplatePreprocessors(this.config.preprocessors); | ||
| } | ||
@@ -137,2 +141,6 @@ | ||
| if (types.data || types.read) { | ||
| this.#preprocessorCache = undefined; | ||
| } | ||
| if (types.data) { | ||
@@ -211,11 +219,2 @@ delete this._dataCache; | ||
| async _getRawPermalinkInstance(permalinkValue) { | ||
| let perm = new TemplatePermalink(permalinkValue, this.extraOutputSubdirectory); | ||
| perm.setUrlTransforms(this.config.urlTransforms); | ||
| this.behavior.setFromPermalink(perm); | ||
| return perm; | ||
| } | ||
| async _getLink(data) { | ||
@@ -230,2 +229,4 @@ if (!data) { | ||
| let permalinkValue; | ||
| let isDynamicPermalinkEnabled = | ||
| this.config.dynamicPermalinks && data.dynamicPermalink !== false; | ||
@@ -237,3 +238,4 @@ // `permalink: false` means render but no file system write, e.g. use in collections only) | ||
| permalinkValue = permalink; | ||
| } else if (permalink && (!this.config.dynamicPermalinks || data.dynamicPermalink === false)) { | ||
| } else if (permalink && !isDynamicPermalinkEnabled) { | ||
| // Issue #838 | ||
| debugDev("Not using dynamic permalinks, using %o", permalink); | ||
@@ -282,3 +284,3 @@ permalinkValue = permalink; | ||
| // Override default permalink behavior. Only do this if permalink was _not_ in the data cascade | ||
| if (!permalink && this.config.dynamicPermalinks && data.dynamicPermalink !== false) { | ||
| if (!permalink && isDynamicPermalinkEnabled) { | ||
| let tr = await this.getTemplateRender(); | ||
@@ -301,3 +303,10 @@ let permalinkCompilation = tr.engine.permalinkNeedsCompilation(""); | ||
| if (permalinkValue !== undefined) { | ||
| return this._getRawPermalinkInstance(permalinkValue); | ||
| let p = new TemplatePermalink( | ||
| permalinkValue, | ||
| this.extraOutputSubdirectory, | ||
| isDynamicPermalinkEnabled, | ||
| ); | ||
| p.setUrlTransforms(this.config.urlTransforms); | ||
| this.behavior.setFromPermalink(p); | ||
| return p; | ||
| } | ||
@@ -311,2 +320,3 @@ | ||
| this.engine.defaultTemplateFileExtension, | ||
| isDynamicPermalinkEnabled, | ||
| ); | ||
@@ -339,7 +349,9 @@ p.setUrlTransforms(this.config.urlTransforms); | ||
| let href = link.toHref(); | ||
| return { | ||
| linkInstance: link, | ||
| rawPath: link.toOutputPath(), | ||
| href: link.toHref(), | ||
| rawPath: link.toOutputPath(), // includes output directory | ||
| href, | ||
| path: path, | ||
| dir: getDirectoryFromUrl(href), // for `page.dir` | ||
| }; | ||
@@ -414,10 +426,3 @@ } | ||
| try { | ||
| let mergedData = TemplateData.mergeDeep( | ||
| this.config.dataDeepMerge, | ||
| {}, | ||
| globalData, | ||
| mergedLayoutData, | ||
| localData, | ||
| frontMatterData, | ||
| ); | ||
| let mergedData = Merge({}, globalData, mergedLayoutData, localData, frontMatterData); | ||
@@ -466,8 +471,8 @@ if (this.config.freezeReservedData) { | ||
| data.page.inputPath = this.inputPath; | ||
| // parsed dir never has the trailing slash | ||
| data.page.inputPathDir = PathNormalizer.getDirectoryFromFilePath(this.inputPath); | ||
| data.page.fileSlug = this.fileSlugStr; | ||
| data.page.filePathStem = this.filePathStem; | ||
| data.page.outputFileExtension = this.engine.defaultTemplateFileExtension; | ||
| data.page.templateSyntax = this.templateRender.getEnginesList( | ||
| data[this.config.keys.engineOverride], | ||
| ); | ||
| data.page.templateSyntax = this.getEngineNames(data[this.config.keys.engineOverride]); | ||
@@ -667,5 +672,6 @@ let newDate = await this.getMappedDate(data); | ||
| let { href, path } = await this.getOutputLocations(data); | ||
| let { href, path, dir } = await this.getOutputLocations(data); | ||
| data.page.url = href; | ||
| data.page.outputPath = path; | ||
| data.page.dir = dir; | ||
| } | ||
@@ -736,78 +742,20 @@ } | ||
| static async runPreprocessors(inputPath, content, data, preprocessors) { | ||
| let skippedVia = false; | ||
| for (let [name, preprocessor] of Object.entries(preprocessors)) { | ||
| let { filter, callback } = preprocessor; | ||
| let filters; | ||
| if (Array.isArray(filter)) { | ||
| filters = filter; | ||
| } else if (typeof filter === "string") { | ||
| filters = filter.split(","); | ||
| } else { | ||
| throw new Error( | ||
| `Expected file extensions passed to "${name}" content preprocessor to be a string or array. Received: ${filter}`, | ||
| ); | ||
| } | ||
| filters = filters.map((extension) => { | ||
| if (extension.startsWith(".") || extension === "*") { | ||
| return extension; | ||
| } | ||
| return `.${extension}`; | ||
| }); | ||
| if (!filters.some((extension) => extension === "*" || inputPath.endsWith(extension))) { | ||
| // skip | ||
| continue; | ||
| } | ||
| try { | ||
| let ret = await callback.call( | ||
| { | ||
| inputPath, | ||
| }, | ||
| data, | ||
| content, | ||
| ); | ||
| // Returning explicit false is the same as ignoring the template | ||
| if (ret === false) { | ||
| skippedVia = name; | ||
| continue; | ||
| } | ||
| // Different from transforms: returning falsy (not false) here does nothing (skips the preprocessor) | ||
| if (ret) { | ||
| content = ret; | ||
| } | ||
| } catch (e) { | ||
| throw new EleventyBaseError( | ||
| `Preprocessor \`${name}\` encountered an error when transforming ${inputPath}.`, | ||
| e, | ||
| ); | ||
| } | ||
| async runPreprocessors(data) { | ||
| // @cachedproperty | ||
| if (!this.#preprocessorCache) { | ||
| this.#preprocessorCache = this.templatePreprocessor.runAll(this, data); | ||
| } | ||
| return { | ||
| skippedVia, | ||
| content, | ||
| }; | ||
| return this.#preprocessorCache; | ||
| } | ||
| async getTemplates(data) { | ||
| let content = await this.getPreRender(); | ||
| let { skippedVia, content: rawInput } = await Template.runPreprocessors( | ||
| this.inputPath, | ||
| content, | ||
| data, | ||
| this.config.preprocessors, | ||
| ); | ||
| let { skippedVia: skippedViaPreprocessorName, content: rawInput } = | ||
| await this.runPreprocessors(data); | ||
| if (skippedVia) { | ||
| if (skippedViaPreprocessorName) { | ||
| debug( | ||
| "Skipping %o, the %o preprocessor returned an explicit `false`", | ||
| this.inputPath, | ||
| skippedVia, | ||
| skippedViaPreprocessorName, | ||
| ); | ||
@@ -1139,5 +1087,9 @@ return []; | ||
| if (dateValue.toLowerCase() === "git last modified") { | ||
| let d = await getDateFromGitLastUpdated(this.inputPath); | ||
| if (d) { | ||
| return d; | ||
| let timestamp = await getUpdatedTimestamp(this.inputPath); | ||
| if (timestamp) { | ||
| debug( | ||
| `getMappedDate: found git last modified timestamp for ${this.inputPath}: %o`, | ||
| timestamp, | ||
| ); | ||
| return new Date(timestamp); | ||
| } | ||
@@ -1152,5 +1104,9 @@ | ||
| if (dateValue.toLowerCase() === "git created") { | ||
| let d = await getDateFromGitFirstAdded(this.inputPath); | ||
| if (d) { | ||
| return d; | ||
| let timestamp = await getCreatedTimestamp(this.inputPath); | ||
| if (timestamp) { | ||
| debug( | ||
| `getMappedDate: found git created timestamp for ${this.inputPath}: %o`, | ||
| timestamp, | ||
| ); | ||
| return new Date(timestamp); | ||
| } | ||
@@ -1157,0 +1113,0 @@ |
@@ -55,2 +55,3 @@ import { existsSync } from "node:fs"; | ||
| #previousBuildModifiedFile; | ||
| #activeConfigPath; | ||
@@ -177,3 +178,3 @@ constructor(customRootConfig, projectConfigPath) { | ||
| let configFiles = this.getLocalProjectConfigFiles(); | ||
| let configFile = configFiles.find((path) => path && existsSync(path)); | ||
| let configFile = configFiles.find((path) => path && this.existsCache.exists(path)); | ||
| if (configFile) { | ||
@@ -192,2 +193,9 @@ return configFile; | ||
| getActiveConfigPath() { | ||
| if (!this.#activeConfigPath) { | ||
| this.#activeConfigPath = this.getLocalProjectConfigFile(); | ||
| } | ||
| return this.#activeConfigPath; | ||
| } | ||
| setProjectUsingEsm(isEsmProject) { | ||
@@ -206,2 +214,4 @@ this.isEsm = !!isEsmProject; | ||
| async reset() { | ||
| this.#existsCache.reset(); | ||
| debugDev("Resetting configuration: TemplateConfig and UserConfig."); | ||
@@ -235,2 +245,4 @@ this.userConfig.reset(); | ||
| async init(overrides) { | ||
| this.#activeConfigPath = undefined; // reset | ||
| await this.initializeRootConfig(); | ||
@@ -390,3 +402,3 @@ | ||
| let path = this.projectConfigPaths.filter((path) => path).find((path) => existsSync(path)); | ||
| let path = this.getActiveConfigPath(); | ||
@@ -393,0 +405,0 @@ if (this.projectConfigPaths.length > 0 && this.#configManuallyDefined && !path) { |
@@ -29,3 +29,3 @@ import { readFileSync } from "node:fs"; | ||
| #templateRender; | ||
| #preprocessorEngine; | ||
| #renderPreprocessorEngine; | ||
| #extensionMap; | ||
@@ -56,3 +56,3 @@ #configOptions; | ||
| let engine = await this.templateRender.getEngineByName(preprocessorEngineName); | ||
| this.#preprocessorEngine = engine; | ||
| this.#renderPreprocessorEngine = engine; | ||
| } | ||
@@ -438,2 +438,6 @@ } | ||
| getEngineNames(engineOverride) { | ||
| return this.templateRender.getEnginesList(engineOverride); | ||
| } | ||
| async getEngineOverride() { | ||
@@ -447,4 +451,4 @@ return this.getFrontMatterData().then((data) => { | ||
| isTemplateCacheable() { | ||
| if (this.#preprocessorEngine) { | ||
| return this.#preprocessorEngine.cacheable; | ||
| if (this.#renderPreprocessorEngine) { | ||
| return this.#renderPreprocessorEngine.cacheable; | ||
| } | ||
@@ -552,4 +556,4 @@ return this.engine.cacheable; | ||
| // TODO pass in engineOverride here | ||
| if (this.#preprocessorEngine) { | ||
| engine = this.#preprocessorEngine; | ||
| if (this.#renderPreprocessorEngine) { | ||
| engine = this.#renderPreprocessorEngine; | ||
| } | ||
@@ -556,0 +560,0 @@ |
@@ -12,3 +12,3 @@ import path from "node:path"; | ||
| this.inputPath = inputPath; | ||
| this.cleanInputPath = inputPath.replace(/^.\//, ""); | ||
| this.cleanInputPath = TemplatePath.stripLeadingDotSlash(inputPath); | ||
@@ -15,0 +15,0 @@ let dirs = this.cleanInputPath.split("/"); |
@@ -1,2 +0,2 @@ | ||
| import { TemplatePath } from "@11ty/eleventy-utils"; | ||
| import { Merge, TemplatePath } from "@11ty/eleventy-utils"; | ||
| import debugUtil from "debug"; | ||
@@ -6,3 +6,2 @@ | ||
| import TemplateContent from "./TemplateContent.js"; | ||
| import TemplateData from "./Data/TemplateData.js"; | ||
| import layoutCache from "./LayoutCache.js"; | ||
@@ -13,2 +12,32 @@ | ||
| // https://github.com/11ty/eleventy/issues/3954 | ||
| class CdataWrapper { | ||
| static PREFIX = "<![CDATA[STARTRAW"; | ||
| static POSTFIX = "ENDRAW]]>"; | ||
| constructor(templateSyntax) { | ||
| this.isEligible = CdataWrapper.isEligible(templateSyntax); | ||
| } | ||
| // Markdown only | ||
| static isEligible(templateSyntax) { | ||
| return templateSyntax.split(",").includes("md"); | ||
| } | ||
| wrap(content) { | ||
| if (this.isEligible) { | ||
| return CdataWrapper.PREFIX + content + CdataWrapper.POSTFIX; | ||
| } | ||
| return content; | ||
| } | ||
| unwrap(content) { | ||
| if (this.isEligible) { | ||
| return content.replaceAll(CdataWrapper.PREFIX, "").replaceAll(CdataWrapper.POSTFIX, ""); | ||
| } | ||
| return content; | ||
| } | ||
| } | ||
| class TemplateLayout extends TemplateContent { | ||
@@ -148,3 +177,3 @@ constructor(key, extensionMap, eleventyConfig) { | ||
| // Deep merge of layout front matter | ||
| let data = TemplateData.mergeDeep(this.config.dataDeepMerge, {}, ...dataToMerge); | ||
| let data = Merge({}, ...dataToMerge); | ||
| delete data[this.config.keys.layout]; | ||
@@ -183,2 +212,4 @@ | ||
| fns.push({ | ||
| inputPath: this.inputPath, | ||
| template: this, | ||
| render: await this.getCachedCompiledLayoutFunction(), | ||
@@ -222,9 +253,13 @@ }); | ||
| let compiledFunctions = await this.getCompiledLayoutFunctions(); | ||
| for (let { render } of compiledFunctions) { | ||
| for (let { render, template } of compiledFunctions) { | ||
| let templateSyntax = template.getEngineNames(pageEntry.data[this.config.keys.engineOverride]); | ||
| let cdata = new CdataWrapper(templateSyntax); | ||
| let data = { | ||
| content: templateContent, | ||
| content: cdata.wrap(templateContent), | ||
| ...pageEntry.data, | ||
| }; | ||
| templateContent = await render(data); | ||
| templateContent = cdata.unwrap(await render(data)); | ||
| } | ||
@@ -231,0 +266,0 @@ |
+17
-16
@@ -7,3 +7,2 @@ import { isPlainObject, TemplatePath } from "@11ty/eleventy-utils"; | ||
| import UsingCircularTemplateContentReferenceError from "./Errors/UsingCircularTemplateContentReferenceError.js"; | ||
| import EleventyBaseError from "./Errors/EleventyBaseError.js"; | ||
| import DuplicatePermalinkOutputError from "./Errors/DuplicatePermalinkOutputError.js"; | ||
@@ -15,5 +14,2 @@ import TemplateData from "./Data/TemplateData.js"; | ||
| class EleventyMapPagesError extends EleventyBaseError {} | ||
| class EleventyDataSchemaError extends EleventyBaseError {} | ||
| // These template URL filenames are allowed to exclude file extensions | ||
@@ -41,2 +37,3 @@ const EXTENSIONLESS_URL_ALLOWLIST = [ | ||
| this.map = []; | ||
| this.inputPathMap = new Map(); // NEW: O(1) lookup Map for performance | ||
| this.collectionsData = null; | ||
@@ -75,5 +72,11 @@ this.cached = false; | ||
| let entries = await template.getTemplateMapEntries(data); | ||
| let { skippedVia } = await template.runPreprocessors(data); | ||
| if (skippedVia) { | ||
| return; | ||
| } | ||
| for (let map of entries) { | ||
| this.map.push(map); | ||
| this._addToInputPathMap(map); // NEW: Add to lookup Map for O(1) access | ||
| } | ||
@@ -86,2 +89,8 @@ } | ||
| _addToInputPathMap(mapEntry) { | ||
| // Store under absolute path | ||
| let absoluteInputPath = TemplatePath.absolutePath(mapEntry.inputPath); | ||
| this.inputPathMap.set(absoluteInputPath, mapEntry); | ||
| } | ||
| getTagTarget(str) { | ||
@@ -199,6 +208,3 @@ if (str === "collections") { | ||
| } catch (e) { | ||
| throw new EleventyMapPagesError( | ||
| "Error generating template page(s) for " + map.inputPath + ".", | ||
| e, | ||
| ); | ||
| throw new Error("Error generating template page(s) for " + map.inputPath + ".", { cause: e }); | ||
| } | ||
@@ -334,10 +340,5 @@ | ||
| // TODO(slightlyoff): hot inner loop? | ||
| getMapEntryForInputPath(inputPath) { | ||
| let absoluteInputPath = TemplatePath.absolutePath(inputPath); | ||
| return this.map.find((entry) => { | ||
| if (entry.inputPath === inputPath || entry.inputPath === absoluteInputPath) { | ||
| return entry; | ||
| } | ||
| }); | ||
| return this.inputPathMap.get(absoluteInputPath); | ||
| } | ||
@@ -361,5 +362,5 @@ | ||
| } catch (e) { | ||
| throw new EleventyDataSchemaError( | ||
| throw new Error( | ||
| `Error in the data schema for: ${map.inputPath} (via \`eleventyDataSchema\`)`, | ||
| e, | ||
| { cause: e }, | ||
| ); | ||
@@ -366,0 +367,0 @@ } |
@@ -5,4 +5,6 @@ import path from "node:path"; | ||
| class TemplatePermalink { | ||
| #dynamicPermalinkEnabled; | ||
| // `link` with template syntax should have already been rendered in Template.js | ||
| constructor(link, extraSubdir) { | ||
| constructor(link, extraSubdir, isDynamicPermalinkEnabled = true) { | ||
| let isLinkAnObject = isPlainObject(link); | ||
@@ -12,2 +14,3 @@ | ||
| this._writeToFileSystem = true; | ||
| this.#dynamicPermalinkEnabled = isDynamicPermalinkEnabled; | ||
@@ -49,6 +52,3 @@ let buildLink; | ||
| throw new Error( | ||
| "Expected permalink value to be a string. Received `" + | ||
| typeof buildLink + | ||
| "`" + | ||
| stringToString, | ||
| `Expected permalink value to be a string. Received \`${typeof buildLink}\` (dynamicPermalink: ${this.#dynamicPermalinkEnabled})${stringToString}`, | ||
| ); | ||
@@ -181,3 +181,9 @@ } | ||
| static generate(dir, filenameNoExt, extraSubdir, fileExtension = "html") { | ||
| static generate( | ||
| dir, | ||
| filenameNoExt, | ||
| extraSubdir, | ||
| fileExtension = "html", | ||
| isDynamicPermalinkEnabled, | ||
| ) { | ||
| let path; | ||
@@ -195,3 +201,3 @@ if (fileExtension === "html") { | ||
| return new TemplatePermalink(path, extraSubdir); | ||
| return new TemplatePermalink(path, extraSubdir, isDynamicPermalinkEnabled); | ||
| } | ||
@@ -198,0 +204,0 @@ } |
@@ -0,6 +1,6 @@ | ||
| import debugUtil from "debug"; | ||
| import EleventyBaseError from "./Errors/EleventyBaseError.js"; | ||
| import TemplateEngineManager from "./Engines/TemplateEngineManager.js"; | ||
| // import debugUtil from "debug"; | ||
| // const debug = debugUtil("Eleventy:TemplateRender"); | ||
| const debugConfiguration = debugUtil("Eleventy:UserConfig"); | ||
@@ -22,2 +22,8 @@ class TemplateRenderUnknownEngineError extends EleventyBaseError {} | ||
| this.parseMarkdownWith = this.config.markdownTemplateEngine; | ||
| if (this.parseMarkdownWith === "md") { | ||
| this.parseMarkdownWith = false; | ||
| debugConfiguration( | ||
| "Misconfiguration warning: the preprocessing template syntax for Markdown files cannot be Markdown, we’re assuming you meant `false` to skip preprocessing altogether (via the `markdownTemplateEngine` configuration property or the `setMarkdownTemplateEngine` configuration method). Read more: https://www.11ty.dev/docs/config/#default-template-engine-for-markdown-files", | ||
| ); | ||
| } | ||
| this.parseHtmlWith = this.config.htmlTemplateEngine; | ||
@@ -24,0 +30,0 @@ } |
+39
-41
@@ -28,4 +28,2 @@ import debugUtil from "debug"; | ||
| #quietModeLocked = false; | ||
| /** @type {boolean} */ | ||
| #dataDeepMergeModified = false; | ||
| /** @type {number|undefined} */ | ||
@@ -66,9 +64,2 @@ #uniqueId; | ||
| // this.DateTime removed in v4 | ||
| get DateTime() { | ||
| throw new Error( | ||
| 'Luxon’s DateTime property in configuration was removed in Eleventy v4. Please `import { DateTime } from "luxon"` directly.', | ||
| ); | ||
| } | ||
| // Internally used in TemplateContent for cache keys | ||
@@ -120,3 +111,3 @@ _getUniqueId() { | ||
| pairedShortcodes: {}, | ||
| parameterParsing: "legacy", // or builtin | ||
| parameterParsing: "builtin", // or legacy (Breaking: default swapped in v4.0.0) | ||
| }; | ||
@@ -179,3 +170,2 @@ | ||
| this.dataDeepMerge = true; | ||
| this.extensionMap = new Set(); | ||
@@ -344,5 +334,5 @@ this.extensionMapClasses = {}; | ||
| // This is a method for plugins, probably shouldn’t use this in projects. | ||
| // Projects should use `setLibrary` as documented here: | ||
| // https://github.com/11ty/eleventy/blob/master/docs/engines/markdown.md#use-your-own-options | ||
| // Don’t use this, projects should use `amendLibrary` as documented here: | ||
| // https://www.11ty.dev/docs/languages/markdown/#optional-amend-the-library-instance | ||
| // Warning: this is in use by the Syntax Highlighting plugin (as of v5.0.2) | ||
| addMarkdownHighlighter(highlightFn) { | ||
@@ -805,6 +795,2 @@ this.markdownHighlighter = highlightFn; | ||
| */ | ||
| _normalizeTemplateFormats() { | ||
| throw new Error("The internal _normalizeTemplateFormats() method was removed in Eleventy 3.0"); | ||
| } | ||
| setTemplateFormats(templateFormats) { | ||
@@ -875,12 +861,2 @@ this.templateFormats = templateFormats; | ||
| setDataDeepMerge(deepMerge) { | ||
| this.#dataDeepMergeModified = true; | ||
| this.dataDeepMerge = !!deepMerge; | ||
| } | ||
| // Used by the Upgrade Helper Plugin | ||
| isDataDeepMergeModified() { | ||
| return this.#dataDeepMergeModified; | ||
| } | ||
| addWatchTarget(additionalWatchTargets, options = {}) { | ||
@@ -907,9 +883,2 @@ // Reset the config when the target path changes | ||
| setBrowserSyncConfig() { | ||
| this._attemptedBrowserSyncUse = true; | ||
| debug( | ||
| "The `setBrowserSyncConfig` method was removed in Eleventy 2.0.0. Use `setServerOptions` with the new Eleventy development server or the `@11ty/eleventy-browser-sync` plugin moving forward.", | ||
| ); | ||
| } | ||
| setChokidarConfig(options = {}) { | ||
@@ -1183,7 +1152,2 @@ this.chokidarConfig = options; | ||
| configureTemplateHandling(options = {}) { | ||
| // Was used for sync/async swapping on file write operations | ||
| throw new Error("Internal configuration API method `configureTemplateHandling` was removed."); | ||
| } | ||
| /* | ||
@@ -1280,3 +1244,2 @@ * Collections | ||
| watchIgnores: this.watchIgnores, | ||
| dataDeepMerge: this.dataDeepMerge, | ||
| watchJavaScriptDependencies: this.watchJavaScriptDependencies, | ||
@@ -1323,2 +1286,33 @@ additionalWatchTargets: this.additionalWatchTargets, | ||
| // Removed features | ||
| get DateTime() { | ||
| throw new Error( | ||
| 'Luxon’s DateTime property in configuration was removed in Eleventy v4. Please `import { DateTime } from "luxon"` directly.', | ||
| ); | ||
| } | ||
| _normalizeTemplateFormats() { | ||
| throw new Error("The internal _normalizeTemplateFormats() method was removed in Eleventy v3"); | ||
| } | ||
| setBrowserSyncConfig() { | ||
| this._attemptedBrowserSyncUse = true; | ||
| debug( | ||
| "The `setBrowserSyncConfig` method was removed in Eleventy v2. Use `setServerOptions` with the new Eleventy development server or the `@11ty/eleventy-browser-sync` plugin moving forward.", | ||
| ); | ||
| } | ||
| configureTemplateHandling(options = {}) { | ||
| // Was used for sync/async swapping on file write operations | ||
| throw new Error("Internal configuration API method `configureTemplateHandling` was removed."); | ||
| } | ||
| setDataDeepMerge(deepMerge) { | ||
| if (Boolean(deepMerge) === false) { | ||
| throw new Error( | ||
| "The `setDataDeepMerge(false)` Configuration API feature was removed in Eleventy v4. Read more at https://github.com/11ty/eleventy/issues/3937", | ||
| ); | ||
| } | ||
| } | ||
| // No-op functions for backwards compat | ||
@@ -1330,4 +1324,8 @@ addHandlebarsHelper() {} | ||
| addPairedHandlebarsShortcode() {} | ||
| // Used by the Upgrade Helper Plugin v1 (no longer relevant) | ||
| // https://github.com/11ty/eleventy-upgrade-help/blob/v1.x/src/data-deep-merge.js#L5-L9 | ||
| isDataDeepMergeModified() {} | ||
| } | ||
| export default UserConfig; |
@@ -13,2 +13,7 @@ import { existsSync, statSync } from "node:fs"; | ||
| reset() { | ||
| this.#exists = new Map(); | ||
| this.#dirs = new Map(); | ||
| } | ||
| get size() { | ||
@@ -15,0 +20,0 @@ return this.#exists.size; |
| import path from "node:path"; | ||
| import { TemplatePath } from "@11ty/eleventy-utils"; | ||
| import isValidUrl from "../Util/ValidUrl.js"; | ||
| import { isValidUrl } from "./UrlUtil.js"; | ||
| import { isGlobMatch } from "./GlobMatcher.js"; | ||
@@ -5,0 +5,0 @@ |
| import dependencyTree from "@11ty/dependency-tree"; | ||
| import { find } from "@11ty/dependency-tree-esm"; | ||
| import { find, findGraph, mergeGraphs } from "@11ty/dependency-tree-esm"; | ||
| import { TemplatePath } from "@11ty/eleventy-utils"; | ||
| import { DepGraph } from "dependency-graph"; | ||
| import { union } from "./SetUtil.js"; | ||
| import EleventyBaseError from "../Errors/EleventyBaseError.js"; | ||
@@ -12,3 +14,3 @@ | ||
| static async getDependencies(inputFiles, isProjectUsingEsm) { | ||
| static async getCommonJsDependencies(inputFiles, isProjectUsingEsm) { | ||
| let depSet = new Set(); | ||
@@ -38,2 +40,8 @@ | ||
| return depSet; | ||
| } | ||
| static async getEsmDependencies(inputFiles, isProjectUsingEsm) { | ||
| let depSet = new Set(); | ||
| let esmFiles = inputFiles.filter( | ||
@@ -53,6 +61,30 @@ (file) => (isProjectUsingEsm && file.endsWith(".js")) || file.endsWith(".mjs"), | ||
| return Array.from(depSet).sort(); | ||
| return depSet; | ||
| } | ||
| static async getDependencies(inputFiles, isProjectUsingEsm) { | ||
| let cjs = await this.getCommonJsDependencies(inputFiles, isProjectUsingEsm); | ||
| let esm = await this.getEsmDependencies(inputFiles, isProjectUsingEsm); | ||
| return Array.from(union(cjs, esm)); | ||
| } | ||
| static async getEsmGraph(inputFiles, isProjectUsingEsm) { | ||
| let rootGraph = new DepGraph(); | ||
| let esmFiles = inputFiles.filter( | ||
| (file) => (isProjectUsingEsm && file.endsWith(".js")) || file.endsWith(".mjs"), | ||
| ); | ||
| for (let file of esmFiles) { | ||
| try { | ||
| let graph = await findGraph(file); | ||
| mergeGraphs(rootGraph, graph); | ||
| } catch (e) { | ||
| throw new EleventyBaseError(this.getErrorMessage(file, "ESM"), e); | ||
| } | ||
| } | ||
| return rootGraph; | ||
| } | ||
| } | ||
| export default JavaScriptDependencies; |
@@ -1,2 +0,2 @@ | ||
| import path from "node:path"; | ||
| import { parse, sep } from "node:path"; | ||
| import { TemplatePath } from "@11ty/eleventy-utils"; | ||
@@ -12,4 +12,4 @@ import { fileURLToPath } from "../Adapters/Packages/url.js"; | ||
| let separator = "/"; | ||
| if (inputPath.includes(path.sep)) { | ||
| separator = path.sep; | ||
| if (inputPath.includes(sep)) { | ||
| separator = sep; | ||
| } | ||
@@ -39,5 +39,19 @@ | ||
| } | ||
| return inputPath.split(path.sep).join("/"); | ||
| return inputPath.split(sep).join("/"); | ||
| } | ||
| static addTrailingSlashToDirectory(dir) { | ||
| if (dir.endsWith("/")) { | ||
| return dir; | ||
| } | ||
| return dir + "/"; | ||
| } | ||
| // returns a path | ||
| static getDirectoryFromFilePath(filePath) { | ||
| let parsed = parse(filePath); | ||
| return this.addTrailingSlashToDirectory(parsed.dir); | ||
| } | ||
| static fullNormalization(inputPath) { | ||
@@ -44,0 +58,0 @@ if (typeof inputPath !== "string") { |
@@ -137,6 +137,6 @@ import { existsSync, statSync } from "node:fs"; | ||
| // Normalize absolute paths to relative, #3805 | ||
| // if(path.isAbsolute(dirOrFile)) { | ||
| // dirOrFile = path.relative(".", dirOrFile); | ||
| // } | ||
| // Normalize absolute paths to relative, #3805 #3896 | ||
| if (path.isAbsolute(dirOrFile)) { | ||
| dirOrFile = path.relative(".", dirOrFile); | ||
| } | ||
@@ -230,2 +230,8 @@ // Input has to exist (assumed glob if it does not exist) | ||
| this.#raw.output = dir; | ||
| // Normalize absolute paths to relative, #3805 #3896 | ||
| if (path.isAbsolute(dir)) { | ||
| dir = path.relative(".", dir); | ||
| } | ||
| this.#dirs.output = ProjectDirectories.normalizeDirectory(dir || ""); | ||
@@ -232,0 +238,0 @@ } |
@@ -1,2 +0,2 @@ | ||
| function withResolvers() { | ||
| export function withResolvers() { | ||
| if ("withResolvers" in Promise) { | ||
@@ -14,3 +14,1 @@ return Promise.withResolvers(); | ||
| } | ||
| export { withResolvers }; |
@@ -5,3 +5,7 @@ import { RetrieveGlobals as NodeRetrieveGlobals } from "node-retrieve-globals"; | ||
| export async function RetrieveGlobals(code, filePath) { | ||
| export async function RetrieveGlobals(code, filePath, options = {}) { | ||
| let { isJavaScriptFrontMatterCompat } = Object.assign( | ||
| { isJavaScriptFrontMatterCompat: false }, | ||
| options, | ||
| ); | ||
| let data = { | ||
@@ -19,3 +23,4 @@ page: { | ||
| if (nonBuiltinImports.length === 0) { | ||
| return importFromString(code, { ast, data, filePath }); | ||
| let implicitExports = isJavaScriptFrontMatterCompat ? false : true; | ||
| return importFromString(code, { ast, data, filePath, implicitExports }); | ||
| } | ||
@@ -22,0 +27,0 @@ |
+34
-13
| import { TemplatePath } from "@11ty/eleventy-utils"; | ||
| import { DepGraph } from "dependency-graph"; | ||
| import { mergeGraphs } from "@11ty/dependency-tree-esm"; | ||
@@ -77,3 +78,3 @@ import JavaScriptDependencies from "./Util/JavaScriptDependencies.js"; | ||
| static normalize(targets) { | ||
| static toArray(targets) { | ||
| if (!targets) { | ||
@@ -90,7 +91,7 @@ return []; | ||
| add(targets) { | ||
| this.addRaw(WatchTargets.normalize(targets)); | ||
| this.addRaw(WatchTargets.toArray(targets)); | ||
| } | ||
| static normalizeToGlobs(targets) { | ||
| return WatchTargets.normalize(targets).map((entry) => | ||
| return WatchTargets.toArray(targets).map((entry) => | ||
| TemplatePath.convertToRecursiveGlobSync(entry), | ||
@@ -106,12 +107,33 @@ ); | ||
| targets = WatchTargets.normalize(targets); | ||
| let deps = await JavaScriptDependencies.getDependencies(targets, this.isEsm); | ||
| targets = WatchTargets.toArray(targets); | ||
| let cjsDeps = Array.from( | ||
| await JavaScriptDependencies.getCommonJsDependencies(targets, this.isEsm), | ||
| ); | ||
| if (filterCallback) { | ||
| deps = deps.filter(filterCallback); | ||
| cjsDeps = cjsDeps.filter(filterCallback); | ||
| } | ||
| for (let target of targets) { | ||
| this.addToDependencyGraph(target, deps); | ||
| this.addToDependencyGraph(target, cjsDeps); | ||
| } | ||
| this.addRaw(deps, true); | ||
| this.addRaw(cjsDeps, true); | ||
| // https://github.com/11ty/eleventy/issues/3899 | ||
| // Note that this fix is ESM-only, dependency-tree CJS doesn’t support returning graphs (yet?) | ||
| let esmGraph = await JavaScriptDependencies.getEsmGraph(targets, this.isEsm); | ||
| if (filterCallback) { | ||
| for (let node of esmGraph.overallOrder()) { | ||
| if (!filterCallback(node)) { | ||
| esmGraph.removeNode(node); | ||
| } | ||
| } | ||
| } | ||
| mergeGraphs(this.graph, esmGraph); | ||
| // ESM graph includes original targets, which we do not want for addRaw so we’ll remove them before adding | ||
| let rawEsmGraph = esmGraph.clone(); | ||
| for (let t of targets) { | ||
| rawEsmGraph.removeNode(t); | ||
| } | ||
| this.addRaw(rawEsmGraph.overallOrder(), true); | ||
| } | ||
@@ -140,6 +162,5 @@ | ||
| // Use GlobalDependencyMap | ||
| if (this.#templateConfig) { | ||
| for (let dep of this.#templateConfig.usesGraph.getDependantsFor(filePath)) { | ||
| paths.add(dep); | ||
| } | ||
| let dependantsMapped = this.#templateConfig?.usesGraph.getDependantsFor(filePath) || []; | ||
| for (let dep of dependantsMapped) { | ||
| paths.add(dep); | ||
| } | ||
@@ -146,0 +167,0 @@ } |
| import { spawnAsync } from "./spawn.js"; | ||
| async function getGitFirstAddedTimeStamp(filePath) { | ||
| try { | ||
| let timestamp = await spawnAsync( | ||
| "git", | ||
| // Formats https://www.git-scm.com/docs/git-log#_pretty_formats | ||
| // %at author date, UNIX timestamp | ||
| ["log", "--diff-filter=A", "--follow", "-1", "--format=%at", filePath], | ||
| ); | ||
| return parseInt(timestamp, 10) * 1000; | ||
| } catch (e) { | ||
| // do nothing | ||
| } | ||
| } | ||
| // return a Date | ||
| export default async function (inputPath) { | ||
| let timestamp = await getGitFirstAddedTimeStamp(inputPath); | ||
| if (timestamp) { | ||
| return new Date(timestamp); | ||
| } | ||
| } |
| import { spawnAsync } from "./spawn.js"; | ||
| async function getGitLastUpdatedTimeStamp(filePath) { | ||
| try { | ||
| let timestamp = await spawnAsync( | ||
| "git", | ||
| // Formats https://www.git-scm.com/docs/git-log#_pretty_formats | ||
| // %at author date, UNIX timestamp | ||
| ["log", "-1", "--format=%at", filePath], | ||
| ); | ||
| return parseInt(timestamp, 10) * 1000; | ||
| } catch (e) { | ||
| // do nothing | ||
| } | ||
| } | ||
| // return a Date | ||
| export default async function (inputPath) { | ||
| let timestamp = await getGitLastUpdatedTimeStamp(inputPath); | ||
| if (timestamp) { | ||
| return new Date(timestamp); | ||
| } | ||
| } |
| function setUnion(...sets) { | ||
| let root = new Set(); | ||
| for (let set of sets) { | ||
| for (let entry of set) { | ||
| root.add(entry); | ||
| } | ||
| } | ||
| return root; | ||
| } | ||
| export { setUnion }; |
| export default function isValidUrl(url) { | ||
| try { | ||
| new URL(url); | ||
| return true; | ||
| } catch (e) { | ||
| // invalid url OR local path | ||
| return false; | ||
| } | ||
| } |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
564782
1.09%16766
0.91%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated
Updated
Updated
Updated
Updated
Updated
Updated
Updated
Updated
Updated
Updated