diff --git a/src/Template.js b/src/Template.js index 9eb40d52f..729eb7411 100755 --- a/src/Template.js +++ b/src/Template.js @@ -142,15 +142,6 @@ class Template extends TemplateContent { return this.extensionMap.removeTemplateExtension(this.parsed.base); } - get htmlIOException() { - // HTML output can’t overwrite the HTML input file. - return ( - this.inputDir === this.outputDir && - this.templateRender.isEngine("html") && - this.baseFile === "index" - ); - } - async _getRawPermalinkInstance(permalinkValue) { let perm = new TemplatePermalink(permalinkValue, this.extraOutputSubdirectory); perm.setUrlTransforms(this.config.urlTransforms); @@ -242,7 +233,6 @@ class Template extends TemplateContent { this.getTemplateSubfolder(), this.baseFile, this.extraOutputSubdirectory, - this.htmlIOException ? this.config.htmlOutputSuffix : "", this.engine.defaultTemplateFileExtension, ); p.setUrlTransforms(this.config.urlTransforms); @@ -259,7 +249,6 @@ class Template extends TemplateContent { return this._usePermalinkRoot; } - // TODO instead of htmlIOException, do a global search to check if output path = input path and then add extra suffix async getOutputLocations(data) { this.bench.get("(count) getOutputLocations").incrementCount(); let link = await this._getLink(data); diff --git a/src/TemplateMap.js b/src/TemplateMap.js index 5ded43f1f..fe68ef2c7 100644 --- a/src/TemplateMap.js +++ b/src/TemplateMap.js @@ -745,18 +745,33 @@ class TemplateMap { } checkForDuplicatePermalinks() { + let inputs = {}; let permalinks = {}; let warnings = {}; for (let entry of this.map) { for (let page of entry._pages) { if (page.outputPath === false || page.url === false) { // do nothing (also serverless) - } else if (!permalinks[page.outputPath]) { - permalinks[page.outputPath] = [entry.inputPath]; } else { - warnings[page.outputPath] = `Output conflict: multiple input files are writing to \`${ - page.outputPath - }\`. Use distinct \`permalink\` values to resolve this conflict. + // Make sure output doesn’t overwrite input (e.g. --input=. --output=.) + // Related to https://github.com/11ty/eleventy/issues/3327 + if (page.outputPath === page.inputPath) { + throw new DuplicatePermalinkOutputError( + `The template at "${page.inputPath}" attempted to overwrite itself.`, + ); + } else if (inputs[page.outputPath]) { + throw new DuplicatePermalinkOutputError( + `The template at "${page.inputPath}" attempted to overwrite an existing template at "${page.outputPath}".`, + ); + } + inputs[page.inputPath] = true; + + if (!permalinks[page.outputPath]) { + permalinks[page.outputPath] = [entry.inputPath]; + } else { + warnings[page.outputPath] = `Output conflict: multiple input files are writing to \`${ + page.outputPath + }\`. Use distinct \`permalink\` values to resolve this conflict. 1. ${entry.inputPath} ${permalinks[page.outputPath] .map(function (inputPath, index) { @@ -764,8 +779,8 @@ ${permalinks[page.outputPath] }) .join("")} `; - - permalinks[page.outputPath].push(entry.inputPath); + permalinks[page.outputPath].push(entry.inputPath); + } } } } diff --git a/src/TemplatePermalink.js b/src/TemplatePermalink.js index 40a149375..2191a512b 100644 --- a/src/TemplatePermalink.js +++ b/src/TemplatePermalink.js @@ -168,7 +168,7 @@ class TemplatePermalink { return folders[folders.length - 1] === base; } - static generate(dir, filenameNoExt, extraSubdir, suffix, fileExtension = "html") { + static generate(dir, filenameNoExt, extraSubdir, fileExtension = "html") { let path; if (fileExtension === "html") { let hasDupeFolder = TemplatePermalink._hasDuplicateFolder(dir, filenameNoExt); @@ -177,10 +177,9 @@ class TemplatePermalink { (dir ? dir + "/" : "") + (filenameNoExt !== "index" && !hasDupeFolder ? filenameNoExt + "/" : "") + "index" + - (suffix || "") + ".html"; } else { - path = (dir ? dir + "/" : "") + filenameNoExt + (suffix || "") + "." + fileExtension; + path = (dir ? dir + "/" : "") + filenameNoExt + "." + fileExtension; } return new TemplatePermalink(path, extraSubdir); diff --git a/src/defaultConfig.js b/src/defaultConfig.js index e1dea40fb..e568852f1 100644 --- a/src/defaultConfig.js +++ b/src/defaultConfig.js @@ -31,7 +31,6 @@ import TransformsUtil from "./Util/TransformsUtil.js"; * @property {string} [markdownTemplateEngine='liquid'] - Template engine to process markdown files with. * @property {string} [htmlTemplateEngine='liquid'] - Template engine to process html files with. * @property {boolean} [dataTemplateEngine=false] - Changed in v1.0 - * @property {string} [htmlOutputSuffix='-o'] * @property {string} [jsDataFileSuffix='.11tydata'] - File suffix for jsData files. * @property {Object} keys * @property {string} [keys.package='pkg'] - Global data property for package.json data @@ -130,7 +129,6 @@ export default function (config) { pathPrefix: "/", markdownTemplateEngine: "liquid", htmlTemplateEngine: "liquid", - htmlOutputSuffix: "-o", // Renamed from `jsDataFileSuffix` in 2.0 (and swapped to an Array) // If you remove "" we won’t look for dir/dir.json or file.json diff --git a/test/EleventyTest.js b/test/EleventyTest.js index 3a3398b51..3b978f8d1 100644 --- a/test/EleventyTest.js +++ b/test/EleventyTest.js @@ -1316,3 +1316,32 @@ test("Custom Markdown Render with permalink, Issue #2780", async (t) => { t.is(results[0].url, `/permalink.html`); t.is(results[0].content.trim(), `

Markdown?

`); }); + +test("Test input/output conflicts (input overwrites output), Issue #3327", async (t) => { + let elev = new Eleventy("./test/stubs-virtual/", "./test/stubs-virtual/", { + config: eleventyConfig => { + eleventyConfig.addTemplate("test.html", `# Markdown`, { permalink: "test.html" }); + } + }); + elev.disableLogger(); + + let e = await t.throwsAsync(async () => { + await elev.toJSON(); + }); + t.true(e.toString().startsWith("DuplicatePermalinkOutputError:")); +}); + +test("Test input/output conflicts (output overwrites another input), Issue #3327", async (t) => { + let elev = new Eleventy("./test/stubs-virtual/", "./test/stubs-virtual/", { + config: eleventyConfig => { + eleventyConfig.addTemplate("test.html", `# Markdown`); + eleventyConfig.addTemplate("index.html", `# Markdown`, { permalink: "test.html" }); + } + }); + elev.disableLogger(); + + let e = await t.throwsAsync(async () => { + await elev.toJSON(); + }); + t.true(e.toString().startsWith("DuplicatePermalinkOutputError:")); +}); diff --git a/test/TemplatePermalinkTest.js b/test/TemplatePermalinkTest.js index d032c13b2..fbd149bf5 100644 --- a/test/TemplatePermalinkTest.js +++ b/test/TemplatePermalinkTest.js @@ -81,17 +81,17 @@ test("Permalink generate", (t) => { }); test("Permalink generate with suffix", (t) => { - t.is(generate(".", "test", null, "-o").toOutputPath(), "test/index-o.html"); - t.is(generate(".", "test", null, "-o").toHref(), "/test/index-o.html"); - t.is(generate(".", "test", "1/", "-o").toOutputPath(), "test/1/index-o.html"); - t.is(generate(".", "test", "1/", "-o").toHref(), "/test/1/index-o.html"); + t.is(generate(".", "test", null).toOutputPath(), "test/index.html"); + t.is(generate(".", "test", null).toHref(), "/test/"); + t.is(generate(".", "test", "1/").toOutputPath(), "test/1/index.html"); + t.is(generate(".", "test", "1/").toHref(), "/test/1/"); }); test("Permalink generate with new extension", (t) => { - t.is(generate(".", "test", null, null, "css").toOutputPath(), "test.css"); - t.is(generate(".", "test", null, null, "css").toHref(), "/test.css"); - t.is(generate(".", "test", "1/", null, "css").toOutputPath(), "1/test.css"); - t.is(generate(".", "test", "1/", null, "css").toHref(), "/1/test.css"); + t.is(generate(".", "test", null, "css").toOutputPath(), "test.css"); + t.is(generate(".", "test", null, "css").toHref(), "/test.css"); + t.is(generate(".", "test", "1/", "css").toOutputPath(), "1/test.css"); + t.is(generate(".", "test", "1/", "css").toHref(), "/1/test.css"); }); test("Permalink generate with subfolders", (t) => { @@ -101,15 +101,15 @@ test("Permalink generate with subfolders", (t) => { "permalinksubfolder/test/index.html" ); t.is( - generate("permalinksubfolder/", "test", "1/", "-o").toOutputPath(), - "permalinksubfolder/test/1/index-o.html" + generate("permalinksubfolder/", "test", "1/").toOutputPath(), + "permalinksubfolder/test/1/index.html" ); t.is(generate("permalinksubfolder/", "index").toHref(), "/permalinksubfolder/"); t.is(generate("permalinksubfolder/", "test").toHref(), "/permalinksubfolder/test/"); t.is( - generate("permalinksubfolder/", "test", "1/", "-o").toHref(), - "/permalinksubfolder/test/1/index-o.html" + generate("permalinksubfolder/", "test", "1/").toHref(), + "/permalinksubfolder/test/1/" ); }); diff --git a/test/TemplateTest.js b/test/TemplateTest.js index 432e32fcc..f2536c39f 100644 --- a/test/TemplateTest.js +++ b/test/TemplateTest.js @@ -104,7 +104,7 @@ test("HTML files output to the same as the input directory have a file suffix ad test("HTML files output to the same as the input directory have a file suffix added (only if index, this _is_ index).", async (t) => { let tmpl = await getNewTemplate("./test/stubs/index.html", "./test/stubs", "./test/stubs"); let data = await tmpl.getData(); - t.is(await tmpl.getOutputPath(data), "./test/stubs/index-o.html"); + t.is(await tmpl.getOutputPath(data), "./test/stubs/index.html"); }); test("HTML files output to the same as the input directory have a file suffix added (only if index, this _is_ index, subfolder).", async (t) => { @@ -114,7 +114,7 @@ test("HTML files output to the same as the input directory have a file suffix ad "./test/stubs" ); let data = await tmpl.getData(); - t.is(await tmpl.getOutputPath(data), "./test/stubs/subfolder/index-o.html"); + t.is(await tmpl.getOutputPath(data), "./test/stubs/subfolder/index.html"); }); test("Test raw front matter from template (yaml)", async (t) => {