@akanjs/devkit 3.0.0-alpha.71 β†’ 3.0.0-alpha.73

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 (117) hide show
  1. package/akanApp/BackendImportGraph.test.ts +120 -0
  2. package/akanApp/BackendImportGraph.ts +167 -0
  3. package/akanApp/akanApp.host.test.ts +16 -678
  4. package/akanApp/akanApp.host.ts +65 -562
  5. package/akanApp/devHostPolicy.test.ts +542 -0
  6. package/akanApp/devHostPolicy.ts +412 -0
  7. package/akanApp/index.ts +2 -0
  8. package/akanContext.ts +17 -3
  9. package/akanMcpContract.ts +25 -4
  10. package/artifact/routeSeedIndex.test.ts +1 -0
  11. package/capacitorApp.ts +10 -0
  12. package/cloud/globalConfig.ts +9 -2
  13. package/commandDecorators/command.ts +30 -6
  14. package/commandDecorators/commandBuilder.ts +20 -5
  15. package/commandDecorators/commandDecorators.test.ts +115 -3
  16. package/dependencyScanner.test.ts +99 -0
  17. package/dependencyScanner.ts +27 -24
  18. package/devkitUtils.test.ts +0 -104
  19. package/executors.test.ts +45 -0
  20. package/executors.ts +29 -5
  21. package/fileEditor.ts +19 -19
  22. package/frontendBuild/autoImportSync.test.ts +58 -0
  23. package/frontendBuild/autoImportSync.ts +4 -0
  24. package/frontendBuild/buildRouteClient.test.ts +19 -22
  25. package/frontendBuild/clientEntryDiscovery.ts +2 -2
  26. package/frontendBuild/fontOptimizer.ts +37 -18
  27. package/frontendBuild/index.ts +0 -1
  28. package/frontendBuild/styleContract.ts +14 -20
  29. package/frontendBuild/themeValidator.ts +22 -17
  30. package/incrementalBuilder/devWatchBatch.ts +6 -2
  31. package/incrementalBuilder/incrementalBuilder.proc.ts +6 -2
  32. package/index.ts +0 -5
  33. package/lint/__fixtures__/README.md +40 -0
  34. package/lint/__fixtures__/no-arbitrary-color/bad.tsx +3 -0
  35. package/lint/__fixtures__/no-arbitrary-color/good.tsx +4 -0
  36. package/lint/__fixtures__/no-async-component-in-ui/bad.tsx +4 -0
  37. package/lint/__fixtures__/no-async-component-in-ui/good.tsx +4 -0
  38. package/lint/__fixtures__/no-bang-comment-in-client/bad.tsx +4 -0
  39. package/lint/__fixtures__/no-bang-comment-in-client/fixture.json +1 -0
  40. package/lint/__fixtures__/no-bang-comment-in-client/good.tsx +3 -0
  41. package/lint/__fixtures__/no-daisyui-legacy-class/bad.tsx +7 -0
  42. package/lint/__fixtures__/no-daisyui-legacy-class/good.tsx +5 -0
  43. package/lint/__fixtures__/no-deep-internal-import/bad.tsx +3 -0
  44. package/lint/__fixtures__/no-deep-internal-import/fixture.json +1 -0
  45. package/lint/__fixtures__/no-deep-internal-import/good.tsx +3 -0
  46. package/lint/__fixtures__/no-deprecated-log-level/bad.tsx +4 -0
  47. package/lint/__fixtures__/no-deprecated-log-level/good.tsx +4 -0
  48. package/lint/__fixtures__/no-import-client-functions/bad.tsx +2 -0
  49. package/lint/__fixtures__/no-import-client-functions/good.tsx +4 -0
  50. package/lint/__fixtures__/no-import-client-in-server/bad.tsx +6 -0
  51. package/lint/__fixtures__/no-import-client-in-server/good.tsx +5 -0
  52. package/lint/__fixtures__/no-import-external-library/bad.tsx +4 -0
  53. package/lint/__fixtures__/no-import-external-library/good.tsx +5 -0
  54. package/lint/__fixtures__/no-import-server-in-client/bad.tsx +6 -0
  55. package/lint/__fixtures__/no-import-server-in-client/good.tsx +4 -0
  56. package/lint/__fixtures__/no-init-fetch-in-client/store-file/bad.ts +1 -0
  57. package/lint/__fixtures__/no-init-fetch-in-client/store-file/fixture.json +1 -0
  58. package/lint/__fixtures__/no-init-fetch-in-client/store-file/good.ts +1 -0
  59. package/lint/__fixtures__/no-init-fetch-in-client/use-client/bad.tsx +4 -0
  60. package/lint/__fixtures__/no-init-fetch-in-client/use-client/good.tsx +5 -0
  61. package/lint/__fixtures__/no-inline-color/bad.tsx +3 -0
  62. package/lint/__fixtures__/no-inline-color/good.tsx +3 -0
  63. package/lint/__fixtures__/no-interpolated-arbitrary-class/bad.tsx +3 -0
  64. package/lint/__fixtures__/no-interpolated-arbitrary-class/good.tsx +3 -0
  65. package/lint/__fixtures__/no-js-private-class-method/bad.tsx +3 -0
  66. package/lint/__fixtures__/no-js-private-class-method/good.tsx +3 -0
  67. package/lint/__fixtures__/no-model-type-in-util-zone/bad.tsx +3 -0
  68. package/lint/__fixtures__/no-model-type-in-util-zone/good.tsx +6 -0
  69. package/lint/__fixtures__/no-raw-palette-class/bad.tsx +6 -0
  70. package/lint/__fixtures__/no-raw-palette-class/good.tsx +8 -0
  71. package/lint/__fixtures__/no-redeclare-predefined-endpoint/bad.ts +10 -0
  72. package/lint/__fixtures__/no-redeclare-predefined-endpoint/fixture.json +1 -0
  73. package/lint/__fixtures__/no-redeclare-predefined-endpoint/good.ts +6 -0
  74. package/lint/__fixtures__/no-return-in-store-action/bad.tsx +2 -0
  75. package/lint/__fixtures__/no-return-in-store-action/good.tsx +6 -0
  76. package/lint/__fixtures__/no-throw-raw-error/bad.tsx +4 -0
  77. package/lint/__fixtures__/no-throw-raw-error/good.tsx +4 -0
  78. package/lint/__fixtures__/no-unpublished-form-setter/bad.tsx +3 -0
  79. package/lint/__fixtures__/no-unpublished-form-setter/good.tsx +5 -0
  80. package/lint/__fixtures__/no-use-client-in-server/bad.tsx +3 -0
  81. package/lint/__fixtures__/no-use-client-in-server/good.tsx +2 -0
  82. package/lint/__fixtures__/non-scalar-props-restricted/bad.tsx +5 -0
  83. package/lint/__fixtures__/non-scalar-props-restricted/good.tsx +4 -0
  84. package/lint/gritRules.test.ts +178 -0
  85. package/lint/no-arbitrary-color.grit +1 -1
  86. package/lint/no-bang-comment-in-client.grit +23 -10
  87. package/lint/no-daisyui-legacy-class.grit +2 -2
  88. package/lint/no-inline-color.grit +10 -8
  89. package/lint/no-interpolated-arbitrary-class.grit +3 -3
  90. package/lint/no-raw-palette-class.grit +3 -3
  91. package/lint/non-scalar-props-restricted.grit +16 -7
  92. package/linter.test.ts +80 -0
  93. package/linter.ts +82 -13
  94. package/package.json +3 -3
  95. package/prompter.ts +9 -4
  96. package/qualityScanner.test.ts +64 -0
  97. package/qualityScanner.ts +43 -3
  98. package/recipeScanner.ts +4 -1
  99. package/scanInfo.ts +3 -3
  100. package/ssrScanner.test.ts +301 -0
  101. package/transforms/barrelImportsPlugin.ts +1 -1
  102. package/transforms/transforms.test.ts +5 -5
  103. package/transforms/tsconfigPackageResolver.test.ts +230 -0
  104. package/tsconfig.json +5 -1
  105. package/typeChecker.ts +1 -1
  106. package/types.ts +1 -0
  107. package/ui/ScrollList.tsx +6 -8
  108. package/uploadRelease.ts +2 -2
  109. package/workflow/executor.test.ts +146 -0
  110. package/builder.ts +0 -164
  111. package/extractDeps.ts +0 -86
  112. package/frontendBuild/styleGuard.test.ts +0 -180
  113. package/frontendBuild/styleGuard.ts +0 -339
  114. package/getCredentials.ts +0 -17
  115. package/getModelFileData.ts +0 -62
  116. package/src/capacitorApp.ts +0 -282
  117. package/streamAi.ts +0 -45
