@codefast/cli 0.9.0 → 0.10.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 (159) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +329 -149
  3. package/dist/arrange/analyze.d.ts +10 -0
  4. package/dist/arrange/cli-schema.d.ts +50 -0
  5. package/dist/arrange/command.d.ts +7 -0
  6. package/dist/arrange/domain/analyze-service.d.ts +18 -0
  7. package/dist/arrange/domain/ast/ast-node.d.ts +394 -0
  8. package/dist/arrange/domain/ast/collectors-cn.d.ts +26 -0
  9. package/dist/arrange/domain/ast/collectors-jsx.d.ts +8 -0
  10. package/dist/arrange/domain/ast/collectors-tv.d.ts +34 -0
  11. package/dist/arrange/domain/ast/helpers.d.ts +36 -0
  12. package/dist/arrange/domain/ast/helpers.js +1 -0
  13. package/dist/arrange/domain/ast/simplify-targets.d.ts +22 -0
  14. package/dist/arrange/domain/ast/targets.d.ts +20 -0
  15. package/dist/arrange/domain/constants.d.ts +111 -0
  16. package/dist/arrange/domain/grouping-service.d.ts +100 -0
  17. package/dist/arrange/domain/grouping.d.ts +21 -0
  18. package/dist/arrange/domain/imports.d.ts +14 -0
  19. package/dist/arrange/domain/source-text-formatters.d.ts +33 -0
  20. package/dist/arrange/domain/tailwind-token.d.ts +24 -0
  21. package/dist/arrange/domain/token-classifier.d.ts +47 -0
  22. package/dist/arrange/domain/types.d.ts +208 -0
  23. package/dist/arrange/output.d.ts +26 -0
  24. package/dist/arrange/process-file.d.ts +11 -0
  25. package/dist/arrange/resolve-target.d.ts +10 -0
  26. package/dist/arrange/resolve-target.js +3 -16
  27. package/dist/arrange/scan-target.d.ts +7 -0
  28. package/dist/arrange/simplify-process-file.d.ts +11 -0
  29. package/dist/arrange/simplify-sync.d.ts +13 -0
  30. package/dist/arrange/source-parse.d.ts +7 -0
  31. package/dist/arrange/suggest.d.ts +8 -0
  32. package/dist/arrange/sync.d.ts +11 -0
  33. package/dist/arrange/typescript-ast-translator.d.ts +31 -0
  34. package/dist/arrange/workspace.d.ts +13 -0
  35. package/dist/arrange/workspace.js +2 -2
  36. package/dist/audit/cli-schema.d.ts +93 -0
  37. package/dist/audit/cli-schema.js +3 -3
  38. package/dist/audit/command.d.ts +8 -0
  39. package/dist/audit/command.js +12 -12
  40. package/dist/audit/domain/audit-file.d.ts +7 -0
  41. package/dist/audit/domain/comment-content.d.ts +26 -0
  42. package/dist/audit/domain/comment-dividers.d.ts +62 -0
  43. package/dist/audit/domain/display-names.d.ts +11 -0
  44. package/dist/audit/domain/import-policy.d.ts +34 -0
  45. package/dist/audit/domain/import-policy.js +147 -0
  46. package/dist/audit/domain/link-references.d.ts +40 -0
  47. package/dist/audit/domain/mappings.d.ts +45 -0
  48. package/dist/audit/domain/markdown-links.d.ts +44 -0
  49. package/dist/audit/domain/since-versions.d.ts +26 -0
  50. package/dist/audit/domain/tokenize.d.ts +14 -0
  51. package/dist/audit/domain/tsdoc-syntax.d.ts +20 -0
  52. package/dist/audit/domain/types.d.ts +171 -0
  53. package/dist/audit/output.d.ts +91 -0
  54. package/dist/audit/output.js +11 -11
  55. package/dist/audit/prepare.d.ts +70 -0
  56. package/dist/audit/prepare.js +11 -11
  57. package/dist/audit/run-comments.d.ts +17 -0
  58. package/dist/audit/run-display-names.d.ts +14 -0
  59. package/dist/audit/run-imports.d.ts +14 -0
  60. package/dist/audit/{run-react.js → run-imports.js} +15 -5
  61. package/dist/audit/run-links.d.ts +14 -0
  62. package/dist/audit/run.d.ts +14 -0
  63. package/dist/bin.d.ts +2 -0
  64. package/dist/cli.d.ts +6 -0
  65. package/dist/core/cli/format-error.d.ts +7 -0
  66. package/dist/core/cli/global-options.d.ts +15 -0
  67. package/dist/core/cli/positional.d.ts +6 -0
  68. package/dist/core/cli/result-handle.d.ts +19 -0
  69. package/dist/core/config/define-config.d.ts +7 -0
  70. package/dist/core/config/define-config.js +8 -0
  71. package/dist/core/config/loader.d.ts +18 -0
  72. package/dist/core/config/loader.js +2 -7
  73. package/dist/core/config/schema.d.ts +99 -0
  74. package/dist/core/config/schema.js +7 -85
  75. package/dist/core/config/warnings.d.ts +6 -0
  76. package/dist/core/config.d.ts +12 -0
  77. package/dist/core/errors.d.ts +25 -0
  78. package/dist/core/exit-codes.d.ts +18 -0
  79. package/dist/core/filesystem/node.d.ts +7 -0
  80. package/dist/core/filesystem/node.js +1 -0
  81. package/dist/core/filesystem/port.d.ts +44 -0
  82. package/dist/core/glob.d.ts +19 -0
  83. package/dist/core/logger.d.ts +9 -0
  84. package/dist/core/result.d.ts +30 -0
  85. package/dist/core/schema-parse.d.ts +9 -0
  86. package/dist/core/source-text-edit.d.ts +33 -0
  87. package/dist/core/verbose-diagnostics.d.ts +6 -0
  88. package/dist/core/workspace/ancestor-directories.d.ts +12 -0
  89. package/dist/core/workspace/ancestor-directories.js +30 -0
  90. package/dist/core/workspace/markdown-walk.d.ts +7 -0
  91. package/dist/core/workspace/markdown-walk.js +2 -20
  92. package/dist/core/workspace/package-version.d.ts +9 -0
  93. package/dist/core/workspace/package-version.js +8 -12
  94. package/dist/core/workspace/resolver.d.ts +39 -0
  95. package/dist/core/workspace/resolver.js +58 -75
  96. package/dist/core/workspace/skip-directories.d.ts +6 -0
  97. package/dist/core/workspace/source-walk.d.ts +16 -0
  98. package/dist/core/workspace/source-walk.js +2 -19
  99. package/dist/core/workspace/typescript-walk.d.ts +7 -0
  100. package/dist/core/workspace/typescript-walk.js +2 -23
  101. package/dist/core/workspace/walk-files.d.ts +7 -0
  102. package/dist/core/workspace/walk-files.js +27 -0
  103. package/dist/core/workspace/well-known-files.d.ts +18 -0
  104. package/dist/core/workspace/well-known-files.js +18 -0
  105. package/dist/index.d.ts +6 -0
  106. package/dist/index.js +5 -0
  107. package/dist/mirror/cli-result.d.ts +13 -0
  108. package/dist/mirror/cli-schema.d.ts +8 -0
  109. package/dist/mirror/command.d.ts +7 -0
  110. package/dist/mirror/dist-filesystem-impl.d.ts +8 -0
  111. package/dist/mirror/domain/constants.d.ts +18 -0
  112. package/dist/mirror/domain/constants.js +0 -12
  113. package/dist/mirror/domain/dirent-guard.d.ts +10 -0
  114. package/dist/mirror/domain/dist-filesystem.d.ts +9 -0
  115. package/dist/mirror/domain/errors.d.ts +24 -0
  116. package/dist/mirror/domain/exports.d.ts +36 -0
  117. package/dist/mirror/domain/package-display-name.d.ts +8 -0
  118. package/dist/mirror/domain/path-normalizer.d.ts +6 -0
  119. package/dist/mirror/domain/types.d.ts +131 -0
  120. package/dist/mirror/output.d.ts +23 -0
  121. package/dist/mirror/package-path.d.ts +19 -0
  122. package/dist/mirror/prepare.d.ts +15 -0
  123. package/dist/mirror/prepare.js +2 -2
  124. package/dist/mirror/supplement-exports.d.ts +27 -0
  125. package/dist/mirror/supplement-exports.js +2 -2
  126. package/dist/mirror/sync-reporter.d.ts +60 -0
  127. package/dist/mirror/sync-reporter.js +4 -0
  128. package/dist/mirror/sync-types.d.ts +43 -0
  129. package/dist/mirror/sync-workspace-package.d.ts +9 -0
  130. package/dist/mirror/sync-workspace-package.js +3 -3
  131. package/dist/mirror/sync.d.ts +12 -0
  132. package/dist/mirror/sync.js +5 -3
  133. package/dist/mirror/write-exports.d.ts +15 -0
  134. package/dist/pack-slim/cli-result.d.ts +13 -0
  135. package/dist/pack-slim/cli-schema.d.ts +17 -0
  136. package/dist/pack-slim/command.d.ts +7 -0
  137. package/dist/pack-slim/command.js +2 -2
  138. package/dist/pack-slim/domain/transform.d.ts +69 -0
  139. package/dist/pack-slim/domain/types.d.ts +46 -0
  140. package/dist/pack-slim/output.d.ts +15 -0
  141. package/dist/pack-slim/sync.d.ts +23 -0
  142. package/dist/pack-slim/sync.js +4 -4
  143. package/dist/pack-slim/working-tree.d.ts +20 -0
  144. package/dist/tag/cli-result.d.ts +7 -0
  145. package/dist/tag/cli-schema.d.ts +8 -0
  146. package/dist/tag/command.d.ts +7 -0
  147. package/dist/tag/domain/types.d.ts +111 -0
  148. package/dist/tag/output.d.ts +17 -0
  149. package/dist/tag/prepare.d.ts +13 -0
  150. package/dist/tag/prepare.js +2 -2
  151. package/dist/tag/resolve-target-path.d.ts +10 -0
  152. package/dist/tag/since-writer.d.ts +32 -0
  153. package/dist/tag/sync.d.ts +42 -0
  154. package/dist/tag/target-candidates.d.ts +8 -0
  155. package/dist/tag/target-candidates.js +1 -1
  156. package/dist/tag/target-runner.d.ts +8 -0
  157. package/dist/tag/version-resolver.d.ts +7 -0
  158. package/package.json +12 -1
  159. package/dist/audit/domain/react-imports.js +0 -91
@@ -0,0 +1,111 @@
1
+ import type { Bucket } from "#/arrange/domain/types";
2
+ /**
3
+ * Analyze report: long literal threshold (token count).
4
+ *
5
+ * @since 0.3.16-canary.0
6
+ */
7
+ export declare const LONG_STRING_TOKEN_THRESHOLD = 18;
8
+ /**
9
+ * Minimum token count for a string to be considered a candidate for grouping
10
+ * in the apply/preview pipeline. Intentionally much lower than
11
+ * {@link LONG_STRING_TOKEN_THRESHOLD} (analyze only).
12
+ *
13
+ * @since 0.3.16-canary.0
14
+ */
15
+ export declare const APPLY_MIN_TOKENS = 2;
16
+ /**
17
+ * Minimum tokens a group must have to stand alone before singleton-merging.
18
+ * Set to 2 so single-token groups are not collapsed into unrelated buckets by the merger.
19
+ *
20
+ * @since 0.3.16-canary.0
21
+ */
22
+ export declare const MIN_GROUP_TOKENS = 2;
23
+ /**
24
+ * Dynamic max groups clamp: base bound.
25
+ *
26
+ * @since 0.3.16-canary.0
27
+ */
28
+ export declare const MAX_GROUPS_BASE = 4;
29
+ /**
30
+ * Dynamic max groups clamp: upper cap.
31
+ *
32
+ * @since 0.3.16-canary.0
33
+ */
34
+ export declare const MAX_GROUPS_CAP = 24;
35
+ /**
36
+ * Extra slots so a few state / aria groups do not force `capGroups` to merge
37
+ * unrelated buckets (e.g. `bg-border` + `outline-hidden`).
38
+ *
39
+ * @since 0.3.16-canary.0
40
+ */
41
+ export declare const MAX_GROUPS_HEADROOM = 2;
42
+ /**
43
+ * Maximum findings printed per category in the analyze report.
44
+ *
45
+ * @since 0.3.16-canary.0
46
+ */
47
+ export declare const MAX_REPORT_LINES = 40;
48
+ /**
49
+ * Maximum recursion depth when traversing `tv()` object literals.
50
+ *
51
+ * @since 0.3.16-canary.0
52
+ */
53
+ export declare const MAX_OBJECT_DEPTH = 12;
54
+ /**
55
+ * Maximum depth when peeling conditional / parens / arrays inside `cn(...)` args.
56
+ *
57
+ * @since 0.3.16-canary.0
58
+ */
59
+ export declare const MAX_CLASS_EXPR_DEPTH = 12;
60
+ /**
61
+ * Maximum variant-stripping passes in {@link stripVariants}.
62
+ * Real-world Tailwind stacks rarely exceed 4–5 segments (e.g. `@md/sidebar:group-hover:dark:focus-visible:`);
63
+ * 12 is a conservative safety cap that prevents runaway loops on malformed input.
64
+ *
65
+ * @since 0.3.16-canary.0
66
+ */
67
+ export declare const MAX_STRIP_VARIANT_PASSES = 12;
68
+ /**
69
+ * Passed when optional `knownBindings` is missing — size 0 disables matching
70
+ * for `cn` / `tv` identifier resolution.
71
+ *
72
+ * @since 0.3.16-canary.0
73
+ */
74
+ export declare const EMPTY_CN_TV_BINDINGS: Set<string>;
75
+ /**
76
+ * Bucket sort order — **render pipeline** (lower → earlier in `cn()` output).
77
+ *
78
+ * Matches README: Existence → Position → Layout → Sizing → Spacing → Shape → Background
79
+ * → Shadow → Typography → Composite → Motion → Starting → Behavior → State → Selector,
80
+ * then `other` and `arbitrary` as sort tails for unknown utilities and arbitrary properties.
81
+ *
82
+ * @since 0.3.16-canary.0
83
+ */
84
+ export declare const BUCKET_ORDER: Record<Bucket, number>;
85
+ /**
86
+ * Compatible bucket **pairs** — two adjacent tokens whose buckets appear in the same
87
+ * `Set` may stay in one `cn()` string chunk. Compatibility is **not transitive**: there is
88
+ * no `{position, sizing}` pair, so `fixed inset-0` and `overflow-auto` become **separate**
89
+ * literals unless **layout** tokens sit between them in sorted order; then
90
+ * `position↔layout` and `layout↔sizing` each justify one hop and the whole “box in flow”
91
+ * (place + tracks + scrollport) stays in one chunk — co-located because they jointly define
92
+ * stacking and scroll behavior for the same surface.
93
+ *
94
+ * @since 0.3.16-canary.0
95
+ */
96
+ export declare const COMPATIBLE_BUCKET_SETS: ReadonlyArray<ReadonlySet<Bucket>>;
97
+ /**
98
+ * Responsive / variant prefix — Tailwind v4 aware.
99
+ *
100
+ * v3: sm: md: … — v4: `@sm:`, `@min-[600px]:`, `@[480px]:`, named `@md/sidebar:`,
101
+ * viewport md/sidebar:, min-[100px]: / max-[100px]:, …
102
+ *
103
+ * @since 0.3.16-canary.0
104
+ */
105
+ export declare const RESPONSIVE_PREFIX: RegExp;
106
+ /**
107
+ * State variant stems — hoisted to module scope (not recreated per call).
108
+ *
109
+ * @since 0.3.16-canary.0
110
+ */
111
+ export declare const STATE_PREFIXES: ReadonlySet<string>;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Rich domain: plan Tailwind class grouping / cn-in-tv unwrap for a single file.
3
+ * Pure: only DomainSourceFile, source strings, and options — no I/O.
4
+ */
5
+ import type { DomainCallExpression, DomainSourceFile } from "#/arrange/domain/ast/ast-node";
6
+ import type { GroupFileResult, PlannedGroupEdit } from "#/arrange/domain/types";
7
+ /**
8
+ * A planned replacement unwrapping one `cn()` call nested in `tv({ ... })`.
9
+ *
10
+ * @since 0.3.16-canary.0
11
+ */
12
+ export type GroupFileUnwrapPlan = {
13
+ readonly start: number;
14
+ readonly end: number;
15
+ readonly replacement: string;
16
+ readonly call: DomainCallExpression;
17
+ };
18
+ type GroupFileUnwrapState = {
19
+ readonly cnInTvCalls: ReadonlyArray<DomainCallExpression>;
20
+ readonly unwrapReplacementByCall: ReadonlyMap<DomainCallExpression, string>;
21
+ readonly unwrapEdits: ReadonlyArray<GroupFileUnwrapPlan>;
22
+ readonly textAfterUnwrap: string;
23
+ readonly cnInTvNoReplacement: number;
24
+ };
25
+ /**
26
+ * The complete per-file plan of unwrap and grouping edits, plus the totals reported for them.
27
+ *
28
+ * @since 0.3.16-canary.0
29
+ */
30
+ export type GroupFileWorkPlan = {
31
+ readonly filePath: string;
32
+ readonly sourceText: string;
33
+ readonly textAfterUnwrap: string;
34
+ readonly domainSfForLineNumbers: DomainSourceFile;
35
+ readonly domainSfGrouped: DomainSourceFile;
36
+ readonly cnInTvCalls: ReadonlyArray<DomainCallExpression>;
37
+ readonly unwrapReplacementByCall: ReadonlyMap<DomainCallExpression, string>;
38
+ readonly unwrapEdits: ReadonlyArray<GroupFileUnwrapPlan>;
39
+ readonly plannedGroupEdits: ReadonlyArray<PlannedGroupEdit>;
40
+ readonly cnInTvNoReplacement: number;
41
+ readonly reportTotal: number;
42
+ readonly editSitesCount: number;
43
+ };
44
+ /**
45
+ * Builds the unwrap phase's state — cn-in-tv calls, their replacements, and the text after unwrapping.
46
+ *
47
+ * @since 0.3.16-canary.0
48
+ */
49
+ export declare function buildGroupFileUnwrapState(domainSfInitial: DomainSourceFile, sourceText: string): GroupFileUnwrapState;
50
+ /**
51
+ * `domainSfGrouped` must be a parse of `unwrap.textAfterUnwrap` (same `text` as source).
52
+ * Returns `null` when there is no cn-in-tv and no grouping targets.
53
+ *
54
+ * @since 0.3.16-canary.0
55
+ */
56
+ export declare function tryBuildGroupFileWorkPlan(input: {
57
+ readonly filePath: string;
58
+ readonly sourceText: string;
59
+ readonly domainSfInitial: DomainSourceFile;
60
+ readonly domainSfGrouped: DomainSourceFile;
61
+ readonly withClassName: boolean;
62
+ readonly unwrap: GroupFileUnwrapState;
63
+ }): GroupFileWorkPlan | null;
64
+ /**
65
+ * Creates the empty per-file result for a dry run with nothing to edit.
66
+ *
67
+ * @since 0.3.16-canary.0
68
+ */
69
+ export declare function groupFileDryRunNoEdits(filePath: string): GroupFileResult;
70
+ /**
71
+ * Derives the preview-mode per-file result from a work plan's report total.
72
+ *
73
+ * @since 0.3.16-canary.0
74
+ */
75
+ export declare function groupFilePreviewTotals(work: GroupFileWorkPlan): GroupFileResult;
76
+ /**
77
+ * Applies a work plan's grouping edits to the unwrapped text and returns the merged file body.
78
+ *
79
+ * @since 0.3.16-canary.0
80
+ */
81
+ export declare function mergeGroupFileBodyText(work: GroupFileWorkPlan): string;
82
+ /**
83
+ * Checks whether any planned edit rewrites a JSX `className` into a `cn()` call.
84
+ *
85
+ * @since 0.3.16-canary.0
86
+ */
87
+ export declare function groupFileEditsTouchJsxCn(work: GroupFileWorkPlan): boolean;
88
+ /**
89
+ * Counts the edits a work plan writes in apply mode — unwraps plus grouping replacements.
90
+ *
91
+ * @since 0.3.16-canary.0
92
+ */
93
+ export declare function countPersistedGroupFileEdits(work: GroupFileWorkPlan): number;
94
+ /**
95
+ * Checks whether a work plan has no edit sites and no skipped cn-in-tv calls to report.
96
+ *
97
+ * @since 0.3.16-canary.0
98
+ */
99
+ export declare function groupFileWorkHasNothingToReport(work: GroupFileWorkPlan): boolean;
100
+ export {};
@@ -0,0 +1,21 @@
1
+ /**
2
+ * True when `staticLiteralTexts` carries the **same partition** of Tailwind tokens as
3
+ * `suggestedGroups` from {@link suggestCnGroups}, ignoring order of arguments and
4
+ * order of tokens within each chunk.
5
+ *
6
+ * @since 0.3.16-canary.0
7
+ */
8
+ export declare function areCnTailwindPartitionsEquivalent(staticLiteralTexts: Array<string>, suggestedGroups: Array<string>): boolean;
9
+ /**
10
+ * Partitions a class string into render-pipeline-ordered groups, one per suggested `cn()` argument.
11
+ *
12
+ * @since 0.3.16-canary.0
13
+ */
14
+ export declare function suggestCnGroups(classString: string): Array<string>;
15
+ /**
16
+ * One label per suggested group: a single bucket name, or `mixed:a+b` when that chunk
17
+ * spans multiple Tailwind buckets (matches `codefast arrange group` stdout).
18
+ *
19
+ * @since 0.3.16-canary.0
20
+ */
21
+ export declare function summarizeGroupBucketLabels(groups: Array<string>): Array<string>;
@@ -0,0 +1,14 @@
1
+ import type { DomainSourceFile } from "#/arrange/domain/ast/ast-node";
2
+ /**
3
+ * After simplify rewrites, drop the `cn` import specifier when `cn` is no
4
+ * longer referenced anywhere outside the import declarations.
5
+ *
6
+ * @since 0.3.16-canary.0
7
+ */
8
+ export declare function dropCnImportIfUnused(sourceFile: DomainSourceFile): string;
9
+ /**
10
+ * Returns the source text with a `cn` import present, adding one when the file lacks it.
11
+ *
12
+ * @since 0.3.16-canary.0
13
+ */
14
+ export declare function ensureCnImport(sourceFile: DomainSourceFile, cnImportOverride?: string): string;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Escapes backslashes and double quotes for embedding in a double-quoted TypeScript string literal.
3
+ *
4
+ * @since 0.3.16-canary.0
5
+ */
6
+ export declare function escapeTsStringLiteralContent(group: string): string;
7
+ /**
8
+ * Formats grouped class strings as a multiline `cn(...)` call.
9
+ *
10
+ * @since 0.3.16-canary.0
11
+ */
12
+ export declare function formatCnCall(groups: Array<string>, options?: {
13
+ trailingClassName?: boolean;
14
+ }): string;
15
+ /**
16
+ * Formats grouped class strings as a multiline array literal.
17
+ *
18
+ * @since 0.3.16-canary.0
19
+ */
20
+ export declare function formatArray(groups: Array<string>): string;
21
+ /**
22
+ * Multiple string literals as sequential lines for insertion **inside** an existing array
23
+ * (e.g. `tv({ base: [ … ] })` — avoids `[[ "a", "b" ]]` when replacing one long string).
24
+ *
25
+ * @since 0.3.16-canary.0
26
+ */
27
+ export declare function formatArrayElementsAsSiblingLines(groups: Array<string>, continuationPrefix: string): string;
28
+ /**
29
+ * Formats grouped class strings as a JSX attribute value wrapping a multiline `cn(...)` call.
30
+ *
31
+ * @since 0.3.16-canary.0
32
+ */
33
+ export declare function formatJsxCnAttributeValue(groups: Array<string>, source: string, valueNodeStart: number): string;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Splits a class string into its whitespace-separated tokens.
3
+ *
4
+ * @since 0.3.16-canary.0
5
+ */
6
+ export declare function tokenizeClassString(classString: string): Array<string>;
7
+ /**
8
+ * Index of the first `:` that separates a Tailwind variant segment from the rest.
9
+ * Colons inside `[...]` (at positive bracket depth) are ignored so selectors like
10
+ * `[&_a:hover]:text-red-500` split as `[&_a:hover]:` + `text-red-500`.
11
+ *
12
+ * @since 0.3.16-canary.0
13
+ */
14
+ export declare function indexOfFirstVariantColon(text: string): number;
15
+ /**
16
+ * Strips every variant prefix off a token, leaving the bare utility name.
17
+ *
18
+ * @remarks
19
+ * `"hover:dark:md:text-sm"` → `"text-sm"`, `"@min-[600px]:flex"` → `"flex"`,
20
+ * `"[&_a:hover]:text-red-500"` → `"text-red-500"`.
21
+ *
22
+ * @since 0.3.16-canary.0
23
+ */
24
+ export declare function stripVariants(token: string): string;
@@ -0,0 +1,47 @@
1
+ import type { Bucket } from "#/arrange/domain/types";
2
+ /**
3
+ * Secondary sort inside the **composite** bucket: opacity / blend / isolation →
4
+ * 3D context → 3D transforms → 2D transforms → filters → will-change.
5
+ *
6
+ * @since 0.3.16-canary.0
7
+ */
8
+ export declare function compositeSecondaryOrder(bareUtility: string): number;
9
+ /**
10
+ * Classifies a Tailwind token into its render-pipeline bucket.
11
+ *
12
+ * @since 0.3.16-canary.0
13
+ */
14
+ export declare function classifyToken(token: string): Bucket;
15
+ /**
16
+ * Stable key for splitting adjacent `state` / `starting` tokens in {@link suggestCnGroups}
17
+ * (`selector` uses {@link selectorKey}).
18
+ * Uses the **full variant stack** (every `:` segment outside `[…]`), not only the
19
+ * outermost prefix, so `@md/foo:[&>*]:w-auto` and `@md/foo:has-[…]:mt-px` stay in
20
+ * separate groups while `hover:opacity` still keys as `hover` + `opacity`…
21
+ *
22
+ * `data-[…]` / `aria-[…]` normalize the first segment via {@link dataAttributeStem} /
23
+ * {@link ariaAttributeStem} (full token required for bracket capture).
24
+ *
25
+ * @since 0.3.16-canary.0
26
+ */
27
+ export declare function stateKey(token: string): string;
28
+ /**
29
+ * Variant key for {@link Bucket} `"selector"` tokens in {@link suggestCnGroups}.
30
+ * Reuses {@link stateKey} layer splitting, then normalizes common Radix SVG patterns so
31
+ * `[&_svg]:…` and `[&_svg:not([class*='size-'])]:…` stay in one chunk.
32
+ *
33
+ * @since 0.3.16-canary.0
34
+ */
35
+ export declare function selectorKey(token: string): string;
36
+ /**
37
+ * Checks whether two buckets may share one group in a suggested partition.
38
+ *
39
+ * @since 0.3.16-canary.0
40
+ */
41
+ export declare function bucketsCompatible(a: Bucket, b: Bucket): boolean;
42
+ /**
43
+ * Like {@link bucketsCompatible}, but never merge two distinct state / starting / selector variant blobs.
44
+ *
45
+ * @since 0.3.16-canary.0
46
+ */
47
+ export declare function bucketsMergeCompatible(a: Bucket, b: Bucket): boolean;
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Shared types for the arrange pipeline (Tailwind `cn()` / `tv()` tooling).
3
+ *
4
+ * **Render Pipeline Order**: Existence → Position → Layout
5
+ * → Sizing → Spacing → Shape → Background → Shadow → Typography → Composite → Motion
6
+ * → Starting → Behavior → State → Selector.
7
+ *
8
+ * `arbitrary` and `other` are non-pipeline tails for `[prop:value]` syntax and unknown utilities.
9
+ */
10
+ import type { DomainAstNode, DomainCallExpression, DomainSourceFile, DomainTailwindClassLiteral } from "#/arrange/domain/ast/ast-node";
11
+ import type { GroupFileWorkPlan } from "#/arrange/domain/grouping-service";
12
+ import type { CodefastConfig } from "#/core/config/schema";
13
+ /**
14
+ * The render-pipeline bucket a Tailwind token classifies into.
15
+ *
16
+ * @since 0.3.16-canary.0
17
+ */
18
+ export type Bucket = "existence" | "position" | "layout" | "sizing" | "spacing" | "shape" | "background" | "shadow" | "typography" | "composite" | "motion" | "starting" | "behavior" | "state"
19
+ /**
20
+ * Selector variants (e.g. `[&…]:`, `*:`, `has-*`) distinct from interactive/data state.
21
+ */
22
+ | "selector" | "arbitrary" | "other";
23
+ /**
24
+ * String or no-substitution template literal used as a Tailwind class blob.
25
+ *
26
+ * @since 0.3.16-canary.0
27
+ */
28
+ export type TailwindClassLiteral = DomainTailwindClassLiteral;
29
+ /**
30
+ * Options controlling which literals a class-expression walk visits.
31
+ *
32
+ * @since 0.3.16-canary.0
33
+ */
34
+ export type ForEachStringLiteralInClassExpressionOptions = {
35
+ /**
36
+ * When `false`, do not visit literals inside `cond ? "a" : "b"`. Used when
37
+ * building the cn/tv **apply** pool so mutually exclusive branch classes are
38
+ * never merged into one static string (while `analyze` still uses the default).
39
+ */
40
+ descendIntoConditional?: boolean;
41
+ };
42
+ /**
43
+ * A static JSX `className` literal paired with the node to replace.
44
+ *
45
+ * @since 0.3.16-canary.0
46
+ */
47
+ export type JsxClassNameStatic = {
48
+ lit: TailwindClassLiteral;
49
+ /**
50
+ * Replace this node: `StringLiteral` or whole `JsxExpression`.
51
+ */
52
+ valueNode: DomainAstNode;
53
+ };
54
+ /**
55
+ * A StringNode represents a single "grouping slot" — the full set of static
56
+ * string literals that belong to one logical class surface.
57
+ *
58
+ * @since 0.3.16-canary.0
59
+ */
60
+ export type StringNode = {
61
+ /**
62
+ * All static string literals belonging to this slot, in source order.
63
+ */
64
+ nodes: Array<TailwindClassLiteral>;
65
+ sf: DomainSourceFile;
66
+ /**
67
+ * String slots in `tv({ ... })` that are not `cn(...)` arguments — use `formatArray` when
68
+ * replacing a whole array, or `formatArrayElementsAsSiblingLines` when replacing one element
69
+ * inside an existing array.
70
+ */
71
+ isTvContext: boolean;
72
+ /**
73
+ * When set, the entire cn(...) call is replaced at once.
74
+ */
75
+ cnCall?: DomainCallExpression | undefined;
76
+ /**
77
+ * First literal in the slot; used for positions and line-number reporting.
78
+ */
79
+ get primaryClassLiteral(): TailwindClassLiteral;
80
+ };
81
+ /**
82
+ * A groupable class surface — a `cn()` argument slot or a static JSX `className`.
83
+ *
84
+ * @since 0.3.16-canary.0
85
+ */
86
+ export type GroupTarget = {
87
+ kind: "cnArg";
88
+ item: StringNode;
89
+ } | {
90
+ kind: "jsxClassName";
91
+ sf: DomainSourceFile;
92
+ lit: TailwindClassLiteral;
93
+ valueNode: DomainAstNode;
94
+ };
95
+ /**
96
+ * A planned text replacement for one grouped class surface.
97
+ *
98
+ * @since 0.3.16-canary.0
99
+ */
100
+ export type PlannedGroupEdit = {
101
+ start: number;
102
+ end: number;
103
+ replacement: string;
104
+ /**
105
+ * Per-chunk bucket labels (same as `arrange group` trailing `// Buckets:` line).
106
+ */
107
+ bucketSummary: Array<string>;
108
+ jsxCn: boolean;
109
+ lineSf: DomainSourceFile;
110
+ reportNode: DomainAstNode;
111
+ label: string;
112
+ };
113
+ /**
114
+ * The accumulated findings of an `arrange analyze` run.
115
+ *
116
+ * @since 0.3.16-canary.0
117
+ */
118
+ export type AnalyzeReport = {
119
+ files: number;
120
+ cnCallExpressions: number;
121
+ tvCallExpressions: number;
122
+ cnInsideTvCalls: Array<{
123
+ file: string;
124
+ line: number;
125
+ argCount: number;
126
+ preview: string;
127
+ }>;
128
+ longCnStringLiterals: Array<{
129
+ file: string;
130
+ line: number;
131
+ tokenCount: number;
132
+ preview: string;
133
+ }>;
134
+ longTvStringLiterals: Array<{
135
+ file: string;
136
+ line: number;
137
+ tokenCount: number;
138
+ preview: string;
139
+ }>;
140
+ longJsxClassNameLiterals: Array<{
141
+ file: string;
142
+ line: number;
143
+ tokenCount: number;
144
+ preview: string;
145
+ }>;
146
+ };
147
+ /**
148
+ * The per-file outcome of a grouping pass.
149
+ *
150
+ * @since 0.3.16-canary.0
151
+ */
152
+ export type GroupFileResult = {
153
+ filePath: string;
154
+ /**
155
+ * Sites worth human review: applied edits plus `cn()` inside `tv` with no args
156
+ * (skipped). Matches preview totals when `groupEdits` correspond to the same plan.
157
+ */
158
+ totalFound: number;
159
+ /**
160
+ * Edits actually written in apply mode; always 0 in preview mode.
161
+ */
162
+ changed: number;
163
+ /** Populated only in preview mode (write=false) when edits exist. */
164
+ workPlan?: GroupFileWorkPlan;
165
+ };
166
+ /**
167
+ * The options a per-file grouping pass runs with.
168
+ *
169
+ * @since 0.3.16-canary.0
170
+ */
171
+ export type ArrangeGroupFileOptions = {
172
+ write: boolean;
173
+ withClassName: boolean;
174
+ cnImport?: string | undefined;
175
+ };
176
+ /**
177
+ * The aggregate outcome of an `arrange` run across files.
178
+ *
179
+ * @since 0.3.16-canary.0
180
+ */
181
+ export type ArrangeRunResult = {
182
+ filePaths: Array<string>;
183
+ modifiedFiles: Array<string>;
184
+ totalFound: number;
185
+ totalChanged: number;
186
+ hookError: string | null;
187
+ /** Populated in preview mode (write=false); empty in apply mode. */
188
+ previewPlans: Array<GroupFileWorkPlan>;
189
+ };
190
+ /**
191
+ * The formatted lines `arrange group` prints for a suggested grouping.
192
+ *
193
+ * @since 0.3.16-canary.0
194
+ */
195
+ export type ArrangeSuggestGroupsOutput = {
196
+ readonly primaryLine: string;
197
+ readonly bucketsCommentLine: string;
198
+ };
199
+ /**
200
+ * The resolved target, workspace root, and loaded config an arrange run starts from.
201
+ *
202
+ * @since 0.3.16-canary.0
203
+ */
204
+ export type ArrangeTargetWorkspaceAndConfig = {
205
+ readonly resolvedTarget: string;
206
+ readonly rootDir: string;
207
+ readonly config: CodefastConfig;
208
+ };
@@ -0,0 +1,26 @@
1
+ import type { GroupFileWorkPlan } from "#/arrange/domain/grouping-service";
2
+ import type { AnalyzeReport, ArrangeRunResult } from "#/arrange/domain/types";
3
+ /**
4
+ * Prints the human-readable summary of an `arrange analyze` report.
5
+ *
6
+ * @since 0.3.16-canary.0
7
+ */
8
+ export declare function printAnalyzeReport(resolvedTargetPath: string, report: AnalyzeReport): void;
9
+ /**
10
+ * Prints the totals and follow-up hints for an `arrange` run.
11
+ *
12
+ * @since 0.3.16-canary.0
13
+ */
14
+ export declare function printSyncResult(result: ArrangeRunResult, write: boolean): void;
15
+ /**
16
+ * Prints the totals for an `arrange simplify` run.
17
+ *
18
+ * @since 0.3.16-canary.0
19
+ */
20
+ export declare function printSimplifyResult(result: ArrangeRunResult, write: boolean): void;
21
+ /**
22
+ * Prints the per-file preview of a work plan's unwrap and grouping edits.
23
+ *
24
+ * @since 0.3.16-canary.0
25
+ */
26
+ export declare function printGroupFilePreviewFromWork(work: GroupFileWorkPlan): void;
@@ -0,0 +1,11 @@
1
+ import type { ArrangeGroupFileOptions, GroupFileResult } from "#/arrange/domain/types";
2
+ import type { FilesystemPort } from "#/core/filesystem/port";
3
+ /**
4
+ * Runs the grouping pipeline on one file — preview or write — and returns its per-file result.
5
+ *
6
+ * @since 0.3.16-canary.0
7
+ */
8
+ export declare function processArrangeGroupFile(fs: FilesystemPort, args: {
9
+ readonly filePath: string;
10
+ readonly options: ArrangeGroupFileOptions;
11
+ }): GroupFileResult;
@@ -0,0 +1,10 @@
1
+ import type { FilesystemPort } from "#/core/filesystem/port";
2
+ /**
3
+ * Resolves the arrange target to a canonical path, defaulting to the nearest package directory.
4
+ *
5
+ * @since 0.3.16-canary.0
6
+ */
7
+ export declare function resolveArrangeTargetPath(fs: FilesystemPort, args: {
8
+ readonly currentWorkingDirectory: string;
9
+ readonly rawTarget: string | undefined;
10
+ }): string;
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
- const packageJsonFileName = "package.json";
2
+ import { findNearestAncestor } from "#/core/workspace/ancestor-directories";
3
+ import { packageJsonFileName } from "#/core/workspace/well-known-files";
3
4
  /**
4
5
  * Resolves the arrange target to a canonical path, defaulting to the nearest package directory.
5
6
  *
@@ -14,21 +15,7 @@ export function resolveArrangeTargetPath(fs, args) {
14
15
  if (explicitTargetPath) {
15
16
  return fs.canonicalPathSync(explicitTargetPath);
16
17
  }
17
- const nearestPackageDirectory = findNearestPackageDirectory(fs, args.currentWorkingDirectory);
18
+ const nearestPackageDirectory = findNearestAncestor(args.currentWorkingDirectory, (directoryPath) => fs.existsSync(path.join(directoryPath, packageJsonFileName)));
18
19
  const resolvedDefaultTarget = nearestPackageDirectory ?? args.currentWorkingDirectory;
19
20
  return fs.canonicalPathSync(resolvedDefaultTarget);
20
- }
21
- function findNearestPackageDirectory(fs, currentWorkingDirectory) {
22
- let currentDir = path.resolve(currentWorkingDirectory);
23
- while (true) {
24
- const packageJsonPath = path.join(currentDir, packageJsonFileName);
25
- if (fs.existsSync(packageJsonPath)) {
26
- return currentDir;
27
- }
28
- const parentDirectory = path.dirname(currentDir);
29
- if (parentDirectory === currentDir) {
30
- return undefined;
31
- }
32
- currentDir = parentDirectory;
33
- }
34
21
  }
@@ -0,0 +1,7 @@
1
+ import type { FilesystemPort } from "#/core/filesystem/port";
2
+ /**
3
+ * Lists the files an arrange run visits — a directory's non-test `.ts`/`.tsx` files, or the single target.
4
+ *
5
+ * @since 0.3.16-canary.0
6
+ */
7
+ export declare function scanArrangeTargets(fs: FilesystemPort, targetPath: string): Array<string>;
@@ -0,0 +1,11 @@
1
+ import type { GroupFileResult } from "#/arrange/domain/types";
2
+ import type { FilesystemPort } from "#/core/filesystem/port";
3
+ /**
4
+ * Runs the simplify pass on one file — flattening class expressions and pruning an unused `cn` import.
5
+ *
6
+ * @since 0.3.16-canary.0
7
+ */
8
+ export declare function processArrangeSimplifyFile(fs: FilesystemPort, args: {
9
+ readonly filePath: string;
10
+ readonly write: boolean;
11
+ }): GroupFileResult;
@@ -0,0 +1,13 @@
1
+ import type { ArrangeRunResult } from "#/arrange/domain/types";
2
+ import type { AppError } from "#/core/errors";
3
+ import type { FilesystemPort } from "#/core/filesystem/port";
4
+ import type { Result } from "#/core/result";
5
+ /**
6
+ * Runs the simplify pass over every target file and returns the aggregated result.
7
+ *
8
+ * @since 0.3.16-canary.0
9
+ */
10
+ export declare function runArrangeSimplify(fs: FilesystemPort, args: {
11
+ targetPath: string;
12
+ write: boolean;
13
+ }): Promise<Result<ArrangeRunResult, AppError>>;