Skip to content

Commit

Permalink
Fixes #3246
Browse files Browse the repository at this point in the history
  • Loading branch information
zachleat committed Apr 10, 2024
1 parent a9985e4 commit 9b425cf
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 15 deletions.
11 changes: 8 additions & 3 deletions src/Eleventy.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ class Eleventy {

/* Programmatic API config */
if (this.options.config && typeof this.options.config === "function") {
debug("Running options.config configuration callback (passed to Eleventy constructor)");
// TODO use return object here?
await this.options.config(this.eleventyConfig.userConfig);
}
Expand Down Expand Up @@ -191,7 +192,7 @@ class Eleventy {
}

if (performance) {
debug("Eleventy warm up time (in ms) %o", performance.now());
debug("Eleventy warm up time: %o (ms)", performance.now());
}

/** @member {Number} - The timestamp of Eleventy start. */
Expand Down Expand Up @@ -232,6 +233,10 @@ class Eleventy {
this.#directories = new ProjectDirectories();
this.#directories.setInput(this.rawInput, this.options.inputDir);
this.#directories.setOutput(this.rawOutput);

if (this.source == "cli" && (this.rawInput !== undefined || this.rawOutput !== undefined)) {
this.#directories.freeze();
}
}

return this.#directories;
Expand Down Expand Up @@ -1275,8 +1280,8 @@ Arguments:
debug("Finished writing templates.");

debug(`
Getting frustrated? Have a suggestion/feature request/feedback?
I want to hear it! Open an issue: https://github.com/11ty/eleventy/issues/new`);
Have a suggestion/feature request/feedback? Feeling frustrated? I want to hear it!
Open an issue: https://github.com/11ty/eleventy/issues/new`);
}

