Skip to content

Commit 6ef0d42

Browse files
authored
Adds GlobRemap to fix bug with parent directories in tinyglobby #3854 (#3862)
2 parents 1c88580 + 08e5600 commit 6ef0d42

20 files changed

Lines changed: 404 additions & 66 deletions

src/Data/TemplateData.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ class TemplateData {
595595
if (inputDir) {
596596
debugDev("dirStr: %o; inputDir: %o", dir, inputDir);
597597
}
598+
// TODO use DirContains
598599
if (!inputDir || (dir.startsWith(inputDir) && dir !== inputDir)) {
599600
if (this.config.dataFileDirBaseNameOverride) {
600601
let indexDataFile = dir + "/" + this.config.dataFileDirBaseNameOverride;

src/Eleventy.js

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import chalk from "kleur";
22
import { performance } from "node:perf_hooks";
33
import debugUtil from "debug";
44
import { filesize } from "filesize";
5+
import path from "node:path";
56

67
/* Eleventy Deps */
78
import { TemplatePath } from "@11ty/eleventy-utils";
@@ -30,7 +31,11 @@ import { isGlobMatch } from "./Util/GlobMatcher.js";
3031
import simplePlural from "./Util/Pluralize.js";
3132
import checkPassthroughCopyBehavior from "./Util/PassthroughCopyBehaviorCheck.js";
3233
import eventBus from "./EventBus.js";
33-
import { getEleventyPackageJson, getWorkingProjectPackageJson } from "./Util/ImportJsonSync.js";
34+
import {
35+
getEleventyPackageJson,
36+
importJsonSync,
37+
getWorkingProjectPackageJsonPath,
38+
} from "./Util/ImportJsonSync.js";
3439
import { EleventyImport } from "./Util/Require.js";
3540
import ProjectTemplateFormats from "./Util/ProjectTemplateFormats.js";
3641
import { withResolvers } from "./Util/PromiseUtil.js";
@@ -41,6 +46,7 @@ import I18nPlugin, * as I18nPluginExtras from "./Plugins/I18nPlugin.js";
4146
import HtmlBasePlugin, * as HtmlBasePluginExtras from "./Plugins/HtmlBasePlugin.js";
4247
import { TransformPlugin as InputPathToUrlTransformPlugin } from "./Plugins/InputPathToUrl.js";
4348
import { IdAttributePlugin } from "./Plugins/IdAttributePlugin.js";
49+
import FileSystemRemap from "./Util/GlobRemap.js";
4450

4551
const pkg = getEleventyPackageJson();
4652
const debug = debugUtil("Eleventy");
@@ -56,6 +62,8 @@ class Eleventy {
5662
* @type {object|undefined}
5763
*/
5864
#projectPackageJson;
65+
/** @type {string} */
66+
#projectPackageJsonPath;
5967
/** @type {ProjectTemplateFormats|undefined} */
6068
#templateFormats;
6169
/** @type {ConsoleLogger|undefined} */
@@ -589,14 +597,12 @@ Verbose Output: ${this.verboseMode}`;
589597

590598
let configPath = this.eleventyConfig.getLocalProjectConfigFile();
591599
if (configPath) {
592-
let absolutePathToConfig = TemplatePath.absolutePath(configPath);
593-
values.config = absolutePathToConfig;
594-
595-
// TODO(zachleat): if config is not in root (e.g. using --config=)
596-
let root = TemplatePath.getDirFromFilePath(absolutePathToConfig);
597-
values.root = root;
600+
values.config = TemplatePath.absolutePath(configPath);
598601
}
599602

603+
// Fixed: instead of configuration directory, explicit root or working directory
604+
values.root = TemplatePath.getWorkingDir();
605+
600606
values.source = this.source;
601607

602608
// Backwards compatibility
@@ -1056,7 +1062,11 @@ Arguments:
10561062
this.watchManager = new EleventyWatch();
10571063
this.watchManager.incremental = this.isIncremental;
10581064

1059-
this.watchTargets.add(["./package.json"]);
1065+
if (this.projectPackageJsonPath) {
1066+
this.watchTargets.add([
1067+
path.relative(TemplatePath.getWorkingDir(), this.projectPackageJsonPath),
1068+
]);
1069+
}
10601070
this.watchTargets.add(this.eleventyFiles.getGlobWatcherFiles());
10611071
this.watchTargets.add(this.eleventyFiles.getIgnoreFiles());
10621072

@@ -1075,9 +1085,17 @@ Arguments:
10751085
}
10761086

10771087
// fetch from project’s package.json
1088+
get projectPackageJsonPath() {
1089+
if (this.#projectPackageJsonPath === undefined) {
1090+
this.#projectPackageJsonPath = getWorkingProjectPackageJsonPath() || false;
1091+
}
1092+
return this.#projectPackageJsonPath;
1093+
}
1094+
10781095
get projectPackageJson() {
10791096
if (!this.#projectPackageJson) {
1080-
this.#projectPackageJson = getWorkingProjectPackageJson();
1097+
let p = this.projectPackageJsonPath;
1098+
this.#projectPackageJson = p ? importJsonSync(p) : {};
10811099
}
10821100
return this.#projectPackageJson;
10831101
}
@@ -1106,6 +1124,7 @@ Arguments:
11061124
return;
11071125
}
11081126

1127+
// TODO use DirContains
11091128
let dataDir = TemplatePath.stripLeadingDotSlash(this.templateData.getDataDir());
11101129
function filterOutGlobalDataFiles(path) {
11111130
return !dataDir || !TemplatePath.stripLeadingDotSlash(path).startsWith(dataDir);
@@ -1201,7 +1220,25 @@ Arguments:
12011220
let rawFiles = await this.getWatchedFiles();
12021221
debug("Watching for changes to: %o", rawFiles);
12031222

1204-
let watcher = chokidar.watch(rawFiles, this.getChokidarConfig());
1223+
let options = this.getChokidarConfig();
1224+
1225+
// Remap all paths to `cwd` if in play (Issue #3854)
1226+
let remapper = new FileSystemRemap(rawFiles);
1227+
let cwd = remapper.getCwd();
1228+
1229+
if (cwd) {
1230+
options.cwd = cwd;
1231+
1232+
rawFiles = remapper.getInput().map((entry) => {
1233+
return TemplatePath.stripLeadingDotSlash(entry);
1234+
});
1235+
1236+
options.ignored = remapper.getRemapped(options.ignored || []).map((entry) => {
1237+
return TemplatePath.stripLeadingDotSlash(entry);
1238+
});
1239+
}
1240+
1241+
let watcher = chokidar.watch(rawFiles, options);
12051242

12061243
initWatchBench.after();
12071244

src/EleventyExtensionMap.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ class EleventyExtensionMap {
142142
return this._getGlobs(this.unfilteredFormatKeys, inputDir);
143143
}
144144

145-
_getGlobs(formatKeys, inputDir) {
146-
let dir = TemplatePath.convertToRecursiveGlobSync(inputDir);
145+
_getGlobs(formatKeys, inputDir = "") {
147146
let extensions = new Set();
148147

149148
for (let key of formatKeys) {
@@ -156,6 +155,7 @@ class EleventyExtensionMap {
156155
}
157156
}
158157

158+
let dir = TemplatePath.convertToRecursiveGlobSync(inputDir);
159159
if (extensions.size === 1) {
160160
return [`${dir}/*.${Array.from(extensions)[0]}`];
161161
} else if (extensions.size > 1) {

src/EleventyFiles.js

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import fs from "node:fs";
33
import { TemplatePath, isPlainObject } from "@11ty/eleventy-utils";
44
import debugUtil from "debug";
55

6+
import DirContains from "./Util/DirContains.js";
67
import TemplateData from "./Data/TemplateData.js";
78
import TemplateGlob from "./TemplateGlob.js";
89
import checkPassthroughCopyBehavior from "./Util/PassthroughCopyBehaviorCheck.js";
@@ -11,6 +12,7 @@ const debug = debugUtil("Eleventy:EleventyFiles");
1112

1213
class EleventyFiles {
1314
#extensionMap;
15+
#watcherGlobs;
1416

1517
constructor(formats, templateConfig) {
1618
if (!templateConfig) {
@@ -69,8 +71,8 @@ class EleventyFiles {
6971
this.setupGlobs();
7072
}
7173

72-
get validTemplateGlobs() {
73-
if (!this._validTemplateGlobs) {
74+
#getWatcherGlobs() {
75+
if (!this.#watcherGlobs) {
7476
let globs;
7577
// Input is a file
7678
if (this.inputFile) {
@@ -79,9 +81,10 @@ class EleventyFiles {
7981
// input is a directory
8082
globs = this.extensionMap.getValidGlobs(this.inputDir);
8183
}
82-
this._validTemplateGlobs = globs;
84+
this.#watcherGlobs = globs;
8385
}
84-
return this._validTemplateGlobs;
86+
87+
return this.#watcherGlobs;
8588
}
8689

8790
get passthroughGlobs() {
@@ -154,7 +157,7 @@ class EleventyFiles {
154157

155158
setupGlobs() {
156159
this.fileIgnores = this.getIgnores();
157-
this.extraIgnores = this._getIncludesAndDataDirs();
160+
this.extraIgnores = this.getIncludesAndDataDirs();
158161
this.uniqueIgnores = this.getIgnoreGlobs();
159162

160163
// Conditional added for tests that don’t have a config
@@ -165,6 +168,13 @@ class EleventyFiles {
165168
this.normalizedTemplateGlobs = this.templateGlobs;
166169
}
167170

171+
normalizeIgnoreEntry(entry) {
172+
if (!entry.startsWith("**/")) {
173+
return TemplateGlob.normalizePath(this.localPathRoot || ".", entry);
174+
}
175+
return entry;
176+
}
177+
168178
getIgnoreGlobs() {
169179
let uniqueIgnores = new Set();
170180
for (let ignore of this.fileIgnores) {
@@ -173,10 +183,12 @@ class EleventyFiles {
173183
for (let ignore of this.extraIgnores) {
174184
uniqueIgnores.add(ignore);
175185
}
186+
176187
// Placing the config ignores last here is important to the tests
177188
for (let ignore of this.config.ignores) {
178-
uniqueIgnores.add(TemplateGlob.normalizePath(this.localPathRoot || ".", ignore));
189+
uniqueIgnores.add(this.normalizeIgnoreEntry(ignore));
179190
}
191+
180192
return Array.from(uniqueIgnores);
181193
}
182194

@@ -261,11 +273,12 @@ class EleventyFiles {
261273
files.add(this.eleventyIgnoreContent);
262274
}
263275

264-
// ignore output dir (unless this excludes all input)
265-
// input: . and output: . (skip)
266-
// input: ./content and output . (skip)
267-
// input: . and output: ./_site (add)
268-
if (!this.inputDir.startsWith(this.outputDir)) {
276+
// Make sure output dir isn’t in the input dir (or it will ignore all input!)
277+
// input: . and output: . (skip ignore)
278+
// input: ./content and output . (skip ignore)
279+
// input: . and output: ./_site (add ignore)
280+
let outputContainsInputDir = DirContains(this.outputDir, this.inputDir);
281+
if (!outputContainsInputDir) {
269282
// both are already normalized in 3.0
270283
files.add(TemplateGlob.map(this.outputDir + "/**"));
271284
}
@@ -284,6 +297,7 @@ class EleventyFiles {
284297
if (this.eleventyIgnoreContent === false) {
285298
let absoluteInputDir = TemplatePath.absolutePath(this.inputDir);
286299
ignoreFiles.add(TemplatePath.join(rootDirectory, ".eleventyignore"));
300+
287301
if (rootDirectory !== absoluteInputDir) {
288302
ignoreFiles.add(TemplatePath.join(this.inputDir, ".eleventyignore"));
289303
}
@@ -427,14 +441,16 @@ class EleventyFiles {
427441
/* For `eleventy --watch` */
428442
getGlobWatcherFiles() {
429443
// TODO improvement: tie the includes and data to specific file extensions (currently using `**`)
430-
let directoryGlobs = this._getIncludesAndDataDirs();
444+
let directoryGlobs = this.getIncludesAndDataDirs();
445+
446+
let globs = this.#getWatcherGlobs();
431447

432448
if (checkPassthroughCopyBehavior(this.config, this.runMode)) {
433-
return this.validTemplateGlobs.concat(directoryGlobs);
449+
return globs.concat(directoryGlobs);
434450
}
435451

436452
// Revert to old passthroughcopy copy files behavior
437-
return this.validTemplateGlobs.concat(this.passthroughGlobs).concat(directoryGlobs);
453+
return globs.concat(this.passthroughGlobs).concat(directoryGlobs);
438454
}
439455

440456
/* For `eleventy --watch` */
@@ -456,7 +472,12 @@ class EleventyFiles {
456472
bench.before();
457473
let results = TemplatePath.addLeadingDotSlashArray(
458474
await this.fileSystemSearch.search("js-dependencies", globs, {
459-
ignore: ["**/node_modules/**"],
475+
ignore: [
476+
"**/node_modules/**",
477+
".git/**",
478+
// TODO outputDir
479+
// this.outputDir,
480+
],
460481
}),
461482
);
462483
bench.after();
@@ -471,14 +492,14 @@ class EleventyFiles {
471492
);
472493

473494
for (let ignore of this.config.watchIgnores) {
474-
entries.add(TemplateGlob.normalizePath(this.localPathRoot || ".", ignore));
495+
entries.add(this.normalizeIgnoreEntry(ignore));
475496
}
476497

477498
// de-duplicated
478499
return Array.from(entries);
479500
}
480501

481-
_getIncludesAndDataDirs() {
502+
getIncludesAndDataDirs() {
482503
let rawPaths = new Set();
483504
rawPaths.add(this.includesDir);
484505
if (this.layoutsDir) {

src/FileSystemSearch.js

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { glob } from "tinyglobby";
22
import { TemplatePath } from "@11ty/eleventy-utils";
33
import debugUtil from "debug";
44

5+
import FileSystemRemap from "./Util/GlobRemap.js";
56
import { isGlobMatch } from "./Util/GlobMatcher.js";
67

78
const debug = debugUtil("Eleventy:FileSystemSearch");
@@ -32,8 +33,17 @@ class FileSystemSearch {
3233
// Strip leading slashes from everything!
3334
globs = globs.map((entry) => TemplatePath.stripLeadingDotSlash(entry));
3435

36+
let cwd = FileSystemRemap.getCwd(globs);
37+
if (cwd) {
38+
options.cwd = cwd;
39+
}
40+
3541
if (options.ignore && Array.isArray(options.ignore)) {
36-
options.ignore = options.ignore.map((entry) => TemplatePath.stripLeadingDotSlash(entry));
42+
options.ignore = options.ignore.map((entry) => {
43+
entry = TemplatePath.stripLeadingDotSlash(entry);
44+
45+
return FileSystemRemap.remapInput(entry, cwd);
46+
});
3747
debug("Glob search (%o) ignoring: %o", key, options.ignore);
3848
}
3949

@@ -52,6 +62,14 @@ class FileSystemSearch {
5262

5363
this.count++;
5464

65+
globs = globs.map((entry) => {
66+
if (cwd && entry.startsWith(cwd)) {
67+
return FileSystemRemap.remapInput(entry, cwd);
68+
}
69+
70+
return entry;
71+
});
72+
5573
this.promises[cacheKey] = glob(
5674
globs,
5775
Object.assign(
@@ -63,8 +81,12 @@ class FileSystemSearch {
6381
),
6482
).then((results) => {
6583
this.outputs[cacheKey] = new Set(
66-
results.map((entry) => TemplatePath.standardizeFilePath(entry)),
84+
results.map((entry) => {
85+
let remapped = FileSystemRemap.remapOutput(entry, options.cwd);
86+
return TemplatePath.standardizeFilePath(remapped);
87+
}),
6788
);
89+
6890
return Array.from(this.outputs[cacheKey]);
6991
});
7092
}
@@ -97,6 +119,11 @@ class FileSystemSearch {
97119
delete(path) {
98120
this._modify(path, "delete");
99121
}
122+
123+
// Issue #3859 get rid of chokidar globs
124+
// getAllOutputFiles() {
125+
// return Object.values(this.outputs).map(set => Array.from(set)).flat();
126+
// }
100127
}
101128

102129
export default FileSystemSearch;

src/TemplateConfig.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,16 +150,17 @@ class TemplateConfig {
150150
*/
151151
getLocalProjectConfigFile() {
152152
let configFiles = this.getLocalProjectConfigFiles();
153-
// Add the configFiles[0] in case of a test, where no file exists on the file system
154-
let configFile = configFiles.find((path) => path && fs.existsSync(path)) || configFiles[0];
153+
154+
let configFile = configFiles.find((path) => path && fs.existsSync(path));
155155
if (configFile) {
156156
return configFile;
157157
}
158158
}
159159

160160
getLocalProjectConfigFiles() {
161-
if (this.projectConfigPaths?.length > 0) {
162-
return TemplatePath.addLeadingDotSlashArray(this.projectConfigPaths.filter((path) => path));
161+
let paths = this.projectConfigPaths;
162+
if (paths?.length > 0) {
163+
return TemplatePath.addLeadingDotSlashArray(paths.filter((path) => Boolean(path)));
163164
}
164165
return [];
165166
}

0 commit comments

Comments
 (0)