diff options
Diffstat (limited to 'node_modules/@11ty/eleventy-plugin-bundle/src')
7 files changed, 806 insertions, 0 deletions
diff --git a/node_modules/@11ty/eleventy-plugin-bundle/src/BundleFileOutput.js b/node_modules/@11ty/eleventy-plugin-bundle/src/BundleFileOutput.js new file mode 100644 index 0000000..332c5b7 --- /dev/null +++ b/node_modules/@11ty/eleventy-plugin-bundle/src/BundleFileOutput.js @@ -0,0 +1,75 @@ +import fs from "node:fs"; +import path from "node:path"; +import debugUtil from "debug"; + +import { createHash } from "@11ty/eleventy-utils"; + +const debug = debugUtil("Eleventy:Bundle"); + +const hashCache = {}; +const directoryExistsCache = {}; +const writingCache = new Set(); + +class BundleFileOutput { + constructor(outputDirectory, bundleDirectory) { + this.outputDirectory = outputDirectory; + this.bundleDirectory = bundleDirectory || ""; + this.hashLength = 10; + this.fileExtension = undefined; + } + + setFileExtension(ext) { + this.fileExtension = ext; + } + + async getFilenameHash(content) { + if(hashCache[content]) { + return hashCache[content]; + } + + let base64hash = await createHash(content); + let filenameHash = base64hash.substring(0, this.hashLength); + hashCache[content] = filenameHash; + return filenameHash; + } + + getFilename(filename, extension) { + return filename + (extension && !extension.startsWith(".") ? `.${extension}` : ""); + } + + modifyPathToUrl(dir, filename) { + return "/" + path.join(dir, filename).split(path.sep).join("/"); + } + + async writeBundle(content, type, writeToFileSystem) { + // do not write a bundle, do not return a file name is content is empty + if(!content) { + return; + } + + let dir = path.join(this.outputDirectory, this.bundleDirectory); + let filenameHash = await this.getFilenameHash(content); + let filename = this.getFilename(filenameHash, this.fileExtension || type); + + if(writeToFileSystem) { + let fullPath = path.join(dir, filename); + + // no duplicate writes, this may be improved with a fs exists check, but it would only save the first write + if(!writingCache.has(fullPath)) { + writingCache.add(fullPath); + + if(!directoryExistsCache[dir]) { + fs.mkdirSync(dir, { recursive: true }); + directoryExistsCache[dir] = true; + } + + debug("Writing bundle %o", fullPath); + fs.writeFileSync(fullPath, content); + } + } + + return this.modifyPathToUrl(this.bundleDirectory, filename); + } +} + +export { BundleFileOutput }; diff --git a/node_modules/@11ty/eleventy-plugin-bundle/src/CodeManager.js b/node_modules/@11ty/eleventy-plugin-bundle/src/CodeManager.js new file mode 100644 index 0000000..809c242 --- /dev/null +++ b/node_modules/@11ty/eleventy-plugin-bundle/src/CodeManager.js @@ -0,0 +1,231 @@ +import { BundleFileOutput } from "./BundleFileOutput.js"; +import debugUtil from "debug"; + +const debug = debugUtil("Eleventy:Bundle"); +const DEBUG_LOG_TRUNCATION_SIZE = 200; + +class CodeManager { + // code is placed in this bucket by default + static DEFAULT_BUCKET_NAME = "default"; + + // code is hoisted to this bucket when necessary + static HOISTED_BUCKET_NAME = "default"; + + constructor(name) { + this.name = name; + this.trimOnAdd = true; + // TODO unindent on add + this.reset(); + this.transforms = []; + this.isHoisting = true; + this.fileExtension = undefined; + this.toFileDirectory = undefined; + this.bundleExportKey = "bundle"; + this.runsAfterHtmlTransformer = false; + this.pluckedSelector = undefined; + } + + setDelayed(isDelayed) { + this.runsAfterHtmlTransformer = Boolean(isDelayed); + } + + isDelayed() { + return this.runsAfterHtmlTransformer; + } + + // posthtml-match-selector friendly + setPluckedSelector(selector) { + this.pluckedSelector = selector; + } + + getPluckedSelector() { + return this.pluckedSelector; + } + + setFileExtension(ext) { + this.fileExtension = ext; + } + + setHoisting(enabled) { + this.isHoisting = !!enabled; + } + + setBundleDirectory(dir) { + this.toFileDirectory = dir; + } + + setBundleExportKey(key) { + this.bundleExportKey = key; + } + + getBundleExportKey() { + return this.bundleExportKey; + } + + reset() { + this.pages = {}; + } + + static normalizeBuckets(bucket) { + if(Array.isArray(bucket)) { + return bucket; + } else if(typeof bucket === "string") { + return bucket.split(","); + } + return [CodeManager.DEFAULT_BUCKET_NAME]; + } + + setTransforms(transforms) { + if(!Array.isArray(transforms)) { + throw new Error("Array expected to setTransforms"); + } + + this.transforms = transforms; + } + + _initBucket(pageUrl, bucket) { + if(!this.pages[pageUrl][bucket]) { + this.pages[pageUrl][bucket] = new Set(); + } + } + + addToPage(pageUrl, code = [], bucket) { + if(!Array.isArray(code) && code) { + code = [code]; + } + if(code.length === 0) { + return; + } + + if(!this.pages[pageUrl]) { + this.pages[pageUrl] = {}; + } + + let buckets = CodeManager.normalizeBuckets(bucket); + + let codeContent = code.map(entry => { + if(this.trimOnAdd) { + return entry.trim(); + } + return entry; + }); + + + for(let b of buckets) { + this._initBucket(pageUrl, b); + + for(let content of codeContent) { + if(content) { + if(!this.pages[pageUrl][b].has(content)) { + debug("Adding code to bundle %o for %o (bucket: %o, size: %o): %o", this.name, pageUrl, b, content.length, content.length > DEBUG_LOG_TRUNCATION_SIZE ? content.slice(0, DEBUG_LOG_TRUNCATION_SIZE) + "…" : content); + this.pages[pageUrl][b].add(content); + } + } + } + } + } + + async runTransforms(str, pageData, buckets) { + for (let callback of this.transforms) { + str = await callback.call( + { + page: pageData, + type: this.name, + buckets: buckets + }, + str + ); + } + + return str; + } + + getBucketsForPage(pageData) { + let pageUrl = pageData.url; + if(!this.pages[pageUrl]) { + return []; + } + return Object.keys(this.pages[pageUrl]); + } + + getRawForPage(pageData, buckets = undefined) { + let url = pageData.url; + if(!this.pages[url]) { + debug("No bundle code found for %o on %o, %O", this.name, url, this.pages); + return new Set(); + } + + buckets = CodeManager.normalizeBuckets(buckets); + + let set = new Set(); + let size = 0; + for(let b of buckets) { + if(!this.pages[url][b]) { + // Just continue, if you retrieve code from a bucket that doesn’t exist or has no code, it will return an empty set + continue; + } + + for(let entry of this.pages[url][b]) { + size += entry.length; + set.add(entry); + } + } + + debug("Retrieving %o for %o (buckets: %o, entries: %o, size: %o)", this.name, url, buckets, set.size, size); + return set; + } + + async getForPage(pageData, buckets = undefined) { + let set = this.getRawForPage(pageData, buckets); + let bundleContent = Array.from(set).join("\n"); + + // returns promise + return this.runTransforms(bundleContent, pageData, buckets); + } + + async writeBundle(pageData, buckets, options = {}) { + let url = pageData.url; + if(!this.pages[url]) { + debug("No bundle code found for %o on %o, %O", this.name, url, this.pages); + return ""; + } + + let { output, write } = options; + + buckets = CodeManager.normalizeBuckets(buckets); + + // TODO the bundle output URL might be useful in the transforms for sourcemaps + let content = await this.getForPage(pageData, buckets); + let writer = new BundleFileOutput(output, this.toFileDirectory); + writer.setFileExtension(this.fileExtension); + return writer.writeBundle(content, this.name, write); + } + + // Used when a bucket is output multiple times on a page and needs to be hoisted + hoistBucket(pageData, bucketName) { + let newTargetBucketName = CodeManager.HOISTED_BUCKET_NAME; + if(!this.isHoisting || bucketName === newTargetBucketName) { + return; + } + + let url = pageData.url; + if(!this.pages[url] || !this.pages[url][bucketName]) { + debug("No bundle code found for %o on %o, %O", this.name, url, this.pages); + return; + } + + debug("Code in bucket (%o) is being hoisted to a new bucket (%o)", bucketName, newTargetBucketName); + + this._initBucket(url, newTargetBucketName); + + for(let codeEntry of this.pages[url][bucketName]) { + this.pages[url][bucketName].delete(codeEntry); + this.pages[url][newTargetBucketName].add(codeEntry); + } + + // delete the bucket + delete this.pages[url][bucketName]; + } +} + +export { CodeManager }; diff --git a/node_modules/@11ty/eleventy-plugin-bundle/src/OutOfOrderRender.js b/node_modules/@11ty/eleventy-plugin-bundle/src/OutOfOrderRender.js new file mode 100644 index 0000000..6cafa49 --- /dev/null +++ b/node_modules/@11ty/eleventy-plugin-bundle/src/OutOfOrderRender.js @@ -0,0 +1,158 @@ +import debugUtil from "debug"; + +const debug = debugUtil("Eleventy:Bundle"); + +/* This class defers any `bundleGet` calls to a post-build transform step, + * to allow `getBundle` to be called before all of the `css` additions have been processed + */ +class OutOfOrderRender { + static SPLIT_REGEX = /(\/\*__EleventyBundle:[^:]*:[^:]*:[^:]*:EleventyBundle__\*\/)/; + static SEPARATOR = ":"; + + constructor(content) { + this.content = content; + this.managers = {}; + } + + // type if `get` (return string) or `file` (bundle writes to file, returns file url) + static getAssetKey(type, name, bucket) { + if(Array.isArray(bucket)) { + bucket = bucket.join(","); + } else if(typeof bucket === "string") { + } else { + bucket = ""; + } + return `/*__EleventyBundle:${type}:${name}:${bucket || "default"}:EleventyBundle__*/` + } + + static parseAssetKey(str) { + if(str.startsWith("/*__EleventyBundle:")) { + let [prefix, type, name, bucket, suffix] = str.split(OutOfOrderRender.SEPARATOR); + return { type, name, bucket }; + } + return false; + } + + setAssetManager(name, assetManager) { + this.managers[name] = assetManager; + } + + setOutputDirectory(dir) { + this.outputDirectory = dir; + } + + normalizeMatch(match) { + let ret = OutOfOrderRender.parseAssetKey(match) + return ret || match; + } + + findAll() { + let matches = this.content.split(OutOfOrderRender.SPLIT_REGEX); + let ret = []; + for(let match of matches) { + ret.push(this.normalizeMatch(match)); + } + return ret; + } + + setWriteToFileSystem(isWrite) { + this.writeToFileSystem = isWrite; + } + + getAllBucketsForPage(pageData) { + let availableBucketsForPage = new Set(); + for(let name in this.managers) { + for(let bucket of this.managers[name].getBucketsForPage(pageData)) { + availableBucketsForPage.add(`${name}::${bucket}`); + } + } + return availableBucketsForPage; + } + + getManager(name) { + if(!this.managers[name]) { + throw new Error(`No asset manager found for ${name}. Known names: ${Object.keys(this.managers)}`); + } + return this.managers[name]; + } + + async replaceAll(pageData, stage = 0) { + let matches = this.findAll(); + let availableBucketsForPage = this.getAllBucketsForPage(pageData); + let usedBucketsOnPage = new Set(); + let bucketsOutputStringCount = {}; + let bucketsFileCount = {}; + + for(let match of matches) { + if(typeof match === "string") { + continue; + } + + // type is `file` or `get` + let {type, name, bucket} = match; + let key = `${name}::${bucket}`; + if(!usedBucketsOnPage.has(key)) { + usedBucketsOnPage.add(key); + } + + if(type === "get") { + if(!bucketsOutputStringCount[key]) { + bucketsOutputStringCount[key] = 0; + } + bucketsOutputStringCount[key]++; + } else if(type === "file") { + if(!bucketsFileCount[key]) { + bucketsFileCount[key] = 0; + } + bucketsFileCount[key]++; + } + } + + // Hoist code in non-default buckets that are output multiple times + // Only hoist if 2+ `get` OR 1+ `get` and 1+ `file` + for(let bucketInfo in bucketsOutputStringCount) { + let stringOutputCount = bucketsOutputStringCount[bucketInfo]; + if(stringOutputCount > 1 || stringOutputCount === 1 && bucketsFileCount[bucketInfo] > 0) { + let [name, bucketName] = bucketInfo.split("::"); + this.getManager(name).hoistBucket(pageData, bucketName); + } + } + + let content = await Promise.all(matches.map(match => { + if(typeof match === "string") { + return match; + } + + let {type, name, bucket} = match; + let manager = this.getManager(name); + + // Quit early if in stage 0, run delayed replacements if in stage 1+ + if(typeof manager.isDelayed === "function" && manager.isDelayed() && stage === 0) { + return OutOfOrderRender.getAssetKey(type, name, bucket); + } + + if(type === "get") { + // returns promise + return manager.getForPage(pageData, bucket); + } else if(type === "file") { + // returns promise + return manager.writeBundle(pageData, bucket, { + output: this.outputDirectory, + write: this.writeToFileSystem, + }); + } + return ""; + })); + + for(let bucketInfo of availableBucketsForPage) { + if(!usedBucketsOnPage.has(bucketInfo)) { + let [name, bucketName] = bucketInfo.split("::"); + debug(`WARNING! \`${pageData.inputPath}\` has unbundled \`${name}\` assets (in the '${bucketName}' bucket) that were not written to or used on the page. You might want to add a call to \`getBundle('${name}', '${bucketName}')\` to your content! Learn more: https://github.com/11ty/eleventy-plugin-bundle#asset-bucketing`); + } + } + + return content.join(""); + } +} + +export { OutOfOrderRender }; diff --git a/node_modules/@11ty/eleventy-plugin-bundle/src/bundlePlucker.js b/node_modules/@11ty/eleventy-plugin-bundle/src/bundlePlucker.js new file mode 100644 index 0000000..5dcad46 --- /dev/null +++ b/node_modules/@11ty/eleventy-plugin-bundle/src/bundlePlucker.js @@ -0,0 +1,69 @@ +import debugUtil from "debug"; +import matchHelper from "posthtml-match-helper"; + +const debug = debugUtil("Eleventy:Bundle"); + +const ATTRS = { + ignore: "eleventy:ignore", + bucket: "eleventy:bucket", +}; + +const POSTHTML_PLUGIN_NAME = "11ty/eleventy/html-bundle-plucker"; + +function hasAttribute(node, name) { + return node?.attrs?.[name] !== undefined; +} + +function addHtmlPlucker(eleventyConfig, bundleManager) { + let matchSelector = bundleManager.getPluckedSelector(); + + if(!matchSelector) { + throw new Error("Internal error: missing plucked selector on bundle manager."); + } + + eleventyConfig.htmlTransformer.addPosthtmlPlugin( + "html", + function (context = {}) { + let pageUrl = context?.url; + if(!pageUrl) { + throw new Error("Internal error: missing `url` property from context."); + } + + return function (tree, ...args) { + tree.match(matchHelper(matchSelector), function (node) { + try { + // ignore + if(hasAttribute(node, ATTRS.ignore)) { + delete node.attrs[ATTRS.ignore]; + return node; + } + + if(Array.isArray(node?.content) && node.content.length > 0) { + // TODO make this better decoupled + if(node?.content.find(entry => entry.includes(`/*__EleventyBundle:`))) { + // preserve {% getBundle %} calls as-is + return node; + } + + let bucketName = node?.attrs?.[ATTRS.bucket]; + bundleManager.addToPage(pageUrl, [ ...node.content ], bucketName); + + return { attrs: [], content: [], tag: false }; + } + } catch(e) { + debug(`Bundle plucker: error adding content to bundle in HTML Assets: %o`, e); + return node; + } + + return node; + }); + }; + }, + { + // pluginOptions + name: POSTHTML_PLUGIN_NAME, + }, + ); +} + +export { addHtmlPlucker }; diff --git a/node_modules/@11ty/eleventy-plugin-bundle/src/eleventy.bundleManagers.js b/node_modules/@11ty/eleventy-plugin-bundle/src/eleventy.bundleManagers.js new file mode 100644 index 0000000..8510abc --- /dev/null +++ b/node_modules/@11ty/eleventy-plugin-bundle/src/eleventy.bundleManagers.js @@ -0,0 +1,85 @@ +import debugUtil from "debug"; +import { CodeManager } from "./CodeManager.js"; +import { addHtmlPlucker } from "./bundlePlucker.js" + +const debug = debugUtil("Eleventy:Bundle"); + +function eleventyBundleManagers(eleventyConfig, pluginOptions = {}) { + if(pluginOptions.force) { + // no errors + } else if(("getBundleManagers" in eleventyConfig || "addBundle" in eleventyConfig)) { + throw new Error("Duplicate addPlugin calls for @11ty/eleventy-plugin-bundle"); + } + + let managers = {}; + + function addBundle(name, bundleOptions = {}) { + if(name in managers) { + // note: shortcode must still be added + debug("Bundle exists %o, skipping.", name); + } else { + debug("Creating new bundle %o", name); + managers[name] = new CodeManager(name); + + if(bundleOptions.delayed !== undefined) { + managers[name].setDelayed(bundleOptions.delayed); + } + + if(bundleOptions.hoist !== undefined) { + managers[name].setHoisting(bundleOptions.hoist); + } + + if(bundleOptions.bundleHtmlContentFromSelector !== undefined) { + managers[name].setPluckedSelector(bundleOptions.bundleHtmlContentFromSelector); + managers[name].setDelayed(true); // must override `delayed` above + + addHtmlPlucker(eleventyConfig, managers[name]); + } + + if(bundleOptions.bundleExportKey !== undefined) { + managers[name].setBundleExportKey(bundleOptions.bundleExportKey); + } + + if(bundleOptions.outputFileExtension) { + managers[name].setFileExtension(bundleOptions.outputFileExtension); + } + + if(bundleOptions.toFileDirectory) { + managers[name].setBundleDirectory(bundleOptions.toFileDirectory); + } + + if(bundleOptions.transforms) { + managers[name].setTransforms(bundleOptions.transforms); + } + } + + // if undefined, defaults to `name` + if(bundleOptions.shortcodeName !== false) { + let shortcodeName = bundleOptions.shortcodeName || name; + + // e.g. `css` shortcode to add code to page bundle + // These shortcode names are not configurable on purpose (for wider plugin compatibility) + eleventyConfig.addPairedShortcode(shortcodeName, function addContent(content, bucket, explicitUrl) { + let url = explicitUrl || this.page?.url; + if(url) { // don’t add if a file doesn’t have an output URL + managers[name].addToPage(url, content, bucket); + } + return ""; + }); + } + }; + + eleventyConfig.addBundle = addBundle; + + eleventyConfig.getBundleManagers = function() { + return managers; + }; + + eleventyConfig.on("eleventy.before", async () => { + for(let key in managers) { + managers[key].reset(); + } + }); +}; + +export default eleventyBundleManagers; diff --git a/node_modules/@11ty/eleventy-plugin-bundle/src/eleventy.pruneEmptyBundles.js b/node_modules/@11ty/eleventy-plugin-bundle/src/eleventy.pruneEmptyBundles.js new file mode 100644 index 0000000..3bcaa72 --- /dev/null +++ b/node_modules/@11ty/eleventy-plugin-bundle/src/eleventy.pruneEmptyBundles.js @@ -0,0 +1,105 @@ +import matchHelper from "posthtml-match-helper"; +import debugUtil from "debug"; + +const debug = debugUtil("Eleventy:Bundle"); + +const ATTRS = { + keep: "eleventy:keep" +}; + +const POSTHTML_PLUGIN_NAME = "11ty/eleventy-bundle/prune-empty"; + +function getTextNodeContent(node) { + if (!node.content) { + return ""; + } + + return node.content + .map((entry) => { + if (typeof entry === "string") { + return entry; + } + if (Array.isArray(entry.content)) { + return getTextNodeContent(entry); + } + return ""; + }) + .join(""); +} + +function eleventyPruneEmptyBundles(eleventyConfig, options = {}) { + // Right now script[src],link[rel="stylesheet"] nodes are removed if the final bundles are empty. + // `false` to disable + options.pruneEmptySelector = options.pruneEmptySelector ?? `style,script,link[rel="stylesheet"]`; + + // Subsequent call can remove a previously added `addPosthtmlPlugin` entry + // htmlTransformer.remove is v3.0.1-alpha.4+ + if(typeof eleventyConfig.htmlTransformer.remove === "function") { + eleventyConfig.htmlTransformer.remove("html", entry => { + if(entry.name === POSTHTML_PLUGIN_NAME) { + return true; + } + + // Temporary workaround for missing `name` property. + let fnStr = entry.fn.toString(); + return !entry.name && fnStr.startsWith("function (pluginOptions = {}) {") && fnStr.includes(`tree.match(matchHelper(options.pruneEmptySelector), function (node)`); + }); + } + + // `false` disables this plugin + if(options.pruneEmptySelector === false) { + return; + } + + if(!eleventyConfig.htmlTransformer || !eleventyConfig.htmlTransformer?.constructor?.SUPPORTS_PLUGINS_ENABLED_CALLBACK) { + debug("You will need to upgrade your version of Eleventy core to remove empty bundle tags automatically (v3 or newer)."); + return; + } + + eleventyConfig.htmlTransformer.addPosthtmlPlugin( + "html", + function bundlePruneEmptyPosthtmlPlugin(pluginOptions = {}) { + return function (tree) { + tree.match(matchHelper(options.pruneEmptySelector), function (node) { + if(node.attrs && node.attrs[ATTRS.keep] !== undefined) { + delete node.attrs[ATTRS.keep]; + return node; + } + + // <link rel="stylesheet" href=""> + if(node.tag === "link") { + if(node.attrs?.rel === "stylesheet" && (node.attrs?.href || "").trim().length === 0) { + return false; + } + } else { + let content = getTextNodeContent(node); + + if(!content) { + // <script></script> or <script src=""></script> + if(node.tag === "script" && (node.attrs?.src || "").trim().length === 0) { + return false; + } + + // <style></style> + if(node.tag === "style") { + return false; + } + } + } + + + return node; + }); + }; + }, + { + name: POSTHTML_PLUGIN_NAME, + // the `enabled` callback for plugins is available on v3.0.0-alpha.20+ and v3.0.0-beta.2+ + enabled: () => { + return Object.keys(eleventyConfig.getBundleManagers()).length > 0; + } + } + ); +} + +export default eleventyPruneEmptyBundles; diff --git a/node_modules/@11ty/eleventy-plugin-bundle/src/eleventy.shortcodes.js b/node_modules/@11ty/eleventy-plugin-bundle/src/eleventy.shortcodes.js new file mode 100644 index 0000000..924731a --- /dev/null +++ b/node_modules/@11ty/eleventy-plugin-bundle/src/eleventy.shortcodes.js @@ -0,0 +1,83 @@ +import { OutOfOrderRender } from "./OutOfOrderRender.js"; +import debugUtil from "debug"; + +const debug = debugUtil("Eleventy:Bundle"); + +export default function(eleventyConfig, pluginOptions = {}) { + let managers = eleventyConfig.getBundleManagers(); + let writeToFileSystem = true; + + function bundleTransform(content, stage = 0) { + // Only run if content is string + // Only run if managers are in play + if(typeof content !== "string" || Object.keys(managers).length === 0) { + return content; + } + + debug("Processing %o", this.page.url); + let render = new OutOfOrderRender(content); + for(let key in managers) { + render.setAssetManager(key, managers[key]); + } + + render.setOutputDirectory(eleventyConfig.directories.output); + render.setWriteToFileSystem(writeToFileSystem); + + return render.replaceAll(this.page, stage); + } + + eleventyConfig.on("eleventy.before", async ({ outputMode }) => { + if(Object.keys(managers).length === 0) { + return; + } + + if(outputMode !== "fs") { + writeToFileSystem = false; + debug("Skipping writing to the file system due to output mode: %o", outputMode); + } + }); + + // e.g. `getBundle` shortcode to get code in current page bundle + // bucket can be an array + // This shortcode name is not configurable on purpose (for wider plugin compatibility) + eleventyConfig.addShortcode("getBundle", function getContent(type, bucket, explicitUrl) { + if(!type || !(type in managers) || Object.keys(managers).length === 0) { + throw new Error(`Invalid bundle type: ${type}. Available options: ${Object.keys(managers)}`); + } + + return OutOfOrderRender.getAssetKey("get", type, bucket); + }); + + // write a bundle to the file system + // This shortcode name is not configurable on purpose (for wider plugin compatibility) + eleventyConfig.addShortcode("getBundleFileUrl", function(type, bucket, explicitUrl) { + if(!type || !(type in managers) || Object.keys(managers).length === 0) { + throw new Error(`Invalid bundle type: ${type}. Available options: ${Object.keys(managers)}`); + } + + return OutOfOrderRender.getAssetKey("file", type, bucket); + }); + + eleventyConfig.addTransform("@11ty/eleventy-bundle", function (content) { + let hasNonDelayedManagers = Boolean(Object.values(eleventyConfig.getBundleManagers()).find(manager => { + return typeof manager.isDelayed !== "function" || !manager.isDelayed(); + })); + if(hasNonDelayedManagers) { + return bundleTransform.call(this, content, 0); + } + return content; + }); + + eleventyConfig.addPlugin((eleventyConfig) => { + // Delayed bundles *MUST* not alter URLs + eleventyConfig.addTransform("@11ty/eleventy-bundle/delayed", function (content) { + let hasDelayedManagers = Boolean(Object.values(eleventyConfig.getBundleManagers()).find(manager => { + return typeof manager.isDelayed === "function" && manager.isDelayed(); + })); + if(hasDelayedManagers) { + return bundleTransform.call(this, content, 1); + } + return content; + }); + }); +}; |