@@ -1,339 +0,0 @@
1
- /**
2
- * styleGuard β€” 색 μ–΄νœ˜ 폐쇄λ₯Ό κ°•μ œν•˜λŠ” 순수 정적 검사기 (μ˜μ‘΄μ„± 0).
3
- *
4
- * μ–΄νœ˜ 폐쇄(styles.css `@theme { --color-*: initial }`)둜 raw νŒ”λ ˆνŠΈκ°€ CSSλ₯Ό λ―Έμƒμ„±ν•˜κ²Œ 되면
5
- * μœ„λ°˜μ€ "μ‘°μš©ν•œ μ‹œκ° 깨짐"이 λœλ‹€. styleGuard λŠ” μ†ŒμŠ€ λ¬Έμžμ—΄μ„ μŠ€μΊ”ν•΄ κ·Έ μœ„λ°˜μ„ λͺ…μ‹œμ  μ§„λ‹¨μœΌλ‘œ
6
- * λŒμ–΄μ˜¬λ¦¬κ³ , μ—μ΄μ „νŠΈκ°€ 읽고 μˆ˜λ¦¬ν•  `suggestion` 을 뢙인닀.
7
- *
8
- * severity λŠ” κ·œμΉ™ κ³ μœ κ°’(νŒλ³„λ ₯)만 λ‹΄λŠ”λ‹€. warn/error λ₯Ό 막을지 μ—¬λΆ€λŠ” λ°°μ„ (호좜자)이 mode 둜 κ²°μ •ν•œλ‹€:
9
- * - dev : μ ˆλŒ€ 막지 μ•ŠμŒ (λͺ¨λ‘ κ²½κ³  둜그)
10
- * - build/CI/lint : severity==="error" μœ„λ°˜μ΄ ν•˜λ‚˜λΌλ„ 있으면 μ‹€νŒ¨
11
- *
12
- * akanjs λŸ°νƒ€μž„μ„ import ν•˜μ§€ μ•ŠλŠ”λ‹€ β€” node ν‘œμ€€ 라이브러리만 μ‚¬μš©.
13
- */
14
-
15
- export type StyleGuardRule =
16
- | "raw-palette"
17
- | "arbitrary-color"
18
- | "inline-color"
19
- | "daisyui-legacy"
20
- | "interpolated-arbitrary";
21
- export type StyleGuardSeverity = "error" | "warn";
22
-
23
- export interface StyleGuardViolation {
24
- rule: StyleGuardRule;
25
- severity: StyleGuardSeverity;
26
- path: string;
27
- line: number;
28
- snippet: string;
29
- suggestion: string;
30
- }
31
-
32
- export interface StyleGuardFile {
33
- path: string;
34
- content: string;
35
- }
36
-
37
- export interface NamedComponentClassMetric {
38
- count: number;
39
- names: string[];
40
- }
41
-
42
- // 색을 λ°›λŠ” μœ ν‹Έ 접두사. daisyuiTokenRename.ts 의 PREFIX 와 동일 계보(λ°©ν–₯μ„± border 포함).
43
- const PREFIX =
44
- "(?:bg|text|border(?:-[tblrxy])?(?:-[se])?|ring(?:-offset)?|fill|stroke|shadow|from|to|via|divide|outline|decoration|placeholder|caret|accent)";
45
-
46
- // Tailwind κΈ°λ³Έ νŒ”λ ˆνŠΈ 이름. `neutral` 은 μ‹œλ§¨ν‹± 토큰이기도 ν•˜λ―€λ‘œ 숫자 suffix λ₯Ό μš”κ΅¬ν•΄ κ΅¬λΆ„ν•œλ‹€
47
- // (`bg-neutral` 은 ν—ˆμš©, `bg-neutral-500` 은 raw νŒ”λ ˆνŠΈ).
48
- const PALETTE =
49
- "(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)";
50
-
51
- // 클래슀 경계: μ•žμ€ 곡백/λ”°μ˜΄ν‘œ/λ°±ν‹±/μ—¬λŠ” κ΄„ν˜Έ/콜둠(λ³€ν˜•)/!(important), λ’€λŠ” μ’…κ²° 문자(+ μŠ¬λž˜μ‹œ=opacity, !).
52
- const LEAD = "(^|[\\s\"'`{(\\[:!])";
53
- const TAIL = "(?=[\\s\"'`})\\]:/!,]|$)";
54
-
55
- const RAW_PALETTE_RE = new RegExp(`${LEAD}((?:${PREFIX})-)${PALETTE}-\\d{2,3}${TAIL}`, "g");
56
-
57
- // μž„μ˜κ°’ λŒ€κ΄„ν˜Έ μ•ˆμ˜ 색 λ¦¬ν„°λŸ΄. `[--var]` 같은 λ³€μˆ˜ μ°Έμ‘°λŠ” λ§€μΉ˜λ˜μ§€ μ•ŠλŠ”λ‹€(μ˜λ„).
58
- const ARBITRARY_COLOR_RE = /\[(#[0-9a-fA-F]{3,8}|(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch|hwb|color)\([^\]]*\))\]/g;
59
-
60
- // λŸ°νƒ€μž„ κ°’μœΌλ‘œ μ‘°λ¦½ν•œ μž„μ˜κ°’(`min-h-[${n}px]`, `bg-[${color}]`). Tailwind λŠ” μ†ŒμŠ€ **ν…μŠ€νŠΈ**μ—μ„œ
61
- // μž„μ˜κ°’μ„ μΆ”μΆœν•˜λ―€λ‘œ CSS κ°€ μ•„μ˜ˆ μƒμ„±λ˜μ§€ μ•ŠλŠ”λ‹€ β€” ν΄λž˜μŠ€λŠ” DOM 에 있고 prop 은 μ—°κ²°λœ λ“― λ³΄μ΄μ§€λ§Œ
62
- // 아무것도 μ μš©λ˜μ§€ μ•ŠλŠ” 무증상 μ‹€νŒ¨λ‹€. 같은 λͺ¨μ–‘μ˜ λ¦¬ν„°λŸ΄μ΄ μ½”λ“œλ² μ΄μŠ€ μ–΄λ”˜κ°€μ— 있으면 κΈ°λ³Έκ°’λ§Œ
63
- // μš°μ—°νžˆ λ™μž‘ν•˜κ³  override 만 쑰용히 μ£½μ–΄ 더 μ°ΎκΈ° μ–΄λ €μ›Œμ§„λ‹€.
64
- const INTERPOLATED_ARBITRARY_RE = new RegExp(`${LEAD}[a-z][a-z0-9-]*-\\[[^\\]\`]*\\$\\{`, "g");
65
-
66
- // style 객체 / <style> 블둝. 색 λ¦¬ν„°λŸ΄μ΄ 이 μ•ˆμ— 있으면 클래슀 μŠ€μΊλ„ˆλ₯Ό μš°νšŒν•œ 것.
67
- const STYLE_OBJECT_RE = /style=\{\{([\s\S]*?)\}\}/g;
68
- const STYLE_TAG_RE = /<style[^>]*>([\s\S]*?)<\/style>/g;
69
- // var(...) λŠ” ν—ˆμš©μ΄λ―€λ‘œ 맀치 λŒ€μƒμ—μ„œ μžμ—°νžˆ λΉ μ§„λ‹€(hex/μƒ‰ν•¨μˆ˜ λ¦¬ν„°λŸ΄λ§Œ 탐지).
70
- const INLINE_COLOR_LITERAL_RE = /#[0-9a-fA-F]{3,8}\b|(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch|hwb)\(/g;
71
-
72
- // daisyUI μž”μž¬. νŒλ³„λ ₯ 높은 compound(λ³€ν˜• suffix 포함) νŒ¨ν„΄λ§Œ error 둜 μž‘μ•„ μ˜€νƒμ„ μ–΅μ œν•œλ‹€.
73
- const DAISYUI_LEGACY_RE = new RegExp(
74
- `${LEAD}(?:` +
75
- // λ²„νŠΌ/λ°°μ§€/μ•Œλ¦Ό/μž…λ ₯ λ“± variant compound
76
- "btn-(?:primary|secondary|accent|neutral|info|success|warning|error|ghost|link|outline|square|circle|wide|block|xs|sm|md|lg)|" +
77
- "badge-(?:primary|secondary|accent|neutral|info|success|warning|error|ghost|outline)|" +
78
- "alert-(?:info|success|warning|error)|" +
79
- "input-(?:bordered|primary|secondary|accent|ghost|error)|" +
80
- "select-(?:bordered|primary|ghost)|textarea-(?:bordered|primary|ghost)|" +
81
- "checkbox-(?:primary|secondary|accent)|toggle-(?:primary|secondary|accent)|" +
82
- "loading-(?:spinner|dots|ring|ball|bars|infinity)|" +
83
- // ꡬ쑰 클래슀(daisyUI μ „μš© 쑰각)
84
- "card-(?:body|title|actions)|modal-(?:box|action|backdrop)|" +
85
- "collapse-(?:title|content|arrow|plus)|dropdown-(?:content|end|start|hover)|" +
86
- "stat-(?:title|value|desc)|tabs-(?:boxed|lifted|bordered)|tab-active|" +
87
- "menu-(?:title|dropdown)|steps-(?:horizontal|vertical)|join-item|" +
88
- "mockup-(?:code|phone|browser|window)|drawer-(?:side|content|toggle)" +
89
- `)${TAIL}`,
90
- "g",
91
- );
92
-
93
- // daisyUI 색 슬둯. μ–΄νœ˜ 폐쇄가 이 이름듀을 μ§€μ› μœΌλ―€λ‘œ CSS κ°€ μƒμ„±λ˜μ§€ μ•ŠλŠ”λ‹€ β€” μ»΄ν¬λ„ŒνŠΈ ν΄λž˜μŠ€μ™€ 같은
94
- // 무증상 μ‹€νŒ¨λ‹€. μ‹œλ§¨ν‹± μ–΄νœ˜μ— 살아남은 슬둯(primaryΒ·secondaryΒ·accentΒ·neutralΒ·infoΒ·successΒ·warning)은
95
- // μ •μƒμ΄λ―€λ‘œ μ œμ™Έν•˜κ³ , 사라진 것(base-*, *-content, error)만 μž‘λŠ”λ‹€.
96
- const DAISYUI_TOKEN =
97
- "(?:base-(?:100|200|300|content)|(?:primary|secondary|accent|neutral|info|success|warning|error)-content|error)";
98
- const DAISYUI_TOKEN_RE = new RegExp(`${LEAD}(?:${PREFIX})-${DAISYUI_TOKEN}${TAIL}`, "g");
99
-
100
- const ALL_RULES: StyleGuardRule[] = [
101
- "raw-palette",
102
- "arbitrary-color",
103
- "inline-color",
104
- "daisyui-legacy",
105
- "interpolated-arbitrary",
106
- ];
107
-
108
- /**
109
- * μœ„λ°˜ μ–΅μ œ μ§€μ‹œμ–΄(escape hatch). μ •λ‹Ήν•œ νŒ”λ ˆνŠΈ μš”μ²­(에디터 μ‹ νƒμŠ€ ν•˜μ΄λΌμ΄νŠΈ, 데이터-viz, μ˜λ„μ  λΈŒλžœλ“œ
110
- * 데λͺ¨)은 거절이 μ•„λ‹ˆλΌ "λͺ…μ‹œμ  μ˜΅νŠΈμ•„μ›ƒ"으둜 λΌμš°νŒ…ν•œλ‹€ β€” 천μž₯은 μ œμ•½ν•˜μ§€ μ•Šλ˜ 흔적을 남긴닀.
111
- * styleguard-disable <rule?> β†’ 파일 전체
112
- * styleguard-disable-next-line <rule?> β†’ λ°”λ‘œ λ‹€μŒ 쀄
113
- * rule 을 μƒλž΅ν•˜λ©΄ λͺ¨λ“  κ·œμΉ™μ„ μ–΅μ œν•œλ‹€.
114
- */
115
- interface Directives {
116
- file: Set<StyleGuardRule> | "all" | null;
117
- nextLine: Map<number, Set<StyleGuardRule> | "all">;
118
- }
119
-
120
- export class StyleGuard {
121
- run(files: StyleGuardFile[]): StyleGuardViolation[] {
122
- const out: StyleGuardViolation[] = [];
123
- for (const file of files) {
124
- const directives = this.#parseDirectives(file.content);
125
- // 주석 λ‚΄μš©μ„ 곡백으둜 μΉ˜ν™˜(offsetΒ·μ€„λ²ˆν˜Έ 보쑴). 주석에 남은 μ˜ˆμ „ 클래슀λͺ…이 μ˜€νƒλ˜μ§€ μ•Šκ²Œ.
126
- // directives λŠ” μ›λ³Έμ—μ„œ νŒŒμ‹±ν•œλ‹€ β€” μ§€μ‹œμ–΄ μžμ²΄κ°€ 주석이기 λ•Œλ¬Έ.
127
- const scan = this.#stripComments(file.content);
128
- const local: StyleGuardViolation[] = [];
129
- this.#scanClassLiterals(file, scan, local);
130
- this.#scanInlineColors(file, scan, local);
131
- for (const v of local) if (!this.#isSuppressed(v, directives)) out.push(v);
132
- }
133
- return out;
134
- }
135
-
136
- /** 라인/블둝 주석을 같은 길이의 곡백으둜 μΉ˜ν™˜. λ¬Έμžμ—΄/ν…œν”Œλ¦Ώ λ¦¬ν„°λŸ΄ μ•ˆμ˜ `//`Β·`/*` λŠ” 보쑴. */
137
- #stripComments(src: string): string {
138
- const out = src.split("");
139
- const blank = (i: number) => {
140
- if (src[i] !== "\n") out[i] = " ";
141
- };
142
- let state: "code" | "line" | "block" | "squote" | "dquote" | "template" = "code";
143
- let i = 0;
144
- while (i < src.length) {
145
- const c = src[i];
146
- const d = src[i + 1];
147
- if (state === "code") {
148
- if (c === "/" && d === "/") {
149
- blank(i);
150
- blank(i + 1);
151
- state = "line";
152
- i += 2;
153
- } else if (c === "/" && d === "*") {
154
- blank(i);
155
- blank(i + 1);
156
- state = "block";
157
- i += 2;
158
- } else {
159
- if (c === "'") state = "squote";
160
- else if (c === '"') state = "dquote";
161
- else if (c === "`") state = "template";
162
- i++;
163
- }
164
- } else if (state === "line") {
165
- if (c === "\n") state = "code";
166
- else blank(i);
167
- i++;
168
- } else if (state === "block") {
169
- if (c === "*" && d === "/") {
170
- blank(i);
171
- blank(i + 1);
172
- state = "code";
173
- i += 2;
174
- } else {
175
- blank(i);
176
- i++;
177
- }
178
- } else {
179
- // λ¬Έμžμ—΄/ν…œν”Œλ¦Ώ: λ‚΄μš© 보쑴, escape κ±΄λ„ˆλ›°κ³  μ’…κ²° λ¬Έμžμ—μ„œ code 둜 볡귀.
180
- const close = state === "squote" ? "'" : state === "dquote" ? '"' : "`";
181
- if (c === "\\") i += 2;
182
- else {
183
- if (c === close) state = "code";
184
- i++;
185
- }
186
- }
187
- }
188
- return out.join("");
189
- }
190
-
191
- #parseDirectives(content: string): Directives {
192
- const directives: Directives = { file: null, nextLine: new Map() };
193
- const lines = content.split("\n");
194
- for (let i = 0; i < lines.length; i++) {
195
- const line = lines[i];
196
- const nextLineMatch = line.match(/styleguard-disable-next-line\b(.*)$/);
197
- if (nextLineMatch) {
198
- directives.nextLine.set(i + 2, this.#extractRules(nextLineMatch[1]));
199
- continue;
200
- }
201
- const fileMatch = line.match(/styleguard-disable\b(.*)$/);
202
- if (fileMatch) directives.file = this.#mergeFileScope(directives.file, this.#extractRules(fileMatch[1]));
203
- }
204
- return directives;
205
- }
206
-
207
- #extractRules(tail: string): Set<StyleGuardRule> | "all" {
208
- const found = ALL_RULES.filter((rule) => tail.includes(rule));
209
- return found.length === 0 ? "all" : new Set(found);
210
- }
211
-
212
- #mergeFileScope(
213
- prev: Set<StyleGuardRule> | "all" | null,
214
- next: Set<StyleGuardRule> | "all",
215
- ): Set<StyleGuardRule> | "all" {
216
- if (prev === "all" || next === "all") return "all";
217
- return new Set([...(prev ?? []), ...next]);
218
- }
219
-
220
- #isSuppressed(v: StyleGuardViolation, directives: Directives): boolean {
221
- if (directives.file === "all" || directives.file?.has(v.rule)) return true;
222
- const nextLine = directives.nextLine.get(v.line);
223
- return nextLine === "all" || (nextLine?.has(v.rule) ?? false);
224
- }
225
-
226
- #scanClassLiterals(file: StyleGuardFile, scan: string, out: StyleGuardViolation[]): void {
227
- for (const m of scan.matchAll(RAW_PALETTE_RE)) {
228
- out.push(
229
- this.#violation(file, m.index ?? 0, {
230
- rule: "raw-palette",
231
- severity: "error",
232
- suggestion:
233
- "μ‹œλ§¨ν‹± ν† ν°μœΌλ‘œ κ΅μ²΄ν•˜μ„Έμš” β€” 예: text-gray-500β†’text-muted-foreground, bg-red-500β†’bg-destructive, bg-blue-500β†’bg-info. λΈŒλžœλ“œ 색은 page/styles.css ν† ν°μ—μ„œ μ‘°μ •ν•©λ‹ˆλ‹€.",
234
- }),
235
- );
236
- }
237
- for (const m of scan.matchAll(ARBITRARY_COLOR_RE)) {
238
- out.push(
239
- this.#violation(file, m.index ?? 0, {
240
- rule: "arbitrary-color",
241
- severity: "error",
242
- suggestion:
243
- "μž„μ˜ 색 λ¦¬ν„°λŸ΄ λŒ€μ‹  토큰을 μ“°μ„Έμš” β€” bg-[#3b82f6]β†’bg-info, text-[rgb(...)]β†’μ‹œλ§¨ν‹± 토큰. κΌ­ ν•„μš”ν•˜λ©΄ μŠ€μ½”ν”„ 토큰(.campaign-x { --primary: … })을 μ •μ˜ν•˜μ„Έμš”.",
244
- }),
245
- );
246
- }
247
- for (const m of scan.matchAll(DAISYUI_LEGACY_RE)) {
248
- out.push(
249
- this.#violation(file, m.index ?? 0, {
250
- rule: "daisyui-legacy",
251
- severity: "error",
252
- suggestion:
253
- "daisyUI ν΄λž˜μŠ€λŠ” μ œκ±°λμŠ΅λ‹ˆλ‹€. akanjs/ui ν”„λ¦¬λ―Έν‹°λΈŒ(Button/Badge λ“±)λ‚˜ buttonRecipe()/badgeRecipe() + μ‹œλ§¨ν‹± ν† ν°μœΌλ‘œ κ΅μ²΄ν•˜μ„Έμš”.",
254
- }),
255
- );
256
- }
257
- for (const m of scan.matchAll(DAISYUI_TOKEN_RE)) {
258
- out.push(
259
- this.#violation(file, m.index ?? 0, {
260
- rule: "daisyui-legacy",
261
- severity: "error",
262
- suggestion:
263
- "daisyUI 색 μŠ¬λ‘―μ€ μ–΄νœ˜μ—μ„œ μ œκ±°λμŠ΅λ‹ˆλ‹€ β€” base-100/200/300β†’background/muted/border, base-contentβ†’foreground, *-contentβ†’*-foreground, errorβ†’destructive.",
264
- }),
265
- );
266
- }
267
- for (const m of scan.matchAll(INTERPOLATED_ARBITRARY_RE)) {
268
- out.push(
269
- this.#violation(file, m.index ?? 0, {
270
- rule: "interpolated-arbitrary",
271
- severity: "error",
272
- suggestion:
273
- "λŸ°νƒ€μž„ κ°’μœΌλ‘œ μž„μ˜κ°’ 클래슀λ₯Ό μ‘°λ¦½ν•˜λ©΄ CSS κ°€ μƒμ„±λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€(μŠ€μΊλ„ˆλŠ” μ†ŒμŠ€ ν…μŠ€νŠΈλ₯Ό μ½μŠ΅λ‹ˆλ‹€). 크기/μœ„μΉ˜λŠ” style prop 으둜 λ„˜κΈ°μ„Έμš” β€” style={{ minHeight }}. 값이 enum 이면 λ¦¬ν„°λŸ΄ 클래슀 맡으둜 λ‘μ„Έμš”.",
274
- }),
275
- );
276
- }
277
- }
278
-
279
- #scanInlineColors(file: StyleGuardFile, scan: string, out: StyleGuardViolation[]): void {
280
- const push = (index: number) =>
281
- out.push(
282
- this.#violation(file, index, {
283
- rule: "inline-color",
284
- severity: "error",
285
- suggestion:
286
- "인라인 색 λ¦¬ν„°λŸ΄ λŒ€μ‹  CSS λ³€μˆ˜/토큰을 μ°Έμ‘°ν•˜μ„Έμš” β€” style={{ color: 'var(--primary)' }} λ˜λŠ” μ‹œλ§¨ν‹± 클래슀(text-primary). ν•˜λ“œμ½”λ”© hex/rgb λŠ” ν…Œλ§ˆ μ „ν™˜μ„ κΉ¨λœ¨λ¦½λ‹ˆλ‹€.",
287
- }),
288
- );
289
- for (const region of [STYLE_OBJECT_RE, STYLE_TAG_RE]) {
290
- for (const m of scan.matchAll(region)) {
291
- const inner = m[1] ?? "";
292
- const innerStart = (m.index ?? 0) + m[0].indexOf(inner);
293
- for (const lit of inner.matchAll(INLINE_COLOR_LITERAL_RE)) {
294
- push(innerStart + (lit.index ?? 0));
295
- }
296
- }
297
- }
298
- }
299
-
300
- #violation(
301
- file: StyleGuardFile,
302
- index: number,
303
- base: Pick<StyleGuardViolation, "rule" | "severity" | "suggestion">,
304
- ): StyleGuardViolation {
305
- const before = file.content.slice(0, index);
306
- const line = before.length === 0 ? 1 : before.split("\n").length;
307
- const lineStart = before.lastIndexOf("\n") + 1;
308
- const lineEnd = file.content.indexOf("\n", index);
309
- const snippet = file.content.slice(lineStart, lineEnd === -1 ? undefined : lineEnd).trim();
310
- return { ...base, path: file.path, line, snippet };
311
- }
312
-
313
- /**
314
- * `@layer components` μ•ˆμ—μ„œ μ„ μ–Έλœ λͺ…λͺ… 클래슀 개수 β€” 그림자 λ””μžμΈ μ‹œμŠ€ν…œ μ¦μ‹μ˜ warn μ§€ν‘œ(차단 X).
315
- */
316
- static countNamedComponentClasses(css: string): NamedComponentClassMetric {
317
- const names = new Set<string>();
318
- const layerRe = /@layer\s+components\s*\{/g;
319
- for (const layer of css.matchAll(layerRe)) {
320
- const bodyStart = (layer.index ?? 0) + layer[0].length;
321
- const body = StyleGuard.#extractBalanced(css, bodyStart);
322
- for (const sel of body.matchAll(/(^|[\s}])\.(-?[a-zA-Z_][\w-]*)/g)) names.add(sel[2]);
323
- }
324
- return { count: names.size, names: [...names].sort() };
325
- }
326
-
327
- static #extractBalanced(css: string, start: number): string {
328
- let depth = 1;
329
- for (let i = start; i < css.length; i++) {
330
- const ch = css[i];
331
- if (ch === "{") depth++;
332
- else if (ch === "}") {
333
- depth--;
334
- if (depth === 0) return css.slice(start, i);
335
- }
336
- }
337
- return css.slice(start);
338
- }
339
- }
package/getCredentials.ts DELETED
@@ -1,17 +0,0 @@
1
- import type { AppExecutor } from "./executors";
2
- import { FileSys } from "./fileSys";
3
-
4
- export interface AppSecret {
5
- postgres?: { account?: { user?: { username: string; password: string } } };
6
- }
7
- interface Secret {
8
- [key: string]: AppSecret;
9
- }
10
-
11
- export const getCredentials = async (app: AppExecutor, environment: string): Promise<AppSecret> => {
12
- const content = await FileSys.readText(`${app.workspace.workspaceRoot}/infra/app/values/${app.name}-secret.yaml`);
13
- const secret = Bun.YAML.parse(content) as Secret;
14
- const appSecret = secret[environment];
15
- if (!appSecret) throw new Error(`No secret found for ${app.name} in ${environment}`);
16
- return appSecret;
17
- };
@@ -1,62 +0,0 @@
1
- import { capitalize } from "akanjs/common";
2
-
3
- interface ModelFileData {
4
- moduleType: "lib" | "app";
5
- moduleName: string;
6
- modelName: string;
7
- constantFilePath: string;
8
- importModelNames: string[];
9
- hasImportScalar: boolean;
10
- importLibNames: string[];
11
- constantFileStr: string;
12
- unitFilePath: string;
13
- unitFileStr: string;
14
- viewFilePath: string;
15
- viewFileStr: string;
16
- }
17
-
18
- export const getModelFileData = async (modulePath: string, modelName: string): Promise<ModelFileData> => {
19
- const moduleType = modulePath.startsWith("apps") ? "app" : "lib";
20
- const moduleName = modulePath.split("/")[1] ?? "";
21
- const modelComponentName = capitalize(modelName);
22
- const constantFilePath = `${modulePath}/lib/${modelName}/${modelName}.constant.ts`;
23
- const unitFilePath = `${modulePath}/lib/${modelName}/${modelComponentName}.Unit.tsx`;
24
- const viewFilePath = `${modulePath}/lib/${modelName}/${modelComponentName}.View.tsx`;
25
- const [constantFileStr, unitFileStr, viewFileStr] = await Promise.all([
26
- Bun.file(constantFilePath).text(),
27
- Bun.file(unitFilePath).text(),
28
- Bun.file(viewFilePath).text(),
29
- ]);
30
-
31
- const constantFileLines = constantFileStr.split("\n");
32
- const importLibNames = constantFileLines
33
- .filter((line) => line.startsWith("import { cnst as "))
34
- .map((line) => line.split("cnst as ")[1]?.split(" ")[0] ?? "");
35
-
36
- const importLocalPaths = constantFileLines
37
- .filter((line) => line.startsWith("import { ") && line.includes('from "../'))
38
- .map((line) => line.split("from ")[1]?.split('"')[1] ?? "");
39
-
40
- const importModelNames = importLocalPaths
41
- .map((path) => path.split("/")[1] ?? "")
42
- .filter((name) => !name.startsWith("_"));
43
-
44
- const hasImportScalar = !!importLocalPaths
45
- .map((path) => path.split("/")[1] ?? "")
46
- .filter((name) => name.startsWith("_")).length;
47
-
48
- return {
49
- moduleType,
50
- moduleName,
51
- modelName,
52
- constantFilePath,
53
- unitFilePath,
54
- viewFilePath,
55
- importModelNames,
56
- hasImportScalar,
57
- importLibNames,
58
- constantFileStr,
59
- unitFileStr,
60
- viewFileStr,
61
- };
62
- };