@rohal12/twee-ts 1.18.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +23 -3
  2. package/THIRD_PARTY_NOTICES +111 -0
  3. package/UNLICENSE +24 -0
  4. package/dist/bin/twee-ts.js +705 -9447
  5. package/dist/bin/twee-ts.js.map +1 -1
  6. package/dist/diagnostic-text-Bj-UQ0Jl.cjs +27007 -0
  7. package/dist/diagnostic-text-Bj-UQ0Jl.cjs.map +1 -0
  8. package/dist/diagnostic-text-DMVZfBh9.js +26530 -0
  9. package/dist/diagnostic-text-DMVZfBh9.js.map +1 -0
  10. package/dist/index.cjs +91 -9140
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +208 -416
  13. package/dist/index.d.ts +208 -416
  14. package/dist/index.js +4 -9317
  15. package/dist/lint-BkpfLGuP.js +288 -0
  16. package/dist/lint-BkpfLGuP.js.map +1 -0
  17. package/dist/options-DuBkFyqi.d.cts +28 -0
  18. package/dist/options-nc5NpnSc.d.ts +28 -0
  19. package/dist/plugins/rollup.cjs +177 -0
  20. package/dist/plugins/rollup.cjs.map +1 -0
  21. package/dist/plugins/rollup.d.cts +52 -0
  22. package/dist/plugins/rollup.d.ts +13 -12
  23. package/dist/plugins/rollup.js +136 -66
  24. package/dist/plugins/rollup.js.map +1 -1
  25. package/dist/plugins/vite.cjs +2225 -0
  26. package/dist/plugins/vite.cjs.map +1 -0
  27. package/dist/plugins/vite.d.cts +15 -0
  28. package/dist/plugins/vite.d.ts +5 -12
  29. package/dist/plugins/vite.js +2120 -516
  30. package/dist/plugins/vite.js.map +1 -1
  31. package/dist/types-BvI6pg2y.d.cts +389 -0
  32. package/dist/types-BvI6pg2y.d.ts +389 -0
  33. package/dist/watch-targets-BrEYmUwU.cjs +643 -0
  34. package/dist/watch-targets-BrEYmUwU.cjs.map +1 -0
  35. package/dist/watch-targets-DzvPyWDo.js +548 -0
  36. package/dist/watch-targets-DzvPyWDo.js.map +1 -0
  37. package/package.json +80 -27
  38. package/schemas/twee-ts.config.schema.json +55 -17
  39. package/dist/index.js.map +0 -1
  40. package/dist/paths-BuTAg2av.js +0 -7044
  41. package/dist/paths-BuTAg2av.js.map +0 -1
  42. package/dist/types-D8Xfe0l8.d.ts +0 -71
@@ -0,0 +1,643 @@
1
+ /*! @rohal12/twee-ts (Unlicense). Bundles third-party code under its own licence: see THIRD_PARTY_NOTICES. */
2
+ const require_diagnostic_text = require("./diagnostic-text-Bj-UQ0Jl.cjs");
3
+ let node_path = require("node:path");
4
+ let node_fs = require("node:fs");
5
+ //#region src/plugins/diagnostics.ts
6
+ /**
7
+ * How the build plugins turn compile results into bundler errors and warnings.
8
+ */
9
+ /** Text assembled here is already escaped; its diagnostic separators must survive outer reporters. */
10
+ var ReportedError = class extends Error {};
11
+ /** Returns the warnings; throws a LocatedError when the compile reported errors. */
12
+ function splitDiagnostics(result) {
13
+ const errors = result.diagnostics.filter((d) => d.level === "error");
14
+ if (errors.length === 0) return result.diagnostics.filter((d) => d.level === "warning");
15
+ const error = new ReportedError(errors.map(require_diagnostic_text.formatDiagnostic).join("\n"));
16
+ const [first] = errors;
17
+ if (first?.file) {
18
+ const file = require_diagnostic_text.escapeTerminalControls(first.file);
19
+ error.id = file;
20
+ error.loc = {
21
+ file,
22
+ line: first.line ?? 1,
23
+ column: 1
24
+ };
25
+ }
26
+ throw error;
27
+ }
28
+ /** An error thrown by the compiler or bundler, with a TweeTsError's error diagnostics folded into its message. */
29
+ function fatalError(e) {
30
+ if (e instanceof ReportedError) return e;
31
+ if (e instanceof require_diagnostic_text.TweeTsError) return new ReportedError([...e.diagnostics.filter((d) => d.level === "error").map(require_diagnostic_text.formatDiagnostic), require_diagnostic_text.escapeTerminalControls(e.message)].join("\n"));
32
+ const message = require_diagnostic_text.escapeTerminalControls(e instanceof Error ? e.message : String(e));
33
+ const located = e instanceof Error ? e : void 0;
34
+ const id = typeof located?.id === "string" ? require_diagnostic_text.escapeTerminalControls(located.id) : void 0;
35
+ const loc = located?.loc;
36
+ const file = typeof loc?.file === "string" ? require_diagnostic_text.escapeTerminalControls(loc.file) : void 0;
37
+ if (message === located?.message && id === located.id && file === loc?.file) return located;
38
+ const error = new ReportedError(message);
39
+ if (id !== void 0) error.id = id;
40
+ if (loc !== void 0 && file !== void 0) error.loc = {
41
+ ...loc,
42
+ file
43
+ };
44
+ return error;
45
+ }
46
+ /** Files covered by directory-only entry watches, including directory links, without following cycles. */
47
+ function* entryFolderFiles(paths) {
48
+ const visited = /* @__PURE__ */ new Set();
49
+ function* walk(path, root) {
50
+ let names;
51
+ try {
52
+ const stat = (0, node_fs.statSync)(path);
53
+ if (!stat.isDirectory()) {
54
+ if (!root && stat.isFile()) yield path;
55
+ return;
56
+ }
57
+ const key = require_diagnostic_text.identify(path).key;
58
+ if (visited.has(key)) return;
59
+ visited.add(key);
60
+ names = (0, node_fs.readdirSync)(path);
61
+ } catch (e) {
62
+ const code = e instanceof Error && "code" in e ? e.code : void 0;
63
+ if (code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP") return;
64
+ throw new Error(`Cannot inspect entry watch path ${path}: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
65
+ }
66
+ for (const name of names) yield* walk((0, node_path.join)(path, name), false);
67
+ }
68
+ for (const path of paths) yield* walk(path, true);
69
+ }
70
+ /**
71
+ * Compiles the story for a plugin, leaving out what a build writes (`outputs`).
72
+ * `writesStory` is whether the story goes to a file (a build, not the dev server): a story path in `outputs`
73
+ * that holds an authored file of a source folder then fails the compile (`OUTPUT_IS_INPUT`), as a named output
74
+ * does. `entryFiles` are physical inputs (files or watched directories) the story must also never replace.
75
+ * Throws a LocatedError when the compile fails or reports errors.
76
+ */
77
+ async function compileStory(options, outputs, cache, writesStory = false, entryFiles = []) {
78
+ const inputs = [...entryFiles].map((path) => ({
79
+ role: "entry",
80
+ path
81
+ }));
82
+ const checkEntry = () => {
83
+ if (!writesStory) return;
84
+ const output = require_diagnostic_text.outputPaths({
85
+ files: outputs.stories ?? [],
86
+ dirs: []
87
+ }, "snapshot");
88
+ require_diagnostic_text.checkNamedInputs(inputs, output, []);
89
+ for (const path of entryFolderFiles(inputs.map((input) => input.path))) {
90
+ if (!output.isFile(path) || require_diagnostic_text.isOwnOutput(path) || require_diagnostic_text.isPreviousBuild(path)) continue;
91
+ require_diagnostic_text.checkNamedInputs([{
92
+ role: "entry",
93
+ path
94
+ }], output, []);
95
+ }
96
+ };
97
+ let result;
98
+ try {
99
+ checkEntry();
100
+ result = await require_diagnostic_text.compileForOutputFile(options, writesStory ? outputs : {
101
+ files: outputs.files,
102
+ dirs: outputs.dirs
103
+ }, cache);
104
+ checkEntry();
105
+ } catch (e) {
106
+ throw fatalError(e);
107
+ }
108
+ return {
109
+ output: result.output,
110
+ warnings: splitDiagnostics(result).map(require_diagnostic_text.formatDiagnostic)
111
+ };
112
+ }
113
+ //#endregion
114
+ //#region src/plugins/paths.ts
115
+ /**
116
+ * Path helpers for the bundler plugins. Watchers and bundlers spell one file
117
+ * differently: macOS FSEvents reports /private/var/… for a file under /var/…,
118
+ * Vite resolves module ids through symbolic links, Windows keeps a short 8.3
119
+ * folder name (C:\Users\RUNNER~1) where another tool gives the long one, and
120
+ * a case-insensitive volume takes any letter case. So the plugins compare
121
+ * files by their identity key (see path-identity.ts), and spell a path they
122
+ * hand a watcher by its canonical (real) path.
123
+ */
124
+ /** The real path of `path`, with forward slashes: how the Vite plugin spells a path it hands a watcher. */
125
+ function canonicalPath(path) {
126
+ return toPosix(require_diagnostic_text.identify(path).canonical);
127
+ }
128
+ /** The key a file is compared by: two paths name the same file exactly when their keys are equal. */
129
+ function fileKey(path) {
130
+ return require_diagnostic_text.identify(path).key;
131
+ }
132
+ /** Whether the file with key `key` is one of the folders (or files) with keys `within`, or inside one. */
133
+ function keyWithin(key, within) {
134
+ return within.some((outer) => require_diagnostic_text.isKeyInside(key, outer, node_path.sep));
135
+ }
136
+ /**
137
+ * The absolute path a bundle writes the file it emits as `fileName` to: inside
138
+ * `output.dir`, or next to `output.file`, either resolved against the working
139
+ * directory, as the bundler resolves them. Undefined when the output names
140
+ * neither, as for a bundle only generated in memory.
141
+ */
142
+ function emittedFilePath(output, fileName) {
143
+ const dir = output.dir ?? (output.file === void 0 ? void 0 : (0, node_path.dirname)(output.file));
144
+ return dir === void 0 ? void 0 : (0, node_path.resolve)(dir, fileName);
145
+ }
146
+ /**
147
+ * The locations a bundler's `output` option names: one output's settings or an
148
+ * array of them, as a config gives them. Outputs that name neither `dir` nor
149
+ * `file`, and anything that isn't an output's settings, are left out.
150
+ */
151
+ function outputLocations(option) {
152
+ return (Array.isArray(option) ? option : [option]).flatMap((output) => {
153
+ if (typeof output !== "object" || output === null) return [];
154
+ const dir = "dir" in output ? output.dir : void 0;
155
+ const file = "file" in output ? output.file : void 0;
156
+ const location = {
157
+ ...typeof dir === "string" ? { dir } : {},
158
+ ...typeof file === "string" ? { file } : {}
159
+ };
160
+ return location.dir === void 0 && location.file === void 0 ? [] : [location];
161
+ });
162
+ }
163
+ /** An empty OutputRecord for a plugin that writes its story as `storyFileName`. */
164
+ function createOutputRecord(storyFileName) {
165
+ const files = /* @__PURE__ */ new Set();
166
+ const stories = /* @__PURE__ */ new Set();
167
+ const dirs = /* @__PURE__ */ new Set();
168
+ const addFiles = (output, fileNames) => {
169
+ for (const fileName of fileNames) {
170
+ const path = emittedFilePath(output, fileName);
171
+ if (path !== void 0) files.add(path);
172
+ }
173
+ };
174
+ return {
175
+ addLocation(output) {
176
+ if (output.dir !== void 0) dirs.add((0, node_path.resolve)(output.dir));
177
+ if (output.file !== void 0) files.add((0, node_path.resolve)(output.file));
178
+ addFiles(output, [storyFileName]);
179
+ const story = emittedFilePath(output, storyFileName);
180
+ if (story !== void 0) stories.add(story);
181
+ },
182
+ addFiles,
183
+ outputs: () => ({
184
+ files: [...files],
185
+ stories: [...stories],
186
+ dirs: [...dirs]
187
+ })
188
+ };
189
+ }
190
+ /**
191
+ * The path with the platform's separators turned into forward slashes. Only Windows has a backslash separator;
192
+ * on POSIX a backslash is an ordinary filename character and stays one.
193
+ */
194
+ function toPosix(path) {
195
+ return node_path.sep === "\\" ? path.replaceAll("\\", "/") : path;
196
+ }
197
+ /**
198
+ * Whether `file` is a temporary copy of a Vite config file. Vite writes one next
199
+ * to the config file each time it loads it (when no node_modules folder is
200
+ * above the config, and always under Deno), then deletes it.
201
+ */
202
+ function isViteConfigTemp(file) {
203
+ return /\.timestamp-\d+-[0-9a-f]*\.mjs$/.test(file);
204
+ }
205
+ //#endregion
206
+ //#region src/plugins/options.ts
207
+ /**
208
+ * The options both bundler plugins take, checked once where the plugin is
209
+ * created and turned into what the plugin works with: the compile options of a
210
+ * build, the files whose changes rebuild the story, and which of them
211
+ * `compileOptions.exclude` leaves out. The Vite and the Rollup plugin share this,
212
+ * so the same options mean the same thing in both.
213
+ */
214
+ /** The option names each plugin accepts. */
215
+ const OPTION_NAMES = {
216
+ rollup: [
217
+ "sources",
218
+ "format",
219
+ "outputFilename",
220
+ "compileOptions"
221
+ ],
222
+ vite: [
223
+ "sources",
224
+ "format",
225
+ "outputFilename",
226
+ "compileOptions",
227
+ "entry"
228
+ ]
229
+ };
230
+ /** The compile options each plugin sets from an option of its own, by that option's name. */
231
+ const PLUGIN_SET_COMPILE_OPTIONS = [["sources", "sources"], ["formatId", "format"]];
232
+ function fail(kind, message) {
233
+ throw new require_diagnostic_text.TweeTsError(`twee-ts ${kind} plugin: ${message}`, [], { code: "INVALID_OPTIONS" });
234
+ }
235
+ /** The oldest Vite the plugin works with (the `vite` peer dependency). */
236
+ const MINIMUM_VITE_MAJOR = 8;
237
+ /**
238
+ * Throws a TweeTsError (`INVALID_OPTIONS`) that names twee-ts and the Vite version when Vite is older than the
239
+ * plugin supports, instead of the obscure failure an older Vite gives (a missing `index.html`, say). A version
240
+ * that is not a SemVer version (a custom build) is accepted.
241
+ */
242
+ function checkViteVersion(viteVersion) {
243
+ const parsed = require_diagnostic_text.parseVersion(viteVersion);
244
+ if (parsed === null) return;
245
+ if (parsed.major < MINIMUM_VITE_MAJOR) fail("vite", `Vite ${viteVersion} is not supported; the plugin needs Vite ${MINIMUM_VITE_MAJOR} or newer. Upgrade Vite, or use an older twee-ts release for this Vite.`);
246
+ }
247
+ /**
248
+ * Characters a file name can't hold on every file system (Windows rejects
249
+ * `<>:"|?*`, the control characters and `\`), and those a URL path reads
250
+ * differently from the file name (`#`, `?`, `%`).
251
+ */
252
+ const UNPORTABLE = /[\u0000-\u001f\u007f<>:"|?*#%\\]/;
253
+ /** Names Windows reserves for devices, with or without an extension. */
254
+ const WINDOWS_DEVICE = /^(con|prn|aux|nul|com[0-9¹²³]|lpt[0-9¹²³])(\..*)?$/i;
255
+ /**
256
+ * Why `name` can't be an output file name, or undefined when it can. It must be
257
+ * a relative path: names separated by forward slashes, none of them empty, `.`
258
+ * or `..`, nor one a file system or a URL would read differently. The bundler
259
+ * writes it inside the output folder, and the dev server serves it at the same
260
+ * path under the base URL.
261
+ */
262
+ function outputFilenameProblem(name) {
263
+ if (name === "") return "it is empty";
264
+ if (UNPORTABLE.test(name)) return "it holds a character that is not portable in a file name or URL path";
265
+ if (name.startsWith("/")) return "it is an absolute path";
266
+ const segments = name.split("/");
267
+ if (segments.includes("")) return "it has an empty name between slashes, or ends with a slash";
268
+ if (segments.some((segment) => segment === "." || segment === "..")) return "it has a \".\" or \"..\" segment";
269
+ if (segments.some((segment) => segment.endsWith(".") || segment.endsWith(" "))) return "a name in it ends with \".\" or a space, which Windows drops";
270
+ if (segments.some((segment) => WINDOWS_DEVICE.test(segment))) return "a name in it is reserved on Windows";
271
+ }
272
+ function checkString(kind, value, name) {
273
+ if (value !== void 0 && (typeof value !== "string" || value === "")) fail(kind, `\`${name}\` must be a non-empty string.`);
274
+ }
275
+ function checkStringList(kind, value, name) {
276
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item === "")) fail(kind, `\`${name}\` must be an array of non-empty strings.`);
277
+ }
278
+ /**
279
+ * Checks the options as a JavaScript caller may pass them, whatever their type
280
+ * says: an object with only the accepted option names, each of the right type.
281
+ */
282
+ function checkOptions(kind, options) {
283
+ if (!require_diagnostic_text.isRecord(options)) fail(kind, "the options must be an object.");
284
+ const accepted = OPTION_NAMES[kind];
285
+ const unknown = Object.keys(options).filter((key) => !accepted.includes(key));
286
+ if (unknown.length > 0) fail(kind, `unknown option ${unknown.map((key) => `\`${key}\``).join(", ")}; the options are ${accepted.join(", ")}.`);
287
+ checkStringList(kind, options["sources"], "sources");
288
+ for (const name of [
289
+ "format",
290
+ "outputFilename",
291
+ "entry"
292
+ ]) checkString(kind, options[name], name);
293
+ const outputFilename = options["outputFilename"];
294
+ const problem = typeof outputFilename === "string" ? outputFilenameProblem(outputFilename) : void 0;
295
+ if (problem !== void 0) fail(kind, `\`outputFilename\` ${JSON.stringify(outputFilename)} can't be used: ${problem}.`);
296
+ const compileOptions = options["compileOptions"];
297
+ if (compileOptions === void 0) return;
298
+ if (!require_diagnostic_text.isRecord(compileOptions)) fail(kind, "`compileOptions` must be an object.");
299
+ for (const [key, own] of PLUGIN_SET_COMPILE_OPTIONS) if (Object.hasOwn(compileOptions, key)) fail(kind, `\`compileOptions.${key}\` is not accepted; set the plugin's \`${own}\` option instead.`);
300
+ const errors = require_diagnostic_text.compileOptionErrors(compileOptions, [], "compileOptions.");
301
+ if (errors.length > 0) fail(kind, errors.join(" "));
302
+ }
303
+ /** Absolute forward-slash path of a path given relative to the working directory. */
304
+ function absolute(path) {
305
+ return toPosix(require_diagnostic_text.absolutePathSpelling(path));
306
+ }
307
+ /**
308
+ * How the compiler spells `file` when it finds it by walking each source it is inside: the source
309
+ * as given, then the file's path below it. A watcher may report the real path of a file the sources
310
+ * (and so the exclude globs) reach through a link, which the globs would otherwise not match.
311
+ */
312
+ function spellingsUnderSources(file, sources) {
313
+ const real = require_diagnostic_text.identify(file).canonical;
314
+ return sources.flatMap((source) => {
315
+ const below = (0, node_path.relative)(require_diagnostic_text.identify(source).canonical, real);
316
+ return below === ".." || below.startsWith(`..${node_path.sep}`) || (0, node_path.isAbsolute)(below) ? [] : [`${absolute(source)}/${toPosix(below)}`];
317
+ });
318
+ }
319
+ /**
320
+ * Checks a plugin's options and resolves them. Throws a TweeTsError naming the
321
+ * option for anything outside what the plugin supports: an unknown option, a
322
+ * value of the wrong type, `compileOptions.sources` or `compileOptions.formatId`
323
+ * (the plugin's `sources` and `format` set them), or an output file name that
324
+ * is not a plain relative path.
325
+ */
326
+ function resolvePluginOptions(kind, options) {
327
+ checkOptions(kind, options);
328
+ const { sources, format, entry } = options;
329
+ const compileOptions = { ...options.compileOptions };
330
+ const { headFile, modules = [], exclude = [] } = compileOptions;
331
+ const notExcludable = [...headFile === void 0 ? [] : [headFile], ...modules].map(absolute);
332
+ return {
333
+ sources: [...sources],
334
+ format,
335
+ outputFilename: options.outputFilename ?? "index.html",
336
+ compileOptions,
337
+ entry: entry === void 0 ? void 0 : require_diagnostic_text.absolutePathSpelling(entry),
338
+ inputs: [...sources.map(absolute), ...notExcludable],
339
+ excluded: (file) => exclude.length > 0 && [file, ...spellingsUnderSources(file, sources)].some((spelling) => require_diagnostic_text.isExcluded(spelling, exclude)) && !notExcludable.some((path) => require_diagnostic_text.isSameOrInside(file, path)),
340
+ compile: (inline = []) => ({
341
+ ...compileOptions,
342
+ sources: [...sources, ...inline],
343
+ formatId: format
344
+ })
345
+ };
346
+ }
347
+ //#endregion
348
+ //#region src/plugins/watch-targets.ts
349
+ /**
350
+ * What a bundler's watch mode (`rollup --watch`, `vite build --watch`) is told
351
+ * to watch for the story, whose inputs the plugins read outside the module
352
+ * graph. Rollup's watcher and the Rolldown watcher of later Vite 8 releases
353
+ * watch a registered folder recursively; earlier ones see only the folder's own
354
+ * entries. So every path is registered on its own as well.
355
+ */
356
+ const NOTHING = {
357
+ targets: [],
358
+ whole: false
359
+ };
360
+ /**
361
+ * The paths to register for the inputs (forward-slash paths): every file under
362
+ * them that source discovery reads, but those `skip` returns true for, and no
363
+ * path the build writes (`outputs`). With `real` spelling, each link an input is reached through is registered as
364
+ * authored too, every hop of a chain of links (see linksOnTheWay), but those that lead to a folder holding an output.
365
+ *
366
+ * A folder is registered, with what it holds, only when watching it reports
367
+ * nothing the story leaves out: nothing below it is skipped (an `exclude`
368
+ * match), is an output or holds one, or is a link to a folder (which source
369
+ * discovery doesn't follow, and which may lead to the output). Of any other
370
+ * folder only what it holds is registered, minus those, so writing the outputs
371
+ * or editing an excluded file starts no build. A file added straight to such a folder is read at the next build
372
+ * something else starts. An input that can't be read is registered as it is
373
+ * (unless it is an output folder), so its creation may start a build; below an
374
+ * input, what can't be read is left out, as source discovery leaves it out.
375
+ */
376
+ function watchTargets(inputs, skip, outputs, spelling) {
377
+ return watchPlan(inputs, skip, outputs, spelling).targets;
378
+ }
379
+ /** What watchTargets registers for the inputs, and the links they are reached through. */
380
+ function watchPlan(inputs, skip, outputs, spelling) {
381
+ const links = /* @__PURE__ */ new Set();
382
+ /** Every link on the way to `path`, noted; those registered by real path are the ones that lead to no output. */
383
+ const hopsOf = (path) => {
384
+ const hops = linksOnTheWay(path);
385
+ for (const hop of hops) links.add(hop);
386
+ return hops.filter((hop) => !outputs.holds(require_diagnostic_text.realPathOf(hop))).map(toPosix);
387
+ };
388
+ const reals = /* @__PURE__ */ new Map();
389
+ const spell = (path, real, isRoot) => {
390
+ if (spelling === "real") return [toPosix(real), ...isRoot || isLink(path) ? hopsOf(path) : []];
391
+ if (!isRoot && isLink(path)) for (const hop of linksOnTheWay(path)) links.add(hop);
392
+ if (isLoop(path)) return [];
393
+ if (!isLoop(real)) reals.set(path, real);
394
+ return [path];
395
+ };
396
+ const walk = (path, real, isRoot) => {
397
+ if (outputs.isFile(real) || !isRoot && outputs.isDir(real)) return NOTHING;
398
+ let entry;
399
+ let names;
400
+ try {
401
+ entry = isRoot ? {
402
+ stat: (0, node_fs.statSync)(path),
403
+ real
404
+ } : require_diagnostic_text.walkedEntry(path, real);
405
+ if (entry === void 0 || outputs.isFile(entry.real)) return NOTHING;
406
+ if (!entry.stat.isDirectory()) return skip(path) ? NOTHING : {
407
+ targets: spell(path, entry.real, isRoot),
408
+ whole: true
409
+ };
410
+ names = (0, node_fs.readdirSync)(path);
411
+ } catch {
412
+ return isRoot && !outputs.isDir(real) ? {
413
+ targets: spell(path, real, isRoot),
414
+ whole: true
415
+ } : NOTHING;
416
+ }
417
+ const folder = entry.real;
418
+ const children = names.map((name) => walk(`${path}/${name}`, (0, node_path.join)(folder, name), false));
419
+ const held = children.flatMap((child) => child.targets);
420
+ const whole = !outputs.holds(folder) && children.every((child) => child.whole);
421
+ return whole ? {
422
+ targets: [...spell(path, folder, isRoot), ...held],
423
+ whole
424
+ } : {
425
+ targets: held,
426
+ whole
427
+ };
428
+ };
429
+ const targets = inputs.flatMap((input) => {
430
+ for (const link of linksOnTheWay(input)) links.add(link);
431
+ return [...spelling === "real" ? ancestorLinks(input, outputs) : [], ...walk(input, require_diagnostic_text.realPathOf(input), true).targets];
432
+ });
433
+ return {
434
+ targets: [...new Set(targets)],
435
+ links: [...links],
436
+ reals
437
+ };
438
+ }
439
+ /**
440
+ * The paths to register, besides the real paths of the files a bundle read, so that the build watch sees what
441
+ * changes how the entry's imports resolve: for each spelling (absolute) of an import that reaches a file through
442
+ * links, every link on the way (see linksOnTheWay), the links a link leads through too (replacing any of them changes
443
+ * the file the import reads); and for a spelling
444
+ * that reaches nothing, the nearest folder that exists, where creating the file shows (a bundle that failed for a
445
+ * missing import). A folder that holds a build output is left out, since the watcher would report what the build
446
+ * writes there and rebuild for ever: a file created straight in it is read with the next build something else starts.
447
+ */
448
+ function importWatchTargets(authored, outputs) {
449
+ const targets = /* @__PURE__ */ new Set();
450
+ const missing = [];
451
+ for (const spelling of authored) {
452
+ if ((0, node_fs.existsSync)(spelling)) {
453
+ if (toPosix(require_diagnostic_text.realPathOf(spelling)) === toPosix(spelling)) continue;
454
+ for (const link of linksOnTheWay(spelling)) if (!outputs.holds(require_diagnostic_text.realPathOf(link))) targets.add(toPosix(link));
455
+ continue;
456
+ }
457
+ missing.push(spelling);
458
+ }
459
+ for (const folder of missingImportFolders(missing)) if (!outputs.holds(require_diagnostic_text.realPathOf(folder))) targets.add(toPosix(folder));
460
+ return [...targets];
461
+ }
462
+ /**
463
+ * The nearest folder that exists, for each of the paths in `authored` that reaches nothing: where creating that
464
+ * file (or the folders above it) shows. Of a path that exists there is none, and neither is the root of a file
465
+ * system (an absolute import such as `/missing.js`, which Vite reads from the project root first), which no watcher
466
+ * is asked to watch whole.
467
+ */
468
+ function missingImportFolders(authored) {
469
+ const folders = /* @__PURE__ */ new Set();
470
+ for (const spelling of authored) {
471
+ if ((0, node_fs.existsSync)(spelling)) continue;
472
+ let folder = (0, node_path.dirname)(spelling);
473
+ while (!(0, node_fs.existsSync)(folder) && (0, node_path.dirname)(folder) !== folder) folder = (0, node_path.dirname)(folder);
474
+ if ((0, node_fs.existsSync)(folder) && (0, node_path.dirname)(folder) !== folder) folders.add(folder);
475
+ }
476
+ return [...folders];
477
+ }
478
+ /**
479
+ * The links among the folders above `path`, as authored (forward slashes), every hop of a chain, in the order they are
480
+ * followed. Replacing one of them changes what the input is, and only its own location sees that. A link directly in
481
+ * the root of the file system (`/var` on macOS) is the system's own alias and is left out, as is a link to a folder that
482
+ * holds a build output, since watching it whole would rebuild for what the build writes.
483
+ */
484
+ function ancestorLinks(path, outputs) {
485
+ return linksOnTheWay((0, node_path.dirname)(path)).filter((link) => !outputs.holds(require_diagnostic_text.realPathOf(link))).map(toPosix);
486
+ }
487
+ /** How many links the file system follows for one path before it gives up (ELOOP on Linux). */
488
+ const MAX_LINK_HOPS = 40;
489
+ /**
490
+ * Every link the file system follows to reach `path`, in the order it follows them: each link along the path, and
491
+ * along the target each one leads to, so every hop of a link to a link (a chain) is there. A link directly in the
492
+ * root of the file system (`/var` on macOS) is the system's own alias and is left out. The walk ends where a name
493
+ * reaches nothing, or after as many hops as the file system follows.
494
+ */
495
+ function linksOnTheWay(path) {
496
+ const links = [];
497
+ /** The names of a path or a link's target below its root; `..` is kept for the walk to take. */
498
+ const names = (text, root) => text.slice(root.length).split(node_path.sep === "\\" ? /[/\\]/ : "/").filter((name) => name !== "" && name !== ".");
499
+ const start = require_diagnostic_text.absolutePathSpelling(path);
500
+ let reached = (0, node_path.parse)(start).root;
501
+ let rest = names(start, reached);
502
+ for (let hops = 0; rest.length > 0 && hops <= MAX_LINK_HOPS;) {
503
+ const [name = "", ...more] = rest;
504
+ rest = more;
505
+ if (name === "..") {
506
+ reached = (0, node_path.dirname)(reached);
507
+ continue;
508
+ }
509
+ const next = (0, node_path.join)(reached, name);
510
+ let target;
511
+ try {
512
+ target = (0, node_fs.lstatSync)(next).isSymbolicLink() ? (0, node_fs.readlinkSync)(next) : void 0;
513
+ } catch {
514
+ break;
515
+ }
516
+ if (target === void 0) {
517
+ reached = next;
518
+ continue;
519
+ }
520
+ hops += 1;
521
+ if ((0, node_path.dirname)(next) !== (0, node_path.dirname)((0, node_path.dirname)(next))) links.push(next);
522
+ const root = (0, node_path.isAbsolute)(target) ? (0, node_path.parse)(target).root : "";
523
+ if (root !== "") reached = root;
524
+ rest = [...names(target, root), ...rest];
525
+ }
526
+ return links;
527
+ }
528
+ /** Whether `path` is a loop of links, which the file system gives up following (ELOOP). */
529
+ function isLoop(path) {
530
+ try {
531
+ (0, node_fs.statSync)(path);
532
+ return false;
533
+ } catch (error) {
534
+ return error instanceof Error && Reflect.get(error, "code") === "ELOOP";
535
+ }
536
+ }
537
+ /** Whether `path` is a symbolic link (or a Windows junction); a path that can't be read is none. */
538
+ function isLink(path) {
539
+ try {
540
+ return (0, node_fs.lstatSync)(path).isSymbolicLink();
541
+ } catch {
542
+ return false;
543
+ }
544
+ }
545
+ //#endregion
546
+ Object.defineProperty(exports, "canonicalPath", {
547
+ enumerable: true,
548
+ get: function() {
549
+ return canonicalPath;
550
+ }
551
+ });
552
+ Object.defineProperty(exports, "checkViteVersion", {
553
+ enumerable: true,
554
+ get: function() {
555
+ return checkViteVersion;
556
+ }
557
+ });
558
+ Object.defineProperty(exports, "compileStory", {
559
+ enumerable: true,
560
+ get: function() {
561
+ return compileStory;
562
+ }
563
+ });
564
+ Object.defineProperty(exports, "createOutputRecord", {
565
+ enumerable: true,
566
+ get: function() {
567
+ return createOutputRecord;
568
+ }
569
+ });
570
+ Object.defineProperty(exports, "emittedFilePath", {
571
+ enumerable: true,
572
+ get: function() {
573
+ return emittedFilePath;
574
+ }
575
+ });
576
+ Object.defineProperty(exports, "fatalError", {
577
+ enumerable: true,
578
+ get: function() {
579
+ return fatalError;
580
+ }
581
+ });
582
+ Object.defineProperty(exports, "fileKey", {
583
+ enumerable: true,
584
+ get: function() {
585
+ return fileKey;
586
+ }
587
+ });
588
+ Object.defineProperty(exports, "importWatchTargets", {
589
+ enumerable: true,
590
+ get: function() {
591
+ return importWatchTargets;
592
+ }
593
+ });
594
+ Object.defineProperty(exports, "isViteConfigTemp", {
595
+ enumerable: true,
596
+ get: function() {
597
+ return isViteConfigTemp;
598
+ }
599
+ });
600
+ Object.defineProperty(exports, "keyWithin", {
601
+ enumerable: true,
602
+ get: function() {
603
+ return keyWithin;
604
+ }
605
+ });
606
+ Object.defineProperty(exports, "missingImportFolders", {
607
+ enumerable: true,
608
+ get: function() {
609
+ return missingImportFolders;
610
+ }
611
+ });
612
+ Object.defineProperty(exports, "outputLocations", {
613
+ enumerable: true,
614
+ get: function() {
615
+ return outputLocations;
616
+ }
617
+ });
618
+ Object.defineProperty(exports, "resolvePluginOptions", {
619
+ enumerable: true,
620
+ get: function() {
621
+ return resolvePluginOptions;
622
+ }
623
+ });
624
+ Object.defineProperty(exports, "toPosix", {
625
+ enumerable: true,
626
+ get: function() {
627
+ return toPosix;
628
+ }
629
+ });
630
+ Object.defineProperty(exports, "watchPlan", {
631
+ enumerable: true,
632
+ get: function() {
633
+ return watchPlan;
634
+ }
635
+ });
636
+ Object.defineProperty(exports, "watchTargets", {
637
+ enumerable: true,
638
+ get: function() {
639
+ return watchTargets;
640
+ }
641
+ });
642
+
643
+ //# sourceMappingURL=watch-targets-BrEYmUwU.cjs.map