@codefast/cli 0.3.12 → 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 (225) hide show
  1. package/README.md +206 -51
  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.js → bin.mjs} +4 -1
  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/index.mjs +16 -0
  54. package/dist/lib/arrange/infra/walk.mjs +2 -0
  55. package/dist/lib/arrange/infra/walk.test.mjs +50 -0
  56. package/dist/lib/arrange/presentation/formatters.mjs +2 -0
  57. package/dist/lib/arrange/presentation/formatters.test.mjs +73 -0
  58. package/dist/lib/arrange/presentation/report.mjs +2 -0
  59. package/dist/lib/config/domain/schema.mjs +2 -0
  60. package/dist/lib/config/index.mjs +4 -0
  61. package/dist/lib/config/infra/loader.mjs +2 -0
  62. package/dist/lib/config/infra/loader.test.mjs +116 -0
  63. package/dist/lib/infra/caught-unknown-message.mjs +2 -0
  64. package/dist/lib/infra/config-reporter.mjs +2 -0
  65. package/dist/lib/infra/node-io.mjs +2 -0
  66. package/dist/lib/infra/workspace/repo-root.mjs +2 -0
  67. package/dist/lib/infra/workspace/repo-root.test.mjs +45 -0
  68. package/dist/lib/mirror/application/engine.mjs +2 -0
  69. package/dist/lib/mirror/application/sync.mjs +2 -0
  70. package/dist/lib/mirror/application/sync.test.mjs +482 -0
  71. package/dist/lib/mirror/domain/constants.mjs +2 -0
  72. package/dist/lib/mirror/domain/errors.mjs +2 -0
  73. package/dist/lib/mirror/domain/types.mjs +1 -0
  74. package/dist/lib/mirror/index.mjs +3 -0
  75. package/dist/lib/mirror/infra/dirent-list.mjs +2 -0
  76. package/dist/lib/mirror/infra/dirent-list.test.mjs +18 -0
  77. package/dist/lib/mirror/infra/package-filter.mjs +2 -0
  78. package/dist/lib/mirror/infra/package-filter.test.mjs +32 -0
  79. package/dist/lib/mirror/infra/path-normalizer.mjs +2 -0
  80. package/dist/lib/mirror/infra/update-pkg.mjs +2 -0
  81. package/dist/lib/mirror/infra/workspace-packages.mjs +2 -0
  82. package/dist/lib/mirror/infra/workspace-packages.test.mjs +167 -0
  83. package/dist/lib/mirror/presentation/reporter.mjs +2 -0
  84. package/dist/lib/tag/application/engine.mjs +2 -0
  85. package/dist/lib/tag/application/engine.test.mjs +159 -0
  86. package/dist/lib/tag/domain/types.mjs +1 -0
  87. package/dist/lib/tag/index.mjs +5 -0
  88. package/dist/lib/tag/infra/target-resolver.mjs +2 -0
  89. package/dist/lib/tag/presentation/colors.mjs +2 -0
  90. package/dist/lib/tag/presentation/tag-presenter.mjs +2 -0
  91. package/dist/lib/tag/presentation/tag-presenter.test.mjs +29 -0
  92. package/dist/loader-DpN-zl5g.mjs +78 -0
  93. package/dist/mirror-CbGiLXKT.mjs +2 -0
  94. package/dist/mirror-DqJE3ejL.mjs +66 -0
  95. package/dist/node-io-DFRbHO6m.mjs +30 -0
  96. package/dist/package-filter-C_8ryqvz.mjs +26 -0
  97. package/dist/path-normalizer-D4ceBO3l.mjs +7 -0
  98. package/dist/program-DR-H4tH3.mjs +34 -0
  99. package/dist/program.mjs +2 -0
  100. package/dist/repo-root-HmtIEm6J.mjs +22 -0
  101. package/dist/report-DYxvw34T.mjs +24 -0
  102. package/dist/reporter-CwIe_tAM.mjs +108 -0
  103. package/dist/run-target-CJWrqNT4.mjs +65 -0
  104. package/dist/schema-Or8RAARd.mjs +25 -0
  105. package/dist/sync-e1Sp-shi.mjs +157 -0
  106. package/dist/tag-Cd9JujU5.mjs +2 -0
  107. package/dist/tag-hEKVZ_-7.mjs +58 -0
  108. package/dist/tag-presenter-Cs66UBsH.mjs +63 -0
  109. package/dist/target-resolver-CR3FXMTy.mjs +170 -0
  110. package/dist/targets-pCTslnFh.mjs +106 -0
  111. package/dist/tokenizer-CeZx73Zu.mjs +216 -0
  112. package/dist/update-pkg-CnGfjWBy.mjs +37 -0
  113. package/dist/walk-DjgQn66n.mjs +33 -0
  114. package/dist/workspace-packages-K2NYTHJq.mjs +159 -0
  115. package/package.json +11 -11
  116. package/dist/bin.d.ts +0 -3
  117. package/dist/bin.d.ts.map +0 -1
  118. package/dist/commands/arrange.d.ts +0 -3
  119. package/dist/commands/arrange.d.ts.map +0 -1
  120. package/dist/commands/arrange.js +0 -114
  121. package/dist/commands/mirror.d.ts +0 -4
  122. package/dist/commands/mirror.d.ts.map +0 -1
  123. package/dist/commands/mirror.js +0 -63
  124. package/dist/lib/arrange/analyze.d.ts +0 -4
  125. package/dist/lib/arrange/analyze.d.ts.map +0 -1
  126. package/dist/lib/arrange/analyze.js +0 -105
  127. package/dist/lib/arrange/ast/collectors-cn.d.ts +0 -10
  128. package/dist/lib/arrange/ast/collectors-cn.d.ts.map +0 -1
  129. package/dist/lib/arrange/ast/collectors-cn.js +0 -84
  130. package/dist/lib/arrange/ast/collectors-jsx.d.ts +0 -4
  131. package/dist/lib/arrange/ast/collectors-jsx.d.ts.map +0 -1
  132. package/dist/lib/arrange/ast/collectors-jsx.js +0 -18
  133. package/dist/lib/arrange/ast/collectors-tv.d.ts +0 -13
  134. package/dist/lib/arrange/ast/collectors-tv.d.ts.map +0 -1
  135. package/dist/lib/arrange/ast/collectors-tv.js +0 -273
  136. package/dist/lib/arrange/ast/targets.d.ts +0 -8
  137. package/dist/lib/arrange/ast/targets.d.ts.map +0 -1
  138. package/dist/lib/arrange/ast/targets.js +0 -143
  139. package/dist/lib/arrange/ast/utils.d.ts +0 -36
  140. package/dist/lib/arrange/ast/utils.d.ts.map +0 -1
  141. package/dist/lib/arrange/ast/utils.js +0 -138
  142. package/dist/lib/arrange/constants.d.ts +0 -68
  143. package/dist/lib/arrange/constants.d.ts.map +0 -1
  144. package/dist/lib/arrange/constants.js +0 -179
  145. package/dist/lib/arrange/errors.d.ts +0 -14
  146. package/dist/lib/arrange/errors.d.ts.map +0 -1
  147. package/dist/lib/arrange/errors.js +0 -16
  148. package/dist/lib/arrange/formatters.d.ts +0 -16
  149. package/dist/lib/arrange/formatters.d.ts.map +0 -1
  150. package/dist/lib/arrange/formatters.js +0 -57
  151. package/dist/lib/arrange/group-file.d.ts +0 -4
  152. package/dist/lib/arrange/group-file.d.ts.map +0 -1
  153. package/dist/lib/arrange/group-file.js +0 -115
  154. package/dist/lib/arrange/grouping.d.ts +0 -41
  155. package/dist/lib/arrange/grouping.d.ts.map +0 -1
  156. package/dist/lib/arrange/grouping.js +0 -280
  157. package/dist/lib/arrange/imports.d.ts +0 -6
  158. package/dist/lib/arrange/imports.d.ts.map +0 -1
  159. package/dist/lib/arrange/imports.js +0 -74
  160. package/dist/lib/arrange/report.d.ts +0 -4
  161. package/dist/lib/arrange/report.d.ts.map +0 -1
  162. package/dist/lib/arrange/report.js +0 -37
  163. package/dist/lib/arrange/run-target.d.ts +0 -4
  164. package/dist/lib/arrange/run-target.d.ts.map +0 -1
  165. package/dist/lib/arrange/run-target.js +0 -28
  166. package/dist/lib/arrange/tokenizer.d.ts +0 -37
  167. package/dist/lib/arrange/tokenizer.d.ts.map +0 -1
  168. package/dist/lib/arrange/tokenizer.js +0 -390
  169. package/dist/lib/arrange/types.d.ts +0 -104
  170. package/dist/lib/arrange/types.d.ts.map +0 -1
  171. package/dist/lib/arrange/types.js +0 -7
  172. package/dist/lib/arrange/walk.d.ts +0 -4
  173. package/dist/lib/arrange/walk.d.ts.map +0 -1
  174. package/dist/lib/arrange/walk.js +0 -34
  175. package/dist/lib/arrange.d.ts +0 -41
  176. package/dist/lib/arrange.d.ts.map +0 -1
  177. package/dist/lib/arrange.js +0 -38
  178. package/dist/lib/infra/fs-contract.d.ts +0 -28
  179. package/dist/lib/infra/fs-contract.d.ts.map +0 -1
  180. package/dist/lib/infra/node-io.d.ts +0 -4
  181. package/dist/lib/infra/node-io.d.ts.map +0 -1
  182. package/dist/lib/infra/node-io.js +0 -27
  183. package/dist/lib/mirror/config.d.ts +0 -7
  184. package/dist/lib/mirror/config.d.ts.map +0 -1
  185. package/dist/lib/mirror/config.js +0 -146
  186. package/dist/lib/mirror/constants.d.ts +0 -10
  187. package/dist/lib/mirror/constants.d.ts.map +0 -1
  188. package/dist/lib/mirror/constants.js +0 -13
  189. package/dist/lib/mirror/engine.d.ts +0 -12
  190. package/dist/lib/mirror/engine.d.ts.map +0 -1
  191. package/dist/lib/mirror/engine.js +0 -248
  192. package/dist/lib/mirror/errors.d.ts +0 -10
  193. package/dist/lib/mirror/errors.d.ts.map +0 -1
  194. package/dist/lib/mirror/errors.js +0 -12
  195. package/dist/lib/mirror/package-filter.d.ts +0 -8
  196. package/dist/lib/mirror/package-filter.d.ts.map +0 -1
  197. package/dist/lib/mirror/package-filter.js +0 -33
  198. package/dist/lib/mirror/reporter.d.ts +0 -24
  199. package/dist/lib/mirror/reporter.d.ts.map +0 -1
  200. package/dist/lib/mirror/reporter.js +0 -126
  201. package/dist/lib/mirror/sync.d.ts +0 -4
  202. package/dist/lib/mirror/sync.d.ts.map +0 -1
  203. package/dist/lib/mirror/sync.js +0 -135
  204. package/dist/lib/mirror/types.d.ts +0 -81
  205. package/dist/lib/mirror/types.d.ts.map +0 -1
  206. package/dist/lib/mirror/update-pkg.d.ts +0 -13
  207. package/dist/lib/mirror/update-pkg.d.ts.map +0 -1
  208. package/dist/lib/mirror/update-pkg.js +0 -39
  209. package/dist/lib/mirror/workspace-packages.d.ts +0 -26
  210. package/dist/lib/mirror/workspace-packages.d.ts.map +0 -1
  211. package/dist/lib/mirror/workspace-packages.js +0 -160
  212. package/dist/lib/mirror.d.ts +0 -7
  213. package/dist/lib/mirror.d.ts.map +0 -1
  214. package/dist/lib/mirror.js +0 -5
  215. package/dist/lib/repo-root.d.ts +0 -7
  216. package/dist/lib/repo-root.d.ts.map +0 -1
  217. package/dist/lib/repo-root.js +0 -23
  218. package/dist/lib/shared/utils.d.ts +0 -9
  219. package/dist/lib/shared/utils.d.ts.map +0 -1
  220. package/dist/lib/shared/utils.js +0 -12
  221. package/dist/program.d.ts +0 -4
  222. package/dist/program.d.ts.map +0 -1
  223. package/dist/program.js +0 -38
  224. /package/dist/lib/{infra/fs-contract.js → arrange/domain/types.mjs} +0 -0
  225. /package/dist/lib/{mirror/types.js → infra/fs-contract.mjs} +0 -0
