@rohal12/twee-ts 2.0.0 → 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.
- package/dist/bin/twee-ts.js +47 -32
- package/dist/bin/twee-ts.js.map +1 -1
- package/dist/{diagnostic-text-BRddVulf.cjs → diagnostic-text-Bj-UQ0Jl.cjs} +4283 -3739
- package/dist/diagnostic-text-Bj-UQ0Jl.cjs.map +1 -0
- package/dist/{diagnostic-text-CebYCTPQ.js → diagnostic-text-DMVZfBh9.js} +4260 -3770
- package/dist/diagnostic-text-DMVZfBh9.js.map +1 -0
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +3 -3
- package/dist/{lint-VayZI_jp.js → lint-BkpfLGuP.js} +3 -3
- package/dist/{lint-VayZI_jp.js.map → lint-BkpfLGuP.js.map} +1 -1
- package/dist/plugins/rollup.cjs +9 -4
- package/dist/plugins/rollup.cjs.map +1 -1
- package/dist/plugins/rollup.d.cts +6 -0
- package/dist/plugins/rollup.d.ts +6 -0
- package/dist/plugins/rollup.js +9 -4
- package/dist/plugins/rollup.js.map +1 -1
- package/dist/plugins/vite.cjs +453 -154
- package/dist/plugins/vite.cjs.map +1 -1
- package/dist/plugins/vite.js +453 -154
- package/dist/plugins/vite.js.map +1 -1
- package/dist/{watch-targets-C0Anuw17.cjs → watch-targets-BrEYmUwU.cjs} +84 -16
- package/dist/watch-targets-BrEYmUwU.cjs.map +1 -0
- package/dist/{watch-targets-BI7jYrwa.js → watch-targets-DzvPyWDo.js} +79 -17
- package/dist/watch-targets-DzvPyWDo.js.map +1 -0
- package/package.json +2 -2
- package/dist/diagnostic-text-BRddVulf.cjs.map +0 -1
- package/dist/diagnostic-text-CebYCTPQ.js.map +0 -1
- package/dist/watch-targets-BI7jYrwa.js.map +0 -1
- package/dist/watch-targets-C0Anuw17.cjs.map +0 -1
package/dist/plugins/vite.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/*! @rohal12/twee-ts (Unlicense). Bundles third-party code under its own licence: see THIRD_PARTY_NOTICES. */
|
|
2
|
-
import { Ct as
|
|
3
|
-
import { a as checkViteVersion, c as createOutputRecord, d as
|
|
2
|
+
import { At as lineColumnFinder, Ct as viteWaitingPage, Dt as AcornParser, Et as javaScriptContexts, G as recordOwnOutput, Mt as isRecord, Ot as javaScriptNodes, St as insertViteClient, Tt as cssContexts, U as sha256Hex, at as outputPaths, dt as isSameOrInside, ft as mediaTypeFromFilename, jt as types$1, kt as trySyntax, lt as identify, nt as getFilenames, pt as normalizedFileExt } from "../diagnostic-text-DMVZfBh9.js";
|
|
3
|
+
import { a as checkViteVersion, c as createOutputRecord, d as isViteConfigTemp, f as keyWithin, g as fatalError, h as compileStory, i as watchTargets, l as emittedFilePath, m as toPosix, n as missingImportFolders, o as resolvePluginOptions, p as outputLocations, s as canonicalPath, t as importWatchTargets, u as fileKey } from "../watch-targets-DzvPyWDo.js";
|
|
4
|
+
import { findPackageJSON } from "node:module";
|
|
4
5
|
import { basename, dirname, isAbsolute, join, posix, relative, resolve, win32 } from "node:path";
|
|
5
6
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
6
7
|
import { stripVTControlCharacters } from "node:util";
|
|
8
|
+
import { pathToFileURL } from "node:url";
|
|
7
9
|
import { build, createIdResolver, loadConfigFromFile, mergeConfig, version } from "vite";
|
|
8
10
|
//#region src/plugins/vite-glob.ts
|
|
9
11
|
/**
|
|
@@ -14,8 +16,8 @@ import { build, createIdResolver, loadConfigFromFile, mergeConfig, version } fro
|
|
|
14
16
|
*
|
|
15
17
|
* A scope is a superset of what the glob matches: a file added to it or removed from it may not match (a negated
|
|
16
18
|
* pattern, a name the wildcards leave out), which only bundles again for nothing. The calls are read with acorn as Vite reads them: patterns are
|
|
17
|
-
* string literals, template literals without expressions, or arrays of those
|
|
18
|
-
*
|
|
19
|
+
* string literals, template literals without expressions, or arrays of those. Options are static literal data,
|
|
20
|
+
* including computed keys and repeated members; expressions outside that subset are diagnosed, never evaluated.
|
|
19
21
|
*
|
|
20
22
|
* Code that is a module is read whole, so only real calls count. Code that is not (yet) one, as a plugin may hand
|
|
21
23
|
* on a component file or TypeScript it transforms later, is read the way Vite's import-glob reads every module:
|
|
@@ -32,18 +34,6 @@ const MODULE_OPTIONS = {
|
|
|
32
34
|
function mayHoldGlob(code) {
|
|
33
35
|
return code.includes("import.meta.glob");
|
|
34
36
|
}
|
|
35
|
-
function isNode(value) {
|
|
36
|
-
return isRecord(value) && typeof value["type"] === "string";
|
|
37
|
-
}
|
|
38
|
-
/** Every node below `root`, `root` first, in source order. */
|
|
39
|
-
function* nodesOf(root) {
|
|
40
|
-
const stack = [root];
|
|
41
|
-
for (let value = stack.pop(); value !== void 0; value = stack.pop()) {
|
|
42
|
-
if (isNode(value)) yield value;
|
|
43
|
-
const children = Array.isArray(value) ? value : isNode(value) ? Object.values(value) : [];
|
|
44
|
-
pushAll(stack, children.toReversed());
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
37
|
/** Whether `node` is the callee `import.meta.glob`. */
|
|
48
38
|
function isGlobCallee(node) {
|
|
49
39
|
return node.type === "MemberExpression" && !node.computed && node.property.type === "Identifier" && node.property.name === "glob" && node.object.type === "MetaProperty" && node.object.meta.name === "import" && node.object.property.name === "meta";
|
|
@@ -54,24 +44,62 @@ function stringOf(node) {
|
|
|
54
44
|
if (node.type === "Literal") return typeof node.value === "string" ? node.value : void 0;
|
|
55
45
|
if (node.type === "TemplateLiteral" && node.expressions.length === 0) return node.quasis[0]?.value.raw;
|
|
56
46
|
}
|
|
57
|
-
/**
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
47
|
+
/** An expression outside the static data subset: never run it or guess the folder it selects. */
|
|
48
|
+
const UNREADABLE = Symbol("unreadable");
|
|
49
|
+
/** Static option data, with JavaScript's cooked strings, computed keys and last-member precedence. */
|
|
50
|
+
function staticValue(node, depth = 0) {
|
|
51
|
+
if (depth > 100) return UNREADABLE;
|
|
52
|
+
switch (node.type) {
|
|
53
|
+
case "Literal": return node.regex === void 0 && node.bigint === void 0 ? node.value : UNREADABLE;
|
|
54
|
+
case "TemplateLiteral": return node.expressions.length === 0 ? node.quasis[0]?.value.cooked ?? UNREADABLE : UNREADABLE;
|
|
55
|
+
case "UnaryExpression": {
|
|
56
|
+
const value = staticValue(node.argument, depth + 1);
|
|
57
|
+
if (value === UNREADABLE) return UNREADABLE;
|
|
58
|
+
if (node.operator === "!") return !value;
|
|
59
|
+
if (typeof value === "number") {
|
|
60
|
+
if (node.operator === "-") return -value;
|
|
61
|
+
if (node.operator === "+") return value;
|
|
62
|
+
}
|
|
63
|
+
return UNREADABLE;
|
|
64
|
+
}
|
|
65
|
+
case "ArrayExpression": {
|
|
66
|
+
const values = [];
|
|
67
|
+
for (const element of node.elements) {
|
|
68
|
+
if (element === null || element.type === "SpreadElement") return UNREADABLE;
|
|
69
|
+
const value = staticValue(element, depth + 1);
|
|
70
|
+
if (value === UNREADABLE) return UNREADABLE;
|
|
71
|
+
values.push(value);
|
|
72
|
+
}
|
|
73
|
+
return values;
|
|
74
|
+
}
|
|
75
|
+
case "ObjectExpression": {
|
|
76
|
+
const entries = /* @__PURE__ */ new Map();
|
|
77
|
+
for (const property of node.properties) {
|
|
78
|
+
if (property.type === "SpreadElement") return UNREADABLE;
|
|
79
|
+
if (property.kind !== "init" || property.method || property.shorthand) return UNREADABLE;
|
|
80
|
+
if (property.computed && property.key.type !== "Literal") return UNREADABLE;
|
|
81
|
+
const key = !property.computed && property.key.type === "Identifier" ? property.key.name : staticValue(property.key, depth + 1);
|
|
82
|
+
const value = staticValue(property.value, depth + 1);
|
|
83
|
+
if (depth === 0 && key === "exhaustive" && property.value.type !== "Literal") return UNREADABLE;
|
|
84
|
+
if (key !== null && typeof key !== "string" && typeof key !== "number" && typeof key !== "boolean" || value === UNREADABLE || !property.computed && key === "__proto__") return UNREADABLE;
|
|
85
|
+
entries.set(String(key), value);
|
|
86
|
+
}
|
|
87
|
+
return Object.fromEntries(entries);
|
|
88
|
+
}
|
|
89
|
+
default: return UNREADABLE;
|
|
63
90
|
}
|
|
64
91
|
}
|
|
65
92
|
/** The call a glob call's arguments make. */
|
|
66
93
|
function callOf(args) {
|
|
67
94
|
const [first, second] = args;
|
|
68
95
|
const patterns = (first === void 0 ? [] : first.type === "ArrayExpression" ? first.elements : [first]).map(stringOf).filter((pattern) => pattern !== void 0);
|
|
69
|
-
const options = second
|
|
70
|
-
|
|
96
|
+
const options = second === void 0 ? {} : second.type === "SpreadElement" ? UNREADABLE : staticValue(second);
|
|
97
|
+
if (options === UNREADABLE) return void 0;
|
|
98
|
+
const base = isRecord(options) ? options["base"] : void 0;
|
|
71
99
|
return {
|
|
72
100
|
patterns,
|
|
73
101
|
base: typeof base === "string" ? base : void 0,
|
|
74
|
-
exhaustive:
|
|
102
|
+
exhaustive: isRecord(options) && options["exhaustive"] === true
|
|
75
103
|
};
|
|
76
104
|
}
|
|
77
105
|
/** Where Vite's import-glob finds a call in a module's text: the callee, any TypeScript type arguments, `(`. */
|
|
@@ -81,13 +109,26 @@ const GLOB_CALL_START = /\bimport\.meta\.glob(?:<\w+>)?\s*\(/g;
|
|
|
81
109
|
* there: past strings, template literals, regular expressions and comments that hold parentheses. Undefined when the
|
|
82
110
|
* list does not close, or the text cannot be tokenized.
|
|
83
111
|
*/
|
|
84
|
-
function argumentsEnd(code, paren) {
|
|
112
|
+
function argumentsEnd(code, paren, budget) {
|
|
113
|
+
const tokenizer = Object.assign(AcornParser.tokenizer("", MODULE_OPTIONS), {
|
|
114
|
+
input: code,
|
|
115
|
+
pos: paren
|
|
116
|
+
});
|
|
85
117
|
const read = trySyntax(() => {
|
|
86
118
|
let depth = 0;
|
|
87
|
-
for (
|
|
119
|
+
for (;;) {
|
|
120
|
+
const from = tokenizer.pos;
|
|
121
|
+
let token;
|
|
122
|
+
try {
|
|
123
|
+
token = tokenizer.getToken();
|
|
124
|
+
} finally {
|
|
125
|
+
const searched = token === void 0 || token.type === types$1.invalidTemplate ? code.length : tokenizer.pos;
|
|
126
|
+
budget.remaining -= Math.max(1, searched - from);
|
|
127
|
+
}
|
|
128
|
+
if (budget.remaining < 0 || token.type === types$1.eof) return void 0;
|
|
88
129
|
if (token.type === types$1.parenL) depth += 1;
|
|
89
130
|
else if (token.type === types$1.parenR) depth -= 1;
|
|
90
|
-
if (depth === 0) return
|
|
131
|
+
if (depth === 0) return token.end;
|
|
91
132
|
}
|
|
92
133
|
});
|
|
93
134
|
return read.ok ? read.value : void 0;
|
|
@@ -102,9 +143,11 @@ function wholeExpression(text, options) {
|
|
|
102
143
|
* of its argument list: as a call expression, or, with type arguments between the two (which JavaScript reads as
|
|
103
144
|
* comparisons), as its argument list in parentheses. Undefined when acorn reads neither.
|
|
104
145
|
*/
|
|
105
|
-
function callAt(code, start, paren) {
|
|
106
|
-
const end = argumentsEnd(code, paren);
|
|
146
|
+
function callAt(code, start, paren, budget) {
|
|
147
|
+
const end = argumentsEnd(code, paren, budget);
|
|
107
148
|
if (end === void 0) return void 0;
|
|
149
|
+
budget.remaining -= end - start + end - paren;
|
|
150
|
+
if (budget.remaining < 0) return void 0;
|
|
108
151
|
const call = wholeExpression(code.slice(start, end), MODULE_OPTIONS);
|
|
109
152
|
if (call?.type === "CallExpression" && isGlobCallee(call.callee)) return callOf(call.arguments);
|
|
110
153
|
const list = wholeExpression(code.slice(paren, end), {
|
|
@@ -118,31 +161,46 @@ function callAt(code, start, paren) {
|
|
|
118
161
|
/** The glob calls of a module's code: read whole when it is a module, else each call on its own. */
|
|
119
162
|
function globCalls(code) {
|
|
120
163
|
const parsed = trySyntax(() => AcornParser.parse(code, MODULE_OPTIONS));
|
|
121
|
-
if (parsed.ok)
|
|
122
|
-
calls
|
|
123
|
-
unreadable
|
|
124
|
-
|
|
164
|
+
if (parsed.ok) {
|
|
165
|
+
const calls = [];
|
|
166
|
+
const unreadable = [];
|
|
167
|
+
for (const node of javaScriptNodes(parsed.value)) {
|
|
168
|
+
if (node.type !== "CallExpression" || !isGlobCallee(node.callee)) continue;
|
|
169
|
+
const call = callOf(node.arguments);
|
|
170
|
+
if (call === void 0) unreadable.push(node.start);
|
|
171
|
+
else calls.push(call);
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
calls,
|
|
175
|
+
unreadable
|
|
176
|
+
};
|
|
177
|
+
}
|
|
125
178
|
const calls = [];
|
|
126
179
|
const unreadable = [];
|
|
180
|
+
const budget = { remaining: code.length * 16 };
|
|
127
181
|
for (const match of code.matchAll(GLOB_CALL_START)) {
|
|
128
|
-
const call = callAt(code, match.index, match.index + match[0].length - 1);
|
|
182
|
+
const call = callAt(code, match.index, match.index + match[0].length - 1, budget);
|
|
129
183
|
if (call === void 0) unreadable.push(match.index);
|
|
130
184
|
else calls.push(call);
|
|
185
|
+
if (budget.remaining < 0) return {
|
|
186
|
+
calls,
|
|
187
|
+
unreadable,
|
|
188
|
+
recoveryLimited: true
|
|
189
|
+
};
|
|
131
190
|
}
|
|
132
191
|
return {
|
|
133
192
|
calls,
|
|
134
193
|
unreadable
|
|
135
194
|
};
|
|
136
195
|
}
|
|
137
|
-
/** Where `offset` is in `code`, as `line:column`, both from 1. */
|
|
138
|
-
function lineColumn(code, offset) {
|
|
139
|
-
const lines = code.slice(0, offset).split("\n");
|
|
140
|
-
return `${String(lines.length)}:${String((lines.at(-1)?.length ?? 0) + 1)}`;
|
|
141
|
-
}
|
|
142
196
|
/** Characters that make a segment of a pattern match more than its own name (picomatch's, and its escape). */
|
|
143
197
|
const MAGIC = /[*?[\]{}()!+@\\]/;
|
|
144
|
-
/** The characters
|
|
145
|
-
|
|
198
|
+
/** The characters after the last MAGIC one, found without retrying each literal prefix as a regex start. */
|
|
199
|
+
function literalEnd(segment) {
|
|
200
|
+
let start = segment.length;
|
|
201
|
+
while (start > 0 && !MAGIC.test(segment.charAt(start - 1))) start -= 1;
|
|
202
|
+
return segment.slice(start).toLowerCase();
|
|
203
|
+
}
|
|
146
204
|
/** Whether a pattern's segments may match a name starting with a dot, which a wildcard alone never does. */
|
|
147
205
|
function namesDot(segments) {
|
|
148
206
|
return segments.some((segment) => segment !== ".." && /(?:^|[{,])\./.test(segment));
|
|
@@ -162,7 +220,7 @@ function scopeOf(dir, rest, exhaustive) {
|
|
|
162
220
|
deep: below.length > 1 || below.some((segment) => segment.includes("**")),
|
|
163
221
|
dot: exhaustive || namesDot(below),
|
|
164
222
|
exhaustive,
|
|
165
|
-
suffix: (
|
|
223
|
+
suffix: literalEnd(below.at(-1) ?? "")
|
|
166
224
|
};
|
|
167
225
|
}
|
|
168
226
|
/**
|
|
@@ -285,7 +343,7 @@ function scopeWatchTargets(scopes) {
|
|
|
285
343
|
async function resolveGlob(context, pattern, importer) {
|
|
286
344
|
const isSubImportsPattern = pattern.startsWith("#") && pattern.includes("*");
|
|
287
345
|
try {
|
|
288
|
-
return (await context.resolve(pattern, importer, { custom: { "vite:import-glob": { isSubImportsPattern } } }))?.id
|
|
346
|
+
return (await context.resolve(pattern, importer, { custom: { "vite:import-glob": { isSubImportsPattern } } }))?.id;
|
|
289
347
|
} catch {
|
|
290
348
|
return;
|
|
291
349
|
}
|
|
@@ -297,10 +355,14 @@ async function resolveGlob(context, pattern, importer) {
|
|
|
297
355
|
*/
|
|
298
356
|
async function moduleGlobScopes(context, code, id, root) {
|
|
299
357
|
if (!mayHoldGlob(code)) return [];
|
|
300
|
-
const { calls, unreadable } = globCalls(code);
|
|
358
|
+
const { calls, unreadable, recoveryLimited } = globCalls(code);
|
|
301
359
|
if (unreadable.length > 0) {
|
|
302
|
-
const
|
|
303
|
-
|
|
360
|
+
const locate = lineColumnFinder(code);
|
|
361
|
+
const where = unreadable.map((offset) => {
|
|
362
|
+
const { line, column } = locate(offset);
|
|
363
|
+
return `${String(line)}:${String(column)}`;
|
|
364
|
+
}).join(", ");
|
|
365
|
+
context.warn(`twee-ts could not read the import.meta.glob() call at ${where} of ${id}; ` + (recoveryLimited === true ? "recovery work limit reached; remaining calls were not read; " : "") + "files added to its folders are bundled the next time something else changes.");
|
|
304
366
|
}
|
|
305
367
|
const file = id.replace(/[?#].*$/s, "");
|
|
306
368
|
const importer = isAbsolute(file) && !id.startsWith("\0") ? file : void 0;
|
|
@@ -422,8 +484,24 @@ function entryInputSettings(inputName, entryPath, userOutput, outputFilename) {
|
|
|
422
484
|
};
|
|
423
485
|
}
|
|
424
486
|
/** Removes a trailing source-map comment that points at a file (a data: URL map stays). */
|
|
425
|
-
function dropFileSourceMapComment(code) {
|
|
426
|
-
|
|
487
|
+
function dropFileSourceMapComment(code, language) {
|
|
488
|
+
const end = code.trimEnd().length;
|
|
489
|
+
const start = Math.max(language === "js" ? code.lastIndexOf("//# sourceMappingURL=", end) : -1, code.lastIndexOf("/*# sourceMappingURL=", end));
|
|
490
|
+
if (start === -1) return code;
|
|
491
|
+
const candidate = code.slice(start, end);
|
|
492
|
+
if (!/^(?:\/\/# sourceMappingURL=(?!data:)\S*|\/\*# sourceMappingURL=(?!data:)[^*]*\*\/)$/u.test(candidate)) return code;
|
|
493
|
+
const ranges = [{
|
|
494
|
+
start,
|
|
495
|
+
end
|
|
496
|
+
}, {
|
|
497
|
+
start: Math.max(0, start - 1),
|
|
498
|
+
end
|
|
499
|
+
}];
|
|
500
|
+
const [at, before] = language === "js" ? javaScriptContexts(code, ranges) : cssContexts(code, ranges);
|
|
501
|
+
const kind = at?.context.kind;
|
|
502
|
+
if (kind !== "line-comment" && kind !== "block-comment" && kind !== "comment") return code;
|
|
503
|
+
if (start > 0 && before?.context.kind === kind) return code;
|
|
504
|
+
return code.slice(0, code[start - 1] === "\n" ? start - 1 : start);
|
|
427
505
|
}
|
|
428
506
|
/** A module id or watched file as a forward-slash file path, without a query or hash (like Vite's cleanUrl). */
|
|
429
507
|
function fileOfId(id) {
|
|
@@ -458,14 +536,14 @@ function takeEntryFromBundle(bundle, inputName) {
|
|
|
458
536
|
const styles = [];
|
|
459
537
|
for (const [fileName, item] of Object.entries(bundle)) if (item.type === "chunk") {
|
|
460
538
|
if (!item.isEntry || inputName !== void 0 && item.name !== inputName) continue;
|
|
461
|
-
script = dropFileSourceMapComment(item.code);
|
|
539
|
+
script = dropFileSourceMapComment(item.code, "js");
|
|
462
540
|
for (const id of item.moduleIds) if (!id.startsWith("\0")) files.add(fileOfId(id));
|
|
463
541
|
removeFromBundle(bundle, fileName);
|
|
464
542
|
} else if (fileName.endsWith(".css") && !isImportedFile(item)) {
|
|
465
543
|
const css = typeof item.source === "string" ? item.source : new TextDecoder().decode(item.source);
|
|
466
|
-
styles.push(dropFileSourceMapComment(css));
|
|
544
|
+
styles.push(dropFileSourceMapComment(css, "css"));
|
|
467
545
|
removeFromBundle(bundle, fileName);
|
|
468
|
-
} else if (fileName.endsWith(".map")) removeFromBundle(bundle, fileName);
|
|
546
|
+
} else if (fileName.endsWith(".map") && (item.originalFileNames?.length ?? 0) === 0) removeFromBundle(bundle, fileName);
|
|
469
547
|
else assets.set(fileName, item.source);
|
|
470
548
|
return {
|
|
471
549
|
script,
|
|
@@ -567,31 +645,69 @@ function appliesTo(plugin, command, config, env) {
|
|
|
567
645
|
/**
|
|
568
646
|
* `plugin` as the entry build runs it: without `apply` (already decided by
|
|
569
647
|
* appliesTo), and, for the dev command, with its `config`, `configEnvironment`
|
|
570
|
-
* and `configResolved` hooks
|
|
571
|
-
*
|
|
648
|
+
* and `configResolved` hooks, environment selection and hook contexts seeing
|
|
649
|
+
* the dev command. Vite's own build plugins still see build. The plugin object itself is left untouched.
|
|
572
650
|
*/
|
|
573
|
-
function asEntryBuildPlugin(plugin, command) {
|
|
651
|
+
function asEntryBuildPlugin(plugin, command, views) {
|
|
574
652
|
const overrides = /* @__PURE__ */ new Map([["apply", void 0]]);
|
|
575
653
|
if (command === "serve") {
|
|
576
654
|
const devEnv = (env) => Object.assign({}, env, {
|
|
577
655
|
command,
|
|
578
656
|
isSsrBuild: false
|
|
579
657
|
});
|
|
580
|
-
const
|
|
581
|
-
|
|
658
|
+
const devConfig = (config) => {
|
|
659
|
+
if (!isRecord(config)) return config;
|
|
660
|
+
let view = views.configs.get(config);
|
|
661
|
+
if (view === void 0) {
|
|
662
|
+
view = withOverrides(config, /* @__PURE__ */ new Map([["command", command]]));
|
|
663
|
+
views.configs.set(config, view);
|
|
664
|
+
}
|
|
665
|
+
return view;
|
|
666
|
+
};
|
|
667
|
+
const devEnvironment = (environment) => {
|
|
668
|
+
if (!isRecord(environment)) return environment;
|
|
669
|
+
const known = views.environments.get(environment);
|
|
670
|
+
if (known !== void 0) return known;
|
|
671
|
+
const overrides = /* @__PURE__ */ new Map([["config", devConfig(environment["config"])]]);
|
|
672
|
+
const topLevel = environment["getTopLevelConfig"];
|
|
673
|
+
if (typeof topLevel === "function") overrides.set("getTopLevelConfig", () => devConfig(Reflect.apply(topLevel, environment, [])));
|
|
674
|
+
const view = withOverrides(environment, overrides);
|
|
675
|
+
views.environments.set(environment, view);
|
|
676
|
+
return view;
|
|
677
|
+
};
|
|
678
|
+
const devContext = (context) => {
|
|
679
|
+
if (!isRecord(context) || context["environment"] === void 0) return context;
|
|
680
|
+
const environment = devEnvironment(context["environment"]);
|
|
681
|
+
return new Proxy(context, { get(target, key) {
|
|
682
|
+
if (key === "environment") return environment;
|
|
683
|
+
const value = Reflect.get(target, key, target);
|
|
684
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
685
|
+
} });
|
|
686
|
+
};
|
|
687
|
+
const wrap = (name, patch = (args) => args) => {
|
|
688
|
+
const hook = Reflect.get(plugin, name);
|
|
582
689
|
const handler = hookHandler(hook);
|
|
583
690
|
if (typeof handler !== "function") return;
|
|
584
691
|
overrides.set(name, withHandler(hook, function(...args) {
|
|
585
|
-
return Reflect.apply(handler, this, patch(args));
|
|
692
|
+
return Reflect.apply(handler, devContext(this), patch(args));
|
|
586
693
|
}));
|
|
587
694
|
};
|
|
695
|
+
const keys = /* @__PURE__ */ new Set();
|
|
696
|
+
for (let object = plugin; object !== null && object !== Object.prototype; object = Reflect.getPrototypeOf(object)) for (const key of Reflect.ownKeys(object)) keys.add(key);
|
|
697
|
+
for (const key of keys) if (key !== "constructor" && key !== "apply" && key !== "applyToEnvironment") wrap(key);
|
|
588
698
|
wrap("config", ([config, env]) => [config, devEnv(env)]);
|
|
589
699
|
wrap("configEnvironment", ([name, config, env]) => [
|
|
590
700
|
name,
|
|
591
701
|
config,
|
|
592
702
|
devEnv(env)
|
|
593
703
|
]);
|
|
594
|
-
wrap("configResolved", ([config]) => [
|
|
704
|
+
wrap("configResolved", ([config]) => [devConfig(config)]);
|
|
705
|
+
const select = plugin.applyToEnvironment;
|
|
706
|
+
if (select !== void 0) overrides.set("applyToEnvironment", async function(environment) {
|
|
707
|
+
const result = await Reflect.apply(select, this, [devEnvironment(environment)]);
|
|
708
|
+
if (result === true || !result) return result;
|
|
709
|
+
return (await flattenPlugins(result)).filter(isPlugin).map((selected) => asEntryBuildPlugin(selected, command, views));
|
|
710
|
+
});
|
|
595
711
|
}
|
|
596
712
|
return pluginWith(plugin, overrides);
|
|
597
713
|
}
|
|
@@ -696,7 +812,7 @@ function entryBuildEnforcer(entryPath, command, outputFilename) {
|
|
|
696
812
|
}
|
|
697
813
|
};
|
|
698
814
|
}
|
|
699
|
-
/**
|
|
815
|
+
/** Plugin hooks that may read an entry input and register it with `this.addWatchFile` before the story is emitted. */
|
|
700
816
|
const WATCH_FILE_HOOKS = [
|
|
701
817
|
"buildStart",
|
|
702
818
|
"resolveId",
|
|
@@ -704,7 +820,11 @@ const WATCH_FILE_HOOKS = [
|
|
|
704
820
|
"load",
|
|
705
821
|
"transform",
|
|
706
822
|
"moduleParsed",
|
|
707
|
-
"buildEnd"
|
|
823
|
+
"buildEnd",
|
|
824
|
+
"renderStart",
|
|
825
|
+
"renderChunk",
|
|
826
|
+
"augmentChunkHash",
|
|
827
|
+
"generateBundle"
|
|
708
828
|
];
|
|
709
829
|
/** A plugin context whose `addWatchFile` also passes the file to `record`. */
|
|
710
830
|
function recordingContext(context, record) {
|
|
@@ -713,7 +833,7 @@ function recordingContext(context, record) {
|
|
|
713
833
|
if (typeof value !== "function") return value;
|
|
714
834
|
if (key !== "addWatchFile") return value.bind(target);
|
|
715
835
|
return (id) => {
|
|
716
|
-
if (typeof id === "string" && !id.startsWith("\0")) record(
|
|
836
|
+
if (typeof id === "string" && !id.startsWith("\0")) record(resolve(id));
|
|
717
837
|
return Reflect.apply(value, target, [id]);
|
|
718
838
|
};
|
|
719
839
|
} });
|
|
@@ -724,13 +844,14 @@ function recordingContext(context, record) {
|
|
|
724
844
|
* never collects wrappers. A plugin without such hooks (a built-in one, for
|
|
725
845
|
* instance) is returned as it is.
|
|
726
846
|
*/
|
|
727
|
-
function recordingPlugin(plugin,
|
|
847
|
+
function recordingPlugin(plugin, recordFor) {
|
|
728
848
|
const overrides = /* @__PURE__ */ new Map();
|
|
729
849
|
for (const name of WATCH_FILE_HOOKS) {
|
|
730
850
|
const hook = plugin[name];
|
|
731
851
|
const handler = hookHandler(hook);
|
|
732
852
|
if (typeof handler !== "function") continue;
|
|
733
853
|
overrides.set(name, withHandler(hook, function(...args) {
|
|
854
|
+
const record = recordFor(name, args);
|
|
734
855
|
const context = typeof this === "object" && this !== null ? recordingContext(this, record) : this;
|
|
735
856
|
return Reflect.apply(handler, context, args);
|
|
736
857
|
}));
|
|
@@ -784,6 +905,45 @@ function writtenPaths(source, importer, root) {
|
|
|
784
905
|
return from !== void 0 && isAbsolute(from) && /^\.\.?(?:[/\\]|$)/.test(source) ? [fileOfId(resolve(dirname(from), source))] : [];
|
|
785
906
|
}
|
|
786
907
|
/**
|
|
908
|
+
* Package locations Node's upward node_modules search can use for an unresolved bare import. These are recovery
|
|
909
|
+
* inputs only: Vite still decides aliases, exports and the actual module. Watching the package itself sees a
|
|
910
|
+
* restored manifest, export target or nested subpath without watching every installed package recursively.
|
|
911
|
+
*/
|
|
912
|
+
function missingPackagePaths(source, importer) {
|
|
913
|
+
if (importer === void 0 || !isAbsolute(fileOfId(importer)) || source === "" || /^[./\\#\0]/.test(source) || source.includes(":")) return [];
|
|
914
|
+
const name = source.split("/").slice(0, source.startsWith("@") ? 2 : 1).join("/");
|
|
915
|
+
const paths = [];
|
|
916
|
+
for (let folder = dirname(fileOfId(importer));; folder = dirname(folder)) {
|
|
917
|
+
if (toPosix(folder).split("/").at(-1) !== "node_modules") paths.push(toPosix(join(folder, "node_modules", name)));
|
|
918
|
+
if (dirname(folder) === folder) break;
|
|
919
|
+
}
|
|
920
|
+
return paths;
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* A package can import itself through its exports without living in node_modules. Node can locate its manifest
|
|
924
|
+
* even when the export target is missing. Track the package's own file listing as a recovery scope, excluding
|
|
925
|
+
* node_modules; recording the whole folder as a watch file would scan all of its installed dependencies too.
|
|
926
|
+
* The node_modules candidates above remain necessary for packages that have no manifest yet.
|
|
927
|
+
*/
|
|
928
|
+
function missingPackageScope(source, importer, candidates) {
|
|
929
|
+
if (importer === void 0 || candidates.length === 0) return void 0;
|
|
930
|
+
let manifest;
|
|
931
|
+
try {
|
|
932
|
+
manifest = findPackageJSON(source, pathToFileURL(fileOfId(importer)));
|
|
933
|
+
} catch {
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
if (manifest === void 0) return void 0;
|
|
937
|
+
const dir = toPosix(dirname(manifest));
|
|
938
|
+
return candidates.includes(dir) ? void 0 : {
|
|
939
|
+
dir,
|
|
940
|
+
deep: true,
|
|
941
|
+
dot: true,
|
|
942
|
+
exhaustive: false,
|
|
943
|
+
suffix: ""
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
787
947
|
* Every path an import of `source` by `importer` is spelled as (see writtenPaths), and the path Vite's resolver
|
|
788
948
|
* reaches the file by, links kept (`kept`, see keptPath). A caller then sees a link on the way that is retargeted,
|
|
789
949
|
* which the file's own path never shows. Nothing for an import from a virtual module, or of one.
|
|
@@ -809,55 +969,100 @@ async function importSpellings(context, source, importer, root) {
|
|
|
809
969
|
* which sees the final plugin list, including the plugins Vite resolves per
|
|
810
970
|
* environment (`applyToEnvironment`) after `configResolved`.
|
|
811
971
|
*/
|
|
812
|
-
function recordWatchFiles(files, root, observer) {
|
|
972
|
+
function recordWatchFiles(files, root, observer = {}) {
|
|
813
973
|
const { onLoad, onResolve, onGlob, onWatchFile } = observer;
|
|
974
|
+
const watches = /* @__PURE__ */ new Map();
|
|
975
|
+
const touched = /* @__PURE__ */ new Set();
|
|
976
|
+
let modules = /* @__PURE__ */ new Set();
|
|
977
|
+
let started = false;
|
|
814
978
|
const record = (file) => {
|
|
815
|
-
files
|
|
979
|
+
files?.add(file);
|
|
816
980
|
onWatchFile?.(file);
|
|
817
981
|
};
|
|
982
|
+
const recordFor = (hook, args) => {
|
|
983
|
+
if (hook === "buildStart" && !started) {
|
|
984
|
+
started = true;
|
|
985
|
+
touched.clear();
|
|
986
|
+
watches.delete(void 0);
|
|
987
|
+
}
|
|
988
|
+
const owner = hook === "transform" || hook === "resolveId" || hook === "resolveDynamicImport" ? args[1] : hook === "load" ? args[0] : hook === "moduleParsed" && isRecord(args[0]) ? args[0]["id"] : void 0;
|
|
989
|
+
const id = typeof owner === "string" ? owner : void 0;
|
|
990
|
+
if (id !== void 0 && (hook === "load" || hook === "transform") && !touched.has(id)) {
|
|
991
|
+
touched.add(id);
|
|
992
|
+
watches.delete(id);
|
|
993
|
+
}
|
|
994
|
+
return (file) => {
|
|
995
|
+
record(file);
|
|
996
|
+
const owned = watches.get(id) ?? /* @__PURE__ */ new Set();
|
|
997
|
+
watches.set(id, owned.add(file));
|
|
998
|
+
};
|
|
999
|
+
};
|
|
818
1000
|
return {
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
1001
|
+
plugin: {
|
|
1002
|
+
name: `${PLUGIN_NAME}:record-watch-files`,
|
|
1003
|
+
buildStart() {
|
|
1004
|
+
recordFor("buildStart", []);
|
|
1005
|
+
},
|
|
1006
|
+
load: {
|
|
1007
|
+
order: "pre",
|
|
1008
|
+
handler(id) {
|
|
1009
|
+
const file = fileOfId(id);
|
|
1010
|
+
if (!isAbsolute(file) || id.startsWith("\0")) return null;
|
|
1011
|
+
onLoad?.(file);
|
|
1012
|
+
onResolve?.(file);
|
|
1013
|
+
return null;
|
|
1014
|
+
}
|
|
1015
|
+
},
|
|
1016
|
+
resolveId: {
|
|
1017
|
+
order: "pre",
|
|
1018
|
+
async handler(source, importer) {
|
|
1019
|
+
if (onResolve === void 0) return null;
|
|
1020
|
+
const { spellings, kept } = await importSpellings(this, source, importer, root);
|
|
1021
|
+
for (const spelled of spellings) onResolve(spelled);
|
|
1022
|
+
if (kept === void 0 && spellings.length === 0) {
|
|
1023
|
+
const candidates = missingPackagePaths(source, importer);
|
|
1024
|
+
for (const path of candidates) record(path);
|
|
1025
|
+
const scope = missingPackageScope(source, importer, candidates);
|
|
1026
|
+
if (scope !== void 0) {
|
|
1027
|
+
record(join(scope.dir, "package.json"));
|
|
1028
|
+
onGlob?.(scope);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
return null;
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
transform: {
|
|
1035
|
+
filter: { code: "import.meta.glob" },
|
|
1036
|
+
async handler(code, id) {
|
|
1037
|
+
if (onGlob === void 0) return null;
|
|
1038
|
+
for (const scope of await moduleGlobScopes(this, code, id, root)) onGlob(scope);
|
|
1039
|
+
return null;
|
|
1040
|
+
}
|
|
1041
|
+
},
|
|
1042
|
+
buildEnd() {
|
|
1043
|
+
modules = new Set(this.getModuleIds());
|
|
1044
|
+
started = false;
|
|
1045
|
+
for (const owner of watches.keys()) if (owner !== void 0 && !modules.has(owner)) watches.delete(owner);
|
|
1046
|
+
for (const id of modules) {
|
|
1047
|
+
const file = fileOfId(id);
|
|
1048
|
+
if (isAbsolute(file)) files?.add(file);
|
|
1049
|
+
}
|
|
1050
|
+
},
|
|
1051
|
+
options: {
|
|
1052
|
+
order: "post",
|
|
1053
|
+
async handler(inputOptions) {
|
|
1054
|
+
const plugins = (await flattenPlugins(inputOptions.plugins)).filter(isPlugin).map((p) => recordingPlugin(p, recordFor));
|
|
1055
|
+
return {
|
|
1056
|
+
...inputOptions,
|
|
1057
|
+
plugins
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
850
1060
|
}
|
|
851
1061
|
},
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
return {
|
|
857
|
-
...inputOptions,
|
|
858
|
-
plugins
|
|
859
|
-
};
|
|
860
|
-
}
|
|
1062
|
+
inputs: () => {
|
|
1063
|
+
const current = new Set([...modules].filter((id) => !id.startsWith("\0")).map(fileOfId).filter(isAbsolute));
|
|
1064
|
+
for (const [owner, watched] of watches) if (owner === void 0 || modules.has(owner)) for (const file of watched) current.add(file);
|
|
1065
|
+
return current;
|
|
861
1066
|
}
|
|
862
1067
|
};
|
|
863
1068
|
}
|
|
@@ -902,7 +1107,13 @@ async function bundleEntry(config, entryPath, command, outputFilename, observer
|
|
|
902
1107
|
isPreview: false
|
|
903
1108
|
};
|
|
904
1109
|
const user = await userConfigFor(config, env);
|
|
905
|
-
const
|
|
1110
|
+
const views = {
|
|
1111
|
+
configs: /* @__PURE__ */ new WeakMap(),
|
|
1112
|
+
environments: /* @__PURE__ */ new WeakMap()
|
|
1113
|
+
};
|
|
1114
|
+
const entryPlugins = async (option) => (await flattenPlugins(option)).filter(isPlugin).filter((plugin) => plugin.name !== "twee-ts" && appliesTo(plugin, command, user, env)).map((plugin) => asEntryBuildPlugin(plugin, command, views));
|
|
1115
|
+
const plugins = await entryPlugins(user.plugins);
|
|
1116
|
+
const workerPlugins = user.worker?.plugins;
|
|
906
1117
|
const inline = {
|
|
907
1118
|
...withoutExcludedKeys(user),
|
|
908
1119
|
configFile: false,
|
|
@@ -915,9 +1126,12 @@ async function bundleEntry(config, entryPath, command, outputFilename, observer
|
|
|
915
1126
|
plugins: [
|
|
916
1127
|
...plugins,
|
|
917
1128
|
entryBuildEnforcer(entryPath, command, outputFilename),
|
|
918
|
-
recordWatchFiles(watchFiles, config.root, observer)
|
|
1129
|
+
recordWatchFiles(watchFiles, config.root, observer).plugin
|
|
919
1130
|
],
|
|
920
|
-
worker: withWorkerPlugin(
|
|
1131
|
+
worker: withWorkerPlugin({
|
|
1132
|
+
...user.worker,
|
|
1133
|
+
plugins: () => [entryPlugins(typeof workerPlugins === "function" ? workerPlugins() : workerPlugins)]
|
|
1134
|
+
}, () => recordWatchFiles(watchFiles, config.root, observer).plugin)
|
|
921
1135
|
};
|
|
922
1136
|
const [result] = [await build(inline)].flat();
|
|
923
1137
|
if (result === void 0 || !("output" in result)) throw new Error("twee-ts: the entry build returned no bundle.");
|
|
@@ -1058,9 +1272,9 @@ function fileState(file) {
|
|
|
1058
1272
|
* `folder` is not a folder.
|
|
1059
1273
|
*/
|
|
1060
1274
|
function folderState(folder) {
|
|
1061
|
-
if (statSync(folder, { throwIfNoEntry: false })?.isDirectory() !== true) return void 0;
|
|
1062
1275
|
let names;
|
|
1063
1276
|
try {
|
|
1277
|
+
if (statSync(folder, { throwIfNoEntry: false })?.isDirectory() !== true) return void 0;
|
|
1064
1278
|
names = readdirSync(folder, {
|
|
1065
1279
|
recursive: true,
|
|
1066
1280
|
encoding: "utf8"
|
|
@@ -1087,6 +1301,15 @@ function fileRead(file) {
|
|
|
1087
1301
|
function tracked(paths) {
|
|
1088
1302
|
return new Map([...paths].map((path) => [fileKey(path), canonicalPath(path)]));
|
|
1089
1303
|
}
|
|
1304
|
+
/** A native identity key and every parent above it, without identifying any path again. */
|
|
1305
|
+
function* keyParents(key) {
|
|
1306
|
+
for (let current = key;;) {
|
|
1307
|
+
yield current;
|
|
1308
|
+
const parent = dirname(current);
|
|
1309
|
+
if (parent === current) return;
|
|
1310
|
+
current = parent;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1090
1313
|
/**
|
|
1091
1314
|
* The key of the file `authored` reaches, when it reaches it through a link (somewhere along the path, or
|
|
1092
1315
|
* at the end of it): the one case where the file's own path says nothing of a change of target.
|
|
@@ -1155,8 +1378,8 @@ function installStoryRoute(middlewares, route) {
|
|
|
1155
1378
|
handle: route
|
|
1156
1379
|
});
|
|
1157
1380
|
}
|
|
1158
|
-
/** The
|
|
1159
|
-
const
|
|
1381
|
+
/** The instances and entry watches of each server; the watcher is shared by every story (#517). */
|
|
1382
|
+
const devServers = /* @__PURE__ */ new WeakMap();
|
|
1160
1383
|
/** The error for a story page whose URL another story or an entry asset of the same dev server also claims. */
|
|
1161
1384
|
function storyCollisionMessage(path) {
|
|
1162
1385
|
return `twee-ts: more than one generated file is served at ${path || "/"} (another story or an entry asset); set a distinct outputFilename.`;
|
|
@@ -1188,11 +1411,20 @@ async function setUpDevStory(server, dev) {
|
|
|
1188
1411
|
let lastError;
|
|
1189
1412
|
let entry;
|
|
1190
1413
|
let servedAssets;
|
|
1191
|
-
const
|
|
1192
|
-
|
|
1414
|
+
const state = devServers.get(server) ?? {
|
|
1415
|
+
instances: /* @__PURE__ */ new Set(),
|
|
1416
|
+
watched: /* @__PURE__ */ new Map(),
|
|
1417
|
+
released: /* @__PURE__ */ new Map()
|
|
1418
|
+
};
|
|
1419
|
+
devServers.set(server, state);
|
|
1420
|
+
const { instances, watched, released } = state;
|
|
1421
|
+
const watchedForEntry = /* @__PURE__ */ new Map();
|
|
1193
1422
|
const self = {
|
|
1194
1423
|
paths,
|
|
1195
|
-
assets: () => servedAssets
|
|
1424
|
+
assets: () => servedAssets,
|
|
1425
|
+
catchUp: () => catchUp(),
|
|
1426
|
+
inputKeys,
|
|
1427
|
+
entryWatches: watchedForEntry
|
|
1196
1428
|
};
|
|
1197
1429
|
instances.add(self);
|
|
1198
1430
|
const fail = (res, message) => {
|
|
@@ -1211,12 +1443,12 @@ async function setUpDevStory(server, dev) {
|
|
|
1211
1443
|
let entryFolders = /* @__PURE__ */ new Map();
|
|
1212
1444
|
let entryReads = /* @__PURE__ */ new Map();
|
|
1213
1445
|
const readsModules = config.server.watch !== null;
|
|
1214
|
-
const watchedForEntry = /* @__PURE__ */ new Map();
|
|
1215
1446
|
let queue = Promise.resolve();
|
|
1216
1447
|
let pending = /* @__PURE__ */ new Set();
|
|
1217
1448
|
let timer;
|
|
1218
1449
|
let closed = false;
|
|
1219
1450
|
let compiledInputs = /* @__PURE__ */ new Map();
|
|
1451
|
+
let compiledInputNames;
|
|
1220
1452
|
let catchingUp;
|
|
1221
1453
|
const closeWatcher = server.watcher.close.bind(server.watcher);
|
|
1222
1454
|
server.watcher.close = async () => {
|
|
@@ -1226,8 +1458,13 @@ async function setUpDevStory(server, dev) {
|
|
|
1226
1458
|
return closeWatcher();
|
|
1227
1459
|
};
|
|
1228
1460
|
const namedInputs = () => inputs.map((input) => {
|
|
1229
|
-
|
|
1230
|
-
|
|
1461
|
+
let kind;
|
|
1462
|
+
try {
|
|
1463
|
+
const stat = statSync(input, { throwIfNoEntry: false });
|
|
1464
|
+
kind = stat === void 0 ? "none" : stat.isFile() ? "file" : stat.isDirectory() ? "folder" : "other";
|
|
1465
|
+
} catch (error) {
|
|
1466
|
+
kind = `failed:${String(error)}`;
|
|
1467
|
+
}
|
|
1231
1468
|
return `${fileKey(input)}\0${kind}`;
|
|
1232
1469
|
}).join("\0");
|
|
1233
1470
|
let inputWalk;
|
|
@@ -1236,7 +1473,7 @@ async function setUpDevStory(server, dev) {
|
|
|
1236
1473
|
const folders = /* @__PURE__ */ new Map();
|
|
1237
1474
|
const files = getFilenames(inputs, outputs, [], "source", (folder) => folders.set(folder, fileState(folder))).files.filter((file) => !excludedGlob(file.path)).map((file) => ({
|
|
1238
1475
|
key: file.key,
|
|
1239
|
-
path:
|
|
1476
|
+
path: identify(file.path).absolute,
|
|
1240
1477
|
link: file.link === true
|
|
1241
1478
|
}));
|
|
1242
1479
|
return {
|
|
@@ -1270,14 +1507,28 @@ async function setUpDevStory(server, dev) {
|
|
|
1270
1507
|
const entryWatchTargets = () => new Map([...entryFiles, ...tracked(scopeWatchTargets(entryGlobs.values()))]);
|
|
1271
1508
|
const watchEntryFiles = (files) => {
|
|
1272
1509
|
const added = [...files].filter(([key]) => !keyWithin(key, [root]) && !keyWithin(key, inputKeys()) && !watchedForEntry.has(key));
|
|
1273
|
-
const
|
|
1274
|
-
for (const [key] of
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1510
|
+
for (const key of watchedForEntry.keys()) if (!files.has(key)) watchedForEntry.delete(key);
|
|
1511
|
+
for (const [key, path] of added) {
|
|
1512
|
+
watchedForEntry.set(key, path);
|
|
1513
|
+
watched.set(key, path);
|
|
1514
|
+
}
|
|
1515
|
+
const owners = /* @__PURE__ */ new Set([root, ...[...instances].flatMap((instance) => [...instance.inputKeys(), ...instance.entryWatches.keys()])]);
|
|
1516
|
+
const aboveOwners = new Set([...owners].flatMap((key) => [...keyParents(key)]));
|
|
1517
|
+
const needed = (key) => aboveOwners.has(key) || [...keyParents(key)].some((parent) => owners.has(parent));
|
|
1518
|
+
const dropped = [...watched].filter(([key]) => !needed(key));
|
|
1519
|
+
for (const [key, path] of dropped) {
|
|
1520
|
+
watched.delete(key);
|
|
1521
|
+
released.set(key, path);
|
|
1522
|
+
}
|
|
1523
|
+
const restored = [...released].filter(([key]) => needed(key));
|
|
1524
|
+
for (const [key, path] of restored) {
|
|
1525
|
+
released.delete(key);
|
|
1526
|
+
watched.set(key, path);
|
|
1280
1527
|
}
|
|
1528
|
+
if (dropped.length > 0) server.watcher.unwatch(dropped.map(([, path]) => path));
|
|
1529
|
+
const registrations = new Map([...restored, ...added]);
|
|
1530
|
+
if (registrations.size > 0) server.watcher.add([...registrations.values()]);
|
|
1531
|
+
if (added.length > 0) recheckOnceWatched(new Map(added)).catch(keepQueueAlive);
|
|
1281
1532
|
};
|
|
1282
1533
|
const isListed = (path) => {
|
|
1283
1534
|
return server.watcher.getWatched()[dirname(path)]?.includes(basename(path)) ?? false;
|
|
@@ -1382,7 +1633,6 @@ async function setUpDevStory(server, dev) {
|
|
|
1382
1633
|
globStates = globsRead;
|
|
1383
1634
|
entryFolders = folders;
|
|
1384
1635
|
entryReads = reads;
|
|
1385
|
-
watchEntryFiles(entryWatchTargets());
|
|
1386
1636
|
};
|
|
1387
1637
|
const alreadyRead = (key) => {
|
|
1388
1638
|
const read = entryReads.get(key);
|
|
@@ -1402,8 +1652,10 @@ async function setUpDevStory(server, dev) {
|
|
|
1402
1652
|
outputs = dev.outputs(config);
|
|
1403
1653
|
output = outputPaths(outputs);
|
|
1404
1654
|
compiledInputs = storyInputFiles(true);
|
|
1655
|
+
compiledInputNames = inputWalk?.named;
|
|
1405
1656
|
for (const key of [...cache.keys()]) if (changed.has(fileKey(key))) cache.delete(key);
|
|
1406
1657
|
if (entryStale || regrouped || unread.some(touchesEntry)) await bundle();
|
|
1658
|
+
watchEntryFiles(entryWatchTargets());
|
|
1407
1659
|
const story = await compileStory(options.compile(entrySources(entry)), outputs, cache);
|
|
1408
1660
|
for (const warning of story.warnings) config.logger.warn(`[twee-ts] ${warning}`);
|
|
1409
1661
|
html = outputMode === "html" ? dev.injectClient(story.output, base) : story.output;
|
|
@@ -1451,6 +1703,7 @@ async function setUpDevStory(server, dev) {
|
|
|
1451
1703
|
if (queue === waited) break;
|
|
1452
1704
|
}
|
|
1453
1705
|
const changed = filesChanged(compiledInputs, storyInputFiles(false));
|
|
1706
|
+
if (compiledInputNames !== inputWalk?.named) for (const input of inputs) changed.add(fileKey(input));
|
|
1454
1707
|
for (const file of filesChanged(entryStates, fileStates(entryFiles))) changed.add(file);
|
|
1455
1708
|
if (changed.size === 0 && pending.size === 0 && !linkMoved() && !globsChanged() && !foldersChanged()) return;
|
|
1456
1709
|
for (const file of pending) changed.add(file);
|
|
@@ -1493,33 +1746,63 @@ async function setUpDevStory(server, dev) {
|
|
|
1493
1746
|
next();
|
|
1494
1747
|
return;
|
|
1495
1748
|
}
|
|
1496
|
-
const others = [...instances].filter((other) => other !== self);
|
|
1497
1749
|
const mine = paths.includes(path);
|
|
1498
|
-
if (
|
|
1499
|
-
|
|
1750
|
+
if (!mine && servedAssets?.has(path) !== true) {
|
|
1751
|
+
next();
|
|
1500
1752
|
return;
|
|
1501
1753
|
}
|
|
1502
|
-
|
|
1503
|
-
|
|
1754
|
+
([...instances].some((instance) => instance.paths.includes(path)) ? Promise.all([...instances].map((instance) => instance.catchUp())) : Promise.resolve()).then(() => {
|
|
1755
|
+
const others = [...instances].filter((other) => other !== self);
|
|
1756
|
+
if ((mine || servedAssets?.has(path) === true) && others.some((other) => other.paths.includes(path) || mine && paths.some((storyPath) => other.assets()?.has(storyPath) === true))) {
|
|
1757
|
+
fail(res, storyCollisionMessage(path));
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
if (mine) {
|
|
1761
|
+
if (paths.some((storyPath) => servedAssets?.has(storyPath) === true)) {
|
|
1762
|
+
fail(res, storyCollisionMessage(path));
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1504
1765
|
if (html) send(req, res, OUTPUT_MEDIA_TYPES[outputMode], html);
|
|
1505
1766
|
else send(req, res, "text/html; charset=utf-8", waitingPage(base));
|
|
1506
|
-
}, next);
|
|
1507
|
-
return;
|
|
1508
|
-
}
|
|
1509
|
-
const asset = servedAssets?.get(path);
|
|
1510
|
-
if (asset === void 0) {
|
|
1511
|
-
next();
|
|
1512
|
-
return;
|
|
1513
|
-
}
|
|
1514
|
-
for (const other of others) {
|
|
1515
|
-
const theirs = other.assets()?.get(path);
|
|
1516
|
-
if (theirs !== void 0 && !sameBytes(theirs, asset)) {
|
|
1517
|
-
fail(res, entryCollisionMessage(path));
|
|
1518
1767
|
return;
|
|
1519
1768
|
}
|
|
1769
|
+
const asset = servedAssets?.get(path);
|
|
1770
|
+
if (asset === void 0) {
|
|
1771
|
+
next();
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
for (const other of others) {
|
|
1775
|
+
const theirs = other.assets()?.get(path);
|
|
1776
|
+
if (theirs !== void 0 && !sameBytes(theirs, asset)) {
|
|
1777
|
+
fail(res, entryCollisionMessage(path));
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
send(req, res, assetMediaType(path), asset);
|
|
1782
|
+
}, next);
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
//#endregion
|
|
1786
|
+
//#region src/plugins/vite-public.ts
|
|
1787
|
+
/** Inventory public copies by their destination names, following directory links as Vite's copyDir does. */
|
|
1788
|
+
function publicFiles(publicDir) {
|
|
1789
|
+
if (!existsSync(publicDir)) return [];
|
|
1790
|
+
const files = [];
|
|
1791
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
1792
|
+
const walk = (folder) => {
|
|
1793
|
+
const key = fileKey(folder);
|
|
1794
|
+
if (ancestors.has(key)) return;
|
|
1795
|
+
ancestors.add(key);
|
|
1796
|
+
for (const name of readdirSync(folder)) {
|
|
1797
|
+
const path = resolve(folder, name);
|
|
1798
|
+
if (!existsSync(path)) continue;
|
|
1799
|
+
if (statSync(path).isDirectory()) walk(path);
|
|
1800
|
+
else files.push(relative(publicDir, path));
|
|
1520
1801
|
}
|
|
1521
|
-
|
|
1802
|
+
ancestors.delete(key);
|
|
1522
1803
|
};
|
|
1804
|
+
walk(publicDir);
|
|
1805
|
+
return files;
|
|
1523
1806
|
}
|
|
1524
1807
|
//#endregion
|
|
1525
1808
|
//#region src/plugins/vite.ts
|
|
@@ -1588,14 +1871,14 @@ function configOutputLocations(config) {
|
|
|
1588
1871
|
* Every path a build with this config writes, as far as the config tells: each
|
|
1589
1872
|
* output's folder (see configOutputLocations), the story in it, and the copies
|
|
1590
1873
|
* of the public files Vite puts there. The plugin adds the files each bundle
|
|
1591
|
-
* writes as it builds (see createOutputRecord).
|
|
1874
|
+
* writes as it builds (see createOutputRecord). A build passes its persistent
|
|
1875
|
+
* record so public copies remain excluded after a file is removed or a link is retargeted.
|
|
1592
1876
|
*/
|
|
1593
|
-
function configOutputs(config,
|
|
1594
|
-
const
|
|
1595
|
-
const publicFiles = config.build.copyPublicDir && config.publicDir ? getFilenames([config.publicDir]).filenames.map((file) => relative(config.publicDir, resolve(file))) : [];
|
|
1877
|
+
function configOutputs(config, record) {
|
|
1878
|
+
const copiedFiles = config.build.copyPublicDir && config.publicDir ? publicFiles(config.publicDir) : [];
|
|
1596
1879
|
for (const location of configOutputLocations(config)) {
|
|
1597
1880
|
record.addLocation(location);
|
|
1598
|
-
record.addFiles(location,
|
|
1881
|
+
record.addFiles(location, copiedFiles);
|
|
1599
1882
|
}
|
|
1600
1883
|
return record.outputs();
|
|
1601
1884
|
}
|
|
@@ -1659,8 +1942,9 @@ function tweeTsPlugin(options) {
|
|
|
1659
1942
|
const entryInput = `${ENTRY_INPUT_NAME}-${instances}`;
|
|
1660
1943
|
const cache = /* @__PURE__ */ new Map();
|
|
1661
1944
|
const ownBuilds = /* @__PURE__ */ new WeakMap();
|
|
1945
|
+
const entryInputs = /* @__PURE__ */ new WeakMap();
|
|
1662
1946
|
const record = createOutputRecord(outputFilename);
|
|
1663
|
-
const allOutputs = (config) => mergeOutputs(configOutputs(config, outputFilename), record.outputs());
|
|
1947
|
+
const allOutputs = (config) => mergeOutputs(configOutputs(config, createOutputRecord(outputFilename)), record.outputs());
|
|
1664
1948
|
/**
|
|
1665
1949
|
* The build settings this instance adds to a client build: none when the
|
|
1666
1950
|
* user's config names an input (the entry is then bundled by a build of its
|
|
@@ -1784,6 +2068,11 @@ function tweeTsPlugin(options) {
|
|
|
1784
2068
|
},
|
|
1785
2069
|
configResolved(config) {
|
|
1786
2070
|
if (entryPath === void 0) return;
|
|
2071
|
+
if (config.command === "build" && (bundlesEntryInside(config) || bundlesEntryInside({ build: config.environments[CLIENT]?.build }))) {
|
|
2072
|
+
const recorder = recordWatchFiles(void 0, config.root);
|
|
2073
|
+
entryInputs.set(config, recorder.inputs);
|
|
2074
|
+
Reflect.set(config.plugins, config.plugins.length, pluginWith(recorder.plugin, /* @__PURE__ */ new Map([["applyToEnvironment", (environment) => environment.name === CLIENT]])));
|
|
2075
|
+
}
|
|
1787
2076
|
if (config.command === "build" && config.build.watch && bundlesEntryInside(config)) config.plugins.forEach((plugin, at) => {
|
|
1788
2077
|
if (!isOwnPlugin(plugin)) Reflect.set(config.plugins, at, claimingPlugin(plugin, (key) => claimed.add(key)));
|
|
1789
2078
|
});
|
|
@@ -1795,7 +2084,7 @@ function tweeTsPlugin(options) {
|
|
|
1795
2084
|
watchOutputs = /* @__PURE__ */ new WeakMap();
|
|
1796
2085
|
forgetKeptResolver(config);
|
|
1797
2086
|
if (this.meta.watchMode) {
|
|
1798
|
-
const outputs = outputPaths(allOutputs(config));
|
|
2087
|
+
const outputs = outputPaths(allOutputs(config), "snapshot");
|
|
1799
2088
|
for (const target of watchTargets(resolved.inputs, resolved.excluded, outputs, "real")) this.addWatchFile(target);
|
|
1800
2089
|
}
|
|
1801
2090
|
if (entryPath === void 0 || bundlesEntryInside(config)) return;
|
|
@@ -1805,7 +2094,7 @@ function tweeTsPlugin(options) {
|
|
|
1805
2094
|
const registerEntryInputs = () => {
|
|
1806
2095
|
if (!this.meta.watchMode) return;
|
|
1807
2096
|
for (const file of watched) this.addWatchFile(canonicalPath(file));
|
|
1808
|
-
const outputs = outputPaths(allOutputs(config));
|
|
2097
|
+
const outputs = outputPaths(allOutputs(config), "snapshot");
|
|
1809
2098
|
for (const target of importWatchTargets(authored, outputs)) this.addWatchFile(target);
|
|
1810
2099
|
for (const target of globWatchTargets(globs, outputs)) this.addWatchFile(target);
|
|
1811
2100
|
};
|
|
@@ -1882,7 +2171,7 @@ function tweeTsPlugin(options) {
|
|
|
1882
2171
|
const items = bundle;
|
|
1883
2172
|
entry = bundlesEntryInside(config) ? takeEntryFromBundle(items, entryInput) : ownBuilds.get(this.environment);
|
|
1884
2173
|
if (!bundlesEntryInside(config)) for (const [fileName, source] of entry?.assets ?? []) {
|
|
1885
|
-
if (Object.hasOwn(bundle, fileName)) return this.error(entryCollisionMessage(fileName));
|
|
2174
|
+
if (fileName === outputFilename || Object.hasOwn(bundle, fileName)) return this.error(entryCollisionMessage(fileName));
|
|
1886
2175
|
this.emitFile({
|
|
1887
2176
|
type: "asset",
|
|
1888
2177
|
fileName,
|
|
@@ -1894,7 +2183,12 @@ function tweeTsPlugin(options) {
|
|
|
1894
2183
|
record.addFiles(outputOptions, [...Object.keys(bundle), ...entry?.assets.keys() ?? []]);
|
|
1895
2184
|
let story;
|
|
1896
2185
|
try {
|
|
1897
|
-
|
|
2186
|
+
const entryFiles = entryPath === void 0 ? [] : [
|
|
2187
|
+
entryPath,
|
|
2188
|
+
...entry?.files ?? [],
|
|
2189
|
+
...entryInputs.get(this.environment.getTopLevelConfig())?.() ?? []
|
|
2190
|
+
].filter((id) => !id.startsWith("\0"));
|
|
2191
|
+
story = await compileStory(resolved.compile(entrySources(entry)), configOutputs(config, record), cache, true, entryFiles);
|
|
1898
2192
|
} catch (e) {
|
|
1899
2193
|
return this.error(fatalError(e));
|
|
1900
2194
|
}
|
|
@@ -1906,6 +2200,11 @@ function tweeTsPlugin(options) {
|
|
|
1906
2200
|
});
|
|
1907
2201
|
}
|
|
1908
2202
|
},
|
|
2203
|
+
writeBundle(outputOptions, bundle) {
|
|
2204
|
+
const story = bundle[outputFilename];
|
|
2205
|
+
const path = emittedFilePath(outputOptions, outputFilename);
|
|
2206
|
+
if (story?.type === "asset" && path !== void 0) recordOwnOutput(path, story.source);
|
|
2207
|
+
},
|
|
1909
2208
|
async configureServer(server) {
|
|
1910
2209
|
const route = await setUpDevStory(server, {
|
|
1911
2210
|
options: resolved,
|