domflax 0.1.1 → 0.1.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/README.md +25 -8
- package/dist/{chunk-DNHOGPYV.js → chunk-3Z5ZWLXX.js} +407 -51
- package/dist/chunk-3Z5ZWLXX.js.map +1 -0
- package/dist/{chunk-DOQEBGWB.js → chunk-5FWENSD2.js} +63 -8
- package/dist/chunk-5FWENSD2.js.map +1 -0
- package/dist/chunk-EVENAJYI.js +336 -0
- package/dist/chunk-EVENAJYI.js.map +1 -0
- package/dist/{chunk-DWLB7FRR.js → chunk-H5KTGI3A.js} +153 -7
- package/dist/chunk-H5KTGI3A.js.map +1 -0
- package/dist/{chunk-6WVVF6AD.js → chunk-U5GOONKV.js} +5 -2
- package/dist/{chunk-6WVVF6AD.js.map → chunk-U5GOONKV.js.map} +1 -1
- package/dist/cli.cjs +1033 -178
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +285 -243
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +614 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -18
- package/dist/index.d.ts +34 -18
- package/dist/index.js +4 -4
- package/dist/{pattern-F5xBtIE-.d.cts → pattern-CP9_HpVK.d.cts} +1 -1
- package/dist/{pattern-CV607P87.d.ts → pattern-CYgsv-jO.d.ts} +1 -1
- package/dist/pattern-kit.cjs.map +1 -1
- package/dist/pattern-kit.d.cts +2 -2
- package/dist/pattern-kit.d.ts +2 -2
- package/dist/pattern-kit.js +2 -2
- package/dist/{resolve-ops-DIwEelH-.d.ts → resolve-ops-Ci7LgYHC.d.cts} +9 -0
- package/dist/{resolve-ops-DIwEelH-.d.cts → resolve-ops-Ci7LgYHC.d.ts} +9 -0
- package/dist/verify.d.cts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +1 -1
- package/dist/webpack-loader.cjs +614 -68
- package/dist/webpack-loader.cjs.map +1 -1
- package/dist/webpack-loader.d.cts +2 -2
- package/dist/webpack-loader.d.ts +2 -2
- package/dist/webpack-loader.js +4 -4
- package/dist/worker.cjs +5955 -0
- package/dist/worker.cjs.map +1 -0
- package/dist/worker.d.cts +2 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.js +72 -0
- package/dist/worker.js.map +1 -0
- package/package.json +4 -2
- package/dist/chunk-DNHOGPYV.js.map +0 -1
- package/dist/chunk-DOQEBGWB.js.map +0 -1
- package/dist/chunk-DWLB7FRR.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import
|
|
10
|
-
buildSelectorIndex,
|
|
11
|
-
createSyntheticSink,
|
|
12
|
-
normalizer,
|
|
13
|
-
runPasses,
|
|
14
|
-
syncClassesFromComputed
|
|
15
|
-
} from "./chunk-DWLB7FRR.js";
|
|
3
|
+
createTransform,
|
|
4
|
+
destinationFor,
|
|
5
|
+
isGitClean,
|
|
6
|
+
planWrites
|
|
7
|
+
} from "./chunk-EVENAJYI.js";
|
|
8
|
+
import "./chunk-3Z5ZWLXX.js";
|
|
9
|
+
import "./chunk-H5KTGI3A.js";
|
|
16
10
|
import {
|
|
17
11
|
__commonJS,
|
|
12
|
+
__dirname,
|
|
18
13
|
__toESM,
|
|
19
14
|
init_esm_shims
|
|
20
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-U5GOONKV.js";
|
|
21
16
|
|
|
22
17
|
// ../../node_modules/sisteransi/src/index.js
|
|
23
18
|
var require_src = __commonJS({
|
|
@@ -172,6 +167,14 @@ function toSafety(raw) {
|
|
|
172
167
|
if (n === 0 || n === 1 || n === 2 || n === 3) return n;
|
|
173
168
|
throw new Error(`domflax: invalid --safety "${raw}" (expected 0, 1, 2 or 3)`);
|
|
174
169
|
}
|
|
170
|
+
function toPositiveInt(raw, flag) {
|
|
171
|
+
if (raw === void 0) return null;
|
|
172
|
+
const n = Number(raw);
|
|
173
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
174
|
+
throw new Error(`domflax: invalid ${flag} "${raw}" (expected a positive integer)`);
|
|
175
|
+
}
|
|
176
|
+
return n;
|
|
177
|
+
}
|
|
175
178
|
function parseInvocation(argv) {
|
|
176
179
|
const { values, positionals } = parseArgs({
|
|
177
180
|
args: argv,
|
|
@@ -187,7 +190,9 @@ function parseInvocation(argv) {
|
|
|
187
190
|
"no-interactive": { type: "boolean", default: false },
|
|
188
191
|
yes: { type: "boolean", short: "y", default: false },
|
|
189
192
|
safety: { type: "string" },
|
|
190
|
-
"project-root": { type: "string" }
|
|
193
|
+
"project-root": { type: "string" },
|
|
194
|
+
"max-memory": { type: "string" },
|
|
195
|
+
concurrency: { type: "string" }
|
|
191
196
|
}
|
|
192
197
|
});
|
|
193
198
|
const provider = values.provider ?? DEFAULT_PROVIDER;
|
|
@@ -206,7 +211,9 @@ function parseInvocation(argv) {
|
|
|
206
211
|
interactive: values["no-interactive"] !== true && values.yes !== true,
|
|
207
212
|
passes: null,
|
|
208
213
|
safety: toSafety(values.safety),
|
|
209
|
-
projectRoot: values["project-root"] ?? null
|
|
214
|
+
projectRoot: values["project-root"] ?? null,
|
|
215
|
+
maxMemory: toPositiveInt(values["max-memory"], "--max-memory"),
|
|
216
|
+
concurrency: toPositiveInt(values.concurrency, "--concurrency")
|
|
210
217
|
};
|
|
211
218
|
}
|
|
212
219
|
function shouldPrompt(options, isTty) {
|
|
@@ -230,200 +237,203 @@ var USAGE = [
|
|
|
230
237
|
" --dangerously-overwrite-source overwrite source in place (needs a clean git tree)",
|
|
231
238
|
" --no-git-check skip the clean-git-tree gate",
|
|
232
239
|
" --safety <0|1|2|3> optimization aggressiveness (default: 2)",
|
|
240
|
+
" --max-memory <MB> memory budget; caps pool RAM AND parallelism (default: ~70% free RAM)",
|
|
241
|
+
" --concurrency <N> max parallel workers (still clamped by --max-memory)",
|
|
233
242
|
" --yes, --no-interactive never launch the wizard (CI-safe)",
|
|
234
243
|
"",
|
|
244
|
+
"Many files are processed across CPU cores by a memory-bounded worker pool; small jobs run inline.",
|
|
235
245
|
"With no paths in an interactive terminal, a guided wizard launches."
|
|
236
246
|
].join("\n");
|
|
237
247
|
|
|
238
|
-
// ../cli/src/
|
|
248
|
+
// ../cli/src/pool.ts
|
|
239
249
|
init_esm_shims();
|
|
240
|
-
import {
|
|
250
|
+
import { existsSync } from "fs";
|
|
251
|
+
import * as os from "os";
|
|
241
252
|
import * as path from "path";
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
253
|
+
import { fileURLToPath } from "url";
|
|
254
|
+
import { Worker } from "worker_threads";
|
|
255
|
+
var PER_WORKER_MB = 160;
|
|
256
|
+
var MIN_OLD_GEN_MB = 64;
|
|
257
|
+
function emptyTotals() {
|
|
258
|
+
return { files: 0, changed: 0, nodesRemoved: 0, classesSaved: 0, bytesSaved: 0 };
|
|
245
259
|
}
|
|
246
|
-
function
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
});
|
|
253
|
-
return out.trim().length === 0;
|
|
254
|
-
} catch {
|
|
255
|
-
return false;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
function planWrites(options, gitClean) {
|
|
259
|
-
if (options.dangerouslyOverwriteSource) {
|
|
260
|
-
if (!options.noGitCheck && !gitClean) {
|
|
261
|
-
return {
|
|
262
|
-
ok: false,
|
|
263
|
-
error: "refusing --dangerously-overwrite-source: git working tree is not clean. Commit or stash first, or pass --no-git-check to override."
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
return { ok: true, value: { mode: "overwrite-source", outDir: null } };
|
|
267
|
-
}
|
|
268
|
-
const outDir = path.resolve(options.out ?? "domflax-out");
|
|
269
|
-
return { ok: true, value: { mode: "out-dir", outDir } };
|
|
270
|
-
}
|
|
271
|
-
function destinationFor(file, inputRoot, plan) {
|
|
272
|
-
const absFile = path.resolve(file);
|
|
273
|
-
if (plan.mode === "overwrite-source") {
|
|
274
|
-
return { ok: true, value: absFile };
|
|
275
|
-
}
|
|
276
|
-
const outDir = plan.outDir;
|
|
277
|
-
const rel = path.relative(inputRoot, absFile);
|
|
278
|
-
const safeRel = rel === "" || rel.startsWith("..") || path.isAbsolute(rel) ? path.basename(absFile) : rel;
|
|
279
|
-
const dest = path.join(outDir, safeRel);
|
|
280
|
-
if (path.resolve(dest) === absFile && !isDisposablePath(absFile)) {
|
|
281
|
-
return {
|
|
282
|
-
ok: false,
|
|
283
|
-
error: `refusing to overwrite source file ${absFile}: the output path resolves onto the source. Choose a different --out, or pass --dangerously-overwrite-source (with a clean git tree).`
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
return { ok: true, value: dest };
|
|
260
|
+
function addStats(totals, stats, changed) {
|
|
261
|
+
totals.files += 1;
|
|
262
|
+
if (changed) totals.changed += 1;
|
|
263
|
+
totals.nodesRemoved += stats.nodesRemoved;
|
|
264
|
+
totals.classesSaved += stats.classesSaved;
|
|
265
|
+
totals.bytesSaved += stats.bytesSaved;
|
|
287
266
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
init_esm_shims();
|
|
291
|
-
function buildResolver(provider, css, projectRoot) {
|
|
292
|
-
if (provider === "custom") {
|
|
293
|
-
return createCssResolver([], { files: css, projectRoot });
|
|
294
|
-
}
|
|
295
|
-
return createTailwindResolver({ projectRoot });
|
|
267
|
+
function clamp(n, lo, hi) {
|
|
268
|
+
return Math.max(lo, Math.min(hi, n));
|
|
296
269
|
}
|
|
297
|
-
function
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
bucket.push(p2);
|
|
307
|
-
}
|
|
308
|
-
const passes = [];
|
|
309
|
-
for (const [phase, pats] of byPhase) {
|
|
310
|
-
passes.push({ phase, category: `${phase}/builtin`, patterns: pats });
|
|
311
|
-
}
|
|
312
|
-
return passes;
|
|
270
|
+
function computeWorkerCount(options) {
|
|
271
|
+
const cpus2 = Math.max(1, os.cpus().length);
|
|
272
|
+
const freeMB = Math.floor(os.freemem() / (1024 * 1024));
|
|
273
|
+
const budgetMB = Math.max(PER_WORKER_MB, options.maxMemory ?? Math.floor(freeMB * 0.7));
|
|
274
|
+
const byMemory = Math.max(1, Math.floor(budgetMB / PER_WORKER_MB));
|
|
275
|
+
const target = options.concurrency ?? Math.max(1, cpus2 - 1);
|
|
276
|
+
const workers = clamp(Math.min(target, byMemory), 1, byMemory);
|
|
277
|
+
const perWorkerCapMB = Math.max(MIN_OLD_GEN_MB, Math.floor(budgetMB / workers));
|
|
278
|
+
return { workers, budgetMB, perWorkerCapMB };
|
|
313
279
|
}
|
|
314
|
-
function
|
|
315
|
-
|
|
316
|
-
const set = new Set(names);
|
|
317
|
-
return builtinPatterns.filter((p2) => set.has(p2.name));
|
|
280
|
+
function inlineThreshold(workers) {
|
|
281
|
+
return Math.max(4, 2 * workers);
|
|
318
282
|
}
|
|
319
|
-
function
|
|
320
|
-
|
|
321
|
-
const lower = clean.toLowerCase();
|
|
322
|
-
if (lower.endsWith(".tsx")) return "tsx";
|
|
323
|
-
if (lower.endsWith(".jsx")) return "jsx";
|
|
324
|
-
return null;
|
|
283
|
+
function shouldUsePool(fileCount, plan) {
|
|
284
|
+
return plan.workers > 1 && fileCount > inlineThreshold(plan.workers);
|
|
325
285
|
}
|
|
326
|
-
function
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
total += m2[1].split(/\s+/).filter((t) => t.length > 0).length;
|
|
286
|
+
function moduleDir() {
|
|
287
|
+
try {
|
|
288
|
+
return path.dirname(fileURLToPath(import.meta.url));
|
|
289
|
+
} catch {
|
|
290
|
+
return typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
332
291
|
}
|
|
333
|
-
return total;
|
|
334
292
|
}
|
|
335
|
-
function
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
classesBefore: 0,
|
|
348
|
-
classesAfter: 0,
|
|
349
|
-
classesSaved: 0,
|
|
350
|
-
bytesBefore: bytes(code),
|
|
351
|
-
bytesAfter: bytes(code),
|
|
352
|
-
bytesSaved: 0
|
|
353
|
-
}
|
|
354
|
-
};
|
|
293
|
+
function resolveWorkerPath() {
|
|
294
|
+
const dir = moduleDir();
|
|
295
|
+
const candidates = [
|
|
296
|
+
path.join(dir, "worker.cjs"),
|
|
297
|
+
path.join(dir, "worker.js"),
|
|
298
|
+
path.join(dir, "..", "dist", "worker.cjs"),
|
|
299
|
+
path.join(dir, "..", "dist", "worker.js")
|
|
300
|
+
];
|
|
301
|
+
for (const c of candidates) {
|
|
302
|
+
if (existsSync(c)) return c;
|
|
303
|
+
}
|
|
304
|
+
return candidates[0];
|
|
355
305
|
}
|
|
356
|
-
function
|
|
357
|
-
const
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
306
|
+
function runPool(files, init, plan, onWrote) {
|
|
307
|
+
const workerPath = resolveWorkerPath();
|
|
308
|
+
const totals = emptyTotals();
|
|
309
|
+
const wrote = [];
|
|
310
|
+
const errors = [];
|
|
311
|
+
let failures = 0;
|
|
312
|
+
const budgetBytes = plan.budgetMB * 1024 * 1024;
|
|
313
|
+
let nextIndex = 0;
|
|
314
|
+
let completed = 0;
|
|
315
|
+
const total = files.length;
|
|
316
|
+
let respawns = 0;
|
|
317
|
+
const maxRespawns = total + plan.workers + 8;
|
|
318
|
+
return new Promise((resolve3) => {
|
|
319
|
+
const handles = /* @__PURE__ */ new Set();
|
|
320
|
+
const finishIfDone = () => {
|
|
321
|
+
if (completed < total) return;
|
|
322
|
+
for (const h2 of handles) {
|
|
323
|
+
if (!h2.dead) {
|
|
324
|
+
try {
|
|
325
|
+
h2.worker.postMessage({ type: "stop" });
|
|
326
|
+
} catch {
|
|
327
|
+
}
|
|
328
|
+
void h2.worker.terminate();
|
|
329
|
+
}
|
|
368
330
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const nodesIn = doc.nodes.size;
|
|
372
|
-
for (const node of doc.nodes.values()) node.meta.safetyFloor = 3;
|
|
373
|
-
const ctx = {
|
|
374
|
-
doc,
|
|
375
|
-
safetyCeiling: options.safety,
|
|
376
|
-
normalizer,
|
|
377
|
-
// Real CSS-selector-safety index from the active resolver (custom-CSS reports combinator /
|
|
378
|
-
// structural-pseudo coupling; Tailwind has none → null index, behaviour unchanged).
|
|
379
|
-
selectors: buildSelectorIndex(doc, resolver),
|
|
380
|
-
resolver,
|
|
381
|
-
gate
|
|
331
|
+
handles.clear();
|
|
332
|
+
resolve3({ totals, failures, wrote, errors });
|
|
382
333
|
};
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
bytesBefore: bytes(code),
|
|
409
|
-
bytesAfter: bytes(out),
|
|
410
|
-
bytesSaved: bytes(code) - bytes(out)
|
|
334
|
+
const recordFailure = (file, error) => {
|
|
335
|
+
failures += 1;
|
|
336
|
+
completed += 1;
|
|
337
|
+
errors.push({ path: file, error });
|
|
338
|
+
};
|
|
339
|
+
const dispatch = (h2) => {
|
|
340
|
+
if (h2.dead) return;
|
|
341
|
+
if (nextIndex >= total) {
|
|
342
|
+
h2.current = null;
|
|
343
|
+
try {
|
|
344
|
+
h2.worker.postMessage({ type: "stop" });
|
|
345
|
+
} catch {
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (process.memoryUsage().rss > budgetBytes) {
|
|
350
|
+
setTimeout(() => dispatch(h2), 25);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const file = files[nextIndex++];
|
|
354
|
+
h2.current = file;
|
|
355
|
+
try {
|
|
356
|
+
h2.worker.postMessage({ type: "file", path: file });
|
|
357
|
+
} catch {
|
|
358
|
+
onWorkerDown(h2);
|
|
411
359
|
}
|
|
412
360
|
};
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
361
|
+
const onMessage = (h2, msg) => {
|
|
362
|
+
if (msg.type === "ready") {
|
|
363
|
+
h2.ready = true;
|
|
364
|
+
dispatch(h2);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
h2.current = null;
|
|
368
|
+
if (msg.ok) {
|
|
369
|
+
addStats(totals, msg.stats, msg.changed);
|
|
370
|
+
if (msg.wrote) {
|
|
371
|
+
wrote.push(msg.wrote);
|
|
372
|
+
onWrote?.(msg.wrote);
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
failures += 1;
|
|
376
|
+
errors.push({ path: msg.path, error: msg.error });
|
|
377
|
+
}
|
|
378
|
+
completed += 1;
|
|
379
|
+
if (completed >= total) {
|
|
380
|
+
finishIfDone();
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
dispatch(h2);
|
|
384
|
+
};
|
|
385
|
+
const drainRemaining = (reason) => {
|
|
386
|
+
while (nextIndex < total) recordFailure(files[nextIndex++], reason);
|
|
387
|
+
finishIfDone();
|
|
388
|
+
};
|
|
389
|
+
const onWorkerDown = (h2) => {
|
|
390
|
+
if (h2.dead) return;
|
|
391
|
+
h2.dead = true;
|
|
392
|
+
handles.delete(h2);
|
|
393
|
+
const lost = h2.current;
|
|
394
|
+
h2.current = null;
|
|
395
|
+
if (lost !== null) recordFailure(lost, "worker crashed while processing this file");
|
|
396
|
+
void h2.worker.terminate();
|
|
397
|
+
if (completed >= total) {
|
|
398
|
+
finishIfDone();
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (nextIndex >= total) {
|
|
402
|
+
if (handles.size === 0) finishIfDone();
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (respawns < maxRespawns) {
|
|
406
|
+
respawns += 1;
|
|
407
|
+
spawn();
|
|
408
|
+
} else if (handles.size === 0) {
|
|
409
|
+
drainRemaining("worker pool exhausted its respawn budget (memory cap too small?)");
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
const spawn = () => {
|
|
413
|
+
let worker;
|
|
414
|
+
try {
|
|
415
|
+
worker = new Worker(workerPath, {
|
|
416
|
+
workerData: init,
|
|
417
|
+
resourceLimits: { maxOldGenerationSizeMb: plan.perWorkerCapMB }
|
|
418
|
+
});
|
|
419
|
+
} catch {
|
|
420
|
+
if (nextIndex < total) recordFailure(files[nextIndex++], "failed to spawn worker");
|
|
421
|
+
if (completed >= total) finishIfDone();
|
|
422
|
+
else if (handles.size === 0 && nextIndex < total) spawn();
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const h2 = { worker, current: null, ready: false, dead: false };
|
|
426
|
+
handles.add(h2);
|
|
427
|
+
worker.on("message", (m2) => onMessage(h2, m2));
|
|
428
|
+
worker.on("error", () => onWorkerDown(h2));
|
|
429
|
+
worker.on("exit", (code) => {
|
|
430
|
+
if (code !== 0) onWorkerDown(h2);
|
|
431
|
+
});
|
|
432
|
+
};
|
|
433
|
+
const initial = Math.min(plan.workers, total);
|
|
434
|
+
for (let i = 0; i < initial; i++) spawn();
|
|
435
|
+
if (initial === 0) finishIfDone();
|
|
436
|
+
});
|
|
427
437
|
}
|
|
428
438
|
|
|
429
439
|
// ../cli/src/walk.ts
|
|
@@ -451,8 +461,8 @@ function walkDir(dir, out) {
|
|
|
451
461
|
if (entry.isDirectory()) {
|
|
452
462
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
453
463
|
walkDir(full, out);
|
|
454
|
-
} else if (entry.isFile()
|
|
455
|
-
out.push(full);
|
|
464
|
+
} else if (entry.isFile()) {
|
|
465
|
+
if (isSupported(entry.name)) out.push(full);
|
|
456
466
|
}
|
|
457
467
|
}
|
|
458
468
|
}
|
|
@@ -490,7 +500,8 @@ function discoverInputs(paths) {
|
|
|
490
500
|
continue;
|
|
491
501
|
}
|
|
492
502
|
if (stat?.isFile()) {
|
|
493
|
-
push(p2);
|
|
503
|
+
if (isSupported(p2)) push(p2);
|
|
504
|
+
else warnings.push(`unsupported file type, skipped: ${p2}`);
|
|
494
505
|
continue;
|
|
495
506
|
}
|
|
496
507
|
if (hasGlobMagic(p2)) {
|
|
@@ -499,9 +510,10 @@ function discoverInputs(paths) {
|
|
|
499
510
|
warnings.push(`glob not supported on this Node version, skipped: ${p2}`);
|
|
500
511
|
continue;
|
|
501
512
|
}
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
513
|
+
const matched = glob(p2);
|
|
514
|
+
const supported = matched.filter(isSupported);
|
|
515
|
+
if (supported.length === 0) warnings.push(`no .jsx/.tsx/.html files matched: ${p2}`);
|
|
516
|
+
for (const m2 of supported) push(m2);
|
|
505
517
|
continue;
|
|
506
518
|
}
|
|
507
519
|
warnings.push(`no such file or directory: ${p2}`);
|
|
@@ -1115,15 +1127,17 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
|
|
|
1115
1127
|
]);
|
|
1116
1128
|
var COMMON_INPUT_DIRS = ["src", "app", "components", "pages", "lib", "ui", "public"];
|
|
1117
1129
|
var CSS_FILE_CAP = 200;
|
|
1130
|
+
var DEFAULT_CSS_DEPTH = 10;
|
|
1118
1131
|
function toRelative(root, abs) {
|
|
1119
1132
|
const rel = path3.relative(root, abs);
|
|
1120
1133
|
return rel.split(path3.sep).join("/");
|
|
1121
1134
|
}
|
|
1122
|
-
function detectCssFiles(root) {
|
|
1123
|
-
const
|
|
1135
|
+
function detectCssFiles(root, scanRoots = [], maxDepth = DEFAULT_CSS_DEPTH) {
|
|
1136
|
+
const base = path3.resolve(root);
|
|
1137
|
+
const found = /* @__PURE__ */ new Map();
|
|
1124
1138
|
let capped = false;
|
|
1125
|
-
const walk = (dir) => {
|
|
1126
|
-
if (capped) return;
|
|
1139
|
+
const walk = (dir, depth) => {
|
|
1140
|
+
if (capped || depth > maxDepth) return;
|
|
1127
1141
|
let entries;
|
|
1128
1142
|
try {
|
|
1129
1143
|
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
@@ -1134,23 +1148,31 @@ function detectCssFiles(root) {
|
|
|
1134
1148
|
const full = path3.join(dir, entry.name);
|
|
1135
1149
|
if (entry.isDirectory()) {
|
|
1136
1150
|
if (SKIP_DIRS2.has(entry.name)) continue;
|
|
1137
|
-
walk(full);
|
|
1151
|
+
walk(full, depth + 1);
|
|
1138
1152
|
if (capped) return;
|
|
1139
1153
|
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".css")) {
|
|
1140
|
-
|
|
1141
|
-
if (found.
|
|
1142
|
-
|
|
1143
|
-
|
|
1154
|
+
const abs = path3.resolve(full);
|
|
1155
|
+
if (!found.has(abs)) {
|
|
1156
|
+
found.set(abs, toRelative(base, abs));
|
|
1157
|
+
if (found.size >= CSS_FILE_CAP) {
|
|
1158
|
+
capped = true;
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1144
1161
|
}
|
|
1145
1162
|
}
|
|
1146
1163
|
}
|
|
1147
1164
|
};
|
|
1148
|
-
walk(
|
|
1149
|
-
|
|
1165
|
+
walk(base, 0);
|
|
1166
|
+
for (const r2 of scanRoots) {
|
|
1167
|
+
if (capped) break;
|
|
1168
|
+
const abs = path3.resolve(r2);
|
|
1169
|
+
if (abs !== base) walk(abs, 0);
|
|
1170
|
+
}
|
|
1171
|
+
const list = [...found.values()].sort((a, b3) => a.localeCompare(b3));
|
|
1150
1172
|
if (capped) {
|
|
1151
1173
|
console.error(`domflax: more than ${CSS_FILE_CAP} CSS files found; showing the first ${CSS_FILE_CAP}.`);
|
|
1152
1174
|
}
|
|
1153
|
-
return
|
|
1175
|
+
return list;
|
|
1154
1176
|
}
|
|
1155
1177
|
function detectInputDirs(root) {
|
|
1156
1178
|
const resolved = path3.resolve(root);
|
|
@@ -1232,15 +1254,6 @@ async function runWizard(base) {
|
|
|
1232
1254
|
} else if (outputMode === "overwrite") {
|
|
1233
1255
|
dangerouslyOverwriteSource = true;
|
|
1234
1256
|
}
|
|
1235
|
-
const allPasses = builtinPatternNames();
|
|
1236
|
-
const passSelection = await fe({
|
|
1237
|
-
message: "Which optimization passes should run?",
|
|
1238
|
-
options: allPasses.map((name) => ({ value: name, label: name })),
|
|
1239
|
-
initialValues: [...allPasses],
|
|
1240
|
-
required: true
|
|
1241
|
-
});
|
|
1242
|
-
if (cancelled(passSelection)) return done();
|
|
1243
|
-
const passes = passSelection;
|
|
1244
1257
|
const provider = await ve({
|
|
1245
1258
|
message: "How should class names resolve to styles?",
|
|
1246
1259
|
options: [
|
|
@@ -1253,7 +1266,7 @@ async function runWizard(base) {
|
|
|
1253
1266
|
if (cancelled(provider)) return done();
|
|
1254
1267
|
let css = base.css;
|
|
1255
1268
|
if (provider === "custom") {
|
|
1256
|
-
const detectedCss = detectCssFiles(root);
|
|
1269
|
+
const detectedCss = detectCssFiles(root, [inputPath]);
|
|
1257
1270
|
if (detectedCss.length > 0) {
|
|
1258
1271
|
const picked = await fe({
|
|
1259
1272
|
message: "Which CSS files should resolve your classes? (all detected files are pre-selected)",
|
|
@@ -1281,7 +1294,7 @@ async function runWizard(base) {
|
|
|
1281
1294
|
css,
|
|
1282
1295
|
dryRun,
|
|
1283
1296
|
dangerouslyOverwriteSource,
|
|
1284
|
-
passes:
|
|
1297
|
+
passes: null,
|
|
1285
1298
|
safety: base.safety ?? DEFAULT_SAFETY
|
|
1286
1299
|
};
|
|
1287
1300
|
function done() {
|
|
@@ -1291,13 +1304,6 @@ async function runWizard(base) {
|
|
|
1291
1304
|
}
|
|
1292
1305
|
|
|
1293
1306
|
// ../cli/src/index.ts
|
|
1294
|
-
function addStats(totals, stats, changed) {
|
|
1295
|
-
totals.files += 1;
|
|
1296
|
-
if (changed) totals.changed += 1;
|
|
1297
|
-
totals.nodesRemoved += stats.nodesRemoved;
|
|
1298
|
-
totals.classesSaved += stats.classesSaved;
|
|
1299
|
-
totals.bytesSaved += stats.bytesSaved;
|
|
1300
|
-
}
|
|
1301
1307
|
function printReport(totals) {
|
|
1302
1308
|
console.log("");
|
|
1303
1309
|
console.log("domflax report");
|
|
@@ -1307,23 +1313,8 @@ function printReport(totals) {
|
|
|
1307
1313
|
console.log(` classes saved : ${totals.classesSaved}`);
|
|
1308
1314
|
console.log(` bytes saved : ${totals.bytesSaved}`);
|
|
1309
1315
|
}
|
|
1310
|
-
|
|
1311
|
-
const { files, inputRoot, warnings } = discoverInputs(options.paths);
|
|
1312
|
-
for (const w2 of warnings) console.error(`domflax: ${w2}`);
|
|
1313
|
-
if (files.length === 0) {
|
|
1314
|
-
console.error("domflax: no .jsx/.tsx/.html files found for the given paths");
|
|
1315
|
-
return { exitCode: 1 };
|
|
1316
|
-
}
|
|
1317
|
-
const projectRoot = options.projectRoot ?? process.cwd();
|
|
1318
|
-
const gitClean = options.dangerouslyOverwriteSource && !options.noGitCheck ? isGitClean(projectRoot) : true;
|
|
1319
|
-
const planned = planWrites(options, gitClean);
|
|
1320
|
-
if (!planned.ok) {
|
|
1321
|
-
console.error(`domflax: ${planned.error}`);
|
|
1322
|
-
return { exitCode: 1 };
|
|
1323
|
-
}
|
|
1324
|
-
const plan = planned.value;
|
|
1316
|
+
function runInline(files, options, inputRoot, plan, totals) {
|
|
1325
1317
|
const transform = createTransform(options);
|
|
1326
|
-
const totals = { files: 0, changed: 0, nodesRemoved: 0, classesSaved: 0, bytesSaved: 0 };
|
|
1327
1318
|
let failures = 0;
|
|
1328
1319
|
for (const file of files) {
|
|
1329
1320
|
let code;
|
|
@@ -1358,8 +1349,59 @@ async function execute(options) {
|
|
|
1358
1349
|
failures += 1;
|
|
1359
1350
|
}
|
|
1360
1351
|
}
|
|
1352
|
+
return failures;
|
|
1353
|
+
}
|
|
1354
|
+
async function execute(options) {
|
|
1355
|
+
const { files, inputRoot, warnings } = discoverInputs(options.paths);
|
|
1356
|
+
for (const w2 of warnings) console.error(`domflax: ${w2}`);
|
|
1357
|
+
if (files.length === 0) {
|
|
1358
|
+
console.error("domflax: no .jsx/.tsx files found for the given paths");
|
|
1359
|
+
return { exitCode: 1 };
|
|
1360
|
+
}
|
|
1361
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
1362
|
+
const gitClean = options.dangerouslyOverwriteSource && !options.noGitCheck ? isGitClean(projectRoot) : true;
|
|
1363
|
+
const planned = planWrites(options, gitClean);
|
|
1364
|
+
if (!planned.ok) {
|
|
1365
|
+
console.error(`domflax: ${planned.error}`);
|
|
1366
|
+
return { exitCode: 1 };
|
|
1367
|
+
}
|
|
1368
|
+
const plan = planned.value;
|
|
1369
|
+
const poolPlan = computeWorkerCount(options);
|
|
1370
|
+
const usePool = !options.dryRun && shouldUsePool(files.length, poolPlan);
|
|
1371
|
+
const totals = emptyTotals();
|
|
1372
|
+
let failures = 0;
|
|
1373
|
+
if (usePool) {
|
|
1374
|
+
const outcome = await runPool(
|
|
1375
|
+
files,
|
|
1376
|
+
{ options, inputRoot, plan },
|
|
1377
|
+
poolPlan
|
|
1378
|
+
// Per-file "wrote" lines are collected and printed in deterministic (sorted) order below.
|
|
1379
|
+
);
|
|
1380
|
+
Object.assign(totals, outcome.totals);
|
|
1381
|
+
failures = outcome.failures;
|
|
1382
|
+
for (const { path: p2, error } of outcome.errors) {
|
|
1383
|
+
console.error(`domflax: failed ${path4.relative(process.cwd(), p2) || p2}: ${error}`);
|
|
1384
|
+
}
|
|
1385
|
+
for (const dest of [...outcome.wrote].sort()) {
|
|
1386
|
+
console.log(`domflax: wrote ${path4.relative(process.cwd(), dest) || dest}`);
|
|
1387
|
+
}
|
|
1388
|
+
} else {
|
|
1389
|
+
failures += runInline(files, options, inputRoot, plan, totals);
|
|
1390
|
+
}
|
|
1391
|
+
if (options.dryRun) {
|
|
1392
|
+
console.log("\ndomflax: dry run \u2014 no files were written.");
|
|
1393
|
+
} else if (totals.changed === 0) {
|
|
1394
|
+
console.log(
|
|
1395
|
+
`
|
|
1396
|
+
domflax: processed ${totals.files} file${totals.files === 1 ? "" : "s"} \u2014 nothing to optimize (0 changed).`
|
|
1397
|
+
);
|
|
1398
|
+
} else {
|
|
1399
|
+
console.log(
|
|
1400
|
+
`
|
|
1401
|
+
domflax: optimized ${totals.changed} of ${totals.files} file${totals.files === 1 ? "" : "s"} (${totals.nodesRemoved} nodes removed, ${totals.classesSaved} classes saved, ${totals.bytesSaved} bytes saved).`
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1361
1404
|
if (options.report) printReport(totals);
|
|
1362
|
-
if (options.dryRun) console.log("\ndomflax: dry run \u2014 no files were written.");
|
|
1363
1405
|
return { exitCode: failures > 0 ? 1 : 0 };
|
|
1364
1406
|
}
|
|
1365
1407
|
async function main(argv = process.argv.slice(2)) {
|