@@ -0,0 +1,159 @@
1
+ import { t as messageFromCaughtUnknown } from "./caught-unknown-message-CRhkDWO7.mjs";
2
+ import { t as normalizePath } from "./path-normalizer-D4ceBO3l.mjs";
3
+ import { s as mirrorGlobWarning } from "./reporter-CwIe_tAM.mjs";
4
+ import { globSync } from "node:fs";
5
+ import path from "node:path";
6
+ import picomatch from "picomatch";
7
+ import { parse } from "yaml";
8
+ //#region src/lib/mirror/infra/workspace-packages.ts
9
+ const PNPM_WORKSPACE = "pnpm-workspace.yaml";
10
+ /** Default when the workspace file is missing or has no `packages` key. */
11
+ const DEFAULT_INCLUDE = ["packages/*"];
12
+ function toPosix(filePath) {
13
+ return filePath.split(path.sep).join("/");
14
+ }
15
+ function isGlobPermissionError(caughtError) {
16
+ if (typeof caughtError !== "object" || caughtError === null || !("code" in caughtError)) return false;
17
+ const code = caughtError.code;
18
+ return code === "EACCES" || code === "EPERM";
19
+ }
20
+ /** Turn a pnpm `packages` glob into a `globSync` pattern that finds `package.json` files. */
21
+ function workspacePatternToPackageJsonGlob(pattern) {
22
+ const normalizedPattern = toPosix(pattern.trim()).replace(/\/+$/, "");
23
+ if (!normalizedPattern) return "**/package.json";
24
+ return `${normalizedPattern}/package.json`;
25
+ }
26
+ function splitWorkspacePackageEntries(raw) {
27
+ const include = [];
28
+ const exclude = [];
29
+ if (!Array.isArray(raw)) return {
30
+ include,
31
+ exclude
32
+ };
33
+ for (const entry of raw) {
34
+ if (typeof entry !== "string") continue;
35
+ const trimmed = entry.trim();
36
+ if (!trimmed) continue;
37
+ if (trimmed.startsWith("!")) exclude.push(trimmed.slice(1).trim());
38
+ else include.push(trimmed);
39
+ }
40
+ return {
41
+ include,
42
+ exclude
43
+ };
44
+ }
45
+ /**
46
+ * Reads `packages` from a parsed pnpm workspace document.
47
+ * @throws If the document is invalid or `packages` is present but not an array.
48
+ */
49
+ function parsePnpmWorkspaceDocument(doc) {
50
+ if (doc === null || doc === void 0) throw new Error(`${PNPM_WORKSPACE} root document must be an object (got empty or null)`);
51
+ if (typeof doc !== "object" || Array.isArray(doc)) throw new Error(`${PNPM_WORKSPACE} root must be a mapping, not an array or scalar`);
52
+ const parsedDoc = doc;
53
+ if (!Object.prototype.hasOwnProperty.call(parsedDoc, "packages")) return {
54
+ include: [],
55
+ exclude: [],
56
+ hasPackagesKey: false,
57
+ isEmptyPackagesArray: false
58
+ };
59
+ const pkgs = parsedDoc.packages;
60
+ if (!Array.isArray(pkgs)) throw new Error(`${PNPM_WORKSPACE} field "packages" must be an array`);
61
+ if (pkgs.length === 0) return {
62
+ include: [],
63
+ exclude: [],
64
+ hasPackagesKey: true,
65
+ isEmptyPackagesArray: true
66
+ };
67
+ return {
68
+ ...splitWorkspacePackageEntries(pkgs),
69
+ hasPackagesKey: true,
70
+ isEmptyPackagesArray: false
71
+ };
72
+ }
73
+ async function readWorkspaceYaml(rootDir, fs) {
74
+ const workspaceYamlPath = path.join(rootDir, PNPM_WORKSPACE);
75
+ if (!fs.existsSync(workspaceYamlPath)) return { exists: false };
76
+ let raw;
77
+ try {
78
+ raw = await fs.readFile(workspaceYamlPath, "utf8");
79
+ } catch (caughtReadError) {
80
+ throw new Error(`Failed to read ${PNPM_WORKSPACE}: ${messageFromCaughtUnknown(caughtReadError)}`);
81
+ }
82
+ let doc;
83
+ try {
84
+ doc = parse(raw);
85
+ } catch (caughtYamlParseError) {
86
+ throw new Error(`Failed to parse ${PNPM_WORKSPACE}: ${messageFromCaughtUnknown(caughtYamlParseError)}`);
87
+ }
88
+ if (doc === null || doc === void 0) throw new Error(`${PNPM_WORKSPACE} must define a mapping at the document root`);
89
+ return {
90
+ exists: true,
91
+ doc
92
+ };
93
+ }
94
+ /**
95
+ * Workspace package directories relative to `rootDir` (POSIX slashes), sorted.
96
+ * Reads `pnpm-workspace.yaml` when present; otherwise uses `packages/*`.
97
+ * Only paths with a `package.json` are returned (same practical rule as pnpm).
98
+ */
99
+ async function findWorkspacePackageRelPaths(rootDir, fs, logger) {
100
+ const workspaceYaml = await readWorkspaceYaml(rootDir, fs);
101
+ let include;
102
+ let exclude;
103
+ let multiSource;
104
+ if (!workspaceYaml.exists) {
105
+ include = [...DEFAULT_INCLUDE];
106
+ exclude = [];
107
+ multiSource = "default-patterns";
108
+ } else {
109
+ const parsed = parsePnpmWorkspaceDocument(workspaceYaml.doc);
110
+ if (!parsed.hasPackagesKey) {
111
+ include = [...DEFAULT_INCLUDE];
112
+ exclude = [];
113
+ multiSource = "default-patterns";
114
+ } else if (parsed.isEmptyPackagesArray) return {
115
+ relPaths: [],
116
+ multiSource: "declared-empty"
117
+ };
118
+ else {
119
+ include = parsed.include;
120
+ exclude = parsed.exclude;
121
+ multiSource = "pnpm-workspace-yaml";
122
+ }
123
+ }
124
+ const found = /* @__PURE__ */ new Set();
125
+ const globOpts = {
126
+ cwd: rootDir,
127
+ posix: true,
128
+ ...path.sep === "\\" ? { windowsPathsNoEscape: true } : {}
129
+ };
130
+ for (const pattern of include) {
131
+ const globPat = workspacePatternToPackageJsonGlob(pattern);
132
+ let matches;
133
+ try {
134
+ matches = globSync(globPat, globOpts);
135
+ } catch (caughtGlobError) {
136
+ if (isGlobPermissionError(caughtGlobError)) {
137
+ mirrorGlobWarning(logger, `Skipping workspace glob "${pattern}" (${messageFromCaughtUnknown(caughtGlobError)})`);
138
+ continue;
139
+ }
140
+ throw new Error(`Invalid workspace glob "${pattern}": ${messageFromCaughtUnknown(caughtGlobError)}`);
141
+ }
142
+ for (const matchedPath of matches) {
143
+ const posix = toPosix(matchedPath);
144
+ if (!posix.endsWith("/package.json")) continue;
145
+ const rel = posix.slice(0, -13);
146
+ if (!rel) continue;
147
+ found.add(normalizePath(rel));
148
+ }
149
+ }
150
+ const excludeMatchers = exclude.map((pat) => picomatch(pat, { dot: true }));
151
+ const relPaths = [...found].filter((rel) => !excludeMatchers.some((isMatch) => isMatch(rel)));
152
+ relPaths.sort((a, b) => a.localeCompare(b));
153
+ return {
154
+ relPaths,
155
+ multiSource
156
+ };
157
+ }
158
+ //#endregion
159
+ export { workspacePatternToPackageJsonGlob as i, parsePnpmWorkspaceDocument as n, splitWorkspacePackageEntries as r, findWorkspacePackageRelPaths as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codefast/cli",
3
- "version": "0.3.12",
3
+ "version": "0.3.13-canary.0",
4
4
  "description": "Developer CLI for the Codefast monorepo (mirror, arrange)",
5
5
  "keywords": [
6
6
  "cli",
@@ -22,7 +22,7 @@
22
22
  "directory": "packages/cli"
23
23
  },
24
24
  "bin": {
25
- "codefast": "./dist/bin.js"
25
+ "codefast": "./dist/bin.mjs"
26
26
  },
27
27
  "files": [
28
28
  "dist",
@@ -33,14 +33,11 @@
33
33
  "type": "module",
34
34
  "imports": {
35
35
  "#*": [
36
- "./dist/*.js",
37
- "./dist/*/index.js",
38
- "./dist/*",
36
+ "./src/*",
39
37
  "./src/*.ts",
40
38
  "./src/*.tsx",
41
39
  "./src/*/index.ts",
42
- "./src/*/index.tsx",
43
- "./src/*"
40
+ "./src/*/index.tsx"
44
41
  ]
45
42
  },
46
43
  "publishConfig": {
@@ -48,9 +45,11 @@
48
45
  },
49
46
  "dependencies": {
50
47
  "commander": "^14.0.3",
48
+ "jiti": "^2.6.1",
51
49
  "picomatch": "^4.0.4",
52
50
  "typescript": "^6.0.2",
53
- "yaml": "^2.8.3"
51
+ "yaml": "^2.8.3",
52
+ "zod": "^4.3.6"
54
53
  },
55
54
  "devDependencies": {
56
55
  "@swc/core": "^1.15.24",
@@ -60,13 +59,14 @@
60
59
  "@types/picomatch": "^4.0.3",
61
60
  "@typescript/native-preview": "7.0.0-dev.20260411.1",
62
61
  "jest": "^30.3.0",
63
- "@codefast/typescript-config": "0.3.12"
62
+ "tsdown": "^0.21.8",
63
+ "@codefast/typescript-config": "0.3.13-canary.0"
64
64
  },
65
65
  "engines": {
66
- "node": ">=24.0.0"
66
+ "node": ">=22.0.0"
67
67
  },
68
68
  "scripts": {
69
- "build": "rm -rf dist && tsgo -p tsconfig.build.json",
69
+ "build": "tsdown",
70
70
  "check-types": "tsgo --noEmit",
71
71
  "clean": "rm -rf dist",
72
72
  "test": "jest",
package/dist/bin.d.ts DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
3
- //# sourceMappingURL=bin.d.ts.map
package/dist/bin.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
@@ -1,3 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerArrangeCommand(program: Command): void;
3
- //# sourceMappingURL=arrange.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"arrange.d.ts","sourceRoot":"","sources":["../../src/commands/arrange.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4EzC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsF7D"}
@@ -1,114 +0,0 @@
1
- import path from "node:path";
2
- import process from "node:process";
3
- import { Option } from "commander";
4
- import { ArrangeError, ArrangeErrorCode, analyzeDirectory, createNodeCliFs, createNodeCliLogger, DEFAULT_ARRANGE_TARGET, formatArray, formatCnCall, printAnalyzeReport, runOnTarget, suggestCnGroups, summarizeGroupBucketLabels, } from "#lib/arrange";
5
- /** Commander attribute `withClassName` (second long flag `--with-class-name`). */
6
- function createWithClassNameOption() {
7
- return new Option("--with-classname, --with-class-name", "Append className as final cn() argument").default(false);
8
- }
9
- function defaultTargetPath() {
10
- return path.resolve(process.cwd(), DEFAULT_ARRANGE_TARGET);
11
- }
12
- function checkTargetExists(resolved, fs, logger) {
13
- if (!fs.existsSync(resolved)) {
14
- logger.err(`Not found: ${resolved}`);
15
- process.exitCode = 1;
16
- return false;
17
- }
18
- return true;
19
- }
20
- function handleArrangeLibError(e, logger) {
21
- if (e instanceof ArrangeError && e.code === ArrangeErrorCode.TARGET_NOT_FOUND) {
22
- logger.err(e.message);
23
- process.exitCode = 1;
24
- return true;
25
- }
26
- return false;
27
- }
28
- function runArrangeAction(resolvedTarget, runOpts, fs, logger) {
29
- try {
30
- runOnTarget(resolvedTarget, {
31
- write: runOpts.write,
32
- withClassName: !!runOpts.withClassName,
33
- cnImport: runOpts.cnImport,
34
- }, fs, logger);
35
- }
36
- catch (e) {
37
- if (!handleArrangeLibError(e, logger))
38
- throw e;
39
- }
40
- }
41
- export function registerArrangeCommand(program) {
42
- const arrange = program
43
- .command("arrange")
44
- .description("Analyze and regroup Tailwind classes in cn() / tv() calls (Tailwind v4)");
45
- arrange
46
- .command("analyze")
47
- .description("Report long strings, nested cn in tv(), and related findings")
48
- .argument("[target]", "Directory or file (default: packages/ui/src/components)")
49
- .action((target) => {
50
- const fs = createNodeCliFs();
51
- const logger = createNodeCliLogger();
52
- const resolved = target ? path.resolve(target) : defaultTargetPath();
53
- if (!checkTargetExists(resolved, fs, logger))
54
- return;
55
- printAnalyzeReport(resolved, analyzeDirectory(resolved, fs), logger);
56
- });
57
- arrange
58
- .command("preview")
59
- .description("Dry-run: print suggested replacements without writing files")
60
- .argument("[target]", "Directory or file (default: packages/ui/src/components)")
61
- .addOption(createWithClassNameOption())
62
- .option("--cn-import <spec>", "Override module specifier when adding cn import")
63
- .action((target, opts) => {
64
- const fs = createNodeCliFs();
65
- const logger = createNodeCliLogger();
66
- const resolved = target ? path.resolve(target) : defaultTargetPath();
67
- if (!checkTargetExists(resolved, fs, logger))
68
- return;
69
- runArrangeAction(resolved, {
70
- write: false,
71
- withClassName: opts.withClassName,
72
- cnImport: opts.cnImport,
73
- }, fs, logger);
74
- });
75
- arrange
76
- .command("apply")
77
- .description("Apply grouping and cn-in-tv unwrap edits to files")
78
- .argument("[target]", "Directory or file (default: packages/ui/src/components)")
79
- .addOption(createWithClassNameOption())
80
- .option("--cn-import <spec>", "Override module specifier when adding cn import")
81
- .action((target, opts) => {
82
- const fs = createNodeCliFs();
83
- const logger = createNodeCliLogger();
84
- const resolved = target ? path.resolve(target) : defaultTargetPath();
85
- if (!checkTargetExists(resolved, fs, logger))
86
- return;
87
- runArrangeAction(resolved, {
88
- write: true,
89
- withClassName: opts.withClassName,
90
- cnImport: opts.cnImport,
91
- }, fs, logger);
92
- });
93
- arrange
94
- .command("group")
95
- .description("Try grouping on a pasted class string (stdout: cn(...) or tv array with --tv)")
96
- .argument("[tokens...]", "Class tokens (quote a single string if it contains spaces)")
97
- .option("--tv", "Emit tv()-style array instead of cn() call", false)
98
- .addOption(createWithClassNameOption())
99
- .action((tokens, opts) => {
100
- const inlineClasses = tokens.join(" ").trim();
101
- if (!inlineClasses) {
102
- process.stderr.write('Pass a class string. Example: codefast arrange group "flex gap-2 text-sm rounded-md"\n');
103
- process.exitCode = 1;
104
- return;
105
- }
106
- const groups = suggestCnGroups(inlineClasses);
107
- const result = opts.tv
108
- ? formatArray(groups)
109
- : formatCnCall(groups, { trailingClassName: !!opts.withClassName });
110
- process.stdout.write(`${result}\n`);
111
- const bucketSummary = summarizeGroupBucketLabels(groups);
112
- process.stdout.write(`\n// Buckets: ${JSON.stringify(bucketSummary)}\n`);
113
- });
114
- }
@@ -1,4 +0,0 @@
1
- import { Command } from "commander";
2
- export declare function packageArgToRelative(rootDir: string, arg: string | undefined): string | undefined;
3
- export declare function registerMirrorCommand(program: Command): void;
4
- //# sourceMappingURL=mirror.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mirror.d.ts","sourceRoot":"","sources":["../../src/commands/mirror.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAiBjG;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmC5D"}
@@ -1,63 +0,0 @@
1
- import { realpathSync } from "node:fs";
2
- import path from "node:path";
3
- import process from "node:process";
4
- import { createNodeCliFs } from "#lib/infra/node-io";
5
- import { normalizePath, runMirrorSync } from "#lib/mirror";
6
- import { findRepoRoot } from "#lib/repo-root";
7
- function tryRealpath(entryPath) {
8
- try {
9
- return realpathSync.native(entryPath);
10
- }
11
- catch {
12
- return path.resolve(entryPath);
13
- }
14
- }
15
- export function packageArgToRelative(rootDir, arg) {
16
- if (!arg)
17
- return undefined;
18
- const rootReal = tryRealpath(path.resolve(rootDir));
19
- const cwdReal = tryRealpath(process.cwd());
20
- const resolved = path.isAbsolute(arg) ? path.resolve(arg) : path.resolve(cwdReal, arg);
21
- const targetReal = tryRealpath(resolved);
22
- const rel = path.relative(rootReal, targetReal);
23
- const normalized = normalizePath(rel);
24
- if (normalized.startsWith("..") ||
25
- path.isAbsolute(normalized) ||
26
- normalized === "" ||
27
- normalized === ".") {
28
- throw new Error(`Package path must be a subdirectory under monorepo root: ${rootDir}`);
29
- }
30
- return normalized;
31
- }
32
- export function registerMirrorCommand(program) {
33
- const mirror = program
34
- .command("mirror")
35
- .description("Keep package manifests aligned with what you ship");
36
- mirror
37
- .command("sync")
38
- .description("Write package.json exports from dist/ for workspace packages")
39
- .argument("[package]", "Optional package path relative to repo root (e.g. packages/ui)")
40
- .option("-v, --verbose", "Print extra diagnostics", false)
41
- .action(async function (pkg, options) {
42
- const globals = this.optsWithGlobals();
43
- const fs = createNodeCliFs();
44
- const rootDir = findRepoRoot(fs);
45
- let packageFilter;
46
- try {
47
- packageFilter = packageArgToRelative(rootDir, pkg);
48
- }
49
- catch (e) {
50
- this.error(e instanceof Error ? e.message : String(e));
51
- return;
52
- }
53
- const exitCode = await runMirrorSync({
54
- rootDir,
55
- verbose: options.verbose,
56
- /** Commander sets `color: false` when `--no-color` is passed (default `color: true`). */
57
- noColor: globals.color === false,
58
- packageFilter,
59
- fs,
60
- });
61
- process.exitCode = exitCode;
62
- });
63
- }
@@ -1,4 +0,0 @@
1
- import type { CliFs } from "#lib/infra/fs-contract";
2
- import type { AnalyzeReport } from "#lib/arrange/types";
3
- export declare function analyzeDirectory(target: string, fs: CliFs): AnalyzeReport;
4
- //# sourceMappingURL=analyze.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../../src/lib/arrange/analyze.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AA8FxD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,GAAG,aAAa,CAsCzE"}
@@ -1,105 +0,0 @@
1
- import ts from "typescript";
2
- import { LONG_STRING_TOKEN_THRESHOLD } from "#lib/arrange/constants";
3
- import { tokenizeClassString } from "#lib/arrange/tokenizer";
4
- import { forEachStringLiteralInClassExpression } from "#lib/arrange/ast/collectors-cn";
5
- import { jsxClassNameStaticLiteral } from "#lib/arrange/ast/collectors-jsx";
6
- import { collectCnCallsInsideTv, traverseTvObject } from "#lib/arrange/ast/collectors-tv";
7
- import { buildKnownCnTvBindings, isCnOrTvIdentifier, lineOf } from "#lib/arrange/ast/utils";
8
- import { walkTsxFiles } from "#lib/arrange/walk";
9
- function analyzeCnCall(sf, call, report) {
10
- for (const arg of call.arguments) {
11
- forEachStringLiteralInClassExpression(arg, (lit) => {
12
- const text = lit.text;
13
- const tokenCount = tokenizeClassString(text).length;
14
- if (tokenCount >= LONG_STRING_TOKEN_THRESHOLD) {
15
- report.longCnStringLiterals.push({
16
- file: sf.fileName,
17
- line: lineOf(sf, lit),
18
- tokenCount,
19
- preview: text.length > 72 ? `${text.slice(0, 72)}…` : text,
20
- });
21
- }
22
- });
23
- }
24
- }
25
- function visitCallExpressionForArrangeAnalyze(node, sf, sourceText, knownBindings, report) {
26
- if (isCnOrTvIdentifier(node.expression, "cn", knownBindings)) {
27
- report.cnCallExpressions++;
28
- analyzeCnCall(sf, node, report);
29
- return;
30
- }
31
- if (!isCnOrTvIdentifier(node.expression, "tv", knownBindings))
32
- return;
33
- report.tvCallExpressions++;
34
- const arg0 = node.arguments[0];
35
- if (!arg0 || !ts.isObjectLiteralExpression(arg0))
36
- return;
37
- for (const nestedCn of collectCnCallsInsideTv(sf, arg0, knownBindings, 0)) {
38
- const src = sourceText.slice(nestedCn.getStart(sf), nestedCn.getEnd());
39
- const preview = src.length > 72 ? `${src.slice(0, 72)}…` : src;
40
- report.cnInsideTvCalls.push({
41
- file: sf.fileName,
42
- line: lineOf(sf, nestedCn),
43
- argCount: nestedCn.arguments.length,
44
- preview,
45
- });
46
- }
47
- traverseTvObject(sf, arg0, (strNode) => {
48
- const text = strNode.text;
49
- const tokenCount = tokenizeClassString(text).length;
50
- if (tokenCount >= LONG_STRING_TOKEN_THRESHOLD) {
51
- report.longTvStringLiterals.push({
52
- file: sf.fileName,
53
- line: lineOf(sf, strNode),
54
- tokenCount,
55
- preview: text.length > 72 ? `${text.slice(0, 72)}…` : text,
56
- });
57
- }
58
- }, 0, knownBindings);
59
- }
60
- function visitJsxAttributeForArrangeAnalyze(node, sf, filePath, report) {
61
- if (!filePath.endsWith(".tsx"))
62
- return;
63
- const parsed = jsxClassNameStaticLiteral(node);
64
- if (!parsed)
65
- return;
66
- const text = parsed.lit.text;
67
- const tokenCount = tokenizeClassString(text).length;
68
- if (tokenCount >= LONG_STRING_TOKEN_THRESHOLD) {
69
- report.longJsxClassNameLiterals.push({
70
- file: sf.fileName,
71
- line: lineOf(sf, parsed.lit),
72
- tokenCount,
73
- preview: text.length > 72 ? `${text.slice(0, 72)}…` : text,
74
- });
75
- }
76
- }
77
- export function analyzeDirectory(target, fs) {
78
- const report = {
79
- files: 0,
80
- cnCallExpressions: 0,
81
- tvCallExpressions: 0,
82
- cnInsideTvCalls: [],
83
- longCnStringLiterals: [],
84
- longTvStringLiterals: [],
85
- longJsxClassNameLiterals: [],
86
- };
87
- const files = fs.statSync(target).isDirectory() ? walkTsxFiles(target, fs) : [target];
88
- for (const filePath of files) {
89
- const sourceText = fs.readFileSync(filePath, "utf8");
90
- const sf = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true, filePath.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
91
- report.files++;
92
- const knownBindings = buildKnownCnTvBindings(sf);
93
- const visit = (node) => {
94
- if (ts.isCallExpression(node)) {
95
- visitCallExpressionForArrangeAnalyze(node, sf, sourceText, knownBindings, report);
96
- }
97
- if (ts.isJsxAttribute(node)) {
98
- visitJsxAttributeForArrangeAnalyze(node, sf, filePath, report);
99
- }
100
- ts.forEachChild(node, visit);
101
- };
102
- visit(sf);
103
- }
104
- return report;
105
- }
@@ -1,10 +0,0 @@
1
- import ts from "typescript";
2
- import type { ForEachStringLiteralInClassExpressionOptions, TailwindClassLiteral } from "#lib/arrange/types";
3
- export declare function forEachStringLiteralInClassExpression(expr: ts.Expression, sink: (node: TailwindClassLiteral) => void, depth?: number, options?: ForEachStringLiteralInClassExpressionOptions): void;
4
- export declare function isUnsafeLiteralForCnStyleApplySplit(node: TailwindClassLiteral): boolean;
5
- export declare const CN_APPLY_LITERAL_WALK_OPTS: ForEachStringLiteralInClassExpressionOptions;
6
- export declare function collectUnconditionalTailwindLiteralsFromCnArguments(args: ts.NodeArray<ts.Expression>): TailwindClassLiteral[];
7
- export declare function mergeCnUnconditionalLiteralPoolForTest(argsSnippet: string, options?: {
8
- callee?: string;
9
- }): string;
10
- //# sourceMappingURL=collectors-cn.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"collectors-cn.d.ts","sourceRoot":"","sources":["../../../../src/lib/arrange/ast/collectors-cn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,KAAK,EACV,4CAA4C,EAC5C,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAE5B,wBAAgB,qCAAqC,CACnD,IAAI,EAAE,EAAE,CAAC,UAAU,EACnB,IAAI,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,EAC1C,KAAK,SAAI,EACT,OAAO,CAAC,EAAE,4CAA4C,GACrD,IAAI,CA4CN;AAED,wBAAgB,mCAAmC,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAEvF;AAED,eAAO,MAAM,0BAA0B,EAAE,4CAExC,CAAC;AAEF,wBAAgB,mDAAmD,CACjE,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,UAAU,CAAC,GAChC,oBAAoB,EAAE,CAqBxB;AAED,wBAAgB,sCAAsC,CACpD,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,MAAM,CAyBR"}
@@ -1,84 +0,0 @@
1
- import ts from "typescript";
2
- import { MAX_CLASS_EXPR_DEPTH } from "#lib/arrange/constants";
3
- export function forEachStringLiteralInClassExpression(expr, sink, depth = 0, options) {
4
- if (depth > MAX_CLASS_EXPR_DEPTH)
5
- return;
6
- if (ts.isStringLiteral(expr) || ts.isNoSubstitutionTemplateLiteral(expr)) {
7
- sink(expr);
8
- return;
9
- }
10
- if (ts.isParenthesizedExpression(expr)) {
11
- forEachStringLiteralInClassExpression(expr.expression, sink, depth + 1, options);
12
- return;
13
- }
14
- if (ts.isAsExpression(expr) || ts.isSatisfiesExpression(expr)) {
15
- forEachStringLiteralInClassExpression(expr.expression, sink, depth + 1, options);
16
- return;
17
- }
18
- if (ts.isNonNullExpression(expr)) {
19
- forEachStringLiteralInClassExpression(expr.expression, sink, depth + 1, options);
20
- return;
21
- }
22
- if (ts.isConditionalExpression(expr)) {
23
- if (options?.descendIntoConditional === false) {
24
- return;
25
- }
26
- forEachStringLiteralInClassExpression(expr.whenTrue, sink, depth + 1, options);
27
- forEachStringLiteralInClassExpression(expr.whenFalse, sink, depth + 1, options);
28
- return;
29
- }
30
- if (ts.isBinaryExpression(expr) && expr.operatorToken.kind === ts.SyntaxKind.PlusToken) {
31
- forEachStringLiteralInClassExpression(expr.left, sink, depth + 1, options);
32
- forEachStringLiteralInClassExpression(expr.right, sink, depth + 1, options);
33
- return;
34
- }
35
- if (ts.isArrayLiteralExpression(expr)) {
36
- for (const el of expr.elements) {
37
- if (ts.isSpreadElement(el))
38
- continue;
39
- forEachStringLiteralInClassExpression(el, sink, depth + 1, options);
40
- }
41
- }
42
- }
43
- export function isUnsafeLiteralForCnStyleApplySplit(node) {
44
- return ts.isArrayLiteralExpression(node.parent);
45
- }
46
- export const CN_APPLY_LITERAL_WALK_OPTS = {
47
- descendIntoConditional: false,
48
- };
49
- export function collectUnconditionalTailwindLiteralsFromCnArguments(args) {
50
- const staticLits = [];
51
- for (const arg of args) {
52
- if (ts.isStringLiteral(arg) || ts.isNoSubstitutionTemplateLiteral(arg)) {
53
- if (!isUnsafeLiteralForCnStyleApplySplit(arg)) {
54
- staticLits.push(arg);
55
- }
56
- }
57
- else {
58
- forEachStringLiteralInClassExpression(arg, (lit) => {
59
- if (!isUnsafeLiteralForCnStyleApplySplit(lit)) {
60
- staticLits.push(lit);
61
- }
62
- }, 0, CN_APPLY_LITERAL_WALK_OPTS);
63
- }
64
- }
65
- return staticLits;
66
- }
67
- export function mergeCnUnconditionalLiteralPoolForTest(argsSnippet, options) {
68
- const callee = options?.callee ?? "cn";
69
- const sourceText = `${callee}(${argsSnippet});`;
70
- const sf = ts.createSourceFile("mergeCnUnconditionalLiteralPoolForTest.ts", sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
71
- const stmt = sf.statements[0];
72
- if (!ts.isExpressionStatement(stmt)) {
73
- throw new Error("expected expression statement");
74
- }
75
- const call = stmt.expression;
76
- if (!ts.isCallExpression(call) ||
77
- !ts.isIdentifier(call.expression) ||
78
- call.expression.text !== callee) {
79
- throw new Error(`expected ${callee}(...) call`);
80
- }
81
- return collectUnconditionalTailwindLiteralsFromCnArguments(call.arguments)
82
- .map((n) => n.text)
83
- .join(" ");
84
- }
@@ -1,4 +0,0 @@
1
- import ts from "typescript";
2
- import type { JsxClassNameStatic } from "#lib/arrange/types";
3
- export declare function jsxClassNameStaticLiteral(attr: ts.JsxAttribute): JsxClassNameStatic | undefined;
4
- //# sourceMappingURL=collectors-jsx.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"collectors-jsx.d.ts","sourceRoot":"","sources":["../../../../src/lib/arrange/ast/collectors-jsx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,GAAG,kBAAkB,GAAG,SAAS,CAe/F"}
@@ -1,18 +0,0 @@
1
- import ts from "typescript";
2
- export function jsxClassNameStaticLiteral(attr) {
3
- if (!ts.isIdentifier(attr.name) || attr.name.text !== "className")
4
- return undefined;
5
- const init = attr.initializer;
6
- if (!init)
7
- return undefined;
8
- if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {
9
- return { lit: init, valueNode: init };
10
- }
11
- if (ts.isJsxExpression(init) && init.expression) {
12
- const innerExpr = init.expression;
13
- if (ts.isStringLiteral(innerExpr) || ts.isNoSubstitutionTemplateLiteral(innerExpr)) {
14
- return { lit: innerExpr, valueNode: init };
15
- }
16
- }
17
- return undefined;
18
- }