return ret;
Expand Down
2 changes: 1 addition & 1 deletion src/Engines/TemplateEngineManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class TemplateEngineManager {

async getCustomEngineClass() {
if (!this._CustomEngine) {
this._CustomEngine = await EleventyImportFromEleventy("./src/Engines/Custom.js");
this._CustomEngine = EleventyImportFromEleventy("./src/Engines/Custom.js");
}
return this._CustomEngine;
}
Expand Down
42 changes: 34 additions & 8 deletions src/TemplateConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import chalk from "kleur";
import { TemplatePath } from "@11ty/eleventy-utils";
import debugUtil from "debug";

import { EleventyImport, EleventyImportFromEleventy } from "./Util/Require.js";
import { EleventyImportRaw, EleventyImportRawFromEleventy } from "./Util/Require.js";
import EleventyBaseError from "./Errors/EleventyBaseError.js";
import UserConfig from "./UserConfig.js";
import GlobalDependencyMap from "./GlobalDependencyMap.js";
Expand Down Expand Up @@ -244,8 +244,11 @@ class TemplateConfig {
* Bootstraps the config object.
*/
async initializeRootConfig() {
this.rootConfig =
this.customRootConfig || (await EleventyImportFromEleventy("./src/defaultConfig.js"));
this.rootConfig = this.customRootConfig;
if (!this.rootConfig) {
let { default: cfg } = await EleventyImportRawFromEleventy("./src/defaultConfig.js");
this.rootConfig = cfg;
}

if (typeof this.rootConfig === "function") {
this.rootConfig = this.rootConfig.call(this, this.userConfig);
Expand Down Expand Up @@ -311,13 +314,23 @@ class TemplateConfig {
*/
async requireLocalConfigFile() {
let localConfig = {};
// TODO option to set config root dir
let path = this.projectConfigPaths.filter((path) => path).find((path) => fs.existsSync(path));

debug(`Merging config with ${path}`);

debug(`Merging default config with ${path}`);
if (path) {
try {
localConfig = await EleventyImport(path, this.isEsm ? "esm" : "cjs");
let { default: cfg, directories } = await EleventyImportRaw(
path,
this.isEsm ? "esm" : "cjs",
);
localConfig = cfg;

if (this.directories && Object.keys(directories || {}).length > 0) {
debug("Setting directories via `directories` export from config file: %o", directories);
this.directories.setViaConfigObject(directories);
}

// debug( "localConfig require return value: %o", localConfig );
if (typeof localConfig === "function") {
localConfig = await localConfig(this.userConfig);
Expand Down Expand Up @@ -352,7 +365,20 @@ class TemplateConfig {
let localConfig = await this.requireLocalConfigFile();

if (this.directories) {
this.directories.setViaConfigObject(this.userConfig.directoryAssignments);
if (Object.keys(this.userConfig.directoryAssignments || {}).length > 0) {
debug(
"Setting directories via set*Directory configuration APIs %o",
this.userConfig.directoryAssignments,
);
this.directories.setViaConfigObject(this.userConfig.directoryAssignments);
}
if (localConfig && Object.keys(localConfig?.dir || {}).length > 0) {
debug(
"Setting directories via `dir` object return from configuration file: %o",
localConfig.dir,
);
this.directories.setViaConfigObject(localConfig.dir);
}
}

// Template Formats:
Expand Down Expand Up @@ -419,7 +445,7 @@ class TemplateConfig {
merge(mergedConfig, eleventyConfigApiMergingObject);

// Apply overrides, currently only pathPrefix uses this I think!
debug("overrides: %o", this.overrides);
debug("Configuration overrides: %o", this.overrides);
merge(mergedConfig, this.overrides);

// Restore templateFormats
Expand Down
18 changes: 18 additions & 0 deletions src/Util/ProjectDirectories.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import isGlob from "is-glob";

/* Directories internally should always use *nix forward slashes */
class ProjectDirectories {
// no updates allowed, input/output set via CLI
#frozen = false;

#raw = {};

#defaults = {
Expand Down Expand Up @@ -43,6 +46,11 @@ class ProjectDirectories {
return path + "/";
}

// If input/output are set via CLI, they take precedence over all other configuration values.
freeze() {
this.#frozen = true;
}

setViaConfigObject(configDirs = {}) {
// input must come last
let inputChanged = false;
Expand Down Expand Up @@ -108,6 +116,11 @@ class ProjectDirectories {

/* Relative to project root, must exist */
#setInputRaw(dirOrFile, inputDir = undefined) {
// is frozen and was defined previously
if (this.#frozen && this.#raw.input !== undefined) {
return;
}

this.#raw.input = dirOrFile;

if (!dirOrFile) {
Expand Down Expand Up @@ -195,6 +208,11 @@ class ProjectDirectories {

/* Relative to project root */
setOutput(dir) {
// is frozen and was defined previously
if (this.#frozen && this.#raw.output !== undefined) {
return;
}

if (dir !== undefined) {
this.#raw.output = dir;
this.#dirs.output = ProjectDirectories.normalizeDirectory(dir || "");
Expand Down
26 changes: 23 additions & 3 deletions src/Util/Require.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ eventBus.on("eleventy.importCacheReset", (fileQueue) => {
}
});

async function dynamicImportAbsolutePath(absolutePath, type) {
async function dynamicImportAbsolutePath(absolutePath, type, returnRaw = false) {
if (absolutePath.endsWith(".json") || type === "json") {
// https://v8.dev/features/import-assertions#dynamic-import() is still experimental in Node 20
let rawInput = await loadContents(absolutePath);
Expand All @@ -69,6 +69,10 @@ async function dynamicImportAbsolutePath(absolutePath, type) {

let target = await import(urlPath);

if (returnRaw) {
return target;
}

// If the only export is `default`, elevate to top (for ESM and CJS)
if (Object.keys(target).length === 1 && "default" in target) {
return target.default;
Expand Down Expand Up @@ -114,14 +118,14 @@ async function dynamicImportAbsolutePath(absolutePath, type) {
}

function normalizeFilePathInEleventyPackage(file) {
// Back up from ./src/Util/Require.js
// Back up relative paths from ./src/Util/Require.js
return path.resolve(fileURLToPath(import.meta.url), "../../../", file);
}

async function dynamicImportFromEleventyPackage(file) {
// points to files relative to the top level Eleventy directory
let filePath = normalizeFilePathInEleventyPackage(file);
return dynamicImportAbsolutePath(filePath);
return dynamicImportAbsolutePath(filePath, "esm");
}

async function dynamicImport(localPath, type) {
Expand All @@ -130,9 +134,25 @@ async function dynamicImport(localPath, type) {
return dynamicImportAbsolutePath(absolutePath, type);
}

/* Used to import default Eleventy configuration file, raw means we don’t normalize away the `default` export */
async function dynamicImportRawFromEleventyPackage(file) {
// points to files relative to the top level Eleventy directory
let filePath = normalizeFilePathInEleventyPackage(file);
return dynamicImportAbsolutePath(filePath, "esm", true);
}

/* Used to import project configuration files, raw means we don’t normalize away the `default` export */
async function dynamicImportRaw(localPath, type) {
let absolutePath = TemplatePath.absolutePath(localPath);
// async
return dynamicImportAbsolutePath(absolutePath, type, true);
}

export {
loadContents as EleventyLoadContent,
dynamicImport as EleventyImport,
dynamicImportRaw as EleventyImportRaw,
dynamicImportFromEleventyPackage as EleventyImportFromEleventy,
dynamicImportRawFromEleventyPackage as EleventyImportRawFromEleventy,
normalizeFilePathInEleventyPackage,
};
1 change: 1 addition & 0 deletions src/defaultConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export default function (config) {
engineOverride: "templateEngineOverride",
computed: "eleventyComputed",
},
// Downstream folks should use `export const directories = {}` instead.
dir: {
input: ".",
includes: "_includes",
Expand Down
36 changes: 36 additions & 0 deletions test/EleventyTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -912,3 +912,39 @@ test("setInputDirectory config method #1503 in a plugin throws error", async (t)
message: "Error processing a plugin",
});
});

test("Eleventy directories export (ESM)", async (t) => {
t.plan(5);
let elev = new Eleventy("test/stubs/cfg-directories-export", null, {
configPath: "./test/stubs/cfg-directories-export/eleventy.config.js",
config: function (eleventyConfig) {
eleventyConfig.on("eleventy.after", arg => {
t.is(arg.directories.input, "./src/");
t.is(arg.directories.includes, "./src/myincludes/");
t.is(arg.directories.data, "./src/mydata/");
t.is(arg.directories.layouts, undefined);
t.is(arg.directories.output, "./dist/");
})
},
});

let result = await elev.toJSON();
});

test("Eleventy directories export (CommonJS)", async (t) => {
t.plan(5);
let elev = new Eleventy("test/stubs/cfg-directories-export-cjs", null, {
configPath: "./test/stubs/cfg-directories-export-cjs/eleventy.config.cjs",
config: function (eleventyConfig) {
eleventyConfig.on("eleventy.after", arg => {
t.is(arg.directories.input, "./src/");
t.is(arg.directories.includes, "./src/myincludes2/");
t.is(arg.directories.data, "./src/mydata2/");
t.is(arg.directories.layouts, undefined);
t.is(arg.directories.output, "./dist2/");
})
},
});

let result = await elev.toJSON();
});
55 changes: 55 additions & 0 deletions test/ProjectDirectoriesTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,59 @@ test("Setting values via config object (dots)", t => {
t.is(d.includes, "./test/stubs/");
});

test("CLI values should override all others (both)", t => {
let d = new ProjectDirectories();
d.setInput("src");
d.setOutput("dist");
d.freeze();

d.setViaConfigObject({
input: "test/stubs",
includes: "myincludes",
});

t.is(d.input, "./src/");
t.is(d.inputFile, undefined);
t.is(d.output, "./dist/");
t.is(d.data, "./src/_data/");
t.is(d.includes, "./src/myincludes/");
t.is(d.layouts, undefined);
});

test("CLI values should override all others (just input)", t => {
let d = new ProjectDirectories();
d.setInput("src");
d.freeze();

d.setViaConfigObject({
input: "test/stubs",
includes: "myincludes", // always okay, not a CLI param
output: "dist",
});

t.is(d.input, "./src/");
t.is(d.inputFile, undefined);
t.is(d.output, "./dist/");
t.is(d.data, "./src/_data/");
t.is(d.includes, "./src/myincludes/");
t.is(d.layouts, undefined);
});

test("CLI values should override all others (just output)", t => {
let d = new ProjectDirectories();
d.setOutput("dist");
d.freeze();

d.setViaConfigObject({
input: "test/stubs",
includes: "myincludes", // always okay, not a CLI param
output: "someotherdir",
});

t.is(d.input, "./test/stubs/");
t.is(d.inputFile, undefined);
t.is(d.output, "./dist/");
t.is(d.data, "./test/stubs/_data/");
t.is(d.includes, "./test/stubs/myincludes/");
t.is(d.layouts, undefined);
});
10 changes: 10 additions & 0 deletions test/stubs/cfg-directories-export-cjs/eleventy.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = function(eleventyConfig) {

};

module.exports.directories = {
input: "src",
includes: "myincludes2",
data: "mydata2",
output: "dist2"
}
Empty file.
10 changes: 10 additions & 0 deletions test/stubs/cfg-directories-export/eleventy.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function(eleventyConfig) {

};

export const directories = {
input: "src",
includes: "myincludes",
data: "mydata",
output: "dist"
}
Empty file.

0 comments on commit 9b425cf

Please sign in to comment.