domflax 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +159 -0
  2. package/dist/{chunk-4HHISSMR.js → chunk-DNHOGPYV.js} +2675 -1503
  3. package/dist/chunk-DNHOGPYV.js.map +1 -0
  4. package/dist/{chunk-ZJ2S36GY.js → chunk-DOQEBGWB.js} +33 -20
  5. package/dist/chunk-DOQEBGWB.js.map +1 -0
  6. package/dist/{chunk-77SLHRN6.js → chunk-DWLB7FRR.js} +341 -176
  7. package/dist/chunk-DWLB7FRR.js.map +1 -0
  8. package/dist/cli.cjs +2169 -760
  9. package/dist/cli.cjs.map +1 -1
  10. package/dist/cli.js +183 -91
  11. package/dist/cli.js.map +1 -1
  12. package/dist/index.cjs +3021 -1699
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +477 -54
  15. package/dist/index.d.ts +477 -54
  16. package/dist/index.js +49 -3
  17. package/dist/pattern-CV607P87.d.ts +547 -0
  18. package/dist/pattern-F5xBtIE-.d.cts +547 -0
  19. package/dist/pattern-kit.cjs +60 -39
  20. package/dist/pattern-kit.cjs.map +1 -1
  21. package/dist/pattern-kit.d.cts +3 -18
  22. package/dist/pattern-kit.d.ts +3 -18
  23. package/dist/pattern-kit.js +3 -1
  24. package/dist/pattern-kit.js.map +1 -1
  25. package/dist/{types-BQ7l6dVe.d.ts → resolve-ops-DIwEelH-.d.cts} +26 -251
  26. package/dist/{types-BQ7l6dVe.d.cts → resolve-ops-DIwEelH-.d.ts} +26 -251
  27. package/dist/verify.d.cts +1 -1
  28. package/dist/verify.d.ts +1 -1
  29. package/dist/webpack-loader.cjs +2975 -1699
  30. package/dist/webpack-loader.cjs.map +1 -1
  31. package/dist/webpack-loader.d.cts +2 -2
  32. package/dist/webpack-loader.d.ts +2 -2
  33. package/dist/webpack-loader.js +3 -3
  34. package/package.json +3 -6
  35. package/dist/chunk-4HHISSMR.js.map +0 -1
  36. package/dist/chunk-77SLHRN6.js.map +0 -1
  37. package/dist/chunk-ZJ2S36GY.js.map +0 -1
  38. package/dist/pattern-CX6iBzTD.d.ts +0 -237
  39. package/dist/pattern-P4FIKAUB.d.cts +0 -237
package/dist/cli.js CHANGED
@@ -5,14 +5,14 @@ import {
5
5
  createJsxBackend,
6
6
  createJsxFrontend,
7
7
  createTailwindResolver
8
- } from "./chunk-4HHISSMR.js";
8
+ } from "./chunk-DNHOGPYV.js";
9
9
  import {
10
10
  buildSelectorIndex,
11
11
  createSyntheticSink,
12
12
  normalizer,
13
13
  runPasses,
14
14
  syncClassesFromComputed
15
- } from "./chunk-77SLHRN6.js";
15
+ } from "./chunk-DWLB7FRR.js";
16
16
  import {
17
17
  __commonJS,
18
18
  __toESM,
@@ -156,8 +156,7 @@ init_esm_shims();
156
156
  // ../cli/src/index.ts
157
157
  init_esm_shims();
158
158
  import { mkdirSync, readFileSync, writeFileSync } from "fs";
159
- import * as path3 from "path";
160
- import { fileURLToPath } from "url";
159
+ import * as path4 from "path";
161
160
 
162
161
  // ../cli/src/options.ts
163
162
  init_esm_shims();
@@ -358,66 +357,68 @@ function createTransform(options) {
358
357
  const projectRoot = options.projectRoot ?? process.cwd();
359
358
  const resolver = buildResolver(options.provider, options.css, projectRoot);
360
359
  const patterns = selectPatterns(options.passes);
360
+ function prepare(code, id, kind, gate) {
361
+ const parsed = createJsxFrontend().parse(code, {
362
+ id,
363
+ kind,
364
+ resolver,
365
+ normalizer,
366
+ config: {},
367
+ onDiagnostic: () => {
368
+ }
369
+ });
370
+ const doc = parsed.doc;
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
382
+ };
383
+ return { doc, ctx, passes: buildPasses(patterns), nodesIn };
384
+ }
385
+ function finish(code, optimized, id, nodesIn) {
386
+ syncClassesFromComputed(optimized, resolver, normalizer);
387
+ const printed = createJsxBackend().print(
388
+ optimized,
389
+ { moduleId: id, ops: [], provenance: /* @__PURE__ */ new Map() },
390
+ { normalizer, resolver, sink: createSyntheticSink(), eol: "\n", onDiagnostic: () => {
391
+ } }
392
+ );
393
+ const out = printed.code;
394
+ const nodesOut = optimized.nodes.size;
395
+ const classesBefore = countClassTokens(code);
396
+ const classesAfter = countClassTokens(out);
397
+ return {
398
+ code: out,
399
+ changed: out !== code,
400
+ passthrough: false,
401
+ stats: {
402
+ nodesIn,
403
+ nodesOut,
404
+ nodesRemoved: Math.max(0, nodesIn - nodesOut),
405
+ classesBefore,
406
+ classesAfter,
407
+ classesSaved: Math.max(0, classesBefore - classesAfter),
408
+ bytesBefore: bytes(code),
409
+ bytesAfter: bytes(out),
410
+ bytesSaved: bytes(code) - bytes(out)
411
+ }
412
+ };
413
+ }
361
414
  return {
362
415
  resolver,
363
416
  transformFile(code, id) {
364
417
  const kind = jsxKindOf(id);
365
418
  if (kind === null) return passthroughResult(code);
366
- const parsed = createJsxFrontend().parse(code, {
367
- id,
368
- kind,
369
- resolver,
370
- normalizer,
371
- config: {},
372
- onDiagnostic: () => {
373
- }
374
- });
375
- const doc = parsed.doc;
376
- const nodesIn = doc.nodes.size;
377
- for (const node of doc.nodes.values()) node.meta.safetyFloor = 3;
378
- const ctx = {
379
- doc,
380
- safetyCeiling: options.safety,
381
- normalizer,
382
- // Real CSS-selector-safety index from the active resolver (custom-CSS reports combinator /
383
- // structural-pseudo coupling; Tailwind has none → null index, behaviour unchanged).
384
- selectors: buildSelectorIndex(doc, resolver),
385
- resolver
386
- };
387
- const { doc: optimized } = runPasses(doc, buildPasses(patterns), ctx);
388
- syncClassesFromComputed(optimized, resolver, normalizer);
389
- const printed = createJsxBackend().print(
390
- optimized,
391
- { moduleId: id, ops: [], provenance: /* @__PURE__ */ new Map() },
392
- {
393
- normalizer,
394
- resolver,
395
- sink: createSyntheticSink(),
396
- eol: "\n",
397
- onDiagnostic: () => {
398
- }
399
- }
400
- );
401
- const out = printed.code;
402
- const nodesOut = optimized.nodes.size;
403
- const classesBefore = countClassTokens(code);
404
- const classesAfter = countClassTokens(out);
405
- return {
406
- code: out,
407
- changed: out !== code,
408
- passthrough: false,
409
- stats: {
410
- nodesIn,
411
- nodesOut,
412
- nodesRemoved: Math.max(0, nodesIn - nodesOut),
413
- classesBefore,
414
- classesAfter,
415
- classesSaved: Math.max(0, classesBefore - classesAfter),
416
- bytesBefore: bytes(code),
417
- bytesAfter: bytes(out),
418
- bytesSaved: bytes(code) - bytes(out)
419
- }
420
- };
419
+ const { doc, ctx, passes, nodesIn } = prepare(code, id, kind, "provably-safe");
420
+ const { doc: optimized } = runPasses(doc, passes, ctx);
421
+ return finish(code, optimized, id, nodesIn);
421
422
  }
422
423
  };
423
424
  }
@@ -1098,19 +1099,112 @@ ${import_picocolors2.default.gray(d2)} ${t}
1098
1099
  };
1099
1100
  var J2 = `${import_picocolors2.default.gray(o)} `;
1100
1101
 
1102
+ // ../cli/src/detect.ts
1103
+ init_esm_shims();
1104
+ import * as fs2 from "fs";
1105
+ import * as path3 from "path";
1106
+ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
1107
+ "node_modules",
1108
+ "dist",
1109
+ "build",
1110
+ ".next",
1111
+ "out",
1112
+ "coverage",
1113
+ ".git",
1114
+ "domflax-out"
1115
+ ]);
1116
+ var COMMON_INPUT_DIRS = ["src", "app", "components", "pages", "lib", "ui", "public"];
1117
+ var CSS_FILE_CAP = 200;
1118
+ function toRelative(root, abs) {
1119
+ const rel = path3.relative(root, abs);
1120
+ return rel.split(path3.sep).join("/");
1121
+ }
1122
+ function detectCssFiles(root) {
1123
+ const found = [];
1124
+ let capped = false;
1125
+ const walk = (dir) => {
1126
+ if (capped) return;
1127
+ let entries;
1128
+ try {
1129
+ entries = fs2.readdirSync(dir, { withFileTypes: true });
1130
+ } catch {
1131
+ return;
1132
+ }
1133
+ for (const entry of entries) {
1134
+ const full = path3.join(dir, entry.name);
1135
+ if (entry.isDirectory()) {
1136
+ if (SKIP_DIRS2.has(entry.name)) continue;
1137
+ walk(full);
1138
+ if (capped) return;
1139
+ } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".css")) {
1140
+ found.push(toRelative(root, full));
1141
+ if (found.length >= CSS_FILE_CAP) {
1142
+ capped = true;
1143
+ return;
1144
+ }
1145
+ }
1146
+ }
1147
+ };
1148
+ walk(path3.resolve(root));
1149
+ found.sort((a, b3) => a.localeCompare(b3));
1150
+ if (capped) {
1151
+ console.error(`domflax: more than ${CSS_FILE_CAP} CSS files found; showing the first ${CSS_FILE_CAP}.`);
1152
+ }
1153
+ return found;
1154
+ }
1155
+ function detectInputDirs(root) {
1156
+ const resolved = path3.resolve(root);
1157
+ return COMMON_INPUT_DIRS.filter((name) => {
1158
+ try {
1159
+ return fs2.statSync(path3.join(resolved, name)).isDirectory();
1160
+ } catch {
1161
+ return false;
1162
+ }
1163
+ });
1164
+ }
1165
+
1101
1166
  // ../cli/src/wizard.ts
1167
+ var OTHER_INPUT = "\0domflax.other";
1102
1168
  var WIZARD_CANCELLED = /* @__PURE__ */ Symbol("domflax.wizard.cancelled");
1103
1169
  function cancelled(value) {
1104
1170
  return pD(value);
1105
1171
  }
1106
1172
  async function runWizard(base) {
1107
1173
  Ie("domflax \u2014 optimize your markup");
1108
- const pathInput = await he({
1109
- message: "Which folder, glob, or file should domflax optimize?",
1110
- placeholder: "src",
1111
- defaultValue: "src"
1112
- });
1113
- if (cancelled(pathInput)) return done();
1174
+ const root = base.projectRoot ?? process.cwd();
1175
+ const detectedInputs = detectInputDirs(root);
1176
+ let inputPath;
1177
+ if (detectedInputs.length > 0) {
1178
+ const defaultInput = detectedInputs.includes("src") ? "src" : detectedInputs[0];
1179
+ const choice = await ve({
1180
+ message: "Which folder should domflax optimize?",
1181
+ options: [
1182
+ ...detectedInputs.map((dir) => ({ value: dir, label: dir, hint: "detected" })),
1183
+ { value: OTHER_INPUT, label: "Other (type a path)\u2026" }
1184
+ ],
1185
+ initialValue: defaultInput
1186
+ });
1187
+ if (cancelled(choice)) return done();
1188
+ if (choice === OTHER_INPUT) {
1189
+ const typed = await he({
1190
+ message: "Which folder, glob, or file should domflax optimize?",
1191
+ placeholder: "src",
1192
+ defaultValue: "src"
1193
+ });
1194
+ if (cancelled(typed)) return done();
1195
+ inputPath = String(typed);
1196
+ } else {
1197
+ inputPath = String(choice);
1198
+ }
1199
+ } else {
1200
+ const typed = await he({
1201
+ message: "Which folder, glob, or file should domflax optimize?",
1202
+ placeholder: "src",
1203
+ defaultValue: "src"
1204
+ });
1205
+ if (cancelled(typed)) return done();
1206
+ inputPath = String(typed);
1207
+ }
1114
1208
  const outputMode = await ve({
1115
1209
  message: "Where should the optimized files go?",
1116
1210
  options: [
@@ -1159,17 +1253,29 @@ async function runWizard(base) {
1159
1253
  if (cancelled(provider)) return done();
1160
1254
  let css = base.css;
1161
1255
  if (provider === "custom") {
1162
- const cssInput = await he({
1163
- message: "CSS files (space-separated):",
1164
- placeholder: "src/styles.css"
1165
- });
1166
- if (cancelled(cssInput)) return done();
1167
- css = String(cssInput).split(/\s+/).filter((s) => s.length > 0);
1256
+ const detectedCss = detectCssFiles(root);
1257
+ if (detectedCss.length > 0) {
1258
+ const picked = await fe({
1259
+ message: "Which CSS files should resolve your classes? (all detected files are pre-selected)",
1260
+ options: detectedCss.map((file) => ({ value: file, label: file })),
1261
+ initialValues: [...detectedCss],
1262
+ required: false
1263
+ });
1264
+ if (cancelled(picked)) return done();
1265
+ css = picked;
1266
+ } else {
1267
+ const cssInput = await he({
1268
+ message: "CSS files (space-separated):",
1269
+ placeholder: "src/styles.css"
1270
+ });
1271
+ if (cancelled(cssInput)) return done();
1272
+ css = String(cssInput).split(/\s+/).filter((s) => s.length > 0);
1273
+ }
1168
1274
  }
1169
1275
  Se("Ready \u2014 running domflax.");
1170
1276
  return {
1171
1277
  ...base,
1172
- paths: [String(pathInput)],
1278
+ paths: [inputPath],
1173
1279
  out,
1174
1280
  provider,
1175
1281
  css,
@@ -1201,7 +1307,7 @@ function printReport(totals) {
1201
1307
  console.log(` classes saved : ${totals.classesSaved}`);
1202
1308
  console.log(` bytes saved : ${totals.bytesSaved}`);
1203
1309
  }
1204
- function execute(options) {
1310
+ async function execute(options) {
1205
1311
  const { files, inputRoot, warnings } = discoverInputs(options.paths);
1206
1312
  for (const w2 of warnings) console.error(`domflax: ${w2}`);
1207
1313
  if (files.length === 0) {
@@ -1231,7 +1337,7 @@ function execute(options) {
1231
1337
  const result = transform.transformFile(code, file);
1232
1338
  addStats(totals, result.stats, result.changed);
1233
1339
  if (options.dryRun) {
1234
- const rel = path3.relative(inputRoot, file) || path3.basename(file);
1340
+ const rel = path4.relative(inputRoot, file) || path4.basename(file);
1235
1341
  if (result.changed) console.log(unifiedDiff(code, result.code, rel));
1236
1342
  else if (!options.report) console.log(` (unchanged) ${rel}`);
1237
1343
  continue;
@@ -1244,9 +1350,9 @@ function execute(options) {
1244
1350
  continue;
1245
1351
  }
1246
1352
  try {
1247
- mkdirSync(path3.dirname(target.value), { recursive: true });
1353
+ mkdirSync(path4.dirname(target.value), { recursive: true });
1248
1354
  writeFileSync(target.value, result.code, "utf8");
1249
- console.log(`domflax: wrote ${path3.relative(process.cwd(), target.value) || target.value}`);
1355
+ console.log(`domflax: wrote ${path4.relative(process.cwd(), target.value) || target.value}`);
1250
1356
  } catch (err) {
1251
1357
  console.error(`domflax: cannot write ${target.value}: ${String(err?.message ?? err)}`);
1252
1358
  failures += 1;
@@ -1283,27 +1389,13 @@ async function main(argv = process.argv.slice(2)) {
1283
1389
  return;
1284
1390
  }
1285
1391
  try {
1286
- const result = execute(options);
1392
+ const result = await execute(options);
1287
1393
  process.exitCode = result.exitCode;
1288
1394
  } catch (err) {
1289
1395
  console.error(`domflax: ${err instanceof Error ? err.message : String(err)}`);
1290
1396
  process.exitCode = 1;
1291
1397
  }
1292
1398
  }
1293
- function isMainEntry() {
1294
- const entry = process.argv[1];
1295
- if (entry === void 0) return false;
1296
- try {
1297
- const self = path3.resolve(fileURLToPath(import.meta.url));
1298
- const argv = path3.resolve(entry);
1299
- return process.platform === "win32" ? self.toLowerCase() === argv.toLowerCase() : self === argv;
1300
- } catch {
1301
- return false;
1302
- }
1303
- }
1304
- if (isMainEntry()) {
1305
- void main();
1306
- }
1307
1399
 
1308
1400
  // src/cli.ts
1309
1401
  main(process.argv.slice(2));