domflax 0.1.1 → 0.1.2
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 +1 -1
- package/dist/cli.cjs +64 -38
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +64 -38
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ It rewrites only the **static shape** of your markup. Dynamic class lists (`clas
|
|
|
27
27
|
- **Flattening is conservative.** A wrapper is removed only when removal is *provably* render-neutral — it establishes no layout context and has no style to reproduce on its child. It never drops a style it can't reproduce, and never touches a wrapper a CSS selector depends on (`.list > .item h3`).
|
|
28
28
|
- domflax runs as a **purely static** source transform. It never launches a browser, so builds stay fast and deterministic.
|
|
29
29
|
|
|
30
|
-
> **Status: v0.1.
|
|
30
|
+
> **Status: v0.1.2.** Works end-to-end on real `.jsx`/`.tsx` — in component-return position **and inside `.map()` / expressions (list rows)** — via Vite, Next.js (webpack), and the CLI, with Tailwind and custom-CSS providers. 22 patterns. Wrappers that establish a layout context (e.g. `flex`/`grid` centering) are **conservatively preserved** — proving those render-identical needs context a static pass can't see; recovering them safely is on the Roadmap. APIs may change before 1.0.
|
|
31
31
|
|
|
32
32
|
## Install
|
|
33
33
|
|
package/dist/cli.cjs
CHANGED
|
@@ -5636,24 +5636,26 @@ function createTransform(options) {
|
|
|
5636
5636
|
}
|
|
5637
5637
|
};
|
|
5638
5638
|
}
|
|
5639
|
-
function builtinPatternNames() {
|
|
5640
|
-
return builtinPatterns.map((p2) => p2.name);
|
|
5641
|
-
}
|
|
5642
5639
|
|
|
5643
5640
|
// ../cli/src/walk.ts
|
|
5644
5641
|
init_cjs_shims();
|
|
5645
5642
|
var fs = __toESM(require("fs"), 1);
|
|
5646
5643
|
var path4 = __toESM(require("path"), 1);
|
|
5647
|
-
var SUPPORTED_EXTS = [".jsx", ".tsx"
|
|
5644
|
+
var SUPPORTED_EXTS = [".jsx", ".tsx"];
|
|
5645
|
+
var HTML_EXTS = [".html", ".htm"];
|
|
5648
5646
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "domflax-out"]);
|
|
5649
5647
|
function isSupported(file) {
|
|
5650
5648
|
const lower = file.toLowerCase();
|
|
5651
5649
|
return SUPPORTED_EXTS.some((ext) => lower.endsWith(ext));
|
|
5652
5650
|
}
|
|
5651
|
+
function isHtml(file) {
|
|
5652
|
+
const lower = file.toLowerCase();
|
|
5653
|
+
return HTML_EXTS.some((ext) => lower.endsWith(ext));
|
|
5654
|
+
}
|
|
5653
5655
|
function hasGlobMagic(p2) {
|
|
5654
5656
|
return /[*?[\]{}]/.test(p2);
|
|
5655
5657
|
}
|
|
5656
|
-
function walkDir(dir, out) {
|
|
5658
|
+
function walkDir(dir, out, counts) {
|
|
5657
5659
|
let entries;
|
|
5658
5660
|
try {
|
|
5659
5661
|
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
@@ -5664,9 +5666,10 @@ function walkDir(dir, out) {
|
|
|
5664
5666
|
const full = path4.join(dir, entry.name);
|
|
5665
5667
|
if (entry.isDirectory()) {
|
|
5666
5668
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
5667
|
-
walkDir(full, out);
|
|
5668
|
-
} else if (entry.isFile()
|
|
5669
|
-
out.push(full);
|
|
5669
|
+
walkDir(full, out, counts);
|
|
5670
|
+
} else if (entry.isFile()) {
|
|
5671
|
+
if (isSupported(entry.name)) out.push(full);
|
|
5672
|
+
else if (isHtml(entry.name)) counts.html += 1;
|
|
5670
5673
|
}
|
|
5671
5674
|
}
|
|
5672
5675
|
}
|
|
@@ -5677,6 +5680,7 @@ function globSyncMaybe() {
|
|
|
5677
5680
|
function discoverInputs(paths) {
|
|
5678
5681
|
const files = [];
|
|
5679
5682
|
const warnings = [];
|
|
5683
|
+
const counts = { html: 0 };
|
|
5680
5684
|
const seen = /* @__PURE__ */ new Set();
|
|
5681
5685
|
const push = (f) => {
|
|
5682
5686
|
const abs = path4.resolve(f);
|
|
@@ -5700,11 +5704,13 @@ function discoverInputs(paths) {
|
|
|
5700
5704
|
stat = null;
|
|
5701
5705
|
}
|
|
5702
5706
|
if (stat?.isDirectory()) {
|
|
5703
|
-
walkDir(path4.resolve(p2), files);
|
|
5707
|
+
walkDir(path4.resolve(p2), files, counts);
|
|
5704
5708
|
continue;
|
|
5705
5709
|
}
|
|
5706
5710
|
if (stat?.isFile()) {
|
|
5707
|
-
push(p2);
|
|
5711
|
+
if (isSupported(p2)) push(p2);
|
|
5712
|
+
else if (isHtml(p2)) counts.html += 1;
|
|
5713
|
+
else warnings.push(`unsupported file type, skipped: ${p2}`);
|
|
5708
5714
|
continue;
|
|
5709
5715
|
}
|
|
5710
5716
|
if (hasGlobMagic(p2)) {
|
|
@@ -5713,9 +5719,11 @@ function discoverInputs(paths) {
|
|
|
5713
5719
|
warnings.push(`glob not supported on this Node version, skipped: ${p2}`);
|
|
5714
5720
|
continue;
|
|
5715
5721
|
}
|
|
5716
|
-
const
|
|
5717
|
-
|
|
5718
|
-
|
|
5722
|
+
const matched = glob(p2);
|
|
5723
|
+
const supported = matched.filter(isSupported);
|
|
5724
|
+
counts.html += matched.filter(isHtml).length;
|
|
5725
|
+
if (supported.length === 0) warnings.push(`no .jsx/.tsx files matched: ${p2}`);
|
|
5726
|
+
for (const m2 of supported) push(m2);
|
|
5719
5727
|
continue;
|
|
5720
5728
|
}
|
|
5721
5729
|
warnings.push(`no such file or directory: ${p2}`);
|
|
@@ -5729,6 +5737,11 @@ function discoverInputs(paths) {
|
|
|
5729
5737
|
deduped.push(abs);
|
|
5730
5738
|
}
|
|
5731
5739
|
}
|
|
5740
|
+
if (deduped.length === 0 && counts.html > 0) {
|
|
5741
|
+
warnings.push(
|
|
5742
|
+
`found ${counts.html} .html file${counts.html === 1 ? "" : "s"} but HTML optimization isn't supported yet (domflax currently optimizes .jsx/.tsx source; HTML is on the roadmap: https://github.com/Krishnesh-Mishra/domflax#roadmap).`
|
|
5743
|
+
);
|
|
5744
|
+
}
|
|
5732
5745
|
return { files: deduped, inputRoot, warnings };
|
|
5733
5746
|
}
|
|
5734
5747
|
|
|
@@ -6329,15 +6342,17 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
|
|
|
6329
6342
|
]);
|
|
6330
6343
|
var COMMON_INPUT_DIRS = ["src", "app", "components", "pages", "lib", "ui", "public"];
|
|
6331
6344
|
var CSS_FILE_CAP = 200;
|
|
6345
|
+
var DEFAULT_CSS_DEPTH = 10;
|
|
6332
6346
|
function toRelative(root, abs) {
|
|
6333
6347
|
const rel = path5.relative(root, abs);
|
|
6334
6348
|
return rel.split(path5.sep).join("/");
|
|
6335
6349
|
}
|
|
6336
|
-
function detectCssFiles(root) {
|
|
6337
|
-
const
|
|
6350
|
+
function detectCssFiles(root, scanRoots = [], maxDepth = DEFAULT_CSS_DEPTH) {
|
|
6351
|
+
const base = path5.resolve(root);
|
|
6352
|
+
const found = /* @__PURE__ */ new Map();
|
|
6338
6353
|
let capped = false;
|
|
6339
|
-
const walk = (dir) => {
|
|
6340
|
-
if (capped) return;
|
|
6354
|
+
const walk = (dir, depth) => {
|
|
6355
|
+
if (capped || depth > maxDepth) return;
|
|
6341
6356
|
let entries;
|
|
6342
6357
|
try {
|
|
6343
6358
|
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
@@ -6348,23 +6363,31 @@ function detectCssFiles(root) {
|
|
|
6348
6363
|
const full = path5.join(dir, entry.name);
|
|
6349
6364
|
if (entry.isDirectory()) {
|
|
6350
6365
|
if (SKIP_DIRS2.has(entry.name)) continue;
|
|
6351
|
-
walk(full);
|
|
6366
|
+
walk(full, depth + 1);
|
|
6352
6367
|
if (capped) return;
|
|
6353
6368
|
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".css")) {
|
|
6354
|
-
|
|
6355
|
-
if (found.
|
|
6356
|
-
|
|
6357
|
-
|
|
6369
|
+
const abs = path5.resolve(full);
|
|
6370
|
+
if (!found.has(abs)) {
|
|
6371
|
+
found.set(abs, toRelative(base, abs));
|
|
6372
|
+
if (found.size >= CSS_FILE_CAP) {
|
|
6373
|
+
capped = true;
|
|
6374
|
+
return;
|
|
6375
|
+
}
|
|
6358
6376
|
}
|
|
6359
6377
|
}
|
|
6360
6378
|
}
|
|
6361
6379
|
};
|
|
6362
|
-
walk(
|
|
6363
|
-
|
|
6380
|
+
walk(base, 0);
|
|
6381
|
+
for (const r2 of scanRoots) {
|
|
6382
|
+
if (capped) break;
|
|
6383
|
+
const abs = path5.resolve(r2);
|
|
6384
|
+
if (abs !== base) walk(abs, 0);
|
|
6385
|
+
}
|
|
6386
|
+
const list = [...found.values()].sort((a, b3) => a.localeCompare(b3));
|
|
6364
6387
|
if (capped) {
|
|
6365
6388
|
console.error(`domflax: more than ${CSS_FILE_CAP} CSS files found; showing the first ${CSS_FILE_CAP}.`);
|
|
6366
6389
|
}
|
|
6367
|
-
return
|
|
6390
|
+
return list;
|
|
6368
6391
|
}
|
|
6369
6392
|
function detectInputDirs(root) {
|
|
6370
6393
|
const resolved = path5.resolve(root);
|
|
@@ -6446,15 +6469,6 @@ async function runWizard(base) {
|
|
|
6446
6469
|
} else if (outputMode === "overwrite") {
|
|
6447
6470
|
dangerouslyOverwriteSource = true;
|
|
6448
6471
|
}
|
|
6449
|
-
const allPasses = builtinPatternNames();
|
|
6450
|
-
const passSelection = await fe({
|
|
6451
|
-
message: "Which optimization passes should run?",
|
|
6452
|
-
options: allPasses.map((name) => ({ value: name, label: name })),
|
|
6453
|
-
initialValues: [...allPasses],
|
|
6454
|
-
required: true
|
|
6455
|
-
});
|
|
6456
|
-
if (cancelled(passSelection)) return done();
|
|
6457
|
-
const passes = passSelection;
|
|
6458
6472
|
const provider = await ve({
|
|
6459
6473
|
message: "How should class names resolve to styles?",
|
|
6460
6474
|
options: [
|
|
@@ -6467,7 +6481,7 @@ async function runWizard(base) {
|
|
|
6467
6481
|
if (cancelled(provider)) return done();
|
|
6468
6482
|
let css = base.css;
|
|
6469
6483
|
if (provider === "custom") {
|
|
6470
|
-
const detectedCss = detectCssFiles(root);
|
|
6484
|
+
const detectedCss = detectCssFiles(root, [inputPath]);
|
|
6471
6485
|
if (detectedCss.length > 0) {
|
|
6472
6486
|
const picked = await fe({
|
|
6473
6487
|
message: "Which CSS files should resolve your classes? (all detected files are pre-selected)",
|
|
@@ -6495,7 +6509,7 @@ async function runWizard(base) {
|
|
|
6495
6509
|
css,
|
|
6496
6510
|
dryRun,
|
|
6497
6511
|
dangerouslyOverwriteSource,
|
|
6498
|
-
passes:
|
|
6512
|
+
passes: null,
|
|
6499
6513
|
safety: base.safety ?? DEFAULT_SAFETY
|
|
6500
6514
|
};
|
|
6501
6515
|
function done() {
|
|
@@ -6525,7 +6539,7 @@ async function execute(options) {
|
|
|
6525
6539
|
const { files, inputRoot, warnings } = discoverInputs(options.paths);
|
|
6526
6540
|
for (const w2 of warnings) console.error(`domflax: ${w2}`);
|
|
6527
6541
|
if (files.length === 0) {
|
|
6528
|
-
console.error("domflax: no .jsx/.tsx
|
|
6542
|
+
console.error("domflax: no .jsx/.tsx files found for the given paths");
|
|
6529
6543
|
return { exitCode: 1 };
|
|
6530
6544
|
}
|
|
6531
6545
|
const projectRoot = options.projectRoot ?? process.cwd();
|
|
@@ -6572,8 +6586,20 @@ async function execute(options) {
|
|
|
6572
6586
|
failures += 1;
|
|
6573
6587
|
}
|
|
6574
6588
|
}
|
|
6589
|
+
if (options.dryRun) {
|
|
6590
|
+
console.log("\ndomflax: dry run \u2014 no files were written.");
|
|
6591
|
+
} else if (totals.changed === 0) {
|
|
6592
|
+
console.log(
|
|
6593
|
+
`
|
|
6594
|
+
domflax: processed ${totals.files} file${totals.files === 1 ? "" : "s"} \u2014 nothing to optimize (0 changed).`
|
|
6595
|
+
);
|
|
6596
|
+
} else {
|
|
6597
|
+
console.log(
|
|
6598
|
+
`
|
|
6599
|
+
domflax: optimized ${totals.changed} of ${totals.files} file${totals.files === 1 ? "" : "s"} (${totals.nodesRemoved} nodes removed, ${totals.classesSaved} classes saved, ${totals.bytesSaved} bytes saved).`
|
|
6600
|
+
);
|
|
6601
|
+
}
|
|
6575
6602
|
if (options.report) printReport(totals);
|
|
6576
|
-
if (options.dryRun) console.log("\ndomflax: dry run \u2014 no files were written.");
|
|
6577
6603
|
return { exitCode: failures > 0 ? 1 : 0 };
|
|
6578
6604
|
}
|
|
6579
6605
|
async function main(argv = process.argv.slice(2)) {
|