@akanjs/devkit 3.0.0-alpha.70 β 3.0.0-alpha.72
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.
- package/akanApp/BackendImportGraph.test.ts +120 -0
- package/akanApp/BackendImportGraph.ts +167 -0
- package/akanApp/akanApp.host.test.ts +16 -678
- package/akanApp/akanApp.host.ts +65 -562
- package/akanApp/devHostPolicy.test.ts +542 -0
- package/akanApp/devHostPolicy.ts +412 -0
- package/akanApp/index.ts +2 -0
- package/akanConfig/akanConfig.test.ts +1 -0
- package/akanConfig/akanConfig.ts +4 -0
- package/akanContext.ts +17 -3
- package/akanMcpContract.ts +25 -4
- package/artifact/routeSeedIndex.test.ts +1 -0
- package/biome.base.json +4 -0
- package/capacitorApp.ts +10 -0
- package/cloud/globalConfig.ts +9 -2
- package/commandDecorators/command.ts +30 -6
- package/commandDecorators/commandBuilder.ts +20 -5
- package/commandDecorators/commandDecorators.test.ts +115 -3
- package/dependencyScanner.test.ts +99 -0
- package/dependencyScanner.ts +27 -24
- package/devkitUtils.test.ts +0 -104
- package/executors.test.ts +45 -0
- package/executors.ts +29 -5
- package/fileEditor.ts +19 -19
- package/frontendBuild/autoImportSync.test.ts +58 -0
- package/frontendBuild/autoImportSync.ts +4 -0
- package/frontendBuild/buildRouteClient.test.ts +19 -22
- package/frontendBuild/clientEntryDiscovery.ts +2 -2
- package/frontendBuild/fontOptimizer.ts +37 -18
- package/frontendBuild/index.ts +0 -1
- package/frontendBuild/styleContract.ts +14 -20
- package/frontendBuild/themeValidator.ts +22 -17
- package/incrementalBuilder/devWatchBatch.ts +6 -2
- package/incrementalBuilder/incrementalBuilder.proc.ts +6 -2
- package/index.ts +0 -5
- package/lint/__fixtures__/README.md +40 -0
- package/lint/__fixtures__/no-arbitrary-color/bad.tsx +3 -0
- package/lint/__fixtures__/no-arbitrary-color/good.tsx +4 -0
- package/lint/__fixtures__/no-async-component-in-ui/bad.tsx +4 -0
- package/lint/__fixtures__/no-async-component-in-ui/good.tsx +4 -0
- package/lint/__fixtures__/no-bang-comment-in-client/bad.tsx +4 -0
- package/lint/__fixtures__/no-bang-comment-in-client/fixture.json +1 -0
- package/lint/__fixtures__/no-bang-comment-in-client/good.tsx +3 -0
- package/lint/__fixtures__/no-daisyui-legacy-class/bad.tsx +7 -0
- package/lint/__fixtures__/no-daisyui-legacy-class/good.tsx +5 -0
- package/lint/__fixtures__/no-deep-internal-import/bad.tsx +3 -0
- package/lint/__fixtures__/no-deep-internal-import/fixture.json +1 -0
- package/lint/__fixtures__/no-deep-internal-import/good.tsx +3 -0
- package/lint/__fixtures__/no-deprecated-log-level/bad.tsx +4 -0
- package/lint/__fixtures__/no-deprecated-log-level/good.tsx +4 -0
- package/lint/__fixtures__/no-import-client-functions/bad.tsx +2 -0
- package/lint/__fixtures__/no-import-client-functions/good.tsx +4 -0
- package/lint/__fixtures__/no-import-client-in-server/bad.tsx +6 -0
- package/lint/__fixtures__/no-import-client-in-server/good.tsx +5 -0
- package/lint/__fixtures__/no-import-external-library/bad.tsx +4 -0
- package/lint/__fixtures__/no-import-external-library/good.tsx +5 -0
- package/lint/__fixtures__/no-import-server-in-client/bad.tsx +6 -0
- package/lint/__fixtures__/no-import-server-in-client/good.tsx +4 -0
- package/lint/__fixtures__/no-init-fetch-in-client/store-file/bad.ts +1 -0
- package/lint/__fixtures__/no-init-fetch-in-client/store-file/fixture.json +1 -0
- package/lint/__fixtures__/no-init-fetch-in-client/store-file/good.ts +1 -0
- package/lint/__fixtures__/no-init-fetch-in-client/use-client/bad.tsx +4 -0
- package/lint/__fixtures__/no-init-fetch-in-client/use-client/good.tsx +5 -0
- package/lint/__fixtures__/no-inline-color/bad.tsx +3 -0
- package/lint/__fixtures__/no-inline-color/good.tsx +3 -0
- package/lint/__fixtures__/no-interpolated-arbitrary-class/bad.tsx +3 -0
- package/lint/__fixtures__/no-interpolated-arbitrary-class/good.tsx +3 -0
- package/lint/__fixtures__/no-js-private-class-method/bad.tsx +3 -0
- package/lint/__fixtures__/no-js-private-class-method/good.tsx +3 -0
- package/lint/__fixtures__/no-model-type-in-util-zone/bad.tsx +3 -0
- package/lint/__fixtures__/no-model-type-in-util-zone/good.tsx +6 -0
- package/lint/__fixtures__/no-raw-palette-class/bad.tsx +6 -0
- package/lint/__fixtures__/no-raw-palette-class/good.tsx +8 -0
- package/lint/__fixtures__/no-redeclare-predefined-endpoint/bad.ts +10 -0
- package/lint/__fixtures__/no-redeclare-predefined-endpoint/fixture.json +1 -0
- package/lint/__fixtures__/no-redeclare-predefined-endpoint/good.ts +6 -0
- package/lint/__fixtures__/no-return-in-store-action/bad.tsx +2 -0
- package/lint/__fixtures__/no-return-in-store-action/good.tsx +6 -0
- package/lint/__fixtures__/no-throw-raw-error/bad.tsx +4 -0
- package/lint/__fixtures__/no-throw-raw-error/good.tsx +4 -0
- package/lint/__fixtures__/no-unpublished-form-setter/bad.tsx +3 -0
- package/lint/__fixtures__/no-unpublished-form-setter/good.tsx +5 -0
- package/lint/__fixtures__/no-use-client-in-server/bad.tsx +3 -0
- package/lint/__fixtures__/no-use-client-in-server/good.tsx +2 -0
- package/lint/__fixtures__/non-scalar-props-restricted/bad.tsx +5 -0
- package/lint/__fixtures__/non-scalar-props-restricted/good.tsx +4 -0
- package/lint/gritRules.test.ts +178 -0
- package/lint/no-arbitrary-color.grit +1 -1
- package/lint/no-bang-comment-in-client.grit +23 -10
- package/lint/no-daisyui-legacy-class.grit +2 -2
- package/lint/no-deprecated-log-level.grit +17 -0
- package/lint/no-inline-color.grit +10 -8
- package/lint/no-interpolated-arbitrary-class.grit +3 -3
- package/lint/no-raw-palette-class.grit +3 -3
- package/lint/non-scalar-props-restricted.grit +16 -7
- package/linter.test.ts +80 -0
- package/linter.ts +82 -13
- package/package.json +3 -3
- package/prompter.ts +9 -4
- package/qualityScanner.test.ts +64 -0
- package/qualityScanner.ts +43 -3
- package/recipeScanner.ts +4 -1
- package/scanInfo.ts +3 -3
- package/ssrScanner.test.ts +301 -0
- package/transforms/barrelImportsPlugin.ts +1 -1
- package/transforms/transforms.test.ts +5 -5
- package/transforms/tsconfigPackageResolver.test.ts +230 -0
- package/tsconfig.json +5 -1
- package/typeChecker.ts +1 -1
- package/types.ts +1 -0
- package/ui/ScrollList.tsx +6 -8
- package/uploadRelease.ts +2 -2
- package/workflow/executor.test.ts +146 -0
- package/builder.ts +0 -164
- package/extractDeps.ts +0 -86
- package/frontendBuild/styleGuard.test.ts +0 -180
- package/frontendBuild/styleGuard.ts +0 -339
- package/getCredentials.ts +0 -17
- package/getModelFileData.ts +0 -62
- package/src/capacitorApp.ts +0 -282
- 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
|
-
};
|
package/getModelFileData.ts
DELETED
|
@@ -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
|
-
};
|