@nipe-solutions/flex-layout-codemod 2.0.0-beta.2 → 2.0.0-beta.4
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/CHANGELOG.md +20 -0
- package/README.md +25 -138
- package/dist/cli.js +1206 -569
- package/dist/cli.js.map +1 -1
- package/package.json +7 -5
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,788 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/migrator/migration-path.validator.ts
|
|
4
|
+
import { lstat, stat } from "fs/promises";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
|
|
7
|
+
// src/migrator/migration-application.error.ts
|
|
8
|
+
var MigrationApplicationError = class extends Error {
|
|
9
|
+
constructor(code, message, paths = [], options) {
|
|
10
|
+
super(message, options);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.paths = paths;
|
|
13
|
+
this.name = "MigrationApplicationError";
|
|
14
|
+
this.paths = Object.freeze([...paths]);
|
|
15
|
+
this.recoveryFailures = Object.freeze([...options?.recoveryFailures ?? []]);
|
|
16
|
+
}
|
|
17
|
+
recoveryFailures;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// src/migrator/migration-path.validator.ts
|
|
21
|
+
async function validateMigrationPaths(request, pathApi = path) {
|
|
22
|
+
const claims = normalizedClaims(request, pathApi);
|
|
23
|
+
await validateCollisions(claims, pathApi);
|
|
24
|
+
const destinations = claims.filter((claim) => claim.kind !== "template-input");
|
|
25
|
+
for (const destination of destinations) {
|
|
26
|
+
await validateDestination(destination.path);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function validateStylesheetRootTopology(request, pathApi = path) {
|
|
30
|
+
if (request.stylesheetPath === void 0) return;
|
|
31
|
+
const stylesheetPath = pathApi.resolve(request.stylesheetPath);
|
|
32
|
+
const templateRoots = [request.inputPath, request.outputPath].map((claim) => pathApi.resolve(claim));
|
|
33
|
+
const reportPath = request.reportPath === void 0 ? void 0 : pathApi.resolve(request.reportPath);
|
|
34
|
+
const exactCollision = (await Promise.all(
|
|
35
|
+
[...templateRoots, reportPath].map(
|
|
36
|
+
(claim) => claim === void 0 ? Promise.resolve(false) : pathsEquivalentOnFileSystem(stylesheetPath, claim, pathApi)
|
|
37
|
+
)
|
|
38
|
+
)).some(Boolean);
|
|
39
|
+
const reportHierarchyCollision = reportPath !== void 0 && await pathsOverlapOnFileSystem(stylesheetPath, reportPath, pathApi);
|
|
40
|
+
if (exactCollision || reportHierarchyCollision) {
|
|
41
|
+
const collisionPaths = reportHierarchyCollision && reportPath !== void 0 && !await pathsEquivalentOnFileSystem(stylesheetPath, reportPath, pathApi) ? [stylesheetPath, reportPath] : [stylesheetPath];
|
|
42
|
+
throw new MigrationApplicationError(
|
|
43
|
+
"path-collision",
|
|
44
|
+
`Stylesheet path collides with another migration path: ${request.stylesheetPathInput ?? request.stylesheetPath}`,
|
|
45
|
+
collisionPaths
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
let stylesheetStat;
|
|
49
|
+
try {
|
|
50
|
+
stylesheetStat = await lstat(stylesheetPath);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (isEnoent(error)) return;
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
const sourcePath = request.stylesheetPathInput ?? request.stylesheetPath;
|
|
56
|
+
if (stylesheetStat.isSymbolicLink()) {
|
|
57
|
+
throw new MigrationApplicationError(
|
|
58
|
+
"unsupported-path-type",
|
|
59
|
+
`Stylesheet path must not be a symbolic link: ${sourcePath}`,
|
|
60
|
+
[stylesheetPath]
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (!stylesheetStat.isFile()) {
|
|
64
|
+
throw new MigrationApplicationError(
|
|
65
|
+
"unsupported-path-type",
|
|
66
|
+
`Stylesheet path must be a regular file: ${sourcePath}`,
|
|
67
|
+
[stylesheetPath]
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function normalizedClaims(request, pathApi) {
|
|
72
|
+
return [
|
|
73
|
+
...request.templates.flatMap((template, templateIndex) => [
|
|
74
|
+
{ path: pathApi.resolve(template.inputPath), kind: "template-input", templateIndex },
|
|
75
|
+
{ path: pathApi.resolve(template.outputPath), kind: "template-output", templateIndex }
|
|
76
|
+
]),
|
|
77
|
+
...request.stylesheetPath ? [{ path: pathApi.resolve(request.stylesheetPath), kind: "stylesheet" }] : [],
|
|
78
|
+
...request.reportPath ? [{ path: pathApi.resolve(request.reportPath), kind: "report" }] : []
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
async function validateCollisions(claims, pathApi) {
|
|
82
|
+
const observations = /* @__PURE__ */ new Map();
|
|
83
|
+
const observe = (candidate) => {
|
|
84
|
+
const normalized = pathApi.resolve(candidate);
|
|
85
|
+
const existing = observations.get(normalized);
|
|
86
|
+
if (existing) return existing;
|
|
87
|
+
const pending = observePath(normalized, pathApi);
|
|
88
|
+
observations.set(normalized, pending);
|
|
89
|
+
return pending;
|
|
90
|
+
};
|
|
91
|
+
for (let leftIndex = 0; leftIndex < claims.length; leftIndex++) {
|
|
92
|
+
const left = claims[leftIndex];
|
|
93
|
+
if (!left) continue;
|
|
94
|
+
for (let rightIndex = leftIndex + 1; rightIndex < claims.length; rightIndex++) {
|
|
95
|
+
const right = claims[rightIndex];
|
|
96
|
+
if (!right) continue;
|
|
97
|
+
const relationship = await fileSystemPathRelationship(left.path, right.path, pathApi, observe);
|
|
98
|
+
if (relationship === "distinct" || isIntentionalInPlacePair(left, right, pathApi)) continue;
|
|
99
|
+
const collisionPaths = relationship === "equivalent" ? [left.path] : [left.path, right.path];
|
|
100
|
+
throw new MigrationApplicationError(
|
|
101
|
+
"path-collision",
|
|
102
|
+
`Migration paths collide: ${collisionPaths.join(" and ")}`,
|
|
103
|
+
collisionPaths
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function isIntentionalInPlacePair(left, right, pathApi) {
|
|
109
|
+
return pathsEquivalent(left.path, right.path, pathApi) && left.templateIndex !== void 0 && left.templateIndex === right.templateIndex && left.kind !== right.kind && left.kind.startsWith("template-") && right.kind.startsWith("template-");
|
|
110
|
+
}
|
|
111
|
+
function pathsEquivalent(left, right, pathApi = path) {
|
|
112
|
+
return normalizedPathsEquivalent(pathApi, pathApi.resolve(left), pathApi.resolve(right));
|
|
113
|
+
}
|
|
114
|
+
async function pathsEquivalentOnFileSystem(left, right, pathApi = path) {
|
|
115
|
+
return await fileSystemPathRelationship(left, right, pathApi) === "equivalent";
|
|
116
|
+
}
|
|
117
|
+
async function pathsOverlapOnFileSystem(left, right, pathApi = path) {
|
|
118
|
+
return await fileSystemPathRelationship(left, right, pathApi) !== "distinct";
|
|
119
|
+
}
|
|
120
|
+
function normalizedPathsEquivalent(pathApi, left, right) {
|
|
121
|
+
return pathApi.relative(left, right) === "";
|
|
122
|
+
}
|
|
123
|
+
function isAncestor(pathApi, ancestor, descendant) {
|
|
124
|
+
const relative3 = pathApi.relative(ancestor, descendant);
|
|
125
|
+
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(`..${pathApi.sep}`) && !pathApi.isAbsolute(relative3);
|
|
126
|
+
}
|
|
127
|
+
async function fileSystemPathRelationship(left, right, pathApi, observe = (candidate) => observePath(candidate, pathApi)) {
|
|
128
|
+
const normalizedLeft = pathApi.resolve(left);
|
|
129
|
+
const normalizedRight = pathApi.resolve(right);
|
|
130
|
+
if (normalizedPathsEquivalent(pathApi, normalizedLeft, normalizedRight)) return "equivalent";
|
|
131
|
+
if (isAncestor(pathApi, normalizedLeft, normalizedRight)) return "ancestor";
|
|
132
|
+
if (isAncestor(pathApi, normalizedRight, normalizedLeft)) return "descendant";
|
|
133
|
+
const [observedLeft, observedRight] = await Promise.all([observe(normalizedLeft), observe(normalizedRight)]);
|
|
134
|
+
if (observedLeft.exactIdentity && observedRight.exactIdentity) {
|
|
135
|
+
if (sameIdentity(observedLeft.exactIdentity, observedRight.exactIdentity)) return "equivalent";
|
|
136
|
+
if (hasIdentityBelow(observedRight, observedLeft.exactIdentity)) return "ancestor";
|
|
137
|
+
if (hasIdentityBelow(observedLeft, observedRight.exactIdentity)) return "descendant";
|
|
138
|
+
return "distinct";
|
|
139
|
+
}
|
|
140
|
+
return relationshipThroughExistingPrefixes(observedLeft, observedRight);
|
|
141
|
+
}
|
|
142
|
+
async function observePath(candidate, pathApi) {
|
|
143
|
+
const prefixes = [];
|
|
144
|
+
const suffix = [];
|
|
145
|
+
let current = candidate;
|
|
146
|
+
let exactIdentity;
|
|
147
|
+
while (true) {
|
|
148
|
+
try {
|
|
149
|
+
const currentStat = await stat(current, { bigint: true });
|
|
150
|
+
const currentIdentity = identity(currentStat);
|
|
151
|
+
if (suffix.length === 0) exactIdentity = currentIdentity;
|
|
152
|
+
prefixes.push({ identity: currentIdentity, suffix: [...suffix] });
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (!isMissingPath(error)) throw error;
|
|
155
|
+
}
|
|
156
|
+
const parent = pathApi.dirname(current);
|
|
157
|
+
if (parent === current) break;
|
|
158
|
+
suffix.unshift(pathApi.basename(current));
|
|
159
|
+
current = parent;
|
|
160
|
+
}
|
|
161
|
+
return { ...exactIdentity ? { exactIdentity } : {}, prefixes };
|
|
162
|
+
}
|
|
163
|
+
function relationshipThroughExistingPrefixes(left, right) {
|
|
164
|
+
for (const leftPrefix of left.prefixes) {
|
|
165
|
+
for (const rightPrefix of right.prefixes) {
|
|
166
|
+
if (!sameIdentity(leftPrefix.identity, rightPrefix.identity)) continue;
|
|
167
|
+
const relationship = suffixRelationship(leftPrefix.suffix, rightPrefix.suffix);
|
|
168
|
+
if (relationship !== "distinct") return relationship;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return "distinct";
|
|
172
|
+
}
|
|
173
|
+
function hasIdentityBelow(observed, candidate) {
|
|
174
|
+
return observed.prefixes.some((prefix) => prefix.suffix.length > 0 && sameIdentity(prefix.identity, candidate));
|
|
175
|
+
}
|
|
176
|
+
function suffixRelationship(left, right) {
|
|
177
|
+
const normalizedLeft = left.map(portablePathSegment);
|
|
178
|
+
const normalizedRight = right.map(portablePathSegment);
|
|
179
|
+
const sharedLength = Math.min(normalizedLeft.length, normalizedRight.length);
|
|
180
|
+
for (let index = 0; index < sharedLength; index++) {
|
|
181
|
+
if (normalizedLeft[index] !== normalizedRight[index]) return "distinct";
|
|
182
|
+
}
|
|
183
|
+
if (normalizedLeft.length === normalizedRight.length) return "equivalent";
|
|
184
|
+
return normalizedLeft.length < normalizedRight.length ? "ancestor" : "descendant";
|
|
185
|
+
}
|
|
186
|
+
function portablePathSegment(value) {
|
|
187
|
+
return value.normalize("NFC").toLowerCase();
|
|
188
|
+
}
|
|
189
|
+
function identity(value) {
|
|
190
|
+
return { device: String(value.dev), inode: String(value.ino) };
|
|
191
|
+
}
|
|
192
|
+
function sameIdentity(left, right) {
|
|
193
|
+
return left.device === right.device && left.inode === right.inode;
|
|
194
|
+
}
|
|
195
|
+
async function validateDestination(destination) {
|
|
196
|
+
let stat4;
|
|
197
|
+
try {
|
|
198
|
+
stat4 = await lstat(destination);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (isEnoent(error)) return;
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
if (stat4.isSymbolicLink()) {
|
|
204
|
+
throw new MigrationApplicationError(
|
|
205
|
+
"unsupported-path-type",
|
|
206
|
+
`Migration destination must not be a symbolic link: ${destination}`,
|
|
207
|
+
[destination]
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
if (!stat4.isFile()) {
|
|
211
|
+
throw new MigrationApplicationError(
|
|
212
|
+
"unsupported-path-type",
|
|
213
|
+
`Migration destination must be a regular file: ${destination}`,
|
|
214
|
+
[destination]
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function isEnoent(error) {
|
|
219
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
220
|
+
}
|
|
221
|
+
function isMissingPath(error) {
|
|
222
|
+
return typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/config/migration-config.ts
|
|
226
|
+
import { readFile, realpath } from "fs/promises";
|
|
227
|
+
import path2 from "path";
|
|
228
|
+
import { createHash } from "crypto";
|
|
229
|
+
import postcss2 from "postcss";
|
|
230
|
+
|
|
231
|
+
// src/util/sha-256.ts
|
|
232
|
+
var ROUND_CONSTANTS = Object.freeze([
|
|
233
|
+
1116352408,
|
|
234
|
+
1899447441,
|
|
235
|
+
3049323471,
|
|
236
|
+
3921009573,
|
|
237
|
+
961987163,
|
|
238
|
+
1508970993,
|
|
239
|
+
2453635748,
|
|
240
|
+
2870763221,
|
|
241
|
+
3624381080,
|
|
242
|
+
310598401,
|
|
243
|
+
607225278,
|
|
244
|
+
1426881987,
|
|
245
|
+
1925078388,
|
|
246
|
+
2162078206,
|
|
247
|
+
2614888103,
|
|
248
|
+
3248222580,
|
|
249
|
+
3835390401,
|
|
250
|
+
4022224774,
|
|
251
|
+
264347078,
|
|
252
|
+
604807628,
|
|
253
|
+
770255983,
|
|
254
|
+
1249150122,
|
|
255
|
+
1555081692,
|
|
256
|
+
1996064986,
|
|
257
|
+
2554220882,
|
|
258
|
+
2821834349,
|
|
259
|
+
2952996808,
|
|
260
|
+
3210313671,
|
|
261
|
+
3336571891,
|
|
262
|
+
3584528711,
|
|
263
|
+
113926993,
|
|
264
|
+
338241895,
|
|
265
|
+
666307205,
|
|
266
|
+
773529912,
|
|
267
|
+
1294757372,
|
|
268
|
+
1396182291,
|
|
269
|
+
1695183700,
|
|
270
|
+
1986661051,
|
|
271
|
+
2177026350,
|
|
272
|
+
2456956037,
|
|
273
|
+
2730485921,
|
|
274
|
+
2820302411,
|
|
275
|
+
3259730800,
|
|
276
|
+
3345764771,
|
|
277
|
+
3516065817,
|
|
278
|
+
3600352804,
|
|
279
|
+
4094571909,
|
|
280
|
+
275423344,
|
|
281
|
+
430227734,
|
|
282
|
+
506948616,
|
|
283
|
+
659060556,
|
|
284
|
+
883997877,
|
|
285
|
+
958139571,
|
|
286
|
+
1322822218,
|
|
287
|
+
1537002063,
|
|
288
|
+
1747873779,
|
|
289
|
+
1955562222,
|
|
290
|
+
2024104815,
|
|
291
|
+
2227730452,
|
|
292
|
+
2361852424,
|
|
293
|
+
2428436474,
|
|
294
|
+
2756734187,
|
|
295
|
+
3204031479,
|
|
296
|
+
3329325298
|
|
297
|
+
]);
|
|
298
|
+
var INITIAL_HASH = Object.freeze([
|
|
299
|
+
1779033703,
|
|
300
|
+
3144134277,
|
|
301
|
+
1013904242,
|
|
302
|
+
2773480762,
|
|
303
|
+
1359893119,
|
|
304
|
+
2600822924,
|
|
305
|
+
528734635,
|
|
306
|
+
1541459225
|
|
307
|
+
]);
|
|
308
|
+
function sha256(value) {
|
|
309
|
+
const source = new TextEncoder().encode(value);
|
|
310
|
+
const paddedLength = Math.ceil((source.length + 9) / 64) * 64;
|
|
311
|
+
const bytes = new Uint8Array(paddedLength);
|
|
312
|
+
bytes.set(source);
|
|
313
|
+
bytes[source.length] = 128;
|
|
314
|
+
const bitLength = source.length * 8;
|
|
315
|
+
const view = new DataView(bytes.buffer);
|
|
316
|
+
view.setUint32(paddedLength - 8, Math.floor(bitLength / 4294967296));
|
|
317
|
+
view.setUint32(paddedLength - 4, bitLength >>> 0);
|
|
318
|
+
const hash = [...INITIAL_HASH];
|
|
319
|
+
const schedule = new Uint32Array(64);
|
|
320
|
+
for (let offset = 0; offset < bytes.length; offset += 64) {
|
|
321
|
+
for (let index = 0; index < 16; index += 1) schedule[index] = view.getUint32(offset + index * 4);
|
|
322
|
+
for (let index = 16; index < schedule.length; index += 1) {
|
|
323
|
+
const earlier = schedule[index - 15];
|
|
324
|
+
const recent = schedule[index - 2];
|
|
325
|
+
const sigma0 = rotateRight(earlier, 7) ^ rotateRight(earlier, 18) ^ earlier >>> 3;
|
|
326
|
+
const sigma1 = rotateRight(recent, 17) ^ rotateRight(recent, 19) ^ recent >>> 10;
|
|
327
|
+
schedule[index] = schedule[index - 16] + sigma0 + schedule[index - 7] + sigma1 >>> 0;
|
|
328
|
+
}
|
|
329
|
+
let [a, b, c, d, e, f, g, h] = hash;
|
|
330
|
+
for (let index = 0; index < schedule.length; index += 1) {
|
|
331
|
+
const sum1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
|
|
332
|
+
const choice = e & f ^ ~e & g;
|
|
333
|
+
const temporary1 = h + sum1 + choice + ROUND_CONSTANTS[index] + schedule[index] >>> 0;
|
|
334
|
+
const sum0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
|
|
335
|
+
const majority = a & b ^ a & c ^ b & c;
|
|
336
|
+
const temporary2 = sum0 + majority >>> 0;
|
|
337
|
+
h = g;
|
|
338
|
+
g = f;
|
|
339
|
+
f = e;
|
|
340
|
+
e = d + temporary1 >>> 0;
|
|
341
|
+
d = c;
|
|
342
|
+
c = b;
|
|
343
|
+
b = a;
|
|
344
|
+
a = temporary1 + temporary2 >>> 0;
|
|
345
|
+
}
|
|
346
|
+
const round = [a, b, c, d, e, f, g, h];
|
|
347
|
+
for (let index = 0; index < hash.length; index += 1) hash[index] = hash[index] + round[index] >>> 0;
|
|
348
|
+
}
|
|
349
|
+
return hash.map((word) => word.toString(16).padStart(8, "0")).join("");
|
|
350
|
+
}
|
|
351
|
+
function rotateRight(value, count) {
|
|
352
|
+
return value >>> count | value << 32 - count;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/config/tailwind-target-profile.ts
|
|
356
|
+
import postcss from "postcss";
|
|
357
|
+
var defaults = { sm: "40rem", md: "48rem", lg: "64rem", xl: "80rem", "2xl": "96rem" };
|
|
358
|
+
function validBreakpoint(value) {
|
|
359
|
+
return /^(?:0|[1-9]\d*)(?:\.\d+)?(?:px|rem|em)$/.test(value) && Number.parseFloat(value) < 1e7;
|
|
360
|
+
}
|
|
361
|
+
function validPrefix(value) {
|
|
362
|
+
return /^[a-z]+$/.test(value);
|
|
363
|
+
}
|
|
364
|
+
function analyzeTailwindStylesheet(css, source = "pasted stylesheet") {
|
|
365
|
+
const operations = [];
|
|
366
|
+
const diagnostics = [];
|
|
367
|
+
const imports = [];
|
|
368
|
+
let tailwind = false;
|
|
369
|
+
let defaultTheme = false;
|
|
370
|
+
let utilities = false;
|
|
371
|
+
const warn = (code, message) => diagnostics.push({ code, message: `${source}: ${message}` });
|
|
372
|
+
if (css.length > 2e6) throw new Error("Stylesheet exceeds the 2 MB static analysis limit.");
|
|
373
|
+
const root = postcss.parse(css, { from: void 0 });
|
|
374
|
+
root.walkAtRules((rule) => {
|
|
375
|
+
if (rule.name === "custom-variant" || rule.name === "utility")
|
|
376
|
+
warn("tailwind-target-unknown", `@${rule.name} may change generated utility semantics and is not evaluated.`);
|
|
377
|
+
if (rule.name === "config" || rule.name === "plugin") {
|
|
378
|
+
warn(
|
|
379
|
+
rule.name === "config" ? "tailwind-config-external" : "tailwind-plugin-external",
|
|
380
|
+
`@${rule.name} ${rule.params} was not executed; additional configuration is unknown.`
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
if (rule.name === "source" && /^not\b/.test(rule.params)) {
|
|
384
|
+
warn("tailwind-source-excluded", `Source exclusion ${rule.params} may exclude migrated templates.`);
|
|
385
|
+
}
|
|
386
|
+
if (rule.name === "import") {
|
|
387
|
+
const match = /^(["'])([^"']+)\1(.*)$/.exec(rule.params);
|
|
388
|
+
if (!match) {
|
|
389
|
+
warn("tailwind-import-unresolved", `Import ${rule.params} was not loaded.`);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
const imported = match[2];
|
|
393
|
+
const modifiers = match[3];
|
|
394
|
+
if (/^tailwindcss(?:\/(?:theme|utilities|preflight)\.css)?$/.test(imported)) {
|
|
395
|
+
tailwind = true;
|
|
396
|
+
if (rule.parent?.type !== "root") {
|
|
397
|
+
warn("tailwind-target-unknown", "Conditional Tailwind import is unresolved.");
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
let remaining = modifiers.trim();
|
|
401
|
+
let prefix = null;
|
|
402
|
+
let important = false;
|
|
403
|
+
const seen = /* @__PURE__ */ new Set();
|
|
404
|
+
while (remaining) {
|
|
405
|
+
const modifier = /^(prefix\(([^)]*)\)|important|source\((?:none|"[^"]*"|'[^']*')\)|layer\([a-zA-Z0-9_.-]+\))(?=\s|$)/.exec(
|
|
406
|
+
remaining
|
|
407
|
+
);
|
|
408
|
+
if (!modifier) {
|
|
409
|
+
warn("tailwind-target-unknown", `Unsupported Tailwind import modifiers: ${remaining}`);
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
const name = modifier[1].split("(")[0];
|
|
413
|
+
if (seen.has(name)) warn("tailwind-target-unknown", `Repeated Tailwind import modifier ${name}.`);
|
|
414
|
+
seen.add(name);
|
|
415
|
+
if (name === "prefix") {
|
|
416
|
+
if (!validPrefix(modifier[2])) warn("tailwind-target-unknown", "Invalid Tailwind v4 prefix.");
|
|
417
|
+
else prefix = modifier[2];
|
|
418
|
+
}
|
|
419
|
+
if (name === "important") important = true;
|
|
420
|
+
if (modifier[1] === "source(none)")
|
|
421
|
+
warn("tailwind-source-excluded", "source(none) disables automatic template discovery.");
|
|
422
|
+
remaining = remaining.slice(modifier[0].length).trim();
|
|
423
|
+
}
|
|
424
|
+
if (imported === "tailwindcss" || imported === "tailwindcss/theme.css") defaultTheme = true;
|
|
425
|
+
if (imported !== "tailwindcss/preflight.css") operations.push({ kind: "prefix", value: prefix, source });
|
|
426
|
+
if (imported === "tailwindcss" || imported === "tailwindcss/utilities.css") {
|
|
427
|
+
utilities = true;
|
|
428
|
+
operations.push({ kind: "important", value: important ? "important" : "normal", source });
|
|
429
|
+
}
|
|
430
|
+
} else if (imported.startsWith("./") || imported.startsWith("../")) {
|
|
431
|
+
if (rule.parent?.type !== "root" || modifiers.trim())
|
|
432
|
+
warn("tailwind-import-unresolved", `Conditional import ${imported} was not loaded.`);
|
|
433
|
+
else imports.push(imported);
|
|
434
|
+
} else warn("tailwind-import-unresolved", `External import ${imported} was not loaded.`);
|
|
435
|
+
}
|
|
436
|
+
if (rule.name !== "theme") return;
|
|
437
|
+
if (rule.parent?.type !== "root" || rule.params.trim() && !/^(?:inline|static)(?:\s+(?:inline|static))*$/.test(rule.params.trim())) {
|
|
438
|
+
warn("tailwind-target-unknown", "Conditional or unsupported @theme block was not resolved.");
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
rule.each((node) => {
|
|
442
|
+
if (node.type !== "decl") return;
|
|
443
|
+
if (node.prop === "--*") {
|
|
444
|
+
if (node.value.trim() === "initial" && !node.important) {
|
|
445
|
+
operations.push({ kind: "breakpoint", name: "*", value: null, source });
|
|
446
|
+
} else warn("tailwind-target-unknown", "Cannot resolve global theme reset.");
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (!node.prop.startsWith("--breakpoint-")) return;
|
|
450
|
+
const name = node.prop.slice("--breakpoint-".length);
|
|
451
|
+
const value = node.value.trim();
|
|
452
|
+
if (!/^(?:\*|[a-zA-Z0-9][a-zA-Z0-9_-]*)$/.test(name) || node.important || value !== "initial" && (name === "*" || !validBreakpoint(value))) {
|
|
453
|
+
warn("tailwind-breakpoint-unknown", `Cannot resolve ${node.prop}: ${value}.`);
|
|
454
|
+
if (/^(?:\*|[a-zA-Z0-9][a-zA-Z0-9_-]*)$/.test(name))
|
|
455
|
+
operations.push({ kind: "breakpoint", name, value: null, source });
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
operations.push({ kind: "breakpoint", name, value: value === "initial" ? null : value, source });
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
return { operations, diagnostics, imports, tailwind, defaultTheme, utilities, source };
|
|
462
|
+
}
|
|
463
|
+
function resolveTailwindTargetProfile(input = {}) {
|
|
464
|
+
let coreUtilities = {
|
|
465
|
+
value: "standard",
|
|
466
|
+
source: input.detected?.source ?? "default",
|
|
467
|
+
confidence: input.detected ? "detected" : "defaulted"
|
|
468
|
+
};
|
|
469
|
+
let prefix = { value: null, source: "default", confidence: "defaulted" };
|
|
470
|
+
let important = { value: "normal", source: "default", confidence: "defaulted" };
|
|
471
|
+
let breakpoints = Object.fromEntries(
|
|
472
|
+
Object.entries(input.detected && !input.detected.defaultTheme ? {} : defaults).map(([name, value]) => [
|
|
473
|
+
name,
|
|
474
|
+
{ value, source: "default", confidence: "defaulted" }
|
|
475
|
+
])
|
|
476
|
+
);
|
|
477
|
+
const diagnostics = [
|
|
478
|
+
...input.detected?.diagnostics ?? [],
|
|
479
|
+
...(input.detected?.imports ?? []).map((imported) => ({
|
|
480
|
+
code: "tailwind-import-unresolved",
|
|
481
|
+
message: `${input.detected.source}: Local import ${imported} was not loaded in this analysis.`
|
|
482
|
+
}))
|
|
483
|
+
];
|
|
484
|
+
function apply(operation, confidence) {
|
|
485
|
+
const { value, source } = operation;
|
|
486
|
+
if (operation.kind === "prefix") {
|
|
487
|
+
if (value !== null && !validPrefix(value))
|
|
488
|
+
throw new Error("Tailwind v4 prefix must contain only lowercase letters.");
|
|
489
|
+
if (prefix.confidence !== "defaulted" && prefix.value !== value)
|
|
490
|
+
diagnostics.push({
|
|
491
|
+
code: "tailwind-prefix-conflict",
|
|
492
|
+
message: `Using prefix ${String(value)} from ${source}; ${prefix.source} declared ${String(prefix.value)}.`
|
|
493
|
+
});
|
|
494
|
+
prefix = { value, source, confidence };
|
|
495
|
+
} else if (operation.kind === "important") important = { value: operation.value, source, confidence };
|
|
496
|
+
else if (operation.name === "*" && value === null) breakpoints = {};
|
|
497
|
+
else if (value === null) delete breakpoints[operation.name];
|
|
498
|
+
else {
|
|
499
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(operation.name) || !validBreakpoint(value))
|
|
500
|
+
throw new Error(`Invalid Tailwind breakpoint ${operation.name}: ${value}`);
|
|
501
|
+
breakpoints[operation.name] = { value, source, confidence };
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
input.detected?.operations.forEach((operation) => apply(operation, "detected"));
|
|
505
|
+
for (const [overrides, source] of [
|
|
506
|
+
[input.explicit, input.explicitSource ?? "migration profile"],
|
|
507
|
+
[input.cli, "CLI"]
|
|
508
|
+
]) {
|
|
509
|
+
if (!overrides) continue;
|
|
510
|
+
if (overrides.version !== void 0 && overrides.version !== 4)
|
|
511
|
+
throw new Error("Only Tailwind v4 targets are supported.");
|
|
512
|
+
if (overrides.coreUtilities !== void 0) {
|
|
513
|
+
if (overrides.coreUtilities !== "standard")
|
|
514
|
+
throw new Error("coreUtilities must be standard when explicitly asserted.");
|
|
515
|
+
coreUtilities = { value: "standard", source, confidence: "explicit" };
|
|
516
|
+
}
|
|
517
|
+
if (overrides.prefix !== void 0) apply({ kind: "prefix", value: overrides.prefix, source }, "explicit");
|
|
518
|
+
if (overrides.important !== void 0) {
|
|
519
|
+
if (!["normal", "important"].includes(overrides.important)) throw new Error("Invalid Tailwind important mode.");
|
|
520
|
+
apply({ kind: "important", value: overrides.important, source }, "explicit");
|
|
521
|
+
}
|
|
522
|
+
const entries = Object.entries(overrides.breakpoints ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
523
|
+
for (const [name, value] of entries)
|
|
524
|
+
apply({ kind: "breakpoint", name, value: value === "initial" ? null : value, source }, "explicit");
|
|
525
|
+
}
|
|
526
|
+
breakpoints = Object.fromEntries(Object.entries(breakpoints).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
|
|
527
|
+
if (new Set(Object.values(breakpoints).map((setting) => setting.value.match(/[a-z]+$/)[0])).size > 1)
|
|
528
|
+
diagnostics.push({
|
|
529
|
+
code: "tailwind-mixed-breakpoint-units",
|
|
530
|
+
message: "Mixed breakpoint units may affect Tailwind variant ordering; units were preserved."
|
|
531
|
+
});
|
|
532
|
+
if (input.detected && !input.detected.utilities)
|
|
533
|
+
diagnostics.push({
|
|
534
|
+
code: "tailwind-target-unknown",
|
|
535
|
+
message: "The supplied stylesheet does not declare Tailwind utility generation."
|
|
536
|
+
});
|
|
537
|
+
if (input.detected && !input.detected.tailwind)
|
|
538
|
+
diagnostics.push({ code: "tailwind-target-unknown", message: "No supported Tailwind v4 import was detected." });
|
|
539
|
+
if (diagnostics.some(
|
|
540
|
+
(item) => [
|
|
541
|
+
"tailwind-config-external",
|
|
542
|
+
"tailwind-plugin-external",
|
|
543
|
+
"tailwind-import-unresolved",
|
|
544
|
+
"tailwind-target-unknown"
|
|
545
|
+
].includes(item.code)
|
|
546
|
+
)) {
|
|
547
|
+
if (coreUtilities.confidence !== "explicit")
|
|
548
|
+
coreUtilities = { ...coreUtilities, value: "unknown", confidence: "unknown" };
|
|
549
|
+
breakpoints = Object.fromEntries(
|
|
550
|
+
Object.entries(breakpoints).map(([name, setting]) => [
|
|
551
|
+
name,
|
|
552
|
+
setting.confidence === "explicit" ? setting : { ...setting, confidence: "unknown" }
|
|
553
|
+
])
|
|
554
|
+
);
|
|
555
|
+
if (prefix.confidence !== "explicit") prefix = { ...prefix, confidence: "unknown" };
|
|
556
|
+
if (important.confidence !== "explicit")
|
|
557
|
+
important = { value: "unknown", source: important.source, confidence: "unknown" };
|
|
558
|
+
}
|
|
559
|
+
const assumptions = [
|
|
560
|
+
"Custom application CSS is not globally analyzed.",
|
|
561
|
+
"Responsive source ranges are preserved, including screen and print conditions."
|
|
562
|
+
];
|
|
563
|
+
if (!input.detected && !input.explicit && !input.cli)
|
|
564
|
+
assumptions.push("Target profile uses Tailwind v4 defaults; project configuration was not discovered.");
|
|
565
|
+
if (diagnostics.some((d) => d.code === "tailwind-config-external" || d.code === "tailwind-plugin-external"))
|
|
566
|
+
assumptions.push("Legacy JavaScript configuration and plugins were not executed.");
|
|
567
|
+
if (coreUtilities.confidence === "explicit")
|
|
568
|
+
assumptions.push(
|
|
569
|
+
"The migration profile explicitly asserts standard Tailwind core utility semantics; this assertion was not verified by executing project code."
|
|
570
|
+
);
|
|
571
|
+
if (important.value === "important")
|
|
572
|
+
assumptions.push("Generated Tailwind utilities are globally important; existing styles still require review.");
|
|
573
|
+
const canonical = JSON.stringify({
|
|
574
|
+
version: 4,
|
|
575
|
+
prefix: prefix.value,
|
|
576
|
+
important: important.value,
|
|
577
|
+
coreUtilities: coreUtilities.value,
|
|
578
|
+
breakpoints: Object.fromEntries(Object.entries(breakpoints).map(([name, setting]) => [name, setting.value]))
|
|
579
|
+
});
|
|
580
|
+
return deepFreeze({
|
|
581
|
+
target: "tailwind",
|
|
582
|
+
version: 4,
|
|
583
|
+
prefix,
|
|
584
|
+
important,
|
|
585
|
+
coreUtilities,
|
|
586
|
+
breakpoints,
|
|
587
|
+
diagnostics,
|
|
588
|
+
assumptions,
|
|
589
|
+
fingerprint: `tw4-${sha256(canonical)}`,
|
|
590
|
+
...input.detected ? { stylesheet: input.detected.source } : {}
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
function deepFreeze(value) {
|
|
594
|
+
if (value && typeof value === "object") {
|
|
595
|
+
Object.values(value).forEach(deepFreeze);
|
|
596
|
+
Object.freeze(value);
|
|
597
|
+
}
|
|
598
|
+
return value;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// src/config/source-breakpoints.ts
|
|
602
|
+
function sourceBreakpointDefinition(alias, input) {
|
|
603
|
+
if (!/^[a-zA-Z][a-zA-Z0-9_.-]*$/.test(alias) || !Number.isSafeInteger(input.priority))
|
|
604
|
+
throw new Error(`Invalid source breakpoint ${alias}; an integer priority is required.`);
|
|
605
|
+
if (typeof input.mediaQuery !== "string") throw new Error(`Invalid source media query for ${alias}.`);
|
|
606
|
+
const parts = input.mediaQuery.trim().split(/\s+and\s+/);
|
|
607
|
+
if (parts.shift() !== "screen" || !parts.length)
|
|
608
|
+
throw new Error(`Source breakpoint ${alias} requires screen and explicit px width/orientation conditions.`);
|
|
609
|
+
const range2 = {};
|
|
610
|
+
for (const part of parts) {
|
|
611
|
+
const width = /^\((min|max)-width:\s*(\d+(?:\.\d+)?)px\)$/.exec(part);
|
|
612
|
+
const orientation = /^\(orientation:\s*(portrait|landscape)\)$/.exec(part);
|
|
613
|
+
if (width) {
|
|
614
|
+
const key = width[1];
|
|
615
|
+
if (range2[key] !== void 0 || !Number.isFinite(Number(width[2])))
|
|
616
|
+
throw new Error(`Duplicate or invalid source condition in ${alias}.`);
|
|
617
|
+
range2[key] = Number(width[2]);
|
|
618
|
+
} else if (orientation && range2.orientation === void 0)
|
|
619
|
+
range2.orientation = orientation[1];
|
|
620
|
+
else throw new Error(`Unsupported source media condition in ${alias}: ${part}`);
|
|
621
|
+
}
|
|
622
|
+
if ((range2.min ?? 0) > (range2.max ?? Infinity)) throw new Error(`Inverted source media range in ${alias}.`);
|
|
623
|
+
const clause = Object.freeze(range2);
|
|
624
|
+
return Object.freeze({
|
|
625
|
+
alias,
|
|
626
|
+
priority: input.priority,
|
|
627
|
+
range: clause,
|
|
628
|
+
media: Object.freeze({ type: "screen", clauses: Object.freeze([clause]) })
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/config/migration-config.ts
|
|
633
|
+
function object(value, allowed, label) {
|
|
634
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object.`);
|
|
635
|
+
for (const key of Object.keys(value)) if (!allowed.includes(key)) throw new Error(`Unknown ${label} setting: ${key}`);
|
|
636
|
+
return value;
|
|
637
|
+
}
|
|
638
|
+
function parseConfig(contents) {
|
|
639
|
+
const config = object(JSON.parse(contents), ["target", "tailwind", "source"], "migration config");
|
|
640
|
+
if (config.target !== void 0 && config.target !== "tailwind" && config.target !== "css")
|
|
641
|
+
throw new Error("Invalid migration target.");
|
|
642
|
+
if (config.tailwind !== void 0) {
|
|
643
|
+
const tw = object(
|
|
644
|
+
config.tailwind,
|
|
645
|
+
["version", "stylesheet", "prefix", "breakpoints", "important", "coreUtilities"],
|
|
646
|
+
"tailwind"
|
|
647
|
+
);
|
|
648
|
+
if (tw.stylesheet !== void 0 && typeof tw.stylesheet !== "string")
|
|
649
|
+
throw new Error("Tailwind stylesheet must be a path.");
|
|
650
|
+
if (tw.prefix !== void 0 && tw.prefix !== null && typeof tw.prefix !== "string")
|
|
651
|
+
throw new Error("Tailwind prefix must be a string or null.");
|
|
652
|
+
if (tw.breakpoints !== void 0) {
|
|
653
|
+
if (!tw.breakpoints || typeof tw.breakpoints !== "object" || Array.isArray(tw.breakpoints))
|
|
654
|
+
throw new Error("Tailwind breakpoints must be an object.");
|
|
655
|
+
for (const value of Object.values(tw.breakpoints))
|
|
656
|
+
if (value !== null && typeof value !== "string")
|
|
657
|
+
throw new Error("Tailwind breakpoint values must be lengths or null.");
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
if (config.source !== void 0) {
|
|
661
|
+
const source = object(config.source, ["flexLayout"], "source");
|
|
662
|
+
if (source.flexLayout !== void 0) {
|
|
663
|
+
const flex = object(source.flexLayout, ["breakpoints"], "source.flexLayout");
|
|
664
|
+
if (flex.breakpoints !== void 0) {
|
|
665
|
+
if (!flex.breakpoints || typeof flex.breakpoints !== "object" || Array.isArray(flex.breakpoints))
|
|
666
|
+
throw new Error("Source breakpoints must be an object.");
|
|
667
|
+
for (const [alias, value] of Object.entries(flex.breakpoints)) {
|
|
668
|
+
object(value, ["mediaQuery", "priority"], `source breakpoint ${alias}`);
|
|
669
|
+
sourceBreakpointDefinition(alias, value);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return config;
|
|
675
|
+
}
|
|
676
|
+
var digest = (value) => createHash("sha256").update(value).digest("hex");
|
|
677
|
+
function inside(root, candidate) {
|
|
678
|
+
const relative3 = path2.relative(root, candidate);
|
|
679
|
+
return relative3 !== ".." && !relative3.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative3);
|
|
680
|
+
}
|
|
681
|
+
async function assertConfigurationUnchanged(snapshots) {
|
|
682
|
+
for (const snapshot of snapshots) {
|
|
683
|
+
let current;
|
|
684
|
+
try {
|
|
685
|
+
if (await realpath(snapshot.path) !== snapshot.canonicalPath) throw new Error("Configuration path changed.");
|
|
686
|
+
current = digest(await readFile(snapshot.path, "utf8"));
|
|
687
|
+
} catch {
|
|
688
|
+
throw new Error(`Target configuration changed or disappeared since planning: ${snapshot.path}`);
|
|
689
|
+
}
|
|
690
|
+
if (current !== snapshot.digest) throw new Error(`Target configuration changed since planning: ${snapshot.path}`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
async function loadMigrationConfig(options) {
|
|
694
|
+
const root = await realpath(options.cwd ?? process.cwd());
|
|
695
|
+
const snapshots = [];
|
|
696
|
+
const read = async (file) => {
|
|
697
|
+
const contents = await readFile(file, "utf8");
|
|
698
|
+
if (contents.length > 2e6) throw new Error("Configuration file exceeds the 2 MB static analysis limit.");
|
|
699
|
+
if (!snapshots.some((snapshot) => snapshot.path === file))
|
|
700
|
+
snapshots.push(Object.freeze({ path: file, canonicalPath: await realpath(file), digest: digest(contents) }));
|
|
701
|
+
return contents;
|
|
702
|
+
};
|
|
703
|
+
const configPath = path2.resolve(root, options.config ?? "flex-layout-migrator.config.json");
|
|
704
|
+
if (!configPath.endsWith(".json")) throw new Error("Migration configuration must be declarative JSON.");
|
|
705
|
+
let config = {};
|
|
706
|
+
try {
|
|
707
|
+
config = parseConfig(await read(configPath));
|
|
708
|
+
} catch (error) {
|
|
709
|
+
if (options.config !== void 0 || !(error && typeof error === "object" && "code" in error && error.code === "ENOENT"))
|
|
710
|
+
throw error;
|
|
711
|
+
}
|
|
712
|
+
const stylesheet = options.stylesheet === void 0 ? config.tailwind?.stylesheet === void 0 ? void 0 : path2.resolve(path2.dirname(configPath), config.tailwind.stylesheet) : path2.resolve(root, options.stylesheet);
|
|
713
|
+
let detected;
|
|
714
|
+
if (stylesheet) {
|
|
715
|
+
const operations = [];
|
|
716
|
+
const diagnostics = [];
|
|
717
|
+
let tailwind = false;
|
|
718
|
+
let defaultTheme = false;
|
|
719
|
+
let utilities = false;
|
|
720
|
+
let visited = 0;
|
|
721
|
+
const relative3 = (file) => path2.relative(root, file).split(path2.sep).join("/");
|
|
722
|
+
const visit = async (file, ancestors, depth) => {
|
|
723
|
+
if (++visited > 128 || depth > 16)
|
|
724
|
+
throw new Error("CSS import analysis limit exceeded (128 imports / depth 16).");
|
|
725
|
+
const canonical = await realpath(file);
|
|
726
|
+
if (!inside(root, canonical)) throw new Error("CSS import is outside the project root.");
|
|
727
|
+
if (ancestors.includes(canonical)) throw new Error("CSS import cycle detected.");
|
|
728
|
+
const contents = await read(file);
|
|
729
|
+
const parsed2 = postcss2.parse(contents, { from: void 0 });
|
|
730
|
+
for (const node of parsed2.nodes) {
|
|
731
|
+
const analysis = analyzeTailwindStylesheet(node.toString(), relative3(canonical));
|
|
732
|
+
operations.push(...analysis.operations);
|
|
733
|
+
diagnostics.push(...analysis.diagnostics);
|
|
734
|
+
tailwind ||= analysis.tailwind;
|
|
735
|
+
defaultTheme ||= analysis.defaultTheme;
|
|
736
|
+
utilities ||= analysis.utilities;
|
|
737
|
+
for (const imported of analysis.imports) {
|
|
738
|
+
try {
|
|
739
|
+
await visit(path2.resolve(path2.dirname(canonical), imported), [...ancestors, canonical], depth + 1);
|
|
740
|
+
} catch (error) {
|
|
741
|
+
diagnostics.push({
|
|
742
|
+
code: "tailwind-import-unresolved",
|
|
743
|
+
message: `${relative3(canonical)}: ${imported}: ${error instanceof Error ? error.message : "Import unresolved"}`
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
await visit(stylesheet, [], 0);
|
|
750
|
+
detected = {
|
|
751
|
+
source: relative3(stylesheet),
|
|
752
|
+
operations,
|
|
753
|
+
diagnostics,
|
|
754
|
+
imports: [],
|
|
755
|
+
defaultTheme,
|
|
756
|
+
utilities,
|
|
757
|
+
tailwind
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
const targetProfile = resolveTailwindTargetProfile({
|
|
761
|
+
detected,
|
|
762
|
+
explicit: config.tailwind,
|
|
763
|
+
explicitSource: path2.relative(root, configPath).split(path2.sep).join("/"),
|
|
764
|
+
...options.prefix !== void 0 ? { cli: { prefix: options.prefix || null } } : {}
|
|
765
|
+
});
|
|
766
|
+
return Object.freeze({
|
|
767
|
+
target: config.target,
|
|
768
|
+
targetProfile,
|
|
769
|
+
sourceBreakpoints: config.source?.flexLayout?.breakpoints,
|
|
770
|
+
snapshots: Object.freeze(snapshots)
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
|
|
3
774
|
// src/cli/run-cli.ts
|
|
4
775
|
import { Command, CommanderError, InvalidArgumentError, Option } from "commander";
|
|
5
|
-
import * as
|
|
776
|
+
import * as path18 from "path";
|
|
6
777
|
|
|
7
778
|
// package.json
|
|
8
779
|
var package_default = {
|
|
9
780
|
name: "@nipe-solutions/flex-layout-codemod",
|
|
10
|
-
version: "2.0.0-beta.
|
|
781
|
+
version: "2.0.0-beta.4",
|
|
11
782
|
description: "A safety-first codemod for migrating Angular Flex-Layout templates to native CSS or Tailwind CSS.",
|
|
12
783
|
type: "module",
|
|
13
784
|
engines: {
|
|
14
|
-
node: ">=
|
|
785
|
+
node: ">=22.12.0"
|
|
15
786
|
},
|
|
16
787
|
packageManager: "npm@11.19.0",
|
|
17
788
|
files: [
|
|
@@ -45,7 +816,8 @@ var package_default = {
|
|
|
45
816
|
format: "prettier --check .",
|
|
46
817
|
"format:write": "prettier --write .",
|
|
47
818
|
verify: "npm run format && npm run lint && npm run typecheck && npm run test:coverage && npm run build && npm run package:check",
|
|
48
|
-
"verify:website": "npm run format && npm run lint && npm run test:website && npm run typecheck:website && npm run verify:website-assets && npm run build:website && npm run verify:website-static && npm run test:e2e:website",
|
|
819
|
+
"verify:website": "npm run format && npm run lint && npm run test:website && npm run typecheck:website && npm run verify:website-assets && npm run verify:docs && npm run build:website && npm run verify:website-static && npm run test:e2e:website",
|
|
820
|
+
"verify:docs": "node scripts/verify-documentation-contract.mjs",
|
|
49
821
|
"verify:website-static": "node scripts/verify-website-static.mjs",
|
|
50
822
|
"package:check": "node scripts/verify-package.mjs",
|
|
51
823
|
changeset: "changeset add",
|
|
@@ -54,7 +826,8 @@ var package_default = {
|
|
|
54
826
|
"release:verify": "node scripts/release-artifact.mjs --verify-retained",
|
|
55
827
|
"verify:website-assets": "node scripts/verify-website-assets.mjs",
|
|
56
828
|
"release:version": "changeset version && npm install --package-lock-only --ignore-scripts --no-audit --no-fund",
|
|
57
|
-
prepare: "husky"
|
|
829
|
+
prepare: "husky",
|
|
830
|
+
check: "npm run verify && npm run verify:website"
|
|
58
831
|
},
|
|
59
832
|
repository: {
|
|
60
833
|
type: "git",
|
|
@@ -79,6 +852,7 @@ var package_default = {
|
|
|
79
852
|
commander: "^15.0.0",
|
|
80
853
|
"fs-extra": "^11.4.0",
|
|
81
854
|
ignore: "5.2.4",
|
|
855
|
+
postcss: "^8.5.26",
|
|
82
856
|
winston: "^3.19.0"
|
|
83
857
|
},
|
|
84
858
|
bin: {
|
|
@@ -106,7 +880,6 @@ var package_default = {
|
|
|
106
880
|
jsdom: "^30.0.1",
|
|
107
881
|
"lint-staged": "^17.4.1",
|
|
108
882
|
"mock-fs": "^5.5.0",
|
|
109
|
-
postcss: "^8.5.26",
|
|
110
883
|
prettier: "^3.9.6",
|
|
111
884
|
react: "^19.2.8",
|
|
112
885
|
"react-dom": "^19.2.8",
|
|
@@ -219,130 +992,6 @@ function isKnownBreakpoint(value) {
|
|
|
219
992
|
return breakpointNames.has(value);
|
|
220
993
|
}
|
|
221
994
|
|
|
222
|
-
// src/util/sha-256.ts
|
|
223
|
-
var ROUND_CONSTANTS = Object.freeze([
|
|
224
|
-
1116352408,
|
|
225
|
-
1899447441,
|
|
226
|
-
3049323471,
|
|
227
|
-
3921009573,
|
|
228
|
-
961987163,
|
|
229
|
-
1508970993,
|
|
230
|
-
2453635748,
|
|
231
|
-
2870763221,
|
|
232
|
-
3624381080,
|
|
233
|
-
310598401,
|
|
234
|
-
607225278,
|
|
235
|
-
1426881987,
|
|
236
|
-
1925078388,
|
|
237
|
-
2162078206,
|
|
238
|
-
2614888103,
|
|
239
|
-
3248222580,
|
|
240
|
-
3835390401,
|
|
241
|
-
4022224774,
|
|
242
|
-
264347078,
|
|
243
|
-
604807628,
|
|
244
|
-
770255983,
|
|
245
|
-
1249150122,
|
|
246
|
-
1555081692,
|
|
247
|
-
1996064986,
|
|
248
|
-
2554220882,
|
|
249
|
-
2821834349,
|
|
250
|
-
2952996808,
|
|
251
|
-
3210313671,
|
|
252
|
-
3336571891,
|
|
253
|
-
3584528711,
|
|
254
|
-
113926993,
|
|
255
|
-
338241895,
|
|
256
|
-
666307205,
|
|
257
|
-
773529912,
|
|
258
|
-
1294757372,
|
|
259
|
-
1396182291,
|
|
260
|
-
1695183700,
|
|
261
|
-
1986661051,
|
|
262
|
-
2177026350,
|
|
263
|
-
2456956037,
|
|
264
|
-
2730485921,
|
|
265
|
-
2820302411,
|
|
266
|
-
3259730800,
|
|
267
|
-
3345764771,
|
|
268
|
-
3516065817,
|
|
269
|
-
3600352804,
|
|
270
|
-
4094571909,
|
|
271
|
-
275423344,
|
|
272
|
-
430227734,
|
|
273
|
-
506948616,
|
|
274
|
-
659060556,
|
|
275
|
-
883997877,
|
|
276
|
-
958139571,
|
|
277
|
-
1322822218,
|
|
278
|
-
1537002063,
|
|
279
|
-
1747873779,
|
|
280
|
-
1955562222,
|
|
281
|
-
2024104815,
|
|
282
|
-
2227730452,
|
|
283
|
-
2361852424,
|
|
284
|
-
2428436474,
|
|
285
|
-
2756734187,
|
|
286
|
-
3204031479,
|
|
287
|
-
3329325298
|
|
288
|
-
]);
|
|
289
|
-
var INITIAL_HASH = Object.freeze([
|
|
290
|
-
1779033703,
|
|
291
|
-
3144134277,
|
|
292
|
-
1013904242,
|
|
293
|
-
2773480762,
|
|
294
|
-
1359893119,
|
|
295
|
-
2600822924,
|
|
296
|
-
528734635,
|
|
297
|
-
1541459225
|
|
298
|
-
]);
|
|
299
|
-
function sha256(value) {
|
|
300
|
-
const source = new TextEncoder().encode(value);
|
|
301
|
-
const paddedLength = Math.ceil((source.length + 9) / 64) * 64;
|
|
302
|
-
const bytes = new Uint8Array(paddedLength);
|
|
303
|
-
bytes.set(source);
|
|
304
|
-
bytes[source.length] = 128;
|
|
305
|
-
const bitLength = source.length * 8;
|
|
306
|
-
const view = new DataView(bytes.buffer);
|
|
307
|
-
view.setUint32(paddedLength - 8, Math.floor(bitLength / 4294967296));
|
|
308
|
-
view.setUint32(paddedLength - 4, bitLength >>> 0);
|
|
309
|
-
const hash = [...INITIAL_HASH];
|
|
310
|
-
const schedule = new Uint32Array(64);
|
|
311
|
-
for (let offset = 0; offset < bytes.length; offset += 64) {
|
|
312
|
-
for (let index = 0; index < 16; index += 1) schedule[index] = view.getUint32(offset + index * 4);
|
|
313
|
-
for (let index = 16; index < schedule.length; index += 1) {
|
|
314
|
-
const earlier = schedule[index - 15];
|
|
315
|
-
const recent = schedule[index - 2];
|
|
316
|
-
const sigma0 = rotateRight(earlier, 7) ^ rotateRight(earlier, 18) ^ earlier >>> 3;
|
|
317
|
-
const sigma1 = rotateRight(recent, 17) ^ rotateRight(recent, 19) ^ recent >>> 10;
|
|
318
|
-
schedule[index] = schedule[index - 16] + sigma0 + schedule[index - 7] + sigma1 >>> 0;
|
|
319
|
-
}
|
|
320
|
-
let [a, b, c, d, e, f, g, h] = hash;
|
|
321
|
-
for (let index = 0; index < schedule.length; index += 1) {
|
|
322
|
-
const sum1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
|
|
323
|
-
const choice = e & f ^ ~e & g;
|
|
324
|
-
const temporary1 = h + sum1 + choice + ROUND_CONSTANTS[index] + schedule[index] >>> 0;
|
|
325
|
-
const sum0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
|
|
326
|
-
const majority = a & b ^ a & c ^ b & c;
|
|
327
|
-
const temporary2 = sum0 + majority >>> 0;
|
|
328
|
-
h = g;
|
|
329
|
-
g = f;
|
|
330
|
-
f = e;
|
|
331
|
-
e = d + temporary1 >>> 0;
|
|
332
|
-
d = c;
|
|
333
|
-
c = b;
|
|
334
|
-
b = a;
|
|
335
|
-
a = temporary1 + temporary2 >>> 0;
|
|
336
|
-
}
|
|
337
|
-
const round = [a, b, c, d, e, f, g, h];
|
|
338
|
-
for (let index = 0; index < hash.length; index += 1) hash[index] = hash[index] + round[index] >>> 0;
|
|
339
|
-
}
|
|
340
|
-
return hash.map((word) => word.toString(16).padStart(8, "0")).join("");
|
|
341
|
-
}
|
|
342
|
-
function rotateRight(value, count) {
|
|
343
|
-
return value >>> count | value << 32 - count;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
995
|
// src/adapter/css/css-invariant.error.ts
|
|
347
996
|
var CssInvariantError = class extends Error {
|
|
348
997
|
constructor(message) {
|
|
@@ -426,8 +1075,8 @@ function canonicalIdentity(family, declarations, context) {
|
|
|
426
1075
|
});
|
|
427
1076
|
}
|
|
428
1077
|
var CssArtifactRegistry = class {
|
|
429
|
-
constructor(
|
|
430
|
-
this.digest =
|
|
1078
|
+
constructor(digest2 = sha256) {
|
|
1079
|
+
this.digest = digest2;
|
|
431
1080
|
}
|
|
432
1081
|
rulesByCanonicalIdentity = /* @__PURE__ */ new Map();
|
|
433
1082
|
canonicalIdentityByDigest = /* @__PURE__ */ new Map();
|
|
@@ -649,7 +1298,8 @@ var CssRenderer = class {
|
|
|
649
1298
|
eligibility(input) {
|
|
650
1299
|
const family = this.cssFamily(input);
|
|
651
1300
|
if (family === void 0) return targetUnsupported(input);
|
|
652
|
-
if (input.breakpoint !== void 0 && !supportedBreakpoints.has(input.breakpoint)
|
|
1301
|
+
if (input.breakpoint !== void 0 && !supportedBreakpoints.has(input.breakpoint) && !Object.hasOwn(this.breakpointConfig.sourceBreakpoints ?? {}, input.breakpoint))
|
|
1302
|
+
return targetUnsupported(input);
|
|
653
1303
|
return void 0;
|
|
654
1304
|
}
|
|
655
1305
|
render(plan, _context) {
|
|
@@ -712,18 +1362,119 @@ var CssRenderer = class {
|
|
|
712
1362
|
}
|
|
713
1363
|
};
|
|
714
1364
|
|
|
715
|
-
// src/
|
|
716
|
-
var
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
1365
|
+
// src/config/tailwind-builtin-variants.ts
|
|
1366
|
+
var tailwindBuiltinVariants = Object.freeze([
|
|
1367
|
+
"*",
|
|
1368
|
+
"**",
|
|
1369
|
+
"@",
|
|
1370
|
+
"@max",
|
|
1371
|
+
"@min",
|
|
1372
|
+
"active",
|
|
1373
|
+
"after",
|
|
1374
|
+
"any-pointer-coarse",
|
|
1375
|
+
"any-pointer-fine",
|
|
1376
|
+
"any-pointer-none",
|
|
1377
|
+
"aria",
|
|
1378
|
+
"autofill",
|
|
1379
|
+
"backdrop",
|
|
1380
|
+
"before",
|
|
1381
|
+
"checked",
|
|
1382
|
+
"contrast-less",
|
|
1383
|
+
"contrast-more",
|
|
1384
|
+
"dark",
|
|
1385
|
+
"data",
|
|
1386
|
+
"default",
|
|
1387
|
+
"details-content",
|
|
1388
|
+
"disabled",
|
|
1389
|
+
"empty",
|
|
1390
|
+
"enabled",
|
|
1391
|
+
"even",
|
|
1392
|
+
"file",
|
|
1393
|
+
"first",
|
|
1394
|
+
"first-letter",
|
|
1395
|
+
"first-line",
|
|
1396
|
+
"first-of-type",
|
|
1397
|
+
"focus",
|
|
1398
|
+
"focus-visible",
|
|
1399
|
+
"focus-within",
|
|
1400
|
+
"forced-colors",
|
|
1401
|
+
"group",
|
|
1402
|
+
"has",
|
|
1403
|
+
"hover",
|
|
1404
|
+
"in",
|
|
1405
|
+
"in-range",
|
|
1406
|
+
"indeterminate",
|
|
1407
|
+
"inert",
|
|
1408
|
+
"invalid",
|
|
1409
|
+
"inverted-colors",
|
|
1410
|
+
"landscape",
|
|
1411
|
+
"last",
|
|
1412
|
+
"last-of-type",
|
|
1413
|
+
"ltr",
|
|
1414
|
+
"marker",
|
|
1415
|
+
"max",
|
|
1416
|
+
"min",
|
|
1417
|
+
"motion-reduce",
|
|
1418
|
+
"motion-safe",
|
|
1419
|
+
"noscript",
|
|
1420
|
+
"not",
|
|
1421
|
+
"nth",
|
|
1422
|
+
"nth-last",
|
|
1423
|
+
"nth-last-of-type",
|
|
1424
|
+
"nth-of-type",
|
|
1425
|
+
"odd",
|
|
1426
|
+
"only",
|
|
1427
|
+
"only-of-type",
|
|
1428
|
+
"open",
|
|
1429
|
+
"optional",
|
|
1430
|
+
"out-of-range",
|
|
1431
|
+
"peer",
|
|
1432
|
+
"placeholder",
|
|
1433
|
+
"placeholder-shown",
|
|
1434
|
+
"pointer-coarse",
|
|
1435
|
+
"pointer-fine",
|
|
1436
|
+
"pointer-none",
|
|
1437
|
+
"portrait",
|
|
1438
|
+
"print",
|
|
1439
|
+
"read-only",
|
|
1440
|
+
"required",
|
|
1441
|
+
"rtl",
|
|
1442
|
+
"selection",
|
|
1443
|
+
"starting",
|
|
1444
|
+
"supports",
|
|
1445
|
+
"target",
|
|
1446
|
+
"user-invalid",
|
|
1447
|
+
"user-valid",
|
|
1448
|
+
"valid",
|
|
1449
|
+
"visited"
|
|
1450
|
+
]);
|
|
1451
|
+
|
|
1452
|
+
// src/config/tailwind-candidate-profile.ts
|
|
1453
|
+
function renderTargetCandidate(candidate, profile) {
|
|
1454
|
+
const match = /^\[@media_screen_and_\(min-width:_(\d+(?:\.\d+)?)px\)\]:(.+)$/.exec(candidate);
|
|
1455
|
+
if (match) {
|
|
1456
|
+
const exact = Object.entries(profile.breakpoints).find(
|
|
1457
|
+
([name, setting]) => setting.confidence !== "unknown" && setting.value === `${match[1]}px` && isNamedBreakpoint(name)
|
|
1458
|
+
);
|
|
1459
|
+
if (exact) candidate = `[@media_screen]:${exact[0]}:${match[2]}`;
|
|
724
1460
|
}
|
|
725
|
-
|
|
726
|
-
}
|
|
1461
|
+
return profile.prefix.value ? `${profile.prefix.value}:${candidate}` : candidate;
|
|
1462
|
+
}
|
|
1463
|
+
function normalizeTargetCandidate(candidate, profile) {
|
|
1464
|
+
const prefix = profile.prefix.value;
|
|
1465
|
+
if (prefix && candidate.startsWith(`${prefix}:`)) candidate = candidate.slice(prefix.length + 1);
|
|
1466
|
+
const match = /^\[@media_screen\]:([^:]+):(.+)$/.exec(candidate);
|
|
1467
|
+
if (match) {
|
|
1468
|
+
const setting = profile.breakpoints[match[1]];
|
|
1469
|
+
const width = setting?.value;
|
|
1470
|
+
if (setting?.confidence !== "unknown" && isNamedBreakpoint(match[1]) && width?.endsWith("px"))
|
|
1471
|
+
return `[@media_screen_and_(min-width:_${width})]:${match[2]}`;
|
|
1472
|
+
}
|
|
1473
|
+
return candidate;
|
|
1474
|
+
}
|
|
1475
|
+
function isNamedBreakpoint(name) {
|
|
1476
|
+
return !tailwindBuiltinVariants.some((root) => name === root || name.startsWith(`${root}-`));
|
|
1477
|
+
}
|
|
727
1478
|
|
|
728
1479
|
// src/edit/html-attribute-value.ts
|
|
729
1480
|
var htmlSourceWhitespace = /[\t\n\f\r ]/u;
|
|
@@ -897,7 +1648,10 @@ var BreakpointCatalog = class {
|
|
|
897
1648
|
this.configuredDefinitions = new Map([
|
|
898
1649
|
...definitionsByAlias,
|
|
899
1650
|
...config.orientationBreakpoints ? orientations.map((definition) => [definition.alias, definition]) : [],
|
|
900
|
-
...config.printWithBreakpoints === void 0 ? [] : [[frozenPrintDefinition.alias, frozenPrintDefinition]]
|
|
1651
|
+
...config.printWithBreakpoints === void 0 ? [] : [[frozenPrintDefinition.alias, frozenPrintDefinition]],
|
|
1652
|
+
...Object.entries(config.sourceBreakpoints ?? {}).map(
|
|
1653
|
+
([alias, value]) => [alias, sourceBreakpointDefinition(alias, value)]
|
|
1654
|
+
)
|
|
901
1655
|
]);
|
|
902
1656
|
}
|
|
903
1657
|
classify(alias) {
|
|
@@ -2718,18 +3472,25 @@ var TailwindCandidateClassifier = class {
|
|
|
2718
3472
|
|
|
2719
3473
|
// src/evidence/tailwind-source-property.evidence.ts
|
|
2720
3474
|
var TailwindSourcePropertyEvidence = class {
|
|
3475
|
+
constructor(profile) {
|
|
3476
|
+
this.profile = profile;
|
|
3477
|
+
}
|
|
2721
3478
|
classifier = new TailwindCandidateClassifier();
|
|
2722
3479
|
styleEncoder = new TailwindArbitraryPropertyEncoder();
|
|
2723
3480
|
classifyClassToken(token) {
|
|
2724
|
-
const
|
|
3481
|
+
const prefix = this.profile?.prefix.value;
|
|
3482
|
+
if (prefix && !token.startsWith(`${prefix}:`))
|
|
3483
|
+
return { status: "unverified", reason: "Class is not a utility in the declared prefixed target." };
|
|
3484
|
+
const normalized = prefix ? token.slice(prefix.length + 1) : token;
|
|
3485
|
+
const classification = this.classifier.classify(normalized);
|
|
2725
3486
|
if (classification.status === "unverified") return classification;
|
|
2726
|
-
const display = describeTailwindDisplay(
|
|
3487
|
+
const display = describeTailwindDisplay(normalized);
|
|
2727
3488
|
return {
|
|
2728
3489
|
status: "verified",
|
|
2729
3490
|
evidence: {
|
|
2730
3491
|
source: token,
|
|
2731
3492
|
properties: classification.descriptor.cssProperties,
|
|
2732
|
-
important: classification.descriptor.important,
|
|
3493
|
+
important: classification.descriptor.important || this.profile?.important.value === "important",
|
|
2733
3494
|
activation: classification.descriptor.activation,
|
|
2734
3495
|
...display === void 0 ? {} : { display: display.utility }
|
|
2735
3496
|
}
|
|
@@ -3313,15 +4074,29 @@ function compatibleVisibilityClasses(plan, existingClassNames) {
|
|
|
3313
4074
|
var TailwindRenderer = class {
|
|
3314
4075
|
target = "tailwind";
|
|
3315
4076
|
breakpointConfig;
|
|
3316
|
-
sourcePropertyEvidence
|
|
4077
|
+
sourcePropertyEvidence;
|
|
4078
|
+
targetProfile;
|
|
3317
4079
|
responsiveEmitter = new ResponsiveVariantEmitter();
|
|
3318
4080
|
visibilityEmitter = new VisibilityEmitter();
|
|
3319
4081
|
extendedEmitter = new ExtendedResponsiveEmitter();
|
|
3320
4082
|
gridRenderer = new TailwindGridRenderer();
|
|
3321
4083
|
constructor(config = { orientationBreakpoints: false }) {
|
|
4084
|
+
this.targetProfile = config.targetProfile ?? resolveTailwindTargetProfile();
|
|
3322
4085
|
this.breakpointConfig = Object.freeze({ ...config });
|
|
4086
|
+
this.sourcePropertyEvidence = new TailwindSourcePropertyEvidence(this.targetProfile);
|
|
3323
4087
|
}
|
|
3324
4088
|
eligibility(input) {
|
|
4089
|
+
const unresolved2 = this.targetProfile.diagnostics.filter(
|
|
4090
|
+
(item) => item.code === "tailwind-prefix-conflict" || item.code === "tailwind-target-unknown" || ["tailwind-config-external", "tailwind-plugin-external", "tailwind-import-unresolved"].includes(item.code) && (this.targetProfile.coreUtilities.value === "unknown" || this.targetProfile.prefix.confidence !== "explicit" || this.targetProfile.important.confidence !== "explicit")
|
|
4091
|
+
);
|
|
4092
|
+
if (unresolved2.length)
|
|
4093
|
+
return {
|
|
4094
|
+
status: "review",
|
|
4095
|
+
input,
|
|
4096
|
+
code: "context-unverified",
|
|
4097
|
+
reason: `Target configuration is unresolved: ${unresolved2.map((item) => item.message).join(" ")}`,
|
|
4098
|
+
suggestion: "Resolve the target stylesheet or supply explicit migration profile settings before migrating."
|
|
4099
|
+
};
|
|
3325
4100
|
if (input.binding !== "property") {
|
|
3326
4101
|
if (!sharedDirectives.has(input.directive) && !visibilityDirectives.has(input.directive) && !extendedDirectives.has(input.directive) && !gridDirectives.has(input.directive)) {
|
|
3327
4102
|
return {
|
|
@@ -3335,7 +4110,12 @@ var TailwindRenderer = class {
|
|
|
3335
4110
|
}
|
|
3336
4111
|
return void 0;
|
|
3337
4112
|
}
|
|
3338
|
-
render(plan,
|
|
4113
|
+
render(plan, context) {
|
|
4114
|
+
const result2 = this.renderUnprefixed(plan, context);
|
|
4115
|
+
if (result2.status !== "converted") return result2;
|
|
4116
|
+
return { ...result2, classNames: result2.classNames.map((token) => renderTargetCandidate(token, this.targetProfile)) };
|
|
4117
|
+
}
|
|
4118
|
+
renderUnprefixed(plan, _context) {
|
|
3339
4119
|
const inputFamily = directiveFamily(plan.input.directive);
|
|
3340
4120
|
if (inputFamily !== plan.family) {
|
|
3341
4121
|
throw new MigrationApplicationError(
|
|
@@ -3413,7 +4193,15 @@ var TailwindRenderer = class {
|
|
|
3413
4193
|
};
|
|
3414
4194
|
}
|
|
3415
4195
|
resolveConflicts(plans, context) {
|
|
3416
|
-
|
|
4196
|
+
const normalize8 = (token) => normalizeTargetCandidate(token, this.targetProfile);
|
|
4197
|
+
const normalized = plans.map(
|
|
4198
|
+
(plan) => plan.status === "converted" ? { ...plan, classNames: plan.classNames.map((token) => normalize8(token)) } : plan
|
|
4199
|
+
);
|
|
4200
|
+
const resolved2 = this.resolveClassConflicts(
|
|
4201
|
+
normalized,
|
|
4202
|
+
context.existingClassNames.map((token) => normalize8(token))
|
|
4203
|
+
);
|
|
4204
|
+
return resolved2.map((plan, index) => plan.status === "converted" ? plans[index] : plan);
|
|
3417
4205
|
}
|
|
3418
4206
|
record(_plans) {
|
|
3419
4207
|
}
|
|
@@ -3447,6 +4235,10 @@ var TailwindRenderer = class {
|
|
|
3447
4235
|
} : plan
|
|
3448
4236
|
);
|
|
3449
4237
|
}
|
|
4238
|
+
unprefix(token) {
|
|
4239
|
+
const prefix = this.targetProfile.prefix.value;
|
|
4240
|
+
return prefix && token.startsWith(`${prefix}:`) ? token.slice(prefix.length + 1) : token;
|
|
4241
|
+
}
|
|
3450
4242
|
decorate(classNames, plan) {
|
|
3451
4243
|
return plan.activations.flatMap(
|
|
3452
4244
|
(planActivation) => planActivation.kind === "base" ? classNames : classNames.flatMap((className) => this.responsiveEmitter.emit(planActivation.definition, className))
|
|
@@ -3489,7 +4281,7 @@ var TailwindRenderer = class {
|
|
|
3489
4281
|
return this.extendedEmitter.emitClass({
|
|
3490
4282
|
input: plan.input,
|
|
3491
4283
|
activation: itemActivation,
|
|
3492
|
-
value: { tokens: state.tokens.map((token) => token.source) }
|
|
4284
|
+
value: { tokens: state.tokens.map((token) => this.unprefix(token.source)) }
|
|
3493
4285
|
});
|
|
3494
4286
|
})
|
|
3495
4287
|
)
|
|
@@ -3615,7 +4407,7 @@ var AdapterFactory = class {
|
|
|
3615
4407
|
};
|
|
3616
4408
|
|
|
3617
4409
|
// src/pipeline/analyze/analyze-project.stage.ts
|
|
3618
|
-
import { readFile } from "fs/promises";
|
|
4410
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
3619
4411
|
|
|
3620
4412
|
// src/analyzer/flex-layout-attribute.analyzer.ts
|
|
3621
4413
|
var responsiveOnlyDirectives = /* @__PURE__ */ new Set(["class", "ngClass", "style", "ngStyle"]);
|
|
@@ -3801,10 +4593,10 @@ var AngularTemplateParser = class {
|
|
|
3801
4593
|
};
|
|
3802
4594
|
|
|
3803
4595
|
// src/pipeline/analyzed-project.ts
|
|
3804
|
-
import * as
|
|
4596
|
+
import * as path4 from "path";
|
|
3805
4597
|
|
|
3806
4598
|
// src/pipeline/project-manifest.ts
|
|
3807
|
-
import * as
|
|
4599
|
+
import * as path3 from "path";
|
|
3808
4600
|
function migrationInvocation(invocation) {
|
|
3809
4601
|
return freezeMigrationInvocation({
|
|
3810
4602
|
inputPath: invocation.inputPath,
|
|
@@ -3840,7 +4632,7 @@ function freezeMigrationInvocation(invocation) {
|
|
|
3840
4632
|
});
|
|
3841
4633
|
}
|
|
3842
4634
|
function normalizedAbsolutePath(value) {
|
|
3843
|
-
return
|
|
4635
|
+
return path3.normalize(path3.resolve(value));
|
|
3844
4636
|
}
|
|
3845
4637
|
|
|
3846
4638
|
// src/pipeline/analyzed-project.ts
|
|
@@ -3935,7 +4727,7 @@ function freezeLocatedInput(input) {
|
|
|
3935
4727
|
});
|
|
3936
4728
|
}
|
|
3937
4729
|
function normalizedAbsolutePath2(value) {
|
|
3938
|
-
return
|
|
4730
|
+
return path4.normalize(path4.resolve(value));
|
|
3939
4731
|
}
|
|
3940
4732
|
function sequenceInvariant(paths = []) {
|
|
3941
4733
|
return internalInvariant(
|
|
@@ -3949,7 +4741,7 @@ function internalInvariant(message, paths = []) {
|
|
|
3949
4741
|
|
|
3950
4742
|
// src/pipeline/analyze/analyze-project.stage.ts
|
|
3951
4743
|
var nodeSourceReader = Object.freeze({
|
|
3952
|
-
read: (
|
|
4744
|
+
read: (path19) => readFile2(path19, "utf8")
|
|
3953
4745
|
});
|
|
3954
4746
|
var AnalyzeProjectStage = class {
|
|
3955
4747
|
constructor(sourceReader = nodeSourceReader, parser = new AngularTemplateParser(), analyzer = new TemplateAnalyzer()) {
|
|
@@ -3996,13 +4788,13 @@ function runtimeArtifact(context, artifact) {
|
|
|
3996
4788
|
[artifact.path]
|
|
3997
4789
|
);
|
|
3998
4790
|
}
|
|
3999
|
-
function
|
|
4791
|
+
function identity2(stat4) {
|
|
4000
4792
|
return { dev: String(stat4.dev), ino: String(stat4.ino) };
|
|
4001
4793
|
}
|
|
4002
4794
|
function fileMode(stat4) {
|
|
4003
4795
|
return Number(stat4.mode) & 4095;
|
|
4004
4796
|
}
|
|
4005
|
-
function
|
|
4797
|
+
function sameIdentity2(left, right) {
|
|
4006
4798
|
return left.dev === right.dev && left.ino === right.ino;
|
|
4007
4799
|
}
|
|
4008
4800
|
function sameArtifactState(left, right) {
|
|
@@ -4014,14 +4806,14 @@ function required(value) {
|
|
|
4014
4806
|
if (value === void 0) throw new Error("Missing transaction state.");
|
|
4015
4807
|
return value;
|
|
4016
4808
|
}
|
|
4017
|
-
function
|
|
4809
|
+
function isEnoent2(error) {
|
|
4018
4810
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
4019
4811
|
}
|
|
4020
4812
|
function isDirectoryNotEmpty(error) {
|
|
4021
4813
|
return typeof error === "object" && error !== null && "code" in error && (error.code === "ENOTEMPTY" || error.code === "EEXIST");
|
|
4022
4814
|
}
|
|
4023
|
-
function pathDepth(
|
|
4024
|
-
return
|
|
4815
|
+
function pathDepth(path19) {
|
|
4816
|
+
return path19.split(/[\\/]/u).filter(Boolean).length;
|
|
4025
4817
|
}
|
|
4026
4818
|
function recoveryOutcome(paths, failures) {
|
|
4027
4819
|
return { paths: sortedUnique(paths), failures: Object.freeze([...failures]) };
|
|
@@ -4081,13 +4873,13 @@ var FileSystemCleanupUnit = class {
|
|
|
4081
4873
|
try {
|
|
4082
4874
|
await this.port.assertNamespace(item);
|
|
4083
4875
|
const before = await this.port.lstat(owned.path);
|
|
4084
|
-
if (!
|
|
4876
|
+
if (!sameIdentity2(identity2(before), owned.identity) || before.isSymbolicLink() || !before.isFile()) {
|
|
4085
4877
|
paths.add(owned.publicPath);
|
|
4086
4878
|
failures.push(new Error("Invocation-owned file identity could not be confirmed."));
|
|
4087
4879
|
return;
|
|
4088
4880
|
}
|
|
4089
4881
|
} catch (error) {
|
|
4090
|
-
if (
|
|
4882
|
+
if (isEnoent2(error)) {
|
|
4091
4883
|
this.port.journal.markOwnedFileAbsent(item, owned.path);
|
|
4092
4884
|
return;
|
|
4093
4885
|
}
|
|
@@ -4098,13 +4890,13 @@ var FileSystemCleanupUnit = class {
|
|
|
4098
4890
|
try {
|
|
4099
4891
|
await this.port.unlink(owned.path);
|
|
4100
4892
|
} catch (error) {
|
|
4101
|
-
if (!
|
|
4893
|
+
if (!isEnoent2(error)) failures.push(error);
|
|
4102
4894
|
}
|
|
4103
4895
|
try {
|
|
4104
4896
|
await this.port.lstat(owned.path);
|
|
4105
4897
|
paths.add(owned.publicPath);
|
|
4106
4898
|
} catch (error) {
|
|
4107
|
-
if (
|
|
4899
|
+
if (isEnoent2(error)) this.port.journal.markOwnedFileAbsent(item, owned.path);
|
|
4108
4900
|
else {
|
|
4109
4901
|
paths.add(owned.publicPath);
|
|
4110
4902
|
failures.push(error);
|
|
@@ -4120,13 +4912,13 @@ var FileSystemCleanupUnit = class {
|
|
|
4120
4912
|
}
|
|
4121
4913
|
try {
|
|
4122
4914
|
const namespaceStat = await this.port.lstat(namespace.path);
|
|
4123
|
-
if (!
|
|
4915
|
+
if (!sameIdentity2(identity2(namespaceStat), namespace.identity) || namespaceStat.isSymbolicLink() || !namespaceStat.isDirectory()) {
|
|
4124
4916
|
paths.add(namespace.publicPath);
|
|
4125
4917
|
failures.push(new Error("Invocation namespace identity could not be confirmed."));
|
|
4126
4918
|
return;
|
|
4127
4919
|
}
|
|
4128
4920
|
} catch (error) {
|
|
4129
|
-
if (
|
|
4921
|
+
if (isEnoent2(error)) {
|
|
4130
4922
|
this.port.journal.markNamespaceAbsent(item);
|
|
4131
4923
|
return;
|
|
4132
4924
|
}
|
|
@@ -4137,13 +4929,13 @@ var FileSystemCleanupUnit = class {
|
|
|
4137
4929
|
try {
|
|
4138
4930
|
await this.port.rmdir(namespace.path);
|
|
4139
4931
|
} catch (error) {
|
|
4140
|
-
if (!isDirectoryNotEmpty(error) && !
|
|
4932
|
+
if (!isDirectoryNotEmpty(error) && !isEnoent2(error)) failures.push(error);
|
|
4141
4933
|
}
|
|
4142
4934
|
try {
|
|
4143
4935
|
await this.port.lstat(namespace.path);
|
|
4144
4936
|
paths.add(namespace.publicPath);
|
|
4145
4937
|
} catch (error) {
|
|
4146
|
-
if (
|
|
4938
|
+
if (isEnoent2(error)) this.port.journal.markNamespaceAbsent(item);
|
|
4147
4939
|
else {
|
|
4148
4940
|
paths.add(namespace.publicPath);
|
|
4149
4941
|
failures.push(error);
|
|
@@ -4169,7 +4961,7 @@ var FileSystemCleanupUnit = class {
|
|
|
4169
4961
|
try {
|
|
4170
4962
|
before = await this.port.lstat(directory.path);
|
|
4171
4963
|
} catch (error) {
|
|
4172
|
-
if (
|
|
4964
|
+
if (isEnoent2(error)) {
|
|
4173
4965
|
this.port.journal.markCreatedDirectoryAbsent(directory.path);
|
|
4174
4966
|
continue;
|
|
4175
4967
|
}
|
|
@@ -4177,7 +4969,7 @@ var FileSystemCleanupUnit = class {
|
|
|
4177
4969
|
failures.push(error);
|
|
4178
4970
|
continue;
|
|
4179
4971
|
}
|
|
4180
|
-
if (!
|
|
4972
|
+
if (!sameIdentity2(identity2(before), directory.identity) || before.isSymbolicLink() || !before.isDirectory()) {
|
|
4181
4973
|
for (const publicPath of directory.publicPaths) paths.add(publicPath);
|
|
4182
4974
|
continue;
|
|
4183
4975
|
}
|
|
@@ -4192,17 +4984,17 @@ var FileSystemCleanupUnit = class {
|
|
|
4192
4984
|
after = await this.port.lstatOrAbsent(directory.path);
|
|
4193
4985
|
} catch (error) {
|
|
4194
4986
|
for (const publicPath of directory.publicPaths) paths.add(publicPath);
|
|
4195
|
-
if (removalFailure !== void 0 && !
|
|
4987
|
+
if (removalFailure !== void 0 && !isEnoent2(removalFailure)) failures.push(removalFailure);
|
|
4196
4988
|
failures.push(error);
|
|
4197
4989
|
continue;
|
|
4198
4990
|
}
|
|
4199
4991
|
if (after === "absent") {
|
|
4200
4992
|
this.port.journal.markCreatedDirectoryAbsent(directory.path);
|
|
4201
|
-
if (removalFailure !== void 0 && !
|
|
4993
|
+
if (removalFailure !== void 0 && !isEnoent2(removalFailure)) failures.push(removalFailure);
|
|
4202
4994
|
continue;
|
|
4203
4995
|
}
|
|
4204
4996
|
for (const publicPath of directory.publicPaths) paths.add(publicPath);
|
|
4205
|
-
if (removalFailure !== void 0 && !
|
|
4997
|
+
if (removalFailure !== void 0 && !isEnoent2(removalFailure)) failures.push(removalFailure);
|
|
4206
4998
|
}
|
|
4207
4999
|
}
|
|
4208
5000
|
async collectUnconfirmedPaths(paths, failures) {
|
|
@@ -4210,7 +5002,7 @@ var FileSystemCleanupUnit = class {
|
|
|
4210
5002
|
try {
|
|
4211
5003
|
await this.port.lstat(candidate);
|
|
4212
5004
|
} catch (error) {
|
|
4213
|
-
if (
|
|
5005
|
+
if (isEnoent2(error)) continue;
|
|
4214
5006
|
failures.push(error);
|
|
4215
5007
|
}
|
|
4216
5008
|
for (const publicPath of publicPaths) paths.add(publicPath);
|
|
@@ -4228,7 +5020,7 @@ var FileSystemCleanupUnit = class {
|
|
|
4228
5020
|
if (!sameArtifactState(observed, item.artifact.original)) return false;
|
|
4229
5021
|
if (observed.status === "absent") return true;
|
|
4230
5022
|
return [item.originalIdentity, item.restoredIdentity].some(
|
|
4231
|
-
(expected) => expected !== void 0 &&
|
|
5023
|
+
(expected) => expected !== void 0 && sameIdentity2(observed.identity, expected)
|
|
4232
5024
|
);
|
|
4233
5025
|
}
|
|
4234
5026
|
};
|
|
@@ -4281,7 +5073,7 @@ var FileSystemCommitUnit = class {
|
|
|
4281
5073
|
}
|
|
4282
5074
|
async captureOriginal(item, signal) {
|
|
4283
5075
|
const firstCapture = await this.port.observePublic(item);
|
|
4284
|
-
if (!sameArtifactState(firstCapture, item.artifact.original) || firstCapture.status !== "present" || !item.originalIdentity || !
|
|
5076
|
+
if (!sameArtifactState(firstCapture, item.artifact.original) || firstCapture.status !== "present" || !item.originalIdentity || !sameIdentity2(firstCapture.identity, item.originalIdentity)) {
|
|
4285
5077
|
throw this.port.concurrentModification(item.artifact.path);
|
|
4286
5078
|
}
|
|
4287
5079
|
const backup = await this.port.createBackupFile(item, firstCapture.contents, signal, firstCapture.mode);
|
|
@@ -4289,7 +5081,7 @@ var FileSystemCommitUnit = class {
|
|
|
4289
5081
|
throw this.port.ownershipFailure(item.artifact.path);
|
|
4290
5082
|
}
|
|
4291
5083
|
const secondCapture = await this.port.observePublic(item);
|
|
4292
|
-
if (secondCapture.status !== "present" || !
|
|
5084
|
+
if (secondCapture.status !== "present" || !sameIdentity2(secondCapture.identity, firstCapture.identity) || secondCapture.contents !== firstCapture.contents) {
|
|
4293
5085
|
throw this.port.concurrentModification(item.artifact.path);
|
|
4294
5086
|
}
|
|
4295
5087
|
await this.quarantineOriginal(item, firstCapture);
|
|
@@ -4298,7 +5090,7 @@ var FileSystemCommitUnit = class {
|
|
|
4298
5090
|
await this.port.assertParentChain(item);
|
|
4299
5091
|
await this.port.assertNamespace(item);
|
|
4300
5092
|
const immediatelyBefore = await this.port.observePublic(item);
|
|
4301
|
-
if (immediatelyBefore.status !== "present" || !
|
|
5093
|
+
if (immediatelyBefore.status !== "present" || !sameIdentity2(immediatelyBefore.identity, captured.identity) || immediatelyBefore.contents !== captured.contents) {
|
|
4302
5094
|
throw this.port.concurrentModification(item.artifact.path);
|
|
4303
5095
|
}
|
|
4304
5096
|
let quarantine = this.port.journal.addQuarantine(
|
|
@@ -4314,9 +5106,9 @@ var FileSystemCommitUnit = class {
|
|
|
4314
5106
|
}
|
|
4315
5107
|
const quarantinedStat = await this.port.lstatOrAbsent(quarantine.path);
|
|
4316
5108
|
if (quarantinedStat !== "absent") {
|
|
4317
|
-
quarantine = this.port.journal.confirmOwnedFile(item, quarantine.path,
|
|
5109
|
+
quarantine = this.port.journal.confirmOwnedFile(item, quarantine.path, identity2(quarantinedStat));
|
|
4318
5110
|
const quarantinedContents = await this.port.readOwnedFile(item, quarantine.path);
|
|
4319
|
-
if (!
|
|
5111
|
+
if (!sameIdentity2(required(quarantine.identity), captured.identity) || quarantinedContents !== captured.contents) {
|
|
4320
5112
|
this.port.journal.setOwnedFilePreserved(item, quarantine.path, true);
|
|
4321
5113
|
await this.restorePreservedQuarantine(item, quarantine);
|
|
4322
5114
|
throw this.port.concurrentModification(item.artifact.path, renameFailure);
|
|
@@ -4328,7 +5120,7 @@ var FileSystemCommitUnit = class {
|
|
|
4328
5120
|
return;
|
|
4329
5121
|
}
|
|
4330
5122
|
const destination = await this.port.lstatOrAbsent(item.artifact.path);
|
|
4331
|
-
if (renameFailure !== void 0 && destination !== "absent" &&
|
|
5123
|
+
if (renameFailure !== void 0 && destination !== "absent" && sameIdentity2(identity2(destination), captured.identity)) {
|
|
4332
5124
|
throw renameFailure;
|
|
4333
5125
|
}
|
|
4334
5126
|
throw this.port.concurrentModification(item.artifact.path, renameFailure);
|
|
@@ -4345,7 +5137,7 @@ var FileSystemCommitUnit = class {
|
|
|
4345
5137
|
linkFailure = error;
|
|
4346
5138
|
}
|
|
4347
5139
|
const destination = await this.port.lstatOrAbsent(item.artifact.path);
|
|
4348
|
-
if (destination !== "absent" && stage.identity &&
|
|
5140
|
+
if (destination !== "absent" && stage.identity && sameIdentity2(identity2(destination), stage.identity)) {
|
|
4349
5141
|
this.port.journal.recordInstalledIdentity(item, stage.identity);
|
|
4350
5142
|
await this.port.assertParentChain(item);
|
|
4351
5143
|
if (linkFailure !== void 0) throw linkFailure;
|
|
@@ -4364,7 +5156,7 @@ var FileSystemCommitUnit = class {
|
|
|
4364
5156
|
this.port.journal.recordRecoveryFailure(error);
|
|
4365
5157
|
}
|
|
4366
5158
|
const destination = await this.port.lstatOrAbsent(item.artifact.path);
|
|
4367
|
-
if (destination !== "absent" &&
|
|
5159
|
+
if (destination !== "absent" && sameIdentity2(identity2(destination), quarantineIdentity)) {
|
|
4368
5160
|
this.port.journal.setOwnedFilePreserved(item, quarantine.path, false);
|
|
4369
5161
|
}
|
|
4370
5162
|
}
|
|
@@ -4426,8 +5218,8 @@ var FileSystemRollbackUnit = class {
|
|
|
4426
5218
|
}
|
|
4427
5219
|
if (current !== "unknown" && this.isConfirmedOriginal(item, current)) return;
|
|
4428
5220
|
if (current !== "unknown" && current.status === "present") {
|
|
4429
|
-
if (!item.installedIdentity || !
|
|
4430
|
-
if (item.stage && item.installedIdentity &&
|
|
5221
|
+
if (!item.installedIdentity || !sameIdentity2(current.identity, item.installedIdentity) || item.artifact.proposed.status !== "present" || current.contents !== item.artifact.proposed.contents) {
|
|
5222
|
+
if (item.stage && item.installedIdentity && sameIdentity2(current.identity, item.installedIdentity)) {
|
|
4431
5223
|
this.port.journal.setOwnedFilePreserved(item, item.stage.path, true);
|
|
4432
5224
|
}
|
|
4433
5225
|
return;
|
|
@@ -4449,7 +5241,7 @@ var FileSystemRollbackUnit = class {
|
|
|
4449
5241
|
failures.push(error);
|
|
4450
5242
|
}
|
|
4451
5243
|
const restored = await this.port.lstatOrAbsent(item.artifact.path);
|
|
4452
|
-
if (restored !== "absent" && !restored.isSymbolicLink() && restored.isFile() && backup.identity &&
|
|
5244
|
+
if (restored !== "absent" && !restored.isSymbolicLink() && restored.isFile() && backup.identity && sameIdentity2(identity2(restored), backup.identity)) {
|
|
4453
5245
|
this.port.journal.recordRestoredIdentity(item, backup.identity);
|
|
4454
5246
|
}
|
|
4455
5247
|
} catch (error) {
|
|
@@ -4465,7 +5257,7 @@ var FileSystemRollbackUnit = class {
|
|
|
4465
5257
|
await this.port.assertParentChain(item);
|
|
4466
5258
|
await this.port.assertNamespace(item);
|
|
4467
5259
|
const before = await this.port.observePublic(item);
|
|
4468
|
-
if (before.status !== "present" || !
|
|
5260
|
+
if (before.status !== "present" || !sameIdentity2(before.identity, current.identity) || before.contents !== current.contents) {
|
|
4469
5261
|
return false;
|
|
4470
5262
|
}
|
|
4471
5263
|
await this.port.assertExpectedAbsent(quarantine.path, item.artifact.path);
|
|
@@ -4476,9 +5268,9 @@ var FileSystemRollbackUnit = class {
|
|
|
4476
5268
|
}
|
|
4477
5269
|
const quarantined = await this.port.lstatOrAbsent(quarantine.path);
|
|
4478
5270
|
if (quarantined === "absent") return false;
|
|
4479
|
-
quarantine = this.port.journal.confirmOwnedFile(item, quarantine.path,
|
|
5271
|
+
quarantine = this.port.journal.confirmOwnedFile(item, quarantine.path, identity2(quarantined));
|
|
4480
5272
|
this.port.journal.setOwnedFilePreserved(item, quarantine.path, true);
|
|
4481
|
-
if (!
|
|
5273
|
+
if (!sameIdentity2(required(quarantine.identity), current.identity)) {
|
|
4482
5274
|
await this.restorePreservedQuarantine(item, quarantine, failures);
|
|
4483
5275
|
return false;
|
|
4484
5276
|
}
|
|
@@ -4502,7 +5294,7 @@ var FileSystemRollbackUnit = class {
|
|
|
4502
5294
|
failures.push(error);
|
|
4503
5295
|
}
|
|
4504
5296
|
const destination = await this.port.lstatOrAbsent(item.artifact.path);
|
|
4505
|
-
if (destination !== "absent" &&
|
|
5297
|
+
if (destination !== "absent" && sameIdentity2(identity2(destination), quarantineIdentity)) {
|
|
4506
5298
|
this.port.journal.setOwnedFilePreserved(item, quarantine.path, false);
|
|
4507
5299
|
}
|
|
4508
5300
|
}
|
|
@@ -4518,7 +5310,7 @@ var FileSystemRollbackUnit = class {
|
|
|
4518
5310
|
if (!sameArtifactState(observed, item.artifact.original)) return false;
|
|
4519
5311
|
if (observed.status === "absent") return true;
|
|
4520
5312
|
return [item.originalIdentity, item.restoredIdentity].some(
|
|
4521
|
-
(expected) => expected !== void 0 &&
|
|
5313
|
+
(expected) => expected !== void 0 && sameIdentity2(observed.identity, expected)
|
|
4522
5314
|
);
|
|
4523
5315
|
}
|
|
4524
5316
|
};
|
|
@@ -4593,7 +5385,7 @@ var FileSystemStagingUnit = class {
|
|
|
4593
5385
|
if (createdStat.isSymbolicLink() || !createdStat.isDirectory()) {
|
|
4594
5386
|
throw this.port.concurrentModification(item.artifact.path);
|
|
4595
5387
|
}
|
|
4596
|
-
this.port.journal.confirmCreatedDirectory(expectation.path,
|
|
5388
|
+
this.port.journal.confirmCreatedDirectory(expectation.path, identity2(createdStat));
|
|
4597
5389
|
if (parentExpectation) await this.port.assertExpectedDirectory(parentExpectation, item);
|
|
4598
5390
|
}
|
|
4599
5391
|
await this.port.assertParentChain(item);
|
|
@@ -4613,7 +5405,7 @@ var FileSystemStagingUnit = class {
|
|
|
4613
5405
|
if (namespaceStat.isSymbolicLink() || !namespaceStat.isDirectory()) {
|
|
4614
5406
|
throw this.port.concurrentModification(item.artifact.path);
|
|
4615
5407
|
}
|
|
4616
|
-
this.port.journal.confirmNamespace(item,
|
|
5408
|
+
this.port.journal.confirmNamespace(item, identity2(namespaceStat));
|
|
4617
5409
|
await this.port.assertParentChain(item);
|
|
4618
5410
|
}
|
|
4619
5411
|
};
|
|
@@ -4646,17 +5438,17 @@ var TransactionSignalRegistrar = class {
|
|
|
4646
5438
|
|
|
4647
5439
|
// src/transaction/transaction-unit.session.ts
|
|
4648
5440
|
import { constants } from "fs";
|
|
4649
|
-
import { access, link, lstat, mkdir, open, rename, rmdir, stat, unlink } from "fs/promises";
|
|
5441
|
+
import { access, link, lstat as lstat2, mkdir, open, rename, rmdir, stat as stat2, unlink } from "fs/promises";
|
|
4650
5442
|
import { dirname as dirname2, join as join4, resolve as resolve3 } from "path";
|
|
4651
5443
|
var nodeTransactionOperations = {
|
|
4652
5444
|
access,
|
|
4653
5445
|
link,
|
|
4654
|
-
lstat,
|
|
5446
|
+
lstat: lstat2,
|
|
4655
5447
|
mkdir,
|
|
4656
5448
|
open: (target, flags) => open(target, flags),
|
|
4657
5449
|
rename,
|
|
4658
5450
|
rmdir,
|
|
4659
|
-
stat,
|
|
5451
|
+
stat: stat2,
|
|
4660
5452
|
unlink
|
|
4661
5453
|
};
|
|
4662
5454
|
var TransactionUnitSession = class {
|
|
@@ -4677,30 +5469,30 @@ var TransactionUnitSession = class {
|
|
|
4677
5469
|
const journal = Object.freeze({
|
|
4678
5470
|
prepare: (artifacts) => this.prepare(artifacts),
|
|
4679
5471
|
artifacts: () => Object.freeze(this.context.items.map(stagingArtifactView)),
|
|
4680
|
-
createdDirectory: (
|
|
4681
|
-
addCreatedDirectoryPublicPath: (
|
|
4682
|
-
recordUnconfirmedEntry: (
|
|
4683
|
-
recordCreatedDirectory: (
|
|
4684
|
-
confirmCreatedDirectory: (
|
|
4685
|
-
required(this.context.createdDirectories.get(
|
|
5472
|
+
createdDirectory: (path19) => createdDirectoryView(this.context.createdDirectories.get(path19)),
|
|
5473
|
+
addCreatedDirectoryPublicPath: (path19, publicPath) => required(this.context.createdDirectories.get(path19)).publicPaths.add(publicPath),
|
|
5474
|
+
recordUnconfirmedEntry: (path19, publicPath) => this.recordUnconfirmedEntry(path19, publicPath),
|
|
5475
|
+
recordCreatedDirectory: (path19, publicPath) => this.recordCreatedDirectory(path19, publicPath),
|
|
5476
|
+
confirmCreatedDirectory: (path19, expected) => {
|
|
5477
|
+
required(this.context.createdDirectories.get(path19)).identity = expected;
|
|
4686
5478
|
},
|
|
4687
|
-
recordNamespace: (item,
|
|
5479
|
+
recordNamespace: (item, path19) => this.recordNamespace(item.artifact, path19),
|
|
4688
5480
|
confirmNamespace: (item, expected) => {
|
|
4689
5481
|
required(this.runtimeArtifact(item.artifact).namespace).identity = expected;
|
|
4690
5482
|
}
|
|
4691
5483
|
});
|
|
4692
5484
|
return Object.freeze({
|
|
4693
5485
|
journal,
|
|
4694
|
-
assertDirectoryExpectation: (
|
|
4695
|
-
assertDirectoryIdentity: (
|
|
4696
|
-
assertExpectedAbsent: (
|
|
5486
|
+
assertDirectoryExpectation: (path19, expected, publicPath) => this.assertDirectoryExpectation(path19, expected, publicPath),
|
|
5487
|
+
assertDirectoryIdentity: (path19, expected, publicPath) => this.assertDirectoryIdentity(path19, expected, publicPath),
|
|
5488
|
+
assertExpectedAbsent: (path19, publicPath) => this.assertExpectedAbsent(path19, publicPath),
|
|
4697
5489
|
assertExpectedDirectory: (expectation, item) => this.assertExpectedDirectory(expectation, this.runtimeArtifact(item.artifact), this.context),
|
|
4698
5490
|
assertNotInterrupted: (signal) => this.assertNotInterrupted(signal),
|
|
4699
5491
|
assertParentChain: (item) => this.assertParentChain(this.runtimeArtifact(item.artifact), this.context),
|
|
4700
5492
|
concurrentModification: (publicPath, cause) => this.concurrentModification(publicPath, cause),
|
|
4701
5493
|
createStageFile: (item, contents, signal, mode) => this.createStageFile(this.runtimeArtifact(item.artifact), contents, signal, mode),
|
|
4702
|
-
lstat: (
|
|
4703
|
-
mkdir: (
|
|
5494
|
+
lstat: (path19) => this.operations.lstat(path19),
|
|
5495
|
+
mkdir: (path19, options) => this.operations.mkdir(path19, options),
|
|
4704
5496
|
readOwnedFile: (item, ownedPath) => {
|
|
4705
5497
|
const runtime = this.runtimeArtifact(item.artifact);
|
|
4706
5498
|
return this.readOwnedFile(runtime, this.ownedFile(runtime, ownedPath));
|
|
@@ -4712,7 +5504,7 @@ var TransactionUnitSession = class {
|
|
|
4712
5504
|
const journal = Object.freeze({
|
|
4713
5505
|
artifact: (artifact) => commitArtifactView(this.runtimeArtifact(artifact)),
|
|
4714
5506
|
artifacts: () => Object.freeze(this.context.items.map(commitArtifactView)),
|
|
4715
|
-
addQuarantine: (item,
|
|
5507
|
+
addQuarantine: (item, path19) => ownedFileView(this.addQuarantine(this.runtimeArtifact(item.artifact), path19)),
|
|
4716
5508
|
confirmOwnedFile: (item, ownedPath, expected) => ownedFileView(this.confirmOwnedFile(this.runtimeArtifact(item.artifact), ownedPath, expected)),
|
|
4717
5509
|
setOwnedFilePreserved: (item, ownedPath, preserve) => {
|
|
4718
5510
|
this.ownedFile(this.runtimeArtifact(item.artifact), ownedPath).preserve = preserve;
|
|
@@ -4724,7 +5516,7 @@ var TransactionUnitSession = class {
|
|
|
4724
5516
|
});
|
|
4725
5517
|
return Object.freeze({
|
|
4726
5518
|
journal,
|
|
4727
|
-
assertExpectedAbsent: (
|
|
5519
|
+
assertExpectedAbsent: (path19, publicPath) => this.assertExpectedAbsent(path19, publicPath),
|
|
4728
5520
|
assertNamespace: (item) => this.assertNamespace(this.runtimeArtifact(item.artifact)),
|
|
4729
5521
|
assertNotInterrupted: (signal) => this.assertNotInterrupted(signal),
|
|
4730
5522
|
assertOwnedIdentity: (item, ownedPath) => {
|
|
@@ -4735,7 +5527,7 @@ var TransactionUnitSession = class {
|
|
|
4735
5527
|
concurrentModification: (publicPath, cause) => this.concurrentModification(publicPath, cause),
|
|
4736
5528
|
createBackupFile: (item, contents, signal, mode) => this.createBackupFile(this.runtimeArtifact(item.artifact), contents, signal, mode),
|
|
4737
5529
|
link: (existingPath, newPath) => this.operations.link(existingPath, newPath),
|
|
4738
|
-
lstatOrAbsent: (
|
|
5530
|
+
lstatOrAbsent: (path19) => this.lstatOrAbsent(path19),
|
|
4739
5531
|
observePublic: (item) => this.observePublic(this.runtimeArtifact(item.artifact), this.context),
|
|
4740
5532
|
ownershipFailure: (publicPath) => this.ownershipFailure(publicPath),
|
|
4741
5533
|
readOwnedFile: (item, ownedPath) => {
|
|
@@ -4750,7 +5542,7 @@ var TransactionUnitSession = class {
|
|
|
4750
5542
|
artifact: (artifact) => rollbackArtifactView(this.runtimeArtifact(artifact)),
|
|
4751
5543
|
artifacts: () => Object.freeze(this.context.items.map(rollbackArtifactView)),
|
|
4752
5544
|
recoveryFailures: () => Object.freeze([...this.context.recoveryFailures]),
|
|
4753
|
-
addQuarantine: (item,
|
|
5545
|
+
addQuarantine: (item, path19) => ownedFileView(this.addQuarantine(this.runtimeArtifact(item.artifact), path19)),
|
|
4754
5546
|
confirmOwnedFile: (item, ownedPath, expected) => ownedFileView(this.confirmOwnedFile(this.runtimeArtifact(item.artifact), ownedPath, expected)),
|
|
4755
5547
|
setOwnedFilePreserved: (item, ownedPath, preserve) => {
|
|
4756
5548
|
this.ownedFile(this.runtimeArtifact(item.artifact), ownedPath).preserve = preserve;
|
|
@@ -4764,11 +5556,11 @@ var TransactionUnitSession = class {
|
|
|
4764
5556
|
});
|
|
4765
5557
|
return Object.freeze({
|
|
4766
5558
|
journal,
|
|
4767
|
-
assertExpectedAbsent: (
|
|
5559
|
+
assertExpectedAbsent: (path19, publicPath) => this.assertExpectedAbsent(path19, publicPath),
|
|
4768
5560
|
assertNamespace: (item) => this.assertNamespace(this.runtimeArtifact(item.artifact)),
|
|
4769
5561
|
assertParentChain: (item) => this.assertParentChain(this.runtimeArtifact(item.artifact), this.context),
|
|
4770
5562
|
link: (existingPath, newPath) => this.operations.link(existingPath, newPath),
|
|
4771
|
-
lstatOrAbsent: (
|
|
5563
|
+
lstatOrAbsent: (path19) => this.lstatOrAbsent(path19),
|
|
4772
5564
|
observePublic: (item) => this.observePublic(this.runtimeArtifact(item.artifact), this.context),
|
|
4773
5565
|
readOwnedFile: (item, ownedPath) => {
|
|
4774
5566
|
const runtime = this.runtimeArtifact(item.artifact);
|
|
@@ -4790,8 +5582,8 @@ var TransactionUnitSession = class {
|
|
|
4790
5582
|
markNamespaceAbsent: (item) => {
|
|
4791
5583
|
required(this.runtimeArtifact(item.artifact).namespace).exists = false;
|
|
4792
5584
|
},
|
|
4793
|
-
markCreatedDirectoryAbsent: (
|
|
4794
|
-
required(this.context.createdDirectories.get(
|
|
5585
|
+
markCreatedDirectoryAbsent: (path19) => {
|
|
5586
|
+
required(this.context.createdDirectories.get(path19)).exists = false;
|
|
4795
5587
|
this.context.ownershipChanged();
|
|
4796
5588
|
},
|
|
4797
5589
|
finishArtifactCleanup: () => this.context.ownershipChanged()
|
|
@@ -4801,11 +5593,11 @@ var TransactionUnitSession = class {
|
|
|
4801
5593
|
assertNamespace: (item) => this.assertNamespace(this.runtimeArtifact(item.artifact)),
|
|
4802
5594
|
closeReadHandles: (item, failures) => this.closeReadHandles(this.runtimeArtifact(item.artifact), failures),
|
|
4803
5595
|
closeOpenHandle: (item, failures) => this.closeOpenHandle(this.runtimeArtifact(item.artifact), failures),
|
|
4804
|
-
lstat: (
|
|
4805
|
-
lstatOrAbsent: (
|
|
5596
|
+
lstat: (path19) => this.operations.lstat(path19),
|
|
5597
|
+
lstatOrAbsent: (path19) => this.lstatOrAbsent(path19),
|
|
4806
5598
|
observePublic: (item) => this.observePublic(this.runtimeArtifact(item.artifact), this.context),
|
|
4807
|
-
rmdir: (
|
|
4808
|
-
unlink: (
|
|
5599
|
+
rmdir: (path19) => this.operations.rmdir(path19),
|
|
5600
|
+
unlink: (path19) => this.operations.unlink(path19)
|
|
4809
5601
|
});
|
|
4810
5602
|
}
|
|
4811
5603
|
async prepareForPreflight(artifacts) {
|
|
@@ -4880,16 +5672,16 @@ var TransactionUnitSession = class {
|
|
|
4880
5672
|
result2.push({
|
|
4881
5673
|
path: candidate,
|
|
4882
5674
|
original: {
|
|
4883
|
-
identity:
|
|
5675
|
+
identity: identity2(candidateStat),
|
|
4884
5676
|
kind: "symbolic-link",
|
|
4885
|
-
followedIdentity:
|
|
5677
|
+
followedIdentity: identity2(followed)
|
|
4886
5678
|
}
|
|
4887
5679
|
});
|
|
4888
5680
|
} else {
|
|
4889
|
-
result2.push({ path: candidate, original: { identity:
|
|
5681
|
+
result2.push({ path: candidate, original: { identity: identity2(candidateStat), kind: "directory" } });
|
|
4890
5682
|
}
|
|
4891
5683
|
} catch (error) {
|
|
4892
|
-
if (!
|
|
5684
|
+
if (!isEnoent2(error)) throw error;
|
|
4893
5685
|
missing = true;
|
|
4894
5686
|
result2.push({ path: candidate, original: "absent" });
|
|
4895
5687
|
}
|
|
@@ -4914,9 +5706,9 @@ var TransactionUnitSession = class {
|
|
|
4914
5706
|
await this.writeOwnedFile(item, owned, contents, signal, mode);
|
|
4915
5707
|
return ownedFileView(owned);
|
|
4916
5708
|
}
|
|
4917
|
-
registerOwnedFile(item,
|
|
5709
|
+
registerOwnedFile(item, path19) {
|
|
4918
5710
|
const owned = {
|
|
4919
|
-
path:
|
|
5711
|
+
path: path19,
|
|
4920
5712
|
publicPath: item.artifact.path,
|
|
4921
5713
|
exists: false,
|
|
4922
5714
|
preserve: false
|
|
@@ -4931,7 +5723,7 @@ var TransactionUnitSession = class {
|
|
|
4931
5723
|
item.openHandle = handle;
|
|
4932
5724
|
this.context.ownershipChanged();
|
|
4933
5725
|
if (mode !== void 0) await handle.chmod(mode);
|
|
4934
|
-
owned.identity =
|
|
5726
|
+
owned.identity = identity2(await handle.stat());
|
|
4935
5727
|
this.assertNotInterrupted(signal);
|
|
4936
5728
|
await handle.writeFile(contents, "utf8");
|
|
4937
5729
|
this.assertNotInterrupted(signal);
|
|
@@ -4945,48 +5737,48 @@ var TransactionUnitSession = class {
|
|
|
4945
5737
|
runtimeArtifact(artifact) {
|
|
4946
5738
|
return runtimeArtifact(this.context, artifact);
|
|
4947
5739
|
}
|
|
4948
|
-
ownedFile(item,
|
|
4949
|
-
const owned = item.ownedFiles.find((candidate) => candidate.path ===
|
|
5740
|
+
ownedFile(item, path19) {
|
|
5741
|
+
const owned = item.ownedFiles.find((candidate) => candidate.path === path19);
|
|
4950
5742
|
if (owned !== void 0) return owned;
|
|
4951
5743
|
throw new MigrationApplicationError(
|
|
4952
5744
|
"internal-invariant",
|
|
4953
|
-
`Migration transaction journal contains an unknown invocation-owned file: ${
|
|
5745
|
+
`Migration transaction journal contains an unknown invocation-owned file: ${path19}`,
|
|
4954
5746
|
[item.artifact.path]
|
|
4955
5747
|
);
|
|
4956
5748
|
}
|
|
4957
|
-
addQuarantine(item,
|
|
4958
|
-
const quarantine = this.registerOwnedFile(item,
|
|
5749
|
+
addQuarantine(item, path19) {
|
|
5750
|
+
const quarantine = this.registerOwnedFile(item, path19);
|
|
4959
5751
|
item.quarantines.push(quarantine);
|
|
4960
5752
|
return quarantine;
|
|
4961
5753
|
}
|
|
4962
|
-
confirmOwnedFile(item,
|
|
4963
|
-
const owned = this.ownedFile(item,
|
|
5754
|
+
confirmOwnedFile(item, path19, expected) {
|
|
5755
|
+
const owned = this.ownedFile(item, path19);
|
|
4964
5756
|
owned.exists = true;
|
|
4965
5757
|
owned.identity = expected;
|
|
4966
5758
|
return owned;
|
|
4967
5759
|
}
|
|
4968
|
-
recordNamespace(artifact,
|
|
5760
|
+
recordNamespace(artifact, path19) {
|
|
4969
5761
|
const item = this.runtimeArtifact(artifact);
|
|
4970
|
-
item.namespace = { path:
|
|
5762
|
+
item.namespace = { path: path19, publicPath: item.artifact.path, exists: true };
|
|
4971
5763
|
this.context.ownershipChanged();
|
|
4972
5764
|
}
|
|
4973
|
-
recordCreatedDirectory(
|
|
4974
|
-
this.context.createdDirectories.set(
|
|
4975
|
-
path:
|
|
5765
|
+
recordCreatedDirectory(path19, publicPath) {
|
|
5766
|
+
this.context.createdDirectories.set(path19, {
|
|
5767
|
+
path: path19,
|
|
4976
5768
|
publicPaths: /* @__PURE__ */ new Set([publicPath]),
|
|
4977
5769
|
exists: true
|
|
4978
5770
|
});
|
|
4979
5771
|
this.context.ownershipChanged();
|
|
4980
5772
|
}
|
|
4981
|
-
recordUnconfirmedEntry(
|
|
4982
|
-
const publicPaths = this.context.unconfirmedEntries.get(
|
|
5773
|
+
recordUnconfirmedEntry(path19, publicPath) {
|
|
5774
|
+
const publicPaths = this.context.unconfirmedEntries.get(path19) ?? /* @__PURE__ */ new Set();
|
|
4983
5775
|
publicPaths.add(publicPath);
|
|
4984
|
-
this.context.unconfirmedEntries.set(
|
|
5776
|
+
this.context.unconfirmedEntries.set(path19, publicPaths);
|
|
4985
5777
|
}
|
|
4986
5778
|
unconfirmedEntryViews() {
|
|
4987
5779
|
return Object.freeze(
|
|
4988
5780
|
[...this.context.unconfirmedEntries].map(
|
|
4989
|
-
([
|
|
5781
|
+
([path19, publicPaths]) => Object.freeze({ path: path19, publicPaths: Object.freeze([...publicPaths]) })
|
|
4990
5782
|
)
|
|
4991
5783
|
);
|
|
4992
5784
|
}
|
|
@@ -5018,18 +5810,18 @@ var TransactionUnitSession = class {
|
|
|
5018
5810
|
[item.artifact.path]
|
|
5019
5811
|
);
|
|
5020
5812
|
}
|
|
5021
|
-
const beforeIdentity =
|
|
5813
|
+
const beforeIdentity = identity2(before);
|
|
5022
5814
|
const handle = await this.operations.open(item.artifact.path, "r");
|
|
5023
5815
|
const contents = await this.readThroughHandle(item, handle, async () => {
|
|
5024
|
-
const handleBefore =
|
|
5025
|
-
if (!
|
|
5816
|
+
const handleBefore = identity2(await handle.stat());
|
|
5817
|
+
if (!sameIdentity2(beforeIdentity, handleBefore)) throw this.concurrentModification(item.artifact.path);
|
|
5026
5818
|
const read = await handle.readFile({ encoding: "utf8" });
|
|
5027
|
-
const handleAfter =
|
|
5028
|
-
if (!
|
|
5819
|
+
const handleAfter = identity2(await handle.stat());
|
|
5820
|
+
if (!sameIdentity2(handleBefore, handleAfter)) throw this.concurrentModification(item.artifact.path);
|
|
5029
5821
|
return read;
|
|
5030
5822
|
});
|
|
5031
5823
|
const after = await this.lstatOrAbsent(item.artifact.path);
|
|
5032
|
-
if (after === "absent" || after.isSymbolicLink() || !after.isFile() || !
|
|
5824
|
+
if (after === "absent" || after.isSymbolicLink() || !after.isFile() || !sameIdentity2(beforeIdentity, identity2(after))) {
|
|
5033
5825
|
throw this.concurrentModification(item.artifact.path);
|
|
5034
5826
|
}
|
|
5035
5827
|
await this.assertParentChain(item, context);
|
|
@@ -5040,11 +5832,11 @@ var TransactionUnitSession = class {
|
|
|
5040
5832
|
const expected = required(owned.identity);
|
|
5041
5833
|
const handle = await this.operations.open(owned.path, "r");
|
|
5042
5834
|
const contents = await this.readThroughHandle(item, handle, async () => {
|
|
5043
|
-
const before =
|
|
5044
|
-
if (!
|
|
5835
|
+
const before = identity2(await handle.stat());
|
|
5836
|
+
if (!sameIdentity2(before, expected)) throw this.ownershipFailure(owned.publicPath);
|
|
5045
5837
|
const read = await handle.readFile({ encoding: "utf8" });
|
|
5046
|
-
const after =
|
|
5047
|
-
if (!
|
|
5838
|
+
const after = identity2(await handle.stat());
|
|
5839
|
+
if (!sameIdentity2(after, expected)) throw this.ownershipFailure(owned.publicPath);
|
|
5048
5840
|
return read;
|
|
5049
5841
|
});
|
|
5050
5842
|
await this.assertOwnedIdentity(item, owned);
|
|
@@ -5103,7 +5895,7 @@ var TransactionUnitSession = class {
|
|
|
5103
5895
|
await this.assertNamespace(item);
|
|
5104
5896
|
if (!owned.identity) throw this.ownershipFailure(owned.publicPath);
|
|
5105
5897
|
const ownedStat = await this.operations.lstat(owned.path);
|
|
5106
|
-
if (ownedStat.isSymbolicLink() || !ownedStat.isFile() || !
|
|
5898
|
+
if (ownedStat.isSymbolicLink() || !ownedStat.isFile() || !sameIdentity2(identity2(ownedStat), owned.identity)) {
|
|
5107
5899
|
throw this.ownershipFailure(owned.publicPath);
|
|
5108
5900
|
}
|
|
5109
5901
|
await this.assertNamespace(item);
|
|
@@ -5112,7 +5904,7 @@ var TransactionUnitSession = class {
|
|
|
5112
5904
|
const namespace = required(item.namespace);
|
|
5113
5905
|
const expectedIdentity = required(namespace.identity);
|
|
5114
5906
|
const namespaceStat = await this.operations.lstat(namespace.path);
|
|
5115
|
-
if (namespaceStat.isSymbolicLink() || !namespaceStat.isDirectory() || !
|
|
5907
|
+
if (namespaceStat.isSymbolicLink() || !namespaceStat.isDirectory() || !sameIdentity2(identity2(namespaceStat), expectedIdentity)) {
|
|
5116
5908
|
throw this.ownershipFailure(namespace.publicPath);
|
|
5117
5909
|
}
|
|
5118
5910
|
}
|
|
@@ -5131,54 +5923,54 @@ var TransactionUnitSession = class {
|
|
|
5131
5923
|
}
|
|
5132
5924
|
await this.assertDirectoryExpectation(expectation.path, expectation.original, item.artifact.path);
|
|
5133
5925
|
}
|
|
5134
|
-
async assertDirectoryExpectation(
|
|
5926
|
+
async assertDirectoryExpectation(path19, expected, publicPath) {
|
|
5135
5927
|
let pathStat;
|
|
5136
5928
|
try {
|
|
5137
|
-
pathStat = await this.operations.lstat(
|
|
5929
|
+
pathStat = await this.operations.lstat(path19);
|
|
5138
5930
|
} catch (error) {
|
|
5139
5931
|
throw this.concurrentModification(publicPath, error);
|
|
5140
5932
|
}
|
|
5141
5933
|
const kind = pathStat.isSymbolicLink() ? "symbolic-link" : pathStat.isDirectory() ? "directory" : void 0;
|
|
5142
|
-
if (kind !== expected.kind || !
|
|
5934
|
+
if (kind !== expected.kind || !sameIdentity2(identity2(pathStat), expected.identity)) {
|
|
5143
5935
|
throw this.concurrentModification(publicPath);
|
|
5144
5936
|
}
|
|
5145
5937
|
if (expected.kind === "symbolic-link") {
|
|
5146
5938
|
let followed;
|
|
5147
5939
|
try {
|
|
5148
|
-
followed = await this.operations.stat(
|
|
5940
|
+
followed = await this.operations.stat(path19);
|
|
5149
5941
|
} catch (error) {
|
|
5150
5942
|
throw this.concurrentModification(publicPath, error);
|
|
5151
5943
|
}
|
|
5152
|
-
if (!followed.isDirectory() || !
|
|
5944
|
+
if (!followed.isDirectory() || !sameIdentity2(identity2(followed), expected.followedIdentity)) {
|
|
5153
5945
|
throw this.concurrentModification(publicPath);
|
|
5154
5946
|
}
|
|
5155
5947
|
}
|
|
5156
5948
|
}
|
|
5157
|
-
async assertDirectoryIdentity(
|
|
5949
|
+
async assertDirectoryIdentity(path19, expected, publicPath) {
|
|
5158
5950
|
let pathStat;
|
|
5159
5951
|
try {
|
|
5160
|
-
pathStat = await this.operations.lstat(
|
|
5952
|
+
pathStat = await this.operations.lstat(path19);
|
|
5161
5953
|
} catch (error) {
|
|
5162
5954
|
throw this.concurrentModification(publicPath, error);
|
|
5163
5955
|
}
|
|
5164
|
-
if (pathStat.isSymbolicLink() || !pathStat.isDirectory() || !
|
|
5956
|
+
if (pathStat.isSymbolicLink() || !pathStat.isDirectory() || !sameIdentity2(identity2(pathStat), expected)) {
|
|
5165
5957
|
throw this.concurrentModification(publicPath);
|
|
5166
5958
|
}
|
|
5167
5959
|
}
|
|
5168
|
-
async assertExpectedAbsent(
|
|
5960
|
+
async assertExpectedAbsent(path19, publicPath) {
|
|
5169
5961
|
try {
|
|
5170
|
-
await this.operations.lstat(
|
|
5962
|
+
await this.operations.lstat(path19);
|
|
5171
5963
|
} catch (error) {
|
|
5172
|
-
if (
|
|
5964
|
+
if (isEnoent2(error)) return;
|
|
5173
5965
|
throw error;
|
|
5174
5966
|
}
|
|
5175
5967
|
throw this.concurrentModification(publicPath);
|
|
5176
5968
|
}
|
|
5177
|
-
async lstatOrAbsent(
|
|
5969
|
+
async lstatOrAbsent(path19) {
|
|
5178
5970
|
try {
|
|
5179
|
-
return await this.operations.lstat(
|
|
5971
|
+
return await this.operations.lstat(path19);
|
|
5180
5972
|
} catch (error) {
|
|
5181
|
-
if (
|
|
5973
|
+
if (isEnoent2(error)) return "absent";
|
|
5182
5974
|
throw error;
|
|
5183
5975
|
}
|
|
5184
5976
|
}
|
|
@@ -5212,9 +6004,9 @@ var TransactionUnitSession = class {
|
|
|
5212
6004
|
);
|
|
5213
6005
|
}
|
|
5214
6006
|
};
|
|
5215
|
-
function directoryChain(
|
|
6007
|
+
function directoryChain(path19) {
|
|
5216
6008
|
const result2 = [];
|
|
5217
|
-
let current = resolve3(
|
|
6009
|
+
let current = resolve3(path19);
|
|
5218
6010
|
while (true) {
|
|
5219
6011
|
result2.unshift(current);
|
|
5220
6012
|
const parent = dirname2(current);
|
|
@@ -5401,7 +6193,7 @@ var MigrationTransaction = class {
|
|
|
5401
6193
|
}
|
|
5402
6194
|
}
|
|
5403
6195
|
rejectParseErrors(plan) {
|
|
5404
|
-
const paths = plan.files.flatMap((file) => file.results.filter((result2) => result2.status === "parse-error").map((result2) => result2.fileName)).filter((
|
|
6196
|
+
const paths = plan.files.flatMap((file) => file.results.filter((result2) => result2.status === "parse-error").map((result2) => result2.fileName)).filter((path19, index, all) => all.indexOf(path19) === index).sort(compareCodeUnits);
|
|
5405
6197
|
if (paths.length === 0) return;
|
|
5406
6198
|
throw new MigrationApplicationError(
|
|
5407
6199
|
"internal-invariant",
|
|
@@ -5520,6 +6312,13 @@ var ApplyProjectStage = class {
|
|
|
5520
6312
|
);
|
|
5521
6313
|
}
|
|
5522
6314
|
const plan = validated.plan;
|
|
6315
|
+
const snapshots = validated.rendered.analyzed.manifest.invocation.options.configurationSnapshots ?? [];
|
|
6316
|
+
await assertConfigurationUnchanged(snapshots);
|
|
6317
|
+
for (const artifact of plan.artifacts)
|
|
6318
|
+
for (const snapshot of snapshots) {
|
|
6319
|
+
if (await pathsOverlapOnFileSystem(artifact.path, snapshot.path))
|
|
6320
|
+
throw new Error(`Migration output collides with target configuration: ${artifact.path}`);
|
|
6321
|
+
}
|
|
5523
6322
|
const hasParseError = plan.files.some((file) => file.results.some((result2) => result2.status === "parse-error"));
|
|
5524
6323
|
if (this.mode === "plan") {
|
|
5525
6324
|
if (!hasParseError) await this.transaction.preflight(plan);
|
|
@@ -5529,27 +6328,28 @@ var ApplyProjectStage = class {
|
|
|
5529
6328
|
return appliedProject({ validated, application: { status: "skipped", reason: "parse-errors" } });
|
|
5530
6329
|
}
|
|
5531
6330
|
await this.transaction.preflight(plan);
|
|
6331
|
+
await assertConfigurationUnchanged(snapshots);
|
|
5532
6332
|
if (plan.artifacts.length > 0) await this.transaction.apply(plan);
|
|
5533
6333
|
return appliedProject({ validated, application: { status: "applied" } });
|
|
5534
6334
|
}
|
|
5535
6335
|
};
|
|
5536
6336
|
|
|
5537
6337
|
// src/pipeline/discover/discover-project.stage.ts
|
|
5538
|
-
import { readdir, stat as
|
|
5539
|
-
import * as
|
|
6338
|
+
import { readdir, stat as stat3 } from "fs/promises";
|
|
6339
|
+
import * as path6 from "path";
|
|
5540
6340
|
|
|
5541
6341
|
// src/lib/gitignore.helper.ts
|
|
5542
6342
|
import fs from "fs-extra";
|
|
5543
6343
|
import ignore from "ignore";
|
|
5544
|
-
import
|
|
6344
|
+
import path5 from "path";
|
|
5545
6345
|
async function createGitIgnoreMatcher(root, displayRoot = root) {
|
|
5546
6346
|
const matcher = ignore();
|
|
5547
|
-
const gitignorePath =
|
|
6347
|
+
const gitignorePath = path5.join(root, ".gitignore");
|
|
5548
6348
|
if (await fs.pathExists(gitignorePath)) {
|
|
5549
6349
|
matcher.add(await fs.readFile(gitignorePath, "utf8"));
|
|
5550
|
-
logger.debug(`Loaded .gitignore file from ${
|
|
6350
|
+
logger.debug(`Loaded .gitignore file from ${path5.join(displayRoot, ".gitignore")}`);
|
|
5551
6351
|
}
|
|
5552
|
-
const relativeIgnorePath = (candidate) =>
|
|
6352
|
+
const relativeIgnorePath = (candidate) => path5.relative(root, candidate).split(path5.sep).join("/");
|
|
5553
6353
|
return Object.freeze({
|
|
5554
6354
|
ignores: (candidate) => matcher.ignores(relativeIgnorePath(candidate)),
|
|
5555
6355
|
ignoresDirectory: (candidate) => matcher.ignores(`${relativeIgnorePath(candidate)}/`)
|
|
@@ -5559,7 +6359,7 @@ async function createGitIgnoreMatcher(root, displayRoot = root) {
|
|
|
5559
6359
|
// src/pipeline/discover/discover-project.stage.ts
|
|
5560
6360
|
var nodeFileSystem = Object.freeze({
|
|
5561
6361
|
async kind(candidate) {
|
|
5562
|
-
const candidateStat = await
|
|
6362
|
+
const candidateStat = await stat3(candidate);
|
|
5563
6363
|
if (candidateStat.isFile()) return "file";
|
|
5564
6364
|
if (candidateStat.isDirectory()) return "directory";
|
|
5565
6365
|
return "other";
|
|
@@ -5594,10 +6394,10 @@ var DiscoverProjectStage = class {
|
|
|
5594
6394
|
return projectManifest({ invocation, templates });
|
|
5595
6395
|
}
|
|
5596
6396
|
singleFile(invocation) {
|
|
5597
|
-
if (
|
|
6397
|
+
if (path6.extname(invocation.canonicalInputPath).toLowerCase() !== ".html") {
|
|
5598
6398
|
throw new Error(`Unsupported file type: ${invocation.inputPath}`);
|
|
5599
6399
|
}
|
|
5600
|
-
if (
|
|
6400
|
+
if (path6.extname(invocation.canonicalOutputPath).toLowerCase() !== ".html") {
|
|
5601
6401
|
throw new Error("Single-file output path must have a .html extension.");
|
|
5602
6402
|
}
|
|
5603
6403
|
return [{ inputPath: invocation.canonicalInputPath, outputPath: invocation.canonicalOutputPath }];
|
|
@@ -5610,7 +6410,7 @@ var DiscoverProjectStage = class {
|
|
|
5610
6410
|
inputs.sort(compareCodeUnits);
|
|
5611
6411
|
return inputs.map((inputPath) => ({
|
|
5612
6412
|
inputPath,
|
|
5613
|
-
outputPath:
|
|
6413
|
+
outputPath: path6.join(invocation.canonicalOutputPath, path6.relative(root, inputPath))
|
|
5614
6414
|
}));
|
|
5615
6415
|
}
|
|
5616
6416
|
async collectInputs(directory, displayDirectory, matcher, exclusions) {
|
|
@@ -5619,30 +6419,30 @@ var DiscoverProjectStage = class {
|
|
|
5619
6419
|
);
|
|
5620
6420
|
const inputs = [];
|
|
5621
6421
|
for (const entry of entries) {
|
|
5622
|
-
const candidate =
|
|
5623
|
-
const displayCandidate =
|
|
6422
|
+
const candidate = path6.join(directory, entry.name);
|
|
6423
|
+
const displayCandidate = path6.join(displayDirectory, entry.name);
|
|
5624
6424
|
logger.debug(`Processing ${displayCandidate}`);
|
|
5625
6425
|
const ignored = entry.kind === "directory" ? matcher.ignoresDirectory(candidate) : matcher.ignores(candidate);
|
|
5626
|
-
if (ignored || exclusions.has(
|
|
6426
|
+
if (ignored || exclusions.has(path6.normalize(candidate))) continue;
|
|
5627
6427
|
const kind = entry.kind === "other" ? await this.fileSystem.kind(candidate) : entry.kind;
|
|
5628
6428
|
if (entry.kind === "other" && kind === "directory" && matcher.ignoresDirectory(candidate)) continue;
|
|
5629
6429
|
if (kind === "directory") {
|
|
5630
6430
|
inputs.push(...await this.collectInputs(candidate, displayCandidate, matcher, exclusions));
|
|
5631
|
-
} else if (kind === "file" &&
|
|
5632
|
-
inputs.push(
|
|
6431
|
+
} else if (kind === "file" && path6.extname(entry.name).toLowerCase() === ".html") {
|
|
6432
|
+
inputs.push(path6.normalize(path6.resolve(candidate)));
|
|
5633
6433
|
}
|
|
5634
6434
|
}
|
|
5635
6435
|
return inputs;
|
|
5636
6436
|
}
|
|
5637
6437
|
excludedPaths(invocation) {
|
|
5638
|
-
const candidates = [invocation.options.stylesheetPath, invocation.options.reportPath].filter((candidate) => candidate !== void 0).map((candidate) =>
|
|
6438
|
+
const candidates = [invocation.options.stylesheetPath, invocation.options.reportPath].filter((candidate) => candidate !== void 0).map((candidate) => path6.normalize(path6.resolve(candidate)));
|
|
5639
6439
|
const outputRoot = invocation.canonicalOutputPath;
|
|
5640
|
-
if (
|
|
6440
|
+
if (path6.relative(invocation.canonicalInputPath, outputRoot) !== "") candidates.push(outputRoot);
|
|
5641
6441
|
return new Set(candidates);
|
|
5642
6442
|
}
|
|
5643
6443
|
};
|
|
5644
6444
|
function preserveTrailingSeparator(canonicalPath, rawPath) {
|
|
5645
|
-
return /[\\/]$/u.test(rawPath) && !/[\\/]$/u.test(canonicalPath) ? `${canonicalPath}${
|
|
6445
|
+
return /[\\/]$/u.test(rawPath) && !/[\\/]$/u.test(canonicalPath) ? `${canonicalPath}${path6.sep}` : canonicalPath;
|
|
5646
6446
|
}
|
|
5647
6447
|
|
|
5648
6448
|
// src/pipeline/pipeline-stage.error.ts
|
|
@@ -5684,7 +6484,7 @@ async function runStage(stage, action) {
|
|
|
5684
6484
|
}
|
|
5685
6485
|
|
|
5686
6486
|
// src/report/migration-report.builder.ts
|
|
5687
|
-
import
|
|
6487
|
+
import path7 from "path";
|
|
5688
6488
|
var MigrationReportBuilder = class {
|
|
5689
6489
|
build(inputRoot, outputRoot, target, mode, application, durationMs, files, stylesheet) {
|
|
5690
6490
|
const pathApi = this.pathApi(
|
|
@@ -5718,9 +6518,9 @@ var MigrationReportBuilder = class {
|
|
|
5718
6518
|
return { path: this.forwardSlashes(pathApi, displayPath), change: stylesheet.change };
|
|
5719
6519
|
}
|
|
5720
6520
|
pathApi(...values) {
|
|
5721
|
-
if (values.some((value) => /^[A-Za-z]:[\\/]/.test(value) || value.includes("\\"))) return
|
|
5722
|
-
if (values.some((value) => value.includes("/"))) return
|
|
5723
|
-
return
|
|
6521
|
+
if (values.some((value) => /^[A-Za-z]:[\\/]/.test(value) || value.includes("\\"))) return path7.win32;
|
|
6522
|
+
if (values.some((value) => value.includes("/"))) return path7.posix;
|
|
6523
|
+
return path7;
|
|
5724
6524
|
}
|
|
5725
6525
|
samePath(pathApi, left, right) {
|
|
5726
6526
|
return this.absolutePath(pathApi, left) === this.absolutePath(pathApi, right);
|
|
@@ -5797,9 +6597,16 @@ var MigrationReportBuilder = class {
|
|
|
5797
6597
|
};
|
|
5798
6598
|
}
|
|
5799
6599
|
};
|
|
6600
|
+
function withReportEnvironment(report, options) {
|
|
6601
|
+
return {
|
|
6602
|
+
...report,
|
|
6603
|
+
...options.targetProfile ? { targetProfile: options.targetProfile } : {},
|
|
6604
|
+
...options.sourceBreakpoints ? { sourceBreakpoints: options.sourceBreakpoints } : {}
|
|
6605
|
+
};
|
|
6606
|
+
}
|
|
5800
6607
|
|
|
5801
6608
|
// src/pipeline/invocation-error-path.mapper.ts
|
|
5802
|
-
import * as
|
|
6609
|
+
import * as path8 from "path";
|
|
5803
6610
|
function remapInvocationErrorPaths(error, invocation) {
|
|
5804
6611
|
if (!(error instanceof Error)) return error;
|
|
5805
6612
|
const roots = invocationRoots(invocation);
|
|
@@ -5823,12 +6630,12 @@ function remapErrorPath(error, field, roots) {
|
|
|
5823
6630
|
error[field] = mapped;
|
|
5824
6631
|
}
|
|
5825
6632
|
function mappedInvocationPath(candidate, roots) {
|
|
5826
|
-
if (!
|
|
6633
|
+
if (!path8.isAbsolute(candidate)) return candidate;
|
|
5827
6634
|
for (const root of roots) {
|
|
5828
|
-
const relativePath =
|
|
6635
|
+
const relativePath = path8.relative(root.canonical, candidate);
|
|
5829
6636
|
if (relativePath === "") return root.raw;
|
|
5830
|
-
if (relativePath === ".." || relativePath.startsWith(`..${
|
|
5831
|
-
return
|
|
6637
|
+
if (relativePath === ".." || relativePath.startsWith(`..${path8.sep}`) || path8.isAbsolute(relativePath)) continue;
|
|
6638
|
+
return path8.join(root.raw, relativePath);
|
|
5832
6639
|
}
|
|
5833
6640
|
return candidate;
|
|
5834
6641
|
}
|
|
@@ -5846,7 +6653,7 @@ var MigrationRunner = class {
|
|
|
5846
6653
|
const durationMs = this.now() - startedAt;
|
|
5847
6654
|
const { validated, application } = applied;
|
|
5848
6655
|
const { plan, rendered, stylesheet } = validated;
|
|
5849
|
-
|
|
6656
|
+
const report = this.reports.build(
|
|
5850
6657
|
rendered.analyzed.manifest.invocation.inputPath,
|
|
5851
6658
|
rendered.analyzed.manifest.invocation.outputPath,
|
|
5852
6659
|
plan.target,
|
|
@@ -5856,6 +6663,7 @@ var MigrationRunner = class {
|
|
|
5856
6663
|
plan.files,
|
|
5857
6664
|
stylesheet
|
|
5858
6665
|
);
|
|
6666
|
+
return withReportEnvironment(report, invocation.options);
|
|
5859
6667
|
}
|
|
5860
6668
|
async execute(invocation) {
|
|
5861
6669
|
try {
|
|
@@ -8898,7 +9706,7 @@ var ConversionPlanner = class {
|
|
|
8898
9706
|
};
|
|
8899
9707
|
|
|
8900
9708
|
// src/pipeline/rendered-project.ts
|
|
8901
|
-
import * as
|
|
9709
|
+
import * as path9 from "path";
|
|
8902
9710
|
function renderedProject(project) {
|
|
8903
9711
|
const analyzed = analyzedProject(project.analyzed);
|
|
8904
9712
|
if (project.files.length !== analyzed.templates.length) throw sequenceInvariant2();
|
|
@@ -9025,7 +9833,7 @@ function samePathPair2(left, right) {
|
|
|
9025
9833
|
return normalizedAbsolutePath3(left.inputPath) === normalizedAbsolutePath3(right.inputPath) && normalizedAbsolutePath3(left.outputPath) === normalizedAbsolutePath3(right.outputPath);
|
|
9026
9834
|
}
|
|
9027
9835
|
function normalizedAbsolutePath3(value) {
|
|
9028
|
-
return
|
|
9836
|
+
return path9.normalize(path9.resolve(value));
|
|
9029
9837
|
}
|
|
9030
9838
|
function sameOwnedValue(left, right) {
|
|
9031
9839
|
if (Object.is(left, right)) return true;
|
|
@@ -9114,10 +9922,10 @@ function parseErrorPlan(template) {
|
|
|
9114
9922
|
}
|
|
9115
9923
|
|
|
9116
9924
|
// src/pipeline/validate/validate-project.stage.ts
|
|
9117
|
-
import * as
|
|
9925
|
+
import * as path15 from "path";
|
|
9118
9926
|
|
|
9119
9927
|
// src/migrator/migration-plan.ts
|
|
9120
|
-
import * as
|
|
9928
|
+
import * as path10 from "path";
|
|
9121
9929
|
|
|
9122
9930
|
// src/migrator/file-migration-result.ts
|
|
9123
9931
|
function fileMigrationResult(result2) {
|
|
@@ -9140,7 +9948,7 @@ function freezeValue2(value) {
|
|
|
9140
9948
|
|
|
9141
9949
|
// src/migrator/migration-plan.ts
|
|
9142
9950
|
function plannedOutputArtifact(artifact) {
|
|
9143
|
-
if (!
|
|
9951
|
+
if (!path10.isAbsolute(artifact.path)) {
|
|
9144
9952
|
throw new Error(`Planned artifact paths must be absolute: ${artifact.path}`);
|
|
9145
9953
|
}
|
|
9146
9954
|
if (artifact.kind === "template" && artifact.proposed.status === "absent") {
|
|
@@ -9151,7 +9959,7 @@ function plannedOutputArtifact(artifact) {
|
|
|
9151
9959
|
}
|
|
9152
9960
|
return Object.freeze({
|
|
9153
9961
|
kind: artifact.kind,
|
|
9154
|
-
path:
|
|
9962
|
+
path: path10.resolve(artifact.path),
|
|
9155
9963
|
original: artifactState(artifact.original),
|
|
9156
9964
|
proposed: artifactState(artifact.proposed)
|
|
9157
9965
|
});
|
|
@@ -9178,216 +9986,9 @@ function sameState(left, right) {
|
|
|
9178
9986
|
return right.status === "present" && left.contents === right.contents;
|
|
9179
9987
|
}
|
|
9180
9988
|
|
|
9181
|
-
// src/migrator/migration-path.validator.ts
|
|
9182
|
-
import { lstat as lstat2, stat as stat3 } from "fs/promises";
|
|
9183
|
-
import * as path9 from "path";
|
|
9184
|
-
async function validateMigrationPaths(request, pathApi = path9) {
|
|
9185
|
-
const claims = normalizedClaims(request, pathApi);
|
|
9186
|
-
await validateCollisions(claims, pathApi);
|
|
9187
|
-
const destinations = claims.filter((claim) => claim.kind !== "template-input");
|
|
9188
|
-
for (const destination of destinations) {
|
|
9189
|
-
await validateDestination(destination.path);
|
|
9190
|
-
}
|
|
9191
|
-
}
|
|
9192
|
-
async function validateStylesheetRootTopology(request, pathApi = path9) {
|
|
9193
|
-
if (request.stylesheetPath === void 0) return;
|
|
9194
|
-
const stylesheetPath = pathApi.resolve(request.stylesheetPath);
|
|
9195
|
-
const templateRoots = [request.inputPath, request.outputPath].map((claim) => pathApi.resolve(claim));
|
|
9196
|
-
const reportPath = request.reportPath === void 0 ? void 0 : pathApi.resolve(request.reportPath);
|
|
9197
|
-
const exactCollision = (await Promise.all(
|
|
9198
|
-
[...templateRoots, reportPath].map(
|
|
9199
|
-
(claim) => claim === void 0 ? Promise.resolve(false) : pathsEquivalentOnFileSystem(stylesheetPath, claim, pathApi)
|
|
9200
|
-
)
|
|
9201
|
-
)).some(Boolean);
|
|
9202
|
-
const reportHierarchyCollision = reportPath !== void 0 && await pathsOverlapOnFileSystem(stylesheetPath, reportPath, pathApi);
|
|
9203
|
-
if (exactCollision || reportHierarchyCollision) {
|
|
9204
|
-
const collisionPaths = reportHierarchyCollision && reportPath !== void 0 && !await pathsEquivalentOnFileSystem(stylesheetPath, reportPath, pathApi) ? [stylesheetPath, reportPath] : [stylesheetPath];
|
|
9205
|
-
throw new MigrationApplicationError(
|
|
9206
|
-
"path-collision",
|
|
9207
|
-
`Stylesheet path collides with another migration path: ${request.stylesheetPathInput ?? request.stylesheetPath}`,
|
|
9208
|
-
collisionPaths
|
|
9209
|
-
);
|
|
9210
|
-
}
|
|
9211
|
-
let stylesheetStat;
|
|
9212
|
-
try {
|
|
9213
|
-
stylesheetStat = await lstat2(stylesheetPath);
|
|
9214
|
-
} catch (error) {
|
|
9215
|
-
if (isEnoent2(error)) return;
|
|
9216
|
-
throw error;
|
|
9217
|
-
}
|
|
9218
|
-
const sourcePath = request.stylesheetPathInput ?? request.stylesheetPath;
|
|
9219
|
-
if (stylesheetStat.isSymbolicLink()) {
|
|
9220
|
-
throw new MigrationApplicationError(
|
|
9221
|
-
"unsupported-path-type",
|
|
9222
|
-
`Stylesheet path must not be a symbolic link: ${sourcePath}`,
|
|
9223
|
-
[stylesheetPath]
|
|
9224
|
-
);
|
|
9225
|
-
}
|
|
9226
|
-
if (!stylesheetStat.isFile()) {
|
|
9227
|
-
throw new MigrationApplicationError(
|
|
9228
|
-
"unsupported-path-type",
|
|
9229
|
-
`Stylesheet path must be a regular file: ${sourcePath}`,
|
|
9230
|
-
[stylesheetPath]
|
|
9231
|
-
);
|
|
9232
|
-
}
|
|
9233
|
-
}
|
|
9234
|
-
function normalizedClaims(request, pathApi) {
|
|
9235
|
-
return [
|
|
9236
|
-
...request.templates.flatMap((template, templateIndex) => [
|
|
9237
|
-
{ path: pathApi.resolve(template.inputPath), kind: "template-input", templateIndex },
|
|
9238
|
-
{ path: pathApi.resolve(template.outputPath), kind: "template-output", templateIndex }
|
|
9239
|
-
]),
|
|
9240
|
-
...request.stylesheetPath ? [{ path: pathApi.resolve(request.stylesheetPath), kind: "stylesheet" }] : [],
|
|
9241
|
-
...request.reportPath ? [{ path: pathApi.resolve(request.reportPath), kind: "report" }] : []
|
|
9242
|
-
];
|
|
9243
|
-
}
|
|
9244
|
-
async function validateCollisions(claims, pathApi) {
|
|
9245
|
-
const observations = /* @__PURE__ */ new Map();
|
|
9246
|
-
const observe = (candidate) => {
|
|
9247
|
-
const normalized = pathApi.resolve(candidate);
|
|
9248
|
-
const existing = observations.get(normalized);
|
|
9249
|
-
if (existing) return existing;
|
|
9250
|
-
const pending = observePath(normalized, pathApi);
|
|
9251
|
-
observations.set(normalized, pending);
|
|
9252
|
-
return pending;
|
|
9253
|
-
};
|
|
9254
|
-
for (let leftIndex = 0; leftIndex < claims.length; leftIndex++) {
|
|
9255
|
-
const left = claims[leftIndex];
|
|
9256
|
-
if (!left) continue;
|
|
9257
|
-
for (let rightIndex = leftIndex + 1; rightIndex < claims.length; rightIndex++) {
|
|
9258
|
-
const right = claims[rightIndex];
|
|
9259
|
-
if (!right) continue;
|
|
9260
|
-
const relationship = await fileSystemPathRelationship(left.path, right.path, pathApi, observe);
|
|
9261
|
-
if (relationship === "distinct" || isIntentionalInPlacePair(left, right, pathApi)) continue;
|
|
9262
|
-
const collisionPaths = relationship === "equivalent" ? [left.path] : [left.path, right.path];
|
|
9263
|
-
throw new MigrationApplicationError(
|
|
9264
|
-
"path-collision",
|
|
9265
|
-
`Migration paths collide: ${collisionPaths.join(" and ")}`,
|
|
9266
|
-
collisionPaths
|
|
9267
|
-
);
|
|
9268
|
-
}
|
|
9269
|
-
}
|
|
9270
|
-
}
|
|
9271
|
-
function isIntentionalInPlacePair(left, right, pathApi) {
|
|
9272
|
-
return pathsEquivalent(left.path, right.path, pathApi) && left.templateIndex !== void 0 && left.templateIndex === right.templateIndex && left.kind !== right.kind && left.kind.startsWith("template-") && right.kind.startsWith("template-");
|
|
9273
|
-
}
|
|
9274
|
-
function pathsEquivalent(left, right, pathApi = path9) {
|
|
9275
|
-
return normalizedPathsEquivalent(pathApi, pathApi.resolve(left), pathApi.resolve(right));
|
|
9276
|
-
}
|
|
9277
|
-
async function pathsEquivalentOnFileSystem(left, right, pathApi = path9) {
|
|
9278
|
-
return await fileSystemPathRelationship(left, right, pathApi) === "equivalent";
|
|
9279
|
-
}
|
|
9280
|
-
async function pathsOverlapOnFileSystem(left, right, pathApi = path9) {
|
|
9281
|
-
return await fileSystemPathRelationship(left, right, pathApi) !== "distinct";
|
|
9282
|
-
}
|
|
9283
|
-
function normalizedPathsEquivalent(pathApi, left, right) {
|
|
9284
|
-
return pathApi.relative(left, right) === "";
|
|
9285
|
-
}
|
|
9286
|
-
function isAncestor(pathApi, ancestor, descendant) {
|
|
9287
|
-
const relative3 = pathApi.relative(ancestor, descendant);
|
|
9288
|
-
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(`..${pathApi.sep}`) && !pathApi.isAbsolute(relative3);
|
|
9289
|
-
}
|
|
9290
|
-
async function fileSystemPathRelationship(left, right, pathApi, observe = (candidate) => observePath(candidate, pathApi)) {
|
|
9291
|
-
const normalizedLeft = pathApi.resolve(left);
|
|
9292
|
-
const normalizedRight = pathApi.resolve(right);
|
|
9293
|
-
if (normalizedPathsEquivalent(pathApi, normalizedLeft, normalizedRight)) return "equivalent";
|
|
9294
|
-
if (isAncestor(pathApi, normalizedLeft, normalizedRight)) return "ancestor";
|
|
9295
|
-
if (isAncestor(pathApi, normalizedRight, normalizedLeft)) return "descendant";
|
|
9296
|
-
const [observedLeft, observedRight] = await Promise.all([observe(normalizedLeft), observe(normalizedRight)]);
|
|
9297
|
-
if (observedLeft.exactIdentity && observedRight.exactIdentity) {
|
|
9298
|
-
if (sameIdentity2(observedLeft.exactIdentity, observedRight.exactIdentity)) return "equivalent";
|
|
9299
|
-
if (hasIdentityBelow(observedRight, observedLeft.exactIdentity)) return "ancestor";
|
|
9300
|
-
if (hasIdentityBelow(observedLeft, observedRight.exactIdentity)) return "descendant";
|
|
9301
|
-
return "distinct";
|
|
9302
|
-
}
|
|
9303
|
-
return relationshipThroughExistingPrefixes(observedLeft, observedRight);
|
|
9304
|
-
}
|
|
9305
|
-
async function observePath(candidate, pathApi) {
|
|
9306
|
-
const prefixes = [];
|
|
9307
|
-
const suffix = [];
|
|
9308
|
-
let current = candidate;
|
|
9309
|
-
let exactIdentity;
|
|
9310
|
-
while (true) {
|
|
9311
|
-
try {
|
|
9312
|
-
const currentStat = await stat3(current, { bigint: true });
|
|
9313
|
-
const currentIdentity = identity2(currentStat);
|
|
9314
|
-
if (suffix.length === 0) exactIdentity = currentIdentity;
|
|
9315
|
-
prefixes.push({ identity: currentIdentity, suffix: [...suffix] });
|
|
9316
|
-
} catch (error) {
|
|
9317
|
-
if (!isMissingPath(error)) throw error;
|
|
9318
|
-
}
|
|
9319
|
-
const parent = pathApi.dirname(current);
|
|
9320
|
-
if (parent === current) break;
|
|
9321
|
-
suffix.unshift(pathApi.basename(current));
|
|
9322
|
-
current = parent;
|
|
9323
|
-
}
|
|
9324
|
-
return { ...exactIdentity ? { exactIdentity } : {}, prefixes };
|
|
9325
|
-
}
|
|
9326
|
-
function relationshipThroughExistingPrefixes(left, right) {
|
|
9327
|
-
for (const leftPrefix of left.prefixes) {
|
|
9328
|
-
for (const rightPrefix of right.prefixes) {
|
|
9329
|
-
if (!sameIdentity2(leftPrefix.identity, rightPrefix.identity)) continue;
|
|
9330
|
-
const relationship = suffixRelationship(leftPrefix.suffix, rightPrefix.suffix);
|
|
9331
|
-
if (relationship !== "distinct") return relationship;
|
|
9332
|
-
}
|
|
9333
|
-
}
|
|
9334
|
-
return "distinct";
|
|
9335
|
-
}
|
|
9336
|
-
function hasIdentityBelow(observed, candidate) {
|
|
9337
|
-
return observed.prefixes.some((prefix) => prefix.suffix.length > 0 && sameIdentity2(prefix.identity, candidate));
|
|
9338
|
-
}
|
|
9339
|
-
function suffixRelationship(left, right) {
|
|
9340
|
-
const normalizedLeft = left.map(portablePathSegment);
|
|
9341
|
-
const normalizedRight = right.map(portablePathSegment);
|
|
9342
|
-
const sharedLength = Math.min(normalizedLeft.length, normalizedRight.length);
|
|
9343
|
-
for (let index = 0; index < sharedLength; index++) {
|
|
9344
|
-
if (normalizedLeft[index] !== normalizedRight[index]) return "distinct";
|
|
9345
|
-
}
|
|
9346
|
-
if (normalizedLeft.length === normalizedRight.length) return "equivalent";
|
|
9347
|
-
return normalizedLeft.length < normalizedRight.length ? "ancestor" : "descendant";
|
|
9348
|
-
}
|
|
9349
|
-
function portablePathSegment(value) {
|
|
9350
|
-
return value.normalize("NFC").toLowerCase();
|
|
9351
|
-
}
|
|
9352
|
-
function identity2(value) {
|
|
9353
|
-
return { device: String(value.dev), inode: String(value.ino) };
|
|
9354
|
-
}
|
|
9355
|
-
function sameIdentity2(left, right) {
|
|
9356
|
-
return left.device === right.device && left.inode === right.inode;
|
|
9357
|
-
}
|
|
9358
|
-
async function validateDestination(destination) {
|
|
9359
|
-
let stat4;
|
|
9360
|
-
try {
|
|
9361
|
-
stat4 = await lstat2(destination);
|
|
9362
|
-
} catch (error) {
|
|
9363
|
-
if (isEnoent2(error)) return;
|
|
9364
|
-
throw error;
|
|
9365
|
-
}
|
|
9366
|
-
if (stat4.isSymbolicLink()) {
|
|
9367
|
-
throw new MigrationApplicationError(
|
|
9368
|
-
"unsupported-path-type",
|
|
9369
|
-
`Migration destination must not be a symbolic link: ${destination}`,
|
|
9370
|
-
[destination]
|
|
9371
|
-
);
|
|
9372
|
-
}
|
|
9373
|
-
if (!stat4.isFile()) {
|
|
9374
|
-
throw new MigrationApplicationError(
|
|
9375
|
-
"unsupported-path-type",
|
|
9376
|
-
`Migration destination must be a regular file: ${destination}`,
|
|
9377
|
-
[destination]
|
|
9378
|
-
);
|
|
9379
|
-
}
|
|
9380
|
-
}
|
|
9381
|
-
function isEnoent2(error) {
|
|
9382
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
9383
|
-
}
|
|
9384
|
-
function isMissingPath(error) {
|
|
9385
|
-
return typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
9386
|
-
}
|
|
9387
|
-
|
|
9388
9989
|
// src/migrator/stylesheet.planner.ts
|
|
9389
|
-
import { lstat as lstat3, readFile as
|
|
9390
|
-
import * as
|
|
9990
|
+
import { lstat as lstat3, readFile as readFile3 } from "fs/promises";
|
|
9991
|
+
import * as path11 from "path";
|
|
9391
9992
|
|
|
9392
9993
|
// src/adapter/css/stylesheet/css-stylesheet.error.ts
|
|
9393
9994
|
var CssStylesheetError = class extends Error {
|
|
@@ -9916,7 +10517,7 @@ function mergeStylesheetContents(existing, rules, references = new Set(rules.map
|
|
|
9916
10517
|
|
|
9917
10518
|
// src/migrator/stylesheet.planner.ts
|
|
9918
10519
|
var nodeFileSystem2 = {
|
|
9919
|
-
readFile: (target) =>
|
|
10520
|
+
readFile: (target) => readFile3(target, "utf8"),
|
|
9920
10521
|
lstat: lstat3
|
|
9921
10522
|
};
|
|
9922
10523
|
var StylesheetPlanner = class {
|
|
@@ -9924,7 +10525,7 @@ var StylesheetPlanner = class {
|
|
|
9924
10525
|
this.fileSystem = fileSystem;
|
|
9925
10526
|
}
|
|
9926
10527
|
async plan(stylesheetPath, rules, references = new Set(rules.map((rule) => rule.className))) {
|
|
9927
|
-
const outputPath =
|
|
10528
|
+
const outputPath = path11.resolve(stylesheetPath);
|
|
9928
10529
|
const original = await this.originalState(outputPath);
|
|
9929
10530
|
const existing = original.status === "present" ? original.contents : "";
|
|
9930
10531
|
let merged;
|
|
@@ -9984,7 +10585,7 @@ function isEnoent3(error) {
|
|
|
9984
10585
|
}
|
|
9985
10586
|
|
|
9986
10587
|
// src/pipeline/validated-project-plan.ts
|
|
9987
|
-
import * as
|
|
10588
|
+
import * as path12 from "path";
|
|
9988
10589
|
function validatedProjectPlan(project) {
|
|
9989
10590
|
const rendered = renderedProject(project.rendered);
|
|
9990
10591
|
if (rendered.target !== rendered.session.target) {
|
|
@@ -10097,7 +10698,7 @@ function isPlainRecord2(value) {
|
|
|
10097
10698
|
return prototype === Object.prototype || prototype === null;
|
|
10098
10699
|
}
|
|
10099
10700
|
function normalizedAbsolutePath4(value) {
|
|
10100
|
-
return
|
|
10701
|
+
return path12.normalize(path12.resolve(value));
|
|
10101
10702
|
}
|
|
10102
10703
|
function fileCongruenceInvariant() {
|
|
10103
10704
|
return internalInvariant4(
|
|
@@ -10109,12 +10710,12 @@ function internalInvariant4(message) {
|
|
|
10109
10710
|
}
|
|
10110
10711
|
|
|
10111
10712
|
// src/pipeline/validate/css-reference.collector.ts
|
|
10112
|
-
import * as
|
|
10713
|
+
import * as path13 from "path";
|
|
10113
10714
|
|
|
10114
10715
|
// src/migrator/destination-template-source.ts
|
|
10115
|
-
import { readFile as
|
|
10716
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
10116
10717
|
var nodeDestinationTemplateSource = Object.freeze({
|
|
10117
|
-
read: (
|
|
10718
|
+
read: (path19) => readFile4(path19, "utf8")
|
|
10118
10719
|
});
|
|
10119
10720
|
|
|
10120
10721
|
// src/pipeline/validate/css-reference.collector.ts
|
|
@@ -10133,10 +10734,10 @@ var CssReferenceCollector = class {
|
|
|
10133
10734
|
if (file.artifact?.kind === "template" && file.artifact.proposed.status === "present") {
|
|
10134
10735
|
return { contents: file.artifact.proposed.contents, complete: true };
|
|
10135
10736
|
}
|
|
10136
|
-
if (
|
|
10737
|
+
if (path13.resolve(file.file.inputPath) === path13.resolve(file.file.outputPath)) {
|
|
10137
10738
|
return { contents: analyzedTemplate.source, complete: true };
|
|
10138
10739
|
}
|
|
10139
|
-
const outputPath =
|
|
10740
|
+
const outputPath = path13.normalize(path13.resolve(file.file.outputPath));
|
|
10140
10741
|
const existing = destinationSources.get(outputPath);
|
|
10141
10742
|
if (existing !== void 0) return existing;
|
|
10142
10743
|
const pending = this.readDestination(outputPath);
|
|
@@ -10202,8 +10803,20 @@ function isEnoent4(error) {
|
|
|
10202
10803
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
10203
10804
|
}
|
|
10204
10805
|
|
|
10806
|
+
// src/template/generated-template-validation.ts
|
|
10807
|
+
function generatedTemplateErrors(source, fileName, parser = new AngularTemplateParser()) {
|
|
10808
|
+
const parsed2 = parser.parse(source, fileName);
|
|
10809
|
+
return parsed2.status === "parse-error" ? parsed2.diagnostics.map((diagnostic2) => ({
|
|
10810
|
+
status: "parse-error",
|
|
10811
|
+
fileName,
|
|
10812
|
+
code: "generated-template-parse-error",
|
|
10813
|
+
reason: diagnostic2.message,
|
|
10814
|
+
source: diagnostic2.source
|
|
10815
|
+
})) : [];
|
|
10816
|
+
}
|
|
10817
|
+
|
|
10205
10818
|
// src/pipeline/validate/template-proposal.validator.ts
|
|
10206
|
-
import * as
|
|
10819
|
+
import * as path14 from "path";
|
|
10207
10820
|
var TemplateProposalValidator = class {
|
|
10208
10821
|
constructor(validationParser = new AngularTemplateParser(), destinationTemplates = nodeDestinationTemplateSource) {
|
|
10209
10822
|
this.validationParser = validationParser;
|
|
@@ -10218,21 +10831,8 @@ var TemplateProposalValidator = class {
|
|
|
10218
10831
|
);
|
|
10219
10832
|
}
|
|
10220
10833
|
if (edited.output === template.source) return planResult(template, rendered, false, rendered.results);
|
|
10221
|
-
const
|
|
10222
|
-
if (
|
|
10223
|
-
return planResult(
|
|
10224
|
-
template,
|
|
10225
|
-
rendered,
|
|
10226
|
-
false,
|
|
10227
|
-
reparsed.diagnostics.map((diagnostic2) => ({
|
|
10228
|
-
status: "parse-error",
|
|
10229
|
-
fileName: template.file.outputPath,
|
|
10230
|
-
code: "generated-template-parse-error",
|
|
10231
|
-
reason: diagnostic2.message,
|
|
10232
|
-
source: diagnostic2.source
|
|
10233
|
-
}))
|
|
10234
|
-
);
|
|
10235
|
-
}
|
|
10834
|
+
const errors = generatedTemplateErrors(edited.output, template.file.outputPath, this.validationParser);
|
|
10835
|
+
if (errors.length) return planResult(template, rendered, false, errors);
|
|
10236
10836
|
const original = await originalState(template, this.destinationTemplates);
|
|
10237
10837
|
const proposed = { status: "present", contents: edited.output };
|
|
10238
10838
|
if (sameState3(original, proposed)) return planResult(template, rendered, false, rendered.results);
|
|
@@ -10240,7 +10840,7 @@ var TemplateProposalValidator = class {
|
|
|
10240
10840
|
file: result(template, rendered, true, rendered.results),
|
|
10241
10841
|
artifact: plannedOutputArtifact({
|
|
10242
10842
|
kind: "template",
|
|
10243
|
-
path:
|
|
10843
|
+
path: path14.normalize(path14.resolve(template.file.outputPath)),
|
|
10244
10844
|
original,
|
|
10245
10845
|
proposed
|
|
10246
10846
|
})
|
|
@@ -10248,7 +10848,7 @@ var TemplateProposalValidator = class {
|
|
|
10248
10848
|
}
|
|
10249
10849
|
};
|
|
10250
10850
|
async function originalState(template, destinationTemplates) {
|
|
10251
|
-
if (
|
|
10851
|
+
if (path14.resolve(template.file.inputPath) === path14.resolve(template.file.outputPath)) {
|
|
10252
10852
|
return { status: "present", contents: template.source };
|
|
10253
10853
|
}
|
|
10254
10854
|
try {
|
|
@@ -10316,7 +10916,7 @@ var ValidateProjectStage = class {
|
|
|
10316
10916
|
let stylesheetArtifact;
|
|
10317
10917
|
let stylesheet;
|
|
10318
10918
|
if (rendered.session.target === "css" && stylesheetPath !== void 0) {
|
|
10319
|
-
const canonicalStylesheetPath =
|
|
10919
|
+
const canonicalStylesheetPath = path15.resolve(stylesheetPath);
|
|
10320
10920
|
const references = await this.cssReferences.collect(rendered, files);
|
|
10321
10921
|
stylesheetArtifact = await this.stylesheetPlanner.plan(
|
|
10322
10922
|
canonicalStylesheetPath,
|
|
@@ -10402,19 +11002,19 @@ var AtomicFileWriter = class {
|
|
|
10402
11002
|
throw cleanupError(targetPath);
|
|
10403
11003
|
}
|
|
10404
11004
|
}
|
|
10405
|
-
async captureNamespaceIdentity(
|
|
10406
|
-
const stat4 = await this.operations.lstat(
|
|
11005
|
+
async captureNamespaceIdentity(path19, targetPath) {
|
|
11006
|
+
const stat4 = await this.operations.lstat(path19);
|
|
10407
11007
|
if (stat4.isSymbolicLink() || !stat4.isDirectory()) throw ownershipError(targetPath);
|
|
10408
11008
|
return identity3(stat4);
|
|
10409
11009
|
}
|
|
10410
|
-
async assertNamespaceIdentity(
|
|
10411
|
-
const stat4 = await this.operations.lstat(
|
|
11010
|
+
async assertNamespaceIdentity(path19, expected, targetPath) {
|
|
11011
|
+
const stat4 = await this.operations.lstat(path19);
|
|
10412
11012
|
if (stat4.isSymbolicLink() || !stat4.isDirectory() || !sameIdentity3(identity3(stat4), expected)) {
|
|
10413
11013
|
throw ownershipError(targetPath);
|
|
10414
11014
|
}
|
|
10415
11015
|
}
|
|
10416
|
-
async assertTemporaryIdentity(
|
|
10417
|
-
const stat4 = await this.operations.lstat(
|
|
11016
|
+
async assertTemporaryIdentity(path19, expected, targetPath) {
|
|
11017
|
+
const stat4 = await this.operations.lstat(path19);
|
|
10418
11018
|
if (stat4.isSymbolicLink() || !stat4.isFile() || !sameIdentity3(identity3(stat4), expected)) {
|
|
10419
11019
|
throw ownershipError(targetPath);
|
|
10420
11020
|
}
|
|
@@ -10478,19 +11078,19 @@ var JsonReportWriter = class {
|
|
|
10478
11078
|
constructor(writer = new AtomicFileWriter()) {
|
|
10479
11079
|
this.writer = writer;
|
|
10480
11080
|
}
|
|
10481
|
-
async write(
|
|
11081
|
+
async write(path19, report, options = {}) {
|
|
10482
11082
|
for (const protectedPath of options.protectedPaths ?? []) {
|
|
10483
|
-
if (!await pathsOverlapOnFileSystem(protectedPath,
|
|
10484
|
-
const collisionPaths = await pathsEquivalentOnFileSystem(protectedPath,
|
|
11083
|
+
if (!await pathsOverlapOnFileSystem(protectedPath, path19)) continue;
|
|
11084
|
+
const collisionPaths = await pathsEquivalentOnFileSystem(protectedPath, path19) ? [protectedPath] : [protectedPath, path19];
|
|
10485
11085
|
throw new MigrationApplicationError(
|
|
10486
11086
|
"path-collision",
|
|
10487
|
-
`Report path collides with a migration output: ${
|
|
11087
|
+
`Report path collides with a migration output: ${path19}`,
|
|
10488
11088
|
collisionPaths
|
|
10489
11089
|
);
|
|
10490
11090
|
}
|
|
10491
11091
|
const contents = `${JSON.stringify(report, null, 2)}
|
|
10492
11092
|
`;
|
|
10493
|
-
await this.writer.write(
|
|
11093
|
+
await this.writer.write(path19, contents);
|
|
10494
11094
|
}
|
|
10495
11095
|
};
|
|
10496
11096
|
|
|
@@ -10508,8 +11108,23 @@ var TerminalPresenter = class {
|
|
|
10508
11108
|
const diagnostics = report.files.flatMap(
|
|
10509
11109
|
(file) => file.results.filter((result2) => result2.status !== "converted").map((result2) => `${file.path}:${result2.offset} [${result2.code}] ${result2.reason}`)
|
|
10510
11110
|
);
|
|
11111
|
+
const profile = report.targetProfile;
|
|
11112
|
+
const environment = profile ? [
|
|
11113
|
+
"Target environment",
|
|
11114
|
+
`Tailwind: v${profile.version} (${profile.fingerprint})`,
|
|
11115
|
+
`Stylesheet: ${profile.stylesheet ?? "not supplied"}`,
|
|
11116
|
+
`Prefix: ${profile.prefix.value ?? "none"} (${profile.prefix.source}; ${profile.prefix.confidence})`,
|
|
11117
|
+
`Important utilities: ${profile.important.value} (${profile.important.source})`,
|
|
11118
|
+
`Core utilities: ${profile.coreUtilities.value} (${profile.coreUtilities.source}; ${profile.coreUtilities.confidence})`,
|
|
11119
|
+
...Object.entries(profile.breakpoints).map(
|
|
11120
|
+
([name, setting]) => `Breakpoint ${name}: ${setting.value} (${setting.source}; ${setting.confidence})`
|
|
11121
|
+
),
|
|
11122
|
+
...profile.diagnostics.map((item) => `[${item.code}] ${item.message}`),
|
|
11123
|
+
...profile.assumptions.map((item) => `Assumption: ${item}`),
|
|
11124
|
+
""
|
|
11125
|
+
] : [];
|
|
10511
11126
|
output.write(
|
|
10512
|
-
[presentation.outcome, totals, stylesheet, ...diagnostics, presentation.footer, ""].filter((line) => line !== void 0).join("\n")
|
|
11127
|
+
[...environment, presentation.outcome, totals, stylesheet, ...diagnostics, presentation.footer, ""].filter((line) => line !== void 0).join("\n")
|
|
10513
11128
|
);
|
|
10514
11129
|
}
|
|
10515
11130
|
applicationPresentation(report) {
|
|
@@ -10610,17 +11225,17 @@ function getErrorMessage(error) {
|
|
|
10610
11225
|
// src/cli/exit-policy.ts
|
|
10611
11226
|
function resolveExitCode(report, allowUnresolved) {
|
|
10612
11227
|
if (report.summary.parseErrors > 0) return 1;
|
|
10613
|
-
const hasUnresolved = report.summary.review > 0 || report.summary.unsupported > 0 || report.summary.invalid > 0;
|
|
11228
|
+
const hasUnresolved = (report.targetProfile?.diagnostics.length ?? 0) > 0 || report.summary.review > 0 || report.summary.unsupported > 0 || report.summary.invalid > 0;
|
|
10614
11229
|
return hasUnresolved && !allowUnresolved ? 2 : 0;
|
|
10615
11230
|
}
|
|
10616
11231
|
|
|
10617
11232
|
// src/cli/report-path.validator.ts
|
|
10618
|
-
import
|
|
11233
|
+
import path16 from "path";
|
|
10619
11234
|
function validateReportPath(reportPath) {
|
|
10620
11235
|
if (reportPath.trim().length === 0) {
|
|
10621
11236
|
throw new Error("Report path must not be empty.");
|
|
10622
11237
|
}
|
|
10623
|
-
if (
|
|
11238
|
+
if (path16.extname(reportPath).toLowerCase() !== ".json") {
|
|
10624
11239
|
throw new Error("Report path must have a .json extension.");
|
|
10625
11240
|
}
|
|
10626
11241
|
}
|
|
@@ -10653,8 +11268,8 @@ function parsePrintWithBreakpoints(value, orientationEnabled) {
|
|
|
10653
11268
|
}
|
|
10654
11269
|
|
|
10655
11270
|
// src/cli/stylesheet-path.validator.ts
|
|
10656
|
-
import * as
|
|
10657
|
-
async function validateStylesheetPath(request, pathApi =
|
|
11271
|
+
import * as path17 from "path";
|
|
11272
|
+
async function validateStylesheetPath(request, pathApi = path17) {
|
|
10658
11273
|
if (request.target !== "css") {
|
|
10659
11274
|
if (request.stylesheetPath !== void 0) {
|
|
10660
11275
|
throw new MigrationApplicationError("invalid-configuration", "--stylesheet can only be used with --target css.", [
|
|
@@ -10684,6 +11299,7 @@ function resolveMigrationMode(argv, write) {
|
|
|
10684
11299
|
if (optionArguments.filter((argument) => argument === "--write").length > 1) {
|
|
10685
11300
|
throw new Error("--write may only be specified once.");
|
|
10686
11301
|
}
|
|
11302
|
+
if (write && optionArguments.includes("--plan")) throw new Error("--plan and --write cannot be combined.");
|
|
10687
11303
|
return write ? "write" : "plan";
|
|
10688
11304
|
}
|
|
10689
11305
|
|
|
@@ -10712,7 +11328,7 @@ async function runCli(argv, output = processOutput, dependencies = {}) {
|
|
|
10712
11328
|
new Option("--stylesheet <path>", "companion stylesheet; required when --target css").argParser(
|
|
10713
11329
|
parseSingleStylesheet
|
|
10714
11330
|
)
|
|
10715
|
-
).option("--write", "apply the validated migration plan", false).option("--report <path>", "atomically write a JSON report; path must end in .json").option("--allow-unresolved", "return success when unresolved inputs remain", false).option("--orientation-breakpoints", "confirm the source enables the archived orientation breakpoints", false).option(
|
|
11331
|
+
).option("--config <path>", "declarative migration JSON configuration").option("--tailwind-stylesheet <path>", "statically analyze a Tailwind v4 target stylesheet").option("--tailwind-prefix <prefix>", "override Tailwind v4 prefix; empty string means none").option("--plan", "explicitly request the default review-only plan").option("--write", "apply the validated migration plan", false).option("--report <path>", "atomically write a JSON report; path must end in .json").option("--allow-unresolved", "return success when unresolved inputs remain", false).option("--orientation-breakpoints", "confirm the source enables the archived orientation breakpoints", false).option(
|
|
10716
11332
|
"--responsive-images",
|
|
10717
11333
|
"wrap eligible responsive images in picture elements; acknowledges selector and layout risk",
|
|
10718
11334
|
false
|
|
@@ -10723,23 +11339,38 @@ async function runCli(argv, output = processOutput, dependencies = {}) {
|
|
|
10723
11339
|
const mode = resolveMigrationMode(argv, options.write);
|
|
10724
11340
|
debug = options.debug;
|
|
10725
11341
|
logger.level = debug ? "debug" : "warn";
|
|
11342
|
+
const configuration = await loadMigrationConfig({
|
|
11343
|
+
config: options.config,
|
|
11344
|
+
stylesheet: options.tailwindStylesheet,
|
|
11345
|
+
prefix: options.tailwindPrefix
|
|
11346
|
+
});
|
|
11347
|
+
const target = program.getOptionValueSource("target") === "cli" ? options.target : configuration.target ?? options.target;
|
|
11348
|
+
if (target !== "tailwind" && (options.tailwindStylesheet !== void 0 || options.tailwindPrefix !== void 0))
|
|
11349
|
+
throw new Error("Tailwind target options require --target tailwind.");
|
|
10726
11350
|
const destination = options.output ?? input;
|
|
10727
11351
|
let reportPath;
|
|
10728
11352
|
if (options.report !== void 0) {
|
|
10729
11353
|
validateReportPath(options.report);
|
|
10730
|
-
reportPath =
|
|
11354
|
+
reportPath = path18.resolve(options.report);
|
|
10731
11355
|
}
|
|
11356
|
+
if (reportPath)
|
|
11357
|
+
for (const snapshot of configuration.snapshots) {
|
|
11358
|
+
if (await pathsOverlapOnFileSystem(reportPath, snapshot.path))
|
|
11359
|
+
throw new Error("Report path collides with a target configuration input.");
|
|
11360
|
+
}
|
|
10732
11361
|
const stylesheetPath = await validateStylesheetPath({
|
|
10733
|
-
target
|
|
11362
|
+
target,
|
|
10734
11363
|
stylesheetPath: options.stylesheet,
|
|
10735
11364
|
inputPath: input,
|
|
10736
11365
|
outputPath: destination,
|
|
10737
11366
|
reportPath
|
|
10738
11367
|
});
|
|
10739
11368
|
const printWithBreakpoints = options.printWithBreakpoints === void 0 ? void 0 : parsePrintWithBreakpoints(options.printWithBreakpoints, options.orientationBreakpoints);
|
|
10740
|
-
const session = AdapterFactory.createRenderSession(
|
|
11369
|
+
const session = AdapterFactory.createRenderSession(target, {
|
|
10741
11370
|
orientationBreakpoints: options.orientationBreakpoints,
|
|
10742
|
-
printWithBreakpoints
|
|
11371
|
+
printWithBreakpoints,
|
|
11372
|
+
targetProfile: target === "tailwind" ? configuration.targetProfile : void 0,
|
|
11373
|
+
sourceBreakpoints: configuration.sourceBreakpoints
|
|
10743
11374
|
});
|
|
10744
11375
|
const render = new RenderProjectStage(session);
|
|
10745
11376
|
const pipeline = new MigrationPipeline(
|
|
@@ -10756,6 +11387,9 @@ async function runCli(argv, output = processOutput, dependencies = {}) {
|
|
|
10756
11387
|
outputPath: destination,
|
|
10757
11388
|
options: {
|
|
10758
11389
|
mode,
|
|
11390
|
+
targetProfile: target === "tailwind" ? configuration.targetProfile : void 0,
|
|
11391
|
+
sourceBreakpoints: configuration.sourceBreakpoints,
|
|
11392
|
+
configurationSnapshots: configuration.snapshots,
|
|
10759
11393
|
responsiveImages: options.responsiveImages,
|
|
10760
11394
|
stylesheetPath,
|
|
10761
11395
|
stylesheetPathInput: options.stylesheet,
|
|
@@ -10767,7 +11401,10 @@ async function runCli(argv, output = processOutput, dependencies = {}) {
|
|
|
10767
11401
|
new TerminalPresenter().present(report, reportOutput);
|
|
10768
11402
|
if (reportPath !== void 0) {
|
|
10769
11403
|
await new JsonReportWriter().write(reportPath, report, {
|
|
10770
|
-
protectedPaths:
|
|
11404
|
+
protectedPaths: [
|
|
11405
|
+
...configuration.snapshots.map((snapshot) => snapshot.path),
|
|
11406
|
+
...stylesheetPath === void 0 ? [] : [stylesheetPath]
|
|
11407
|
+
]
|
|
10771
11408
|
});
|
|
10772
11409
|
}
|
|
10773
11410
|
exitCode = resolveExitCode(report, options.allowUnresolved);
|