From ff0b012ca7aed099e2fc93eca4fd1588bb401156 Mon Sep 17 00:00:00 2001 From: Zach Leatherman Date: Mon, 9 Dec 2024 11:52:04 -0600 Subject: [PATCH] Swap from built-in plugin to use addPassthroughCopy method --- src/Eleventy.js | 6 -- src/Plugins/AutoCopyPlugin.js | 63 ++++++++++---------- src/UserConfig.js | 14 ++++- src/defaultConfig.js | 4 ++ test/AutoCopyPluginTest.js | 104 +++++++++++++++++++++------------- 5 files changed, 115 insertions(+), 76 deletions(-) diff --git a/src/Eleventy.js b/src/Eleventy.js index 960b764d0..1a1787037 100644 --- a/src/Eleventy.js +++ b/src/Eleventy.js @@ -40,7 +40,6 @@ import RenderPlugin, * as RenderPluginExtras from "./Plugins/RenderPlugin.js"; import I18nPlugin, * as I18nPluginExtras from "./Plugins/I18nPlugin.js"; import HtmlBasePlugin, * as HtmlBasePluginExtras from "./Plugins/HtmlBasePlugin.js"; import { TransformPlugin as InputPathToUrlTransformPlugin } from "./Plugins/InputPathToUrl.js"; -import { AutoCopyPlugin } from "./Plugins/AutoCopyPlugin.js"; import { IdAttributePlugin } from "./Plugins/IdAttributePlugin.js"; const pkg = getEleventyPackageJson(); @@ -1487,9 +1486,4 @@ export { * @type {module:11ty/eleventy/Plugins/IdAttributePlugin} */ IdAttributePlugin, - - /** - * @type {module:11ty/eleventy/Plugins/AutoCopyPlugin} - */ - AutoCopyPlugin, }; diff --git a/src/Plugins/AutoCopyPlugin.js b/src/Plugins/AutoCopyPlugin.js index 0e1c0069b..6faee3dc9 100644 --- a/src/Plugins/AutoCopyPlugin.js +++ b/src/Plugins/AutoCopyPlugin.js @@ -1,48 +1,51 @@ import { AutoCopy } from "../Util/AutoCopy.js"; -function AutoCopyPlugin(eleventyConfig, defaultOptions = {}) { +// one AutoCopy instance per entry +function init(eleventyConfig, options) { let opts = Object.assign( { extensions: "html", - match: false, + match: false, // can be one glob string or an array of globs paths: [], // directories to also look in for files failOnError: true, // fails when a path matches (via `match`) but not found on file system copyOptions: undefined, }, - defaultOptions, + options, ); - let ac = eleventyConfig.autoCopy; - if (!ac) { - ac = new AutoCopy(); - ac.setUserConfig(eleventyConfig); - eleventyConfig.autoCopy = ac; - - ac.setFailOnError(opts.failOnError); - ac.setCopyOptions(opts.copyOptions); - - eleventyConfig.htmlTransformer.addUrlTransform( - opts.extensions, - function (targetFilepathOrUrl) { - // @ts-ignore - ac.copy(targetFilepathOrUrl, this.page.inputPath, this.page.outputPath); - - // TODO front matter option for manual copy - - return targetFilepathOrUrl; - }, - { - enabled: () => ac.isEnabled(), - // - MUST run after other plugins but BEFORE HtmlBase plugin - priority: -1, - }, - ); - } - + let ac = new AutoCopy(); + ac.setUserConfig(eleventyConfig); ac.addMatchingGlob(opts.match); + ac.setFailOnError(opts.failOnError); + ac.setCopyOptions(opts.copyOptions); + + eleventyConfig.htmlTransformer.addUrlTransform( + opts.extensions, + function (targetFilepathOrUrl) { + // @ts-ignore + ac.copy(targetFilepathOrUrl, this.page.inputPath, this.page.outputPath); + + // TODO front matter option for manual copy + + return targetFilepathOrUrl; + }, + { + enabled: () => ac.isEnabled(), + // - MUST run after other plugins but BEFORE HtmlBase plugin + priority: -1, + }, + ); + ac.addPaths(opts.paths); } +function AutoCopyPlugin(eleventyConfig) { + // Important: if this is empty, no URL transforms are added + for (let options of eleventyConfig.autoCopies) { + init(eleventyConfig, options); + } +} + Object.defineProperty(AutoCopyPlugin, "eleventyPackage", { value: "@11ty/eleventy/autocopy-transform-plugin", }); diff --git a/src/UserConfig.js b/src/UserConfig.js index d1edc26f0..233ce5ccc 100644 --- a/src/UserConfig.js +++ b/src/UserConfig.js @@ -145,6 +145,7 @@ class UserConfig { /** @type {object} */ this.passthroughCopies = {}; + this.autoCopies = new Set(); /** @type {object} */ this.layoutAliases = {}; @@ -791,7 +792,17 @@ class UserConfig { * @returns {any} a reference to the `EleventyConfig` object. */ addPassthroughCopy(fileOrDir, copyOptions = {}) { - if (typeof fileOrDir === "string") { + if (copyOptions.mode === "auto") { + if (isPlainObject(fileOrDir)) { + throw new Error( + "mode: 'auto' does not yet support input -> output (objects) mapping. Please pass a string glob or an Array of string globs!", + ); + } + this.autoCopies?.add({ + match: fileOrDir, + ...copyOptions, + }); + } else if (typeof fileOrDir === "string") { this.passthroughCopies[fileOrDir] = { outputPath: true, copyOptions }; } else { for (let [inputPath, outputPath] of Object.entries(fileOrDir)) { @@ -1215,6 +1226,7 @@ class UserConfig { globalData: this.globalData, layoutAliases: this.layoutAliases, layoutResolution: this.layoutResolution, + autoCopies: this.autoCopies, passthroughCopies: this.passthroughCopies, // Liquid diff --git a/src/defaultConfig.js b/src/defaultConfig.js index c3ee045e1..b7069c116 100644 --- a/src/defaultConfig.js +++ b/src/defaultConfig.js @@ -9,6 +9,7 @@ import { FilterPlugin as InputPathToUrlFilterPlugin } from "./Plugins/InputPathT import { HtmlTransformer } from "./Util/HtmlTransformer.js"; import TransformsUtil from "./Util/TransformsUtil.js"; import MemoizeUtil from "./Util/MemoizeFunction.js"; +import { AutoCopyPlugin } from "./Plugins/AutoCopyPlugin.js"; /** * @module 11ty/eleventy/defaultConfig @@ -130,6 +131,9 @@ export default function (config) { return ut.transformContent(this.outputPath, content, this); }); + // Requires user configuration, so must run as second-stage + config.addPlugin(AutoCopyPlugin); + return { templateFormats: ["liquid", "md", "njk", "html", "11ty.js"], // if your site deploys to a subdirectory, change this diff --git a/test/AutoCopyPluginTest.js b/test/AutoCopyPluginTest.js index 08339ba4e..b35739572 100644 --- a/test/AutoCopyPluginTest.js +++ b/test/AutoCopyPluginTest.js @@ -3,7 +3,6 @@ import fs from "node:fs"; import { rimrafSync } from "rimraf"; import { TemplatePath } from "@11ty/eleventy-utils"; -import { AutoCopyPlugin } from "../src/Plugins/AutoCopyPlugin.js"; import { TransformPlugin as InputPathToUrlTransformPlugin } from "../src/Plugins/InputPathToUrl.js"; import { default as HtmlBasePlugin } from "../src/Plugins/HtmlBasePlugin.js"; import Eleventy from "../src/Eleventy.js"; @@ -12,9 +11,9 @@ test("Basic usage", async (t) => { let elev = new Eleventy("./test/stubs-autocopy/", "./test/stubs-autocopy/_site-basica", { configPath: false, config: function (eleventyConfig) { - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.png" - }); + eleventyConfig.addPassthroughCopy("**/*.png", { + mode: "auto" + }) eleventyConfig.on("eleventy.passthrough", copyMap => { t.deepEqual(copyMap, { @@ -62,9 +61,9 @@ test("More complex image path (parent dir)", async (t) => { let elev = new Eleventy("./test/stubs-autocopy/", "./test/stubs-autocopy/_site-basicb", { configPath: false, config: function (eleventyConfig) { - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.png" - }); + eleventyConfig.addPassthroughCopy("**/*.png", { + mode: "auto" + }) eleventyConfig.on("eleventy.passthrough", copyMap => { t.deepEqual(copyMap, { @@ -113,9 +112,9 @@ test("No matches", async (t) => { let elev = new Eleventy("./test/stubs-autocopy/", "./test/stubs-autocopy/_site2", { configPath: false, config: function (eleventyConfig) { - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.jpeg" - }); + eleventyConfig.addPassthroughCopy("**/*.jpeg", { + mode: "auto" + }) eleventyConfig.on("eleventy.passthrough", copyMap => { t.deepEqual(copyMap, { map: {} }) @@ -144,8 +143,8 @@ test("Match but does not exist (throws error)", async (t) => { let elev = new Eleventy("./test/stubs-autocopy/", "./test/stubs-autocopy/_site3", { configPath: false, config: function (eleventyConfig) { - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.png" + eleventyConfig.addPassthroughCopy("**/*.png", { + mode: "auto" }); eleventyConfig.on("eleventy.passthrough", copyMap => { @@ -167,18 +166,14 @@ test("Match but does not exist (throws error)", async (t) => { t.is(fs.existsSync("test/stubs-autocopy/_site3/test/index.html"), false); }); -test.after.always("cleanup dirs", () => { - rimrafSync("test/stubs-autocopy/_site3"); -}); - test("Match but does not exist (no error, using `failOnError: false`)", async (t) => { let elev = new Eleventy("./test/stubs-autocopy/", "./test/stubs-autocopy/_site4", { configPath: false, config: function (eleventyConfig) { - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.png", + eleventyConfig.addPassthroughCopy("**/*.png", { + mode: "auto", failOnError: false, - }); + }) eleventyConfig.on("eleventy.passthrough", copyMap => { t.deepEqual(copyMap, { map: {} }) @@ -207,9 +202,12 @@ test("Copying dotfiles are not allowed", async (t) => { let elev = new Eleventy("./test/stubs-autocopy/", "./test/stubs-autocopy/_site5", { configPath: false, config: function (eleventyConfig) { - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*", // WARNING don’t do this - // copyOptions: { debug: true }, + // WARNING: don’t do this + eleventyConfig.addPassthroughCopy("**/*", { + mode: "auto", + copyOptions: { + // debug: true, + } }); eleventyConfig.on("eleventy.passthrough", copyMap => { @@ -242,8 +240,8 @@ test("Using with InputPathToUrl plugin", async (t) => { configPath: false, config: function (eleventyConfig) { // order of addPlugin shouldn’t matter here - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.{html,njk}", // you probably wouldn’t do this + eleventyConfig.addPassthroughCopy("**/*.{html,njk}", { + mode: "auto" }); eleventyConfig.addPlugin(InputPathToUrlTransformPlugin); @@ -281,8 +279,8 @@ test("Using with InputPathToUrl plugin (reverse addPlugin order)", async (t) => // order of addPlugin shouldn’t matter here eleventyConfig.addPlugin(InputPathToUrlTransformPlugin); - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.{html,njk}", // you probably wouldn’t do this + eleventyConfig.addPassthroughCopy("**/*.{html,njk}", { + mode: "auto" }); eleventyConfig.on("eleventy.passthrough", copyMap => { @@ -316,8 +314,8 @@ test("Use with HtmlBasePlugin usage", async (t) => { pathPrefix: "yolo", config: function (eleventyConfig) { eleventyConfig.addPlugin(HtmlBasePlugin); - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.png" + eleventyConfig.addPassthroughCopy("**/*.png", { + mode: "auto" }); eleventyConfig.on("eleventy.passthrough", copyMap => { @@ -368,8 +366,8 @@ test("Using with InputPathToUrl plugin and HtmlBasePlugin", async (t) => { pathPrefix: "yolo", config: function (eleventyConfig) { // order of addPlugin shouldn’t matter here - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.{html,njk}", // you probably wouldn’t do this + eleventyConfig.addPassthroughCopy("**/*.{html,njk}", { + mode: "auto" }); eleventyConfig.addPlugin(InputPathToUrlTransformPlugin); @@ -405,11 +403,11 @@ test("Multiple addPlugin calls (use both globs)", async (t) => { let elev = new Eleventy("./test/stubs-autocopy/", "./test/stubs-autocopy/_site9", { configPath: false, config: function (eleventyConfig) { - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.jpg" + eleventyConfig.addPassthroughCopy("**/*.jpg", { + mode: "auto" }); - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.png" + eleventyConfig.addPassthroughCopy("**/*.png", { + mode: "auto" }); eleventyConfig.on("eleventy.passthrough", copyMap => { @@ -466,8 +464,8 @@ test("Array of globs", async (t) => { let elev = new Eleventy("./test/stubs-autocopy/", "./test/stubs-autocopy/_site10", { configPath: false, config: function (eleventyConfig) { - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: ["**/*.jpg", "**/*.png"] + eleventyConfig.addPassthroughCopy(["**/*.jpg", "**/*.png"], { + mode: "auto" }); eleventyConfig.on("eleventy.passthrough", copyMap => { @@ -527,10 +525,10 @@ test("overwrite: false", async (t) => { let elev = new Eleventy("./test/stubs-autocopy/", "./test/stubs-autocopy/_site11", { configPath: false, config: function (eleventyConfig) { - eleventyConfig.addPlugin(AutoCopyPlugin, { - match: "**/*.png", + eleventyConfig.addPassthroughCopy("**/*.png", { + mode: "auto", copyOptions: { - overwrite: false + overwrite: false, } }); @@ -571,3 +569,31 @@ test("overwrite: false", async (t) => { test.after.always("cleanup dirs", () => { rimrafSync("test/stubs-autocopy/_site11"); }); + +test("Input -> output remapping not yet supported (throws error)", async (t) => { + let elev = new Eleventy("./test/stubs-autocopy/", "./test/stubs-autocopy/_site12", { + configPath: false, + config: function (eleventyConfig) { + // not yet supported + eleventyConfig.addPassthroughCopy({"**/*.png": "yo"}, { + mode: "auto" + }); + + eleventyConfig.on("eleventy.passthrough", copyMap => { + t.deepEqual(copyMap, { map: {} }) + }); + + eleventyConfig.addTemplate("test.njk", ``) + }, + }); + + elev.disableLogger(); + + await t.throwsAsync(async () => { + await elev.write(); + }, { + message: `mode: 'auto' does not yet support input -> output (objects) mapping. Please pass a string glob or an Array of string globs!` + }); + + t.is(fs.existsSync("test/stubs-autocopy/_site12/test/index.html"), false); +});