@codefast/cli 0.3.7-canary.0 → 0.3.13-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +178 -262
  2. package/dist/analyze-DvtJ3j9m.mjs +92 -0
  3. package/dist/arrange-Bbh62DrU.mjs +107 -0
  4. package/dist/arrange-BqP2DI3w.mjs +8 -0
  5. package/dist/ast-helpers-MDRYuIAk.mjs +115 -0
  6. package/dist/bin.mjs +8 -0
  7. package/dist/caught-unknown-message-CRhkDWO7.mjs +20 -0
  8. package/dist/collectors-cn-BS8TTQ8O.mjs +61 -0
  9. package/dist/collectors-jsx-D9tL2ZPM.mjs +20 -0
  10. package/dist/collectors-tv-D2PXZaRt.mjs +170 -0
  11. package/dist/colors-BjdkCfGK.mjs +12 -0
  12. package/dist/commands/arrange.mjs +2 -0
  13. package/dist/commands/mirror.mjs +2 -0
  14. package/dist/commands/mirror.test.mjs +48 -0
  15. package/dist/commands/tag.mjs +2 -0
  16. package/dist/config-DVia_f9a.mjs +3 -0
  17. package/dist/config-reporter-SCcWDZc1.mjs +10 -0
  18. package/dist/constants-AtPhF-Ct.mjs +169 -0
  19. package/dist/constants-Bq5wdyLC.mjs +8 -0
  20. package/dist/dirent-list-BSDeRG5i.mjs +14 -0
  21. package/dist/engine-CcWEHSCq.mjs +287 -0
  22. package/dist/engine-DRTdkc5u.mjs +236 -0
  23. package/dist/errors-CFH4w6ic.mjs +15 -0
  24. package/dist/errors-CpU1ogfA.mjs +19 -0
  25. package/dist/formatters-DGE8jJ53.mjs +52 -0
  26. package/dist/group-file-ClW7XdD6.mjs +113 -0
  27. package/dist/grouping-c1grpCQT.mjs +235 -0
  28. package/dist/imports-DSVq1xhB.mjs +54 -0
  29. package/dist/lib/arrange/application/analyze.mjs +2 -0
  30. package/dist/lib/arrange/application/analyze.test.mjs +171 -0
  31. package/dist/lib/arrange/application/group-file.mjs +2 -0
  32. package/dist/lib/arrange/application/group-file.test.mjs +273 -0
  33. package/dist/lib/arrange/application/run-target.mjs +2 -0
  34. package/dist/lib/arrange/application/run-target.test.mjs +97 -0
  35. package/dist/lib/arrange/domain/ast/ast-helpers.mjs +2 -0
  36. package/dist/lib/arrange/domain/ast/ast-helpers.test.mjs +148 -0
  37. package/dist/lib/arrange/domain/ast/collectors-cn.mjs +2 -0
  38. package/dist/lib/arrange/domain/ast/collectors-cn.test.mjs +58 -0
  39. package/dist/lib/arrange/domain/ast/collectors-jsx.mjs +2 -0
  40. package/dist/lib/arrange/domain/ast/collectors-jsx.test.mjs +20 -0
  41. package/dist/lib/arrange/domain/ast/collectors-tv.mjs +2 -0
  42. package/dist/lib/arrange/domain/ast/collectors-tv.test.mjs +145 -0
  43. package/dist/lib/arrange/domain/ast/targets.mjs +2 -0
  44. package/dist/lib/arrange/domain/ast/targets.test.mjs +50 -0
  45. package/dist/lib/arrange/domain/constants.mjs +2 -0
  46. package/dist/lib/arrange/domain/errors.mjs +2 -0
  47. package/dist/lib/arrange/domain/grouping.mjs +2 -0
  48. package/dist/lib/arrange/domain/grouping.test.mjs +186 -0
  49. package/dist/lib/arrange/domain/imports.mjs +2 -0
  50. package/dist/lib/arrange/domain/imports.test.mjs +48 -0
  51. package/dist/lib/arrange/domain/tokenizer.mjs +2 -0
  52. package/dist/lib/arrange/domain/tokenizer.test.mjs +165 -0
  53. package/dist/lib/arrange/domain/types.mjs +1 -0
  54. package/dist/lib/arrange/index.mjs +16 -0
  55. package/dist/lib/arrange/infra/walk.mjs +2 -0
  56. package/dist/lib/arrange/infra/walk.test.mjs +50 -0
  57. package/dist/lib/arrange/presentation/formatters.mjs +2 -0
  58. package/dist/lib/arrange/presentation/formatters.test.mjs +73 -0
  59. package/dist/lib/arrange/presentation/report.mjs +2 -0
  60. package/dist/lib/config/domain/schema.mjs +2 -0
  61. package/dist/lib/config/index.mjs +4 -0
  62. package/dist/lib/config/infra/loader.mjs +2 -0
  63. package/dist/lib/config/infra/loader.test.mjs +116 -0
  64. package/dist/lib/infra/caught-unknown-message.mjs +2 -0
  65. package/dist/lib/infra/config-reporter.mjs +2 -0
  66. package/dist/lib/infra/fs-contract.mjs +1 -0
  67. package/dist/lib/infra/node-io.mjs +2 -0
  68. package/dist/lib/infra/workspace/repo-root.mjs +2 -0
  69. package/dist/lib/infra/workspace/repo-root.test.mjs +45 -0
  70. package/dist/lib/mirror/application/engine.mjs +2 -0
  71. package/dist/lib/mirror/application/sync.mjs +2 -0
  72. package/dist/lib/mirror/application/sync.test.mjs +482 -0
  73. package/dist/lib/mirror/domain/constants.mjs +2 -0
  74. package/dist/lib/mirror/domain/errors.mjs +2 -0
  75. package/dist/lib/mirror/domain/types.mjs +1 -0
  76. package/dist/lib/mirror/index.mjs +3 -0
  77. package/dist/lib/mirror/infra/dirent-list.mjs +2 -0
  78. package/dist/lib/mirror/infra/dirent-list.test.mjs +18 -0
  79. package/dist/lib/mirror/infra/package-filter.mjs +2 -0
  80. package/dist/lib/mirror/infra/package-filter.test.mjs +32 -0
  81. package/dist/lib/mirror/infra/path-normalizer.mjs +2 -0
  82. package/dist/lib/mirror/infra/update-pkg.mjs +2 -0
  83. package/dist/lib/mirror/infra/workspace-packages.mjs +2 -0
  84. package/dist/lib/mirror/infra/workspace-packages.test.mjs +167 -0
  85. package/dist/lib/mirror/presentation/reporter.mjs +2 -0
  86. package/dist/lib/tag/application/engine.mjs +2 -0
  87. package/dist/lib/tag/application/engine.test.mjs +159 -0
  88. package/dist/lib/tag/domain/types.mjs +1 -0
  89. package/dist/lib/tag/index.mjs +5 -0
  90. package/dist/lib/tag/infra/target-resolver.mjs +2 -0
  91. package/dist/lib/tag/presentation/colors.mjs +2 -0
  92. package/dist/lib/tag/presentation/tag-presenter.mjs +2 -0
  93. package/dist/lib/tag/presentation/tag-presenter.test.mjs +29 -0
  94. package/dist/loader-DpN-zl5g.mjs +78 -0
  95. package/dist/mirror-CbGiLXKT.mjs +2 -0
  96. package/dist/mirror-DqJE3ejL.mjs +66 -0
  97. package/dist/node-io-DFRbHO6m.mjs +30 -0
  98. package/dist/package-filter-C_8ryqvz.mjs +26 -0
  99. package/dist/path-normalizer-D4ceBO3l.mjs +7 -0
  100. package/dist/program-DR-H4tH3.mjs +34 -0
  101. package/dist/program.mjs +2 -0
  102. package/dist/repo-root-HmtIEm6J.mjs +22 -0
  103. package/dist/report-DYxvw34T.mjs +24 -0
  104. package/dist/reporter-CwIe_tAM.mjs +108 -0
  105. package/dist/run-target-CJWrqNT4.mjs +65 -0
  106. package/dist/schema-Or8RAARd.mjs +25 -0
  107. package/dist/sync-e1Sp-shi.mjs +157 -0
  108. package/dist/tag-Cd9JujU5.mjs +2 -0
  109. package/dist/tag-hEKVZ_-7.mjs +58 -0
  110. package/dist/tag-presenter-Cs66UBsH.mjs +63 -0
  111. package/dist/target-resolver-CR3FXMTy.mjs +170 -0
  112. package/dist/targets-pCTslnFh.mjs +106 -0
  113. package/dist/tokenizer-CeZx73Zu.mjs +216 -0
  114. package/dist/update-pkg-CnGfjWBy.mjs +37 -0
  115. package/dist/walk-DjgQn66n.mjs +33 -0
  116. package/dist/workspace-packages-K2NYTHJq.mjs +159 -0
  117. package/package.json +42 -53
  118. package/CHANGELOG.md +0 -15
  119. package/dist/cjs/index.cjs +0 -2
  120. package/dist/cjs/src/index.d.ts +0 -3
  121. package/dist/cjs/src/index.d.ts.map +0 -1
  122. package/dist/esm/index.js +0 -2
  123. package/dist/esm/src/index.d.ts +0 -3
  124. package/dist/esm/src/index.d.ts.map +0 -1
@@ -0,0 +1,48 @@
1
+ import { n as ensureCnImport, t as cnModuleSpecifierForFile } from "../../../imports-DSVq1xhB.mjs";
2
+ import "../../../arrange-BqP2DI3w.mjs";
3
+ //#region src/lib/arrange/domain/imports.test.ts
4
+ describe("cnModuleSpecifierForFile", () => {
5
+ it("documents that default module specifier is stable across file paths", () => {
6
+ expect(cnModuleSpecifierForFile("/repo/packages/ui/src/components/a.tsx")).toBe("@codefast/tailwind-variants");
7
+ expect(cnModuleSpecifierForFile("/repo/packages/cli/src/lib/a.ts")).toBe("@codefast/tailwind-variants");
8
+ });
9
+ it("documents that explicit override always takes precedence", () => {
10
+ expect(cnModuleSpecifierForFile("/repo/packages/ui/src/components/a.tsx", "clsx")).toBe("clsx");
11
+ });
12
+ });
13
+ describe("ensureCnImport", () => {
14
+ it("does not change source when cn is imported as default binding", () => {
15
+ const source = "import cn from \"@codefast/tailwind-variants\";\nconst x = 1;\n";
16
+ expect(ensureCnImport(source, "/repo/x.ts")).toBe(source);
17
+ });
18
+ it("does not change source when cn already imported", () => {
19
+ const source = "import { cn, tv } from \"@codefast/tailwind-variants\";\nconst x = 1;\n";
20
+ expect(ensureCnImport(source, "/repo/x.ts")).toBe(source);
21
+ });
22
+ it("injects cn into an existing named import from matching module", () => {
23
+ const out = ensureCnImport("import { tv } from \"clsx\";\nconst x = 1;\n", "/repo/x.ts", "clsx");
24
+ expect(out).toContain("import { cn, tv } from \"clsx\";");
25
+ });
26
+ it("inserts after use client directive", () => {
27
+ const out = ensureCnImport("\"use client\";\n\nexport const x = 1;\n", "/repo/x.tsx");
28
+ expect(out.startsWith("\"use client\";\n\nimport { cn }")).toBe(true);
29
+ });
30
+ it("inserts before first import when no matching import exists", () => {
31
+ const out = ensureCnImport("import { tv } from \"clsx\";\nconst x = 1;\n", "/repo/x.ts");
32
+ expect(out.indexOf("import { cn } from \"@codefast/tailwind-variants\";")).toBe(0);
33
+ });
34
+ it("inserts at file start when there are no imports", () => {
35
+ const out = ensureCnImport("const x = 1;\n", "/repo/x.ts");
36
+ expect(out.startsWith("import { cn } from \"@codefast/tailwind-variants\";\n")).toBe(true);
37
+ });
38
+ it("respects module override when adding new import", () => {
39
+ const out = ensureCnImport("const x = 1;\n", "/repo/x.ts", "clsx");
40
+ expect(out.startsWith("import { cn } from \"clsx\";\n")).toBe(true);
41
+ });
42
+ it("keeps existing legacy tailwind-variants import unchanged (backward compat)", () => {
43
+ const source = "import { cn, tv } from \"tailwind-variants\";\nconst x = 1;\n";
44
+ expect(ensureCnImport(source, "/repo/x.ts")).toBe(source);
45
+ });
46
+ });
47
+ //#endregion
48
+ export {};
@@ -0,0 +1,2 @@
1
+ import { a as compositeSecondaryOrder, c as selectorKey, d as tokenizeClassString, i as classifyToken, l as stateKey, n as bucketsMergeCompatible, o as indexOfFirstVariantColon, r as classifyBareUtility, s as isArbitraryParentSelectorStateToken, t as bucketsCompatible, u as stripVariants } from "../../../tokenizer-CeZx73Zu.mjs";
2
+ export { bucketsCompatible, bucketsMergeCompatible, classifyBareUtility, classifyToken, compositeSecondaryOrder, indexOfFirstVariantColon, isArbitraryParentSelectorStateToken, selectorKey, stateKey, stripVariants, tokenizeClassString };
@@ -0,0 +1,165 @@
1
+ import { c as selectorKey, d as tokenizeClassString, i as classifyToken, l as stateKey, n as bucketsMergeCompatible, o as indexOfFirstVariantColon, t as bucketsCompatible, u as stripVariants } from "../../../tokenizer-CeZx73Zu.mjs";
2
+ import "../../../arrange-BqP2DI3w.mjs";
3
+ //#region src/lib/arrange/domain/tokenizer.test.ts
4
+ describe("tokenizeClassString", () => {
5
+ it("trims and splits on whitespace runs", () => {
6
+ expect(tokenizeClassString(" flex gap-2 ")).toEqual(["flex", "gap-2"]);
7
+ });
8
+ it("returns empty for whitespace-only input", () => {
9
+ expect(tokenizeClassString(" \n ")).toEqual([]);
10
+ });
11
+ });
12
+ describe("bucketsMergeCompatible", () => {
13
+ it.each([
14
+ [
15
+ "state",
16
+ "state",
17
+ false
18
+ ],
19
+ [
20
+ "starting",
21
+ "starting",
22
+ false
23
+ ],
24
+ [
25
+ "layout",
26
+ "sizing",
27
+ true
28
+ ],
29
+ [
30
+ "shape",
31
+ "shadow",
32
+ true
33
+ ],
34
+ [
35
+ "layout",
36
+ "state",
37
+ false
38
+ ],
39
+ [
40
+ "arbitrary",
41
+ "arbitrary",
42
+ false
43
+ ],
44
+ [
45
+ "arbitrary",
46
+ "state",
47
+ false
48
+ ],
49
+ [
50
+ "selector",
51
+ "selector",
52
+ false
53
+ ],
54
+ [
55
+ "layout",
56
+ "selector",
57
+ false
58
+ ],
59
+ [
60
+ "selector",
61
+ "state",
62
+ false
63
+ ]
64
+ ])("merge compatibility %s + %s -> %s", (a, b, expected) => {
65
+ expect(bucketsMergeCompatible(a, b)).toBe(expected);
66
+ expect(bucketsMergeCompatible(b, a)).toBe(expected);
67
+ });
68
+ });
69
+ describe("bucketsCompatible", () => {
70
+ it.each([
71
+ [
72
+ "layout",
73
+ "layout",
74
+ true
75
+ ],
76
+ [
77
+ "layout",
78
+ "sizing",
79
+ true
80
+ ],
81
+ [
82
+ "shape",
83
+ "shadow",
84
+ true
85
+ ],
86
+ [
87
+ "layout",
88
+ "state",
89
+ false
90
+ ],
91
+ [
92
+ "arbitrary",
93
+ "state",
94
+ false
95
+ ],
96
+ [
97
+ "layout",
98
+ "selector",
99
+ false
100
+ ]
101
+ ])("compatibility %s + %s -> %s", (a, b, expected) => {
102
+ expect(bucketsCompatible(a, b)).toBe(expected);
103
+ expect(bucketsCompatible(b, a)).toBe(expected);
104
+ });
105
+ });
106
+ describe("stateKey", () => {
107
+ it("uses the full variant stack for unique keys", () => {
108
+ expect(stateKey("@md/field-group:[&>*]:w-auto")).not.toBe(stateKey("@md/field-group:[&>[data-slot=field-label]]:flex-auto"));
109
+ });
110
+ it("normalizes bracketed data variant stems", () => {
111
+ expect(stateKey("data-[state=open]:opacity-100")).toBe("data-[state=open]");
112
+ expect(stateKey("in-data-[slot=tooltip-content]:bg-background/20")).toBe("in-data-[slot=tooltip-content]");
113
+ });
114
+ it("keeps distinct keys for in-data-side-* stems", () => {
115
+ expect(stateKey("in-data-side-right:cursor-e-resize")).toBe("in-data-side-right");
116
+ expect(stateKey("in-data-side-left:cursor-w-resize")).not.toBe(stateKey("in-data-side-right:cursor-e-resize"));
117
+ });
118
+ });
119
+ describe("indexOfFirstVariantColon + stripVariants", () => {
120
+ it("ignores colons inside square brackets", () => {
121
+ const tok = "[&_a:hover]:text-red-500";
122
+ expect(indexOfFirstVariantColon(tok)).toBe(11);
123
+ expect(stripVariants(tok)).toBe("text-red-500");
124
+ });
125
+ it("strips stacked variant prefixes", () => {
126
+ expect(stripVariants("@min-[600px]:@max-[900px]:hidden")).toBe("hidden");
127
+ });
128
+ });
129
+ describe("classifyToken", () => {
130
+ it.each([
131
+ ["flex", "layout"],
132
+ ["w-4", "sizing"],
133
+ ["px-3", "spacing"],
134
+ ["rounded-md", "shape"],
135
+ ["text-sm", "typography"],
136
+ ["transition", "motion"],
137
+ ["outline-hidden", "shadow"],
138
+ ["focus-visible:outline-hidden", "state"],
139
+ ["hover:opacity-80", "state"],
140
+ ["[&_svg]:size-4", "selector"],
141
+ ["*:rounded", "selector"],
142
+ ["**:text-sm", "selector"],
143
+ ["has-[img]:bg-muted", "selector"],
144
+ ["in-[.popover]:opacity-100", "selector"],
145
+ ["group-[.is-published]:block", "selector"],
146
+ ["peer-[.is-dirty]:peer-required:block", "selector"],
147
+ ["not-[.prose]:mt-4", "selector"],
148
+ ["starting:opacity-0", "starting"],
149
+ ["cursor-default", "behavior"],
150
+ ["@container/sidebar", "existence"],
151
+ ["@md/sidebar:w-full", "state"]
152
+ ])("classifies %s -> %s", (token, bucket) => {
153
+ expect(classifyToken(token)).toBe(bucket);
154
+ });
155
+ });
156
+ describe("selectorKey", () => {
157
+ it("merges shadcn svg size guard with plain [&_svg] utilities", () => {
158
+ expect(selectorKey("[&_svg]:pointer-events-none")).toBe(selectorKey("[&_svg:not([class*='size-'])]:size-6"));
159
+ });
160
+ it("still splits unrelated arbitrary parent selectors", () => {
161
+ expect(selectorKey("[&_svg]:size-4")).not.toBe(selectorKey("[&>*]:block"));
162
+ });
163
+ });
164
+ //#endregion
165
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import { i as DEFAULT_ARRANGE_TARGET, o as LONG_STRING_TOKEN_THRESHOLD, p as MAX_STRIP_VARIANT_PASSES } from "../../constants-AtPhF-Ct.mjs";
2
+ import { c as selectorKey, d as tokenizeClassString, i as classifyToken, l as stateKey, n as bucketsMergeCompatible, o as indexOfFirstVariantColon, t as bucketsCompatible, u as stripVariants } from "../../tokenizer-CeZx73Zu.mjs";
3
+ import { a as mergeCnUnconditionalLiteralPoolForTest, r as forEachStringLiteralInClassExpression } from "../../collectors-cn-BS8TTQ8O.mjs";
4
+ import { a as isCnOrTvIdentifier, i as indentOfLineContaining, l as unwrapCnInsideTvCallReplacement, n as applyEditsDescending, o as lineOf, r as buildKnownCnTvBindings, s as moduleLooksLikeCnTvReexport, t as KNOWN_CN_TV_MODULES } from "../../ast-helpers-MDRYuIAk.mjs";
5
+ import { n as walkTsxFiles, t as DEFAULT_SKIP_DIRS } from "../../walk-DjgQn66n.mjs";
6
+ import { t as analyzeDirectory } from "../../analyze-DvtJ3j9m.mjs";
7
+ import { t as printAnalyzeReport } from "../../report-DYxvw34T.mjs";
8
+ import { a as suggestCnGroups, i as mergeSingletons, n as capGroups, o as summarizeGroupBucketLabels, r as mergeEaseTimingIntoFollowingAnimatedState, t as areCnTailwindPartitionsEquivalent } from "../../grouping-c1grpCQT.mjs";
9
+ import { a as formatJsxCnAttributeValue, i as formatCnCall, n as formatArray, r as formatCnArguments, t as escapeTsStringLiteralContent } from "../../formatters-DGE8jJ53.mjs";
10
+ import { n as ensureCnImport, t as cnModuleSpecifierForFile } from "../../imports-DSVq1xhB.mjs";
11
+ import { t as groupFile } from "../../group-file-ClW7XdD6.mjs";
12
+ import { n as ArrangeErrorCode, t as ArrangeError } from "../../errors-CpU1ogfA.mjs";
13
+ import { n as runOnTarget, t as runArrangeSync } from "../../run-target-CJWrqNT4.mjs";
14
+ import { n as createNodeCliLogger, t as createNodeCliFs } from "../../node-io-DFRbHO6m.mjs";
15
+ import "../../arrange-BqP2DI3w.mjs";
16
+ export { ArrangeError, ArrangeErrorCode, DEFAULT_ARRANGE_TARGET, DEFAULT_SKIP_DIRS, KNOWN_CN_TV_MODULES, LONG_STRING_TOKEN_THRESHOLD, MAX_STRIP_VARIANT_PASSES, analyzeDirectory, applyEditsDescending, areCnTailwindPartitionsEquivalent, bucketsCompatible, bucketsMergeCompatible, buildKnownCnTvBindings, capGroups, classifyToken, cnModuleSpecifierForFile, createNodeCliFs, createNodeCliLogger, ensureCnImport, escapeTsStringLiteralContent, forEachStringLiteralInClassExpression, formatArray, formatCnArguments, formatCnCall, formatJsxCnAttributeValue, groupFile, indentOfLineContaining, indexOfFirstVariantColon, isCnOrTvIdentifier, lineOf, mergeCnUnconditionalLiteralPoolForTest, mergeEaseTimingIntoFollowingAnimatedState, mergeSingletons, moduleLooksLikeCnTvReexport, printAnalyzeReport, runArrangeSync, runOnTarget, selectorKey, stateKey, stripVariants, suggestCnGroups, summarizeGroupBucketLabels, tokenizeClassString, unwrapCnInsideTvCallReplacement, walkTsxFiles };
@@ -0,0 +1,2 @@
1
+ import { n as walkTsxFiles, t as DEFAULT_SKIP_DIRS } from "../../../walk-DjgQn66n.mjs";
2
+ export { DEFAULT_SKIP_DIRS, walkTsxFiles };
@@ -0,0 +1,50 @@
1
+ import { n as walkTsxFiles, t as DEFAULT_SKIP_DIRS } from "../../../walk-DjgQn66n.mjs";
2
+ import { t as createNodeCliFs } from "../../../node-io-DFRbHO6m.mjs";
3
+ import "../../../arrange-BqP2DI3w.mjs";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import os from "node:os";
7
+ //#region src/lib/arrange/infra/walk.test.ts
8
+ const arrangeFs = createNodeCliFs();
9
+ describe("DEFAULT_SKIP_DIRS", () => {
10
+ it("contains common generated directories", () => {
11
+ expect(DEFAULT_SKIP_DIRS.has("node_modules")).toBe(true);
12
+ expect(DEFAULT_SKIP_DIRS.has(".git")).toBe(true);
13
+ expect(DEFAULT_SKIP_DIRS.has("dist")).toBe(true);
14
+ expect(DEFAULT_SKIP_DIRS.has(".turbo")).toBe(true);
15
+ });
16
+ });
17
+ describe("walkTsxFiles", () => {
18
+ it("collects .ts/.tsx, skips .d.ts and nested skipped directories", () => {
19
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "arr-walk-"));
20
+ try {
21
+ fs.mkdirSync(path.join(dir, "a", "node_modules", "x"), { recursive: true });
22
+ fs.mkdirSync(path.join(dir, ".git", "hooks"), { recursive: true });
23
+ fs.writeFileSync(path.join(dir, "root.ts"), "", "utf8");
24
+ fs.writeFileSync(path.join(dir, "root.tsx"), "", "utf8");
25
+ fs.writeFileSync(path.join(dir, "types.d.ts"), "", "utf8");
26
+ fs.writeFileSync(path.join(dir, "a", "node_modules", "x", "ignored.tsx"), "", "utf8");
27
+ fs.writeFileSync(path.join(dir, ".git", "hooks", "ignored.ts"), "", "utf8");
28
+ const rel = walkTsxFiles(dir, arrangeFs).map((p) => path.relative(dir, p).split(path.sep).join("/")).sort();
29
+ expect(rel).toEqual(["root.ts", "root.tsx"]);
30
+ } finally {
31
+ fs.rmSync(dir, {
32
+ recursive: true,
33
+ force: true
34
+ });
35
+ }
36
+ });
37
+ it("returns empty array for empty directory", () => {
38
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "arr-walk-empty-"));
39
+ try {
40
+ expect(walkTsxFiles(dir, arrangeFs)).toEqual([]);
41
+ } finally {
42
+ fs.rmSync(dir, {
43
+ recursive: true,
44
+ force: true
45
+ });
46
+ }
47
+ });
48
+ });
49
+ //#endregion
50
+ export {};
@@ -0,0 +1,2 @@
1
+ import { a as formatJsxCnAttributeValue, i as formatCnCall, n as formatArray, r as formatCnArguments, t as escapeTsStringLiteralContent } from "../../../formatters-DGE8jJ53.mjs";
2
+ export { escapeTsStringLiteralContent, formatArray, formatCnArguments, formatCnCall, formatJsxCnAttributeValue };
@@ -0,0 +1,73 @@
1
+ import { a as formatJsxCnAttributeValue, i as formatCnCall, n as formatArray, r as formatCnArguments, t as escapeTsStringLiteralContent } from "../../../formatters-DGE8jJ53.mjs";
2
+ import "../../../arrange-BqP2DI3w.mjs";
3
+ //#region src/lib/arrange/presentation/formatters.test.ts
4
+ describe("escapeTsStringLiteralContent", () => {
5
+ it("escapes backslashes and quotes", () => {
6
+ expect(escapeTsStringLiteralContent(String.raw`x\"y`)).toBe(String.raw`x\\\"y`);
7
+ });
8
+ it("keeps plain strings unchanged", () => {
9
+ expect(escapeTsStringLiteralContent("plain")).toBe("plain");
10
+ });
11
+ it("handles empty strings", () => {
12
+ expect(escapeTsStringLiteralContent("")).toBe("");
13
+ });
14
+ });
15
+ describe("formatCnArguments", () => {
16
+ it("renders indented args without wrapping cn()", () => {
17
+ expect(formatCnArguments(["flex gap-2", "text-sm"], { indent: " " })).toBe([" \"flex gap-2\",", " \"text-sm\""].join("\n"));
18
+ });
19
+ it("adds comma on last group when commaAfterLastGroup=true", () => {
20
+ expect(formatCnArguments(["a", "b"], { commaAfterLastGroup: true })).toBe([" \"a\",", " \"b\","].join("\n"));
21
+ });
22
+ it("appends trailing className line when requested", () => {
23
+ expect(formatCnArguments(["a", "b"], { trailingClassName: true })).toBe([
24
+ " \"a\",",
25
+ " \"b\",",
26
+ " className,"
27
+ ].join("\n"));
28
+ });
29
+ it("escapes literals", () => {
30
+ expect(formatCnArguments(["x\"y"])).toBe(" \"x\\\"y\"");
31
+ });
32
+ });
33
+ describe("formatCnCall", () => {
34
+ it("omits trailing comma for single group", () => {
35
+ expect(formatCnCall(["only"])).toBe([
36
+ "cn(",
37
+ " \"only\"",
38
+ ")"
39
+ ].join("\n"));
40
+ });
41
+ it("renders multiline cn() with trailing commas for multi-arg", () => {
42
+ expect(formatCnCall(["a", "b"])).toBe([
43
+ "cn(",
44
+ " \"a\",",
45
+ " \"b\",",
46
+ ")"
47
+ ].join("\n"));
48
+ });
49
+ it("supports trailing className", () => {
50
+ expect(formatCnCall(["a", "b"], { trailingClassName: true })).toContain("className,");
51
+ });
52
+ });
53
+ describe("formatArray", () => {
54
+ it("renders multiline string array", () => {
55
+ expect(formatArray(["a", "b"])).toBe([
56
+ "[",
57
+ " \"a\",",
58
+ " \"b\",",
59
+ "]"
60
+ ].join("\n"));
61
+ });
62
+ });
63
+ describe("formatJsxCnAttributeValue", () => {
64
+ it("formats className={cn(...)} with source-derived indentation", () => {
65
+ const source = `export function F() {\n return <div className="x" />;\n}`;
66
+ const out = formatJsxCnAttributeValue(["flex gap-2", "text-sm"], source, source.indexOf("\""));
67
+ expect(out.startsWith("{cn(")).toBe(true);
68
+ expect(out.endsWith(")}")).toBe(true);
69
+ expect(out).toContain("\"flex gap-2\",");
70
+ });
71
+ });
72
+ //#endregion
73
+ export {};
@@ -0,0 +1,2 @@
1
+ import { t as printAnalyzeReport } from "../../../report-DYxvw34T.mjs";
2
+ export { printAnalyzeReport };
@@ -0,0 +1,2 @@
1
+ import { a as mirrorConfigSchema, i as hookSchema, n as codefastConfigSchema, r as codefastTagConfigSchema, t as codefastArrangeConfigSchema } from "../../../schema-Or8RAARd.mjs";
2
+ export { codefastArrangeConfigSchema, codefastConfigSchema, codefastTagConfigSchema, hookSchema, mirrorConfigSchema };
@@ -0,0 +1,4 @@
1
+ import { a as mirrorConfigSchema, i as hookSchema, n as codefastConfigSchema, r as codefastTagConfigSchema, t as codefastArrangeConfigSchema } from "../../schema-Or8RAARd.mjs";
2
+ import { n as resetConfigLoaderCacheForTests, t as loadConfig } from "../../loader-DpN-zl5g.mjs";
3
+ import "../../config-DVia_f9a.mjs";
4
+ export { codefastArrangeConfigSchema, codefastConfigSchema, codefastTagConfigSchema, hookSchema, loadConfig, mirrorConfigSchema, resetConfigLoaderCacheForTests };
@@ -0,0 +1,2 @@
1
+ import { n as resetConfigLoaderCacheForTests, t as loadConfig } from "../../../loader-DpN-zl5g.mjs";
2
+ export { loadConfig, resetConfigLoaderCacheForTests };
@@ -0,0 +1,116 @@
1
+ import { n as resetConfigLoaderCacheForTests, t as loadConfig } from "../../../loader-DpN-zl5g.mjs";
2
+ import { t as createNodeCliFs } from "../../../node-io-DFRbHO6m.mjs";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import os from "node:os";
6
+ //#region src/lib/config/infra/loader.test.ts
7
+ describe("loadConfig", () => {
8
+ const cliFs = createNodeCliFs();
9
+ async function withCwd(cwd, fn) {
10
+ const prev = process.cwd();
11
+ process.chdir(cwd);
12
+ resetConfigLoaderCacheForTests();
13
+ try {
14
+ return await fn();
15
+ } finally {
16
+ process.chdir(prev);
17
+ resetConfigLoaderCacheForTests();
18
+ }
19
+ }
20
+ it("loads mirror + lifecycle hooks from codefast.config.cjs", async () => {
21
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "codefast-config-hooks-"));
22
+ try {
23
+ fs.writeFileSync(path.join(root, "codefast.config.cjs"), [
24
+ "module.exports = {",
25
+ " mirror: { skipPackages: [\"@acme/private\"] },",
26
+ " tag: { onAfterWrite: async ({ files }) => {",
27
+ " if (files.length === 0) return;",
28
+ " } },",
29
+ " arrange: { onAfterWrite: ({ files }) => files.length },",
30
+ "};",
31
+ ""
32
+ ].join("\n"), "utf8");
33
+ await withCwd(root, async () => {
34
+ const { config, warnings } = await loadConfig(cliFs);
35
+ expect(warnings).toEqual([]);
36
+ expect(config.mirror).toEqual({ skipPackages: ["@acme/private"] });
37
+ expect(typeof config.tag?.onAfterWrite).toBe("function");
38
+ expect(typeof config.arrange?.onAfterWrite).toBe("function");
39
+ });
40
+ } finally {
41
+ fs.rmSync(root, {
42
+ recursive: true,
43
+ force: true
44
+ });
45
+ }
46
+ });
47
+ it("requires mirror config to be nested under the mirror key", async () => {
48
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "codefast-config-mirror-top-"));
49
+ try {
50
+ fs.writeFileSync(path.join(root, "codefast.config.json"), JSON.stringify({ skipPackages: ["@acme/legacy"] }), "utf8");
51
+ await withCwd(root, async () => {
52
+ await expect(loadConfig(cliFs)).rejects.toThrow("Invalid config schema");
53
+ await expect(loadConfig(cliFs)).rejects.toThrow("Unrecognized key: \"skipPackages\"");
54
+ });
55
+ } finally {
56
+ fs.rmSync(root, {
57
+ recursive: true,
58
+ force: true
59
+ });
60
+ }
61
+ });
62
+ it("throws schema validation errors for invalid hook values", async () => {
63
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "codefast-config-invalid-hook-"));
64
+ try {
65
+ fs.writeFileSync(path.join(root, "codefast.config.json"), JSON.stringify({
66
+ tag: { onAfterWrite: "not-a-function" },
67
+ arrange: { onAfterWrite: 123 }
68
+ }), "utf8");
69
+ await withCwd(root, async () => {
70
+ await expect(loadConfig(cliFs)).rejects.toThrow("Invalid config schema");
71
+ await expect(loadConfig(cliFs)).rejects.toThrow("tag.onAfterWrite");
72
+ });
73
+ } finally {
74
+ fs.rmSync(root, {
75
+ recursive: true,
76
+ force: true
77
+ });
78
+ }
79
+ });
80
+ it("resolves config with upward search from process.cwd()", async () => {
81
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "codefast-config-upward-"));
82
+ const nested = path.join(root, "packages", "ui", "src");
83
+ try {
84
+ fs.mkdirSync(nested, { recursive: true });
85
+ fs.writeFileSync(path.join(root, "codefast.config.json"), JSON.stringify({ mirror: { skipPackages: ["@acme/infra"] } }), "utf8");
86
+ await withCwd(nested, async () => {
87
+ const { config } = await loadConfig(cliFs);
88
+ expect(config.mirror).toEqual({ skipPackages: ["@acme/infra"] });
89
+ });
90
+ } finally {
91
+ fs.rmSync(root, {
92
+ recursive: true,
93
+ force: true
94
+ });
95
+ }
96
+ });
97
+ it("uses singleton cache within one process", async () => {
98
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "codefast-config-cache-"));
99
+ try {
100
+ fs.writeFileSync(path.join(root, "codefast.config.json"), JSON.stringify({ mirror: { skipPackages: ["@acme/one"] } }), "utf8");
101
+ await withCwd(root, async () => {
102
+ const first = await loadConfig(cliFs);
103
+ fs.writeFileSync(path.join(root, "codefast.config.json"), JSON.stringify({ mirror: { skipPackages: ["@acme/two"] } }), "utf8");
104
+ const second = await loadConfig(cliFs);
105
+ expect(second.config.mirror).toEqual(first.config.mirror);
106
+ });
107
+ } finally {
108
+ fs.rmSync(root, {
109
+ recursive: true,
110
+ force: true
111
+ });
112
+ }
113
+ });
114
+ });
115
+ //#endregion
116
+ export {};
@@ -0,0 +1,2 @@
1
+ import { t as messageFromCaughtUnknown } from "../../caught-unknown-message-CRhkDWO7.mjs";
2
+ export { messageFromCaughtUnknown };
@@ -0,0 +1,2 @@
1
+ import { t as printConfigSchemaWarnings } from "../../config-reporter-SCcWDZc1.mjs";
2
+ export { printConfigSchemaWarnings };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { n as createNodeCliLogger, t as createNodeCliFs } from "../../node-io-DFRbHO6m.mjs";
2
+ export { createNodeCliFs, createNodeCliLogger };
@@ -0,0 +1,2 @@
1
+ import { t as findRepoRoot } from "../../../repo-root-HmtIEm6J.mjs";
2
+ export { findRepoRoot };
@@ -0,0 +1,45 @@
1
+ import { t as findRepoRoot } from "../../../repo-root-HmtIEm6J.mjs";
2
+ import path from "node:path";
3
+ //#region src/lib/infra/workspace/repo-root.test.ts
4
+ function makeFs(existsSync) {
5
+ return {
6
+ existsSync,
7
+ statSync: () => ({
8
+ isDirectory: () => true,
9
+ isFile: () => false
10
+ }),
11
+ readFileSync: () => "",
12
+ writeFileSync: () => void 0,
13
+ readdirSync: () => [],
14
+ readFile: async () => "",
15
+ writeFile: async () => void 0,
16
+ readdir: async () => [],
17
+ rename: async () => void 0,
18
+ unlink: async () => void 0
19
+ };
20
+ }
21
+ describe("findRepoRoot", () => {
22
+ it("returns the nearest ancestor containing pnpm-workspace.yaml", () => {
23
+ const root = "/tmp/repo";
24
+ const marker = path.join(root, "pnpm-workspace.yaml");
25
+ const cwdSpy = jest.spyOn(process, "cwd").mockReturnValue("/tmp/repo/packages/cli");
26
+ try {
27
+ const fs = makeFs((targetPath) => targetPath === marker);
28
+ expect(findRepoRoot(fs)).toBe(root);
29
+ } finally {
30
+ cwdSpy.mockRestore();
31
+ }
32
+ });
33
+ it("includes searched candidate paths when root cannot be resolved", () => {
34
+ const cwdSpy = jest.spyOn(process, "cwd").mockReturnValue("/tmp/no-repo");
35
+ try {
36
+ const fs = makeFs(() => false);
37
+ expect(() => findRepoRoot(fs)).toThrow(/Searched from:/);
38
+ expect(() => findRepoRoot(fs)).toThrow(/\/tmp\/no-repo/);
39
+ } finally {
40
+ cwdSpy.mockRestore();
41
+ }
42
+ });
43
+ });
44
+ //#endregion
45
+ export {};
@@ -0,0 +1,2 @@
1
+ import { n as generateExports, t as createPathTransform } from "../../../engine-DRTdkc5u.mjs";
2
+ export { createPathTransform, generateExports };
@@ -0,0 +1,2 @@
1
+ import { t as runMirrorSync } from "../../../sync-e1Sp-shi.mjs";
2
+ export { runMirrorSync };