@akanjs/cli 2.4.2-rc.3 → 3.0.0-alpha.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.
- package/.build-stamp +1 -1
- package/{abstractCompactor-ghh1q9an.js → abstractCompactor-rk6twzvj.js} +4 -4
- package/{agent.command-de4r6vk8.js → agent.command-jfz8yfwy.js} +8 -7
- package/{application.command-nfsfm96y.js → application.command-3smsv7mq.js} +5 -5
- package/{applicationBuildRunner-554a2vnk.js → applicationBuildRunner-aq7mb3z6.js} +2 -1
- package/buildBatch.proc.js +7 -6
- package/{capacitorApp-yg9b4ajz.js → capacitorApp-q3mpmkkz.js} +3 -3
- package/{cloud.command-1qf95x67.js → cloud.command-rk4e9vrt.js} +10 -10
- package/{context.command-hhjyjtp7.js → context.command-ygzyb0xb.js} +16 -15
- package/{guideline.command-vj956kfr.js → guideline.command-1m1ctar3.js} +10 -10
- package/guidelines/componentRule/componentRule.instruction.md +2 -2
- package/guidelines/cssRule/cssRule.generate.json +1 -1
- package/guidelines/cssRule/cssRule.instruction.md +57 -34
- package/guidelines/framework/framework.instruction.md +1 -1
- package/guidelines/recipeRule/recipeRule.generate.json +31 -0
- package/guidelines/recipeRule/recipeRule.instruction.md +72 -0
- package/incrementalBuilder.proc.js +5 -5
- package/{index-v9jqrdd1.js → index-4yp06pn1.js} +100 -2
- package/{index-pfnh87f9.js → index-4zgs8g1w.js} +1 -1
- package/{index-7ppn9v4n.js → index-b978vs2e.js} +3 -3
- package/{index-s9s0n5fz.js → index-cn24dcqn.js} +2 -2
- package/{index-h971erkm.js → index-csqk9z5j.js} +3 -3
- package/{index-z36qzgzp.js → index-e3qskyt2.js} +3 -4
- package/{index-fbv96xaw.js → index-ev6rjw9n.js} +11 -11
- package/{index-h6eav7zx.js → index-f4gqk0dd.js} +6 -6
- package/{index-z5v766nx.js → index-fmgbwsr5.js} +6 -6
- package/{index-y1e79mz3.js → index-h0bngh79.js} +13 -13
- package/{index-dgeaqwmw.js → index-hjzr8ja1.js} +1 -1
- package/{index-d0h48d2f.js → index-jk9q6pse.js} +1 -1
- package/{index-xwfg9bam.js → index-jtm86byd.js} +120 -4
- package/{index-h5cvg9z9.js → index-ktprgc5g.js} +4 -4
- package/{index-zn9rh7sh.js → index-n0z3f2xg.js} +73 -24
- package/{index-7k9sj7j5.js → index-pyt2b4nw.js} +212 -0
- package/{index-acwxfncv.js → index-r4w8cry5.js} +4 -4
- package/{index-zfp1gz9f.js → index-sggm9qkv.js} +1 -1
- package/{index-04as43rp.js → index-vhv3xnjz.js} +16 -15
- package/index-xjd3rqzj.js +129 -0
- package/{index-xftstr5s.js → index-xwgm94zr.js} +1 -1
- package/index-xys926f2.js +136 -0
- package/index.js +19 -19
- package/{library.command-68ah07a3.js → library.command-pwfx8dx2.js} +4 -4
- package/{localRegistry.command-84dz000g.js → localRegistry.command-bxhqmd1d.js} +9 -9
- package/{module.command-t68xhamx.js → module.command-0t3ewata.js} +8 -8
- package/{package.command-00ncttab.js → package.command-7hmkxvpa.js} +4 -4
- package/package.json +2 -3
- package/{page.command-1096s18n.js → page.command-st6r4s0q.js} +4 -4
- package/{primitive.command-02eqpx6t.js → primitive.command-1qkxpzsn.js} +9 -9
- package/{quality.command-r9cageqb.js → quality.command-hy5vt1bh.js} +3 -3
- package/recipeScanner-tghkzrrt.js +12 -0
- package/{repair.command-7dynpnf1.js → repair.command-nakkq2zp.js} +7 -6
- package/{scalar.command-3kxy3fkh.js → scalar.command-fjq272gs.js} +7 -7
- package/templates/app/page/_index.tsx +55 -52
- package/templates/app/page/styles.css.template +12 -71
- package/templates/appSample/lib/task/Task.Template.tsx +2 -2
- package/templates/appSample/lib/task/Task.Unit.tsx +11 -11
- package/templates/appSample/lib/task/Task.Util.tsx +27 -28
- package/templates/appSample/lib/task/Task.View.tsx +22 -19
- package/templates/appSample/lib/task/Task.Zone.tsx +3 -3
- package/templates/appSample/page/task/[taskId]/_index.tsx +3 -3
- package/templates/appSample/page/task/[taskId]/edit.tsx +5 -5
- package/templates/appSample/page/task/_index.tsx +4 -4
- package/templates/appSample/page/task/_layout.tsx +1 -1
- package/templates/appSample/page/task/new.tsx +5 -5
- package/templates/appSample/ui/GlobalLoading.tsx +4 -4
- package/templates/appSample/ui/QuantityControl.tsx +5 -4
- package/templates/crudPages/[__model__Id]/page.tsx +1 -1
- package/templates/crudPages/page.tsx +1 -1
- package/templates/crudSinglePage/page.tsx +1 -1
- package/templates/module/__Model__.Template.tsx +2 -1
- package/templates/module/__Model__.Unit.tsx +2 -1
- package/templates/module/__Model__.Util.tsx +2 -1
- package/templates/module/__Model__.View.tsx +4 -3
- package/templates/module/__Model__.Zone.tsx +2 -1
- package/templates/workspaceRoot/AGENTS.md.template +11 -13
- package/templates/workspaceRoot/biome.json.template +1 -1
- package/{workflow.command-yy0d9gk3.js → workflow.command-jswbgk90.js} +15 -14
- package/{workspace.command-stv9g4a0.js → workspace.command-p9r9nckg.js} +87 -27
- package/{applicationReleasePackager-pjbxc793.js → applicationReleasePackager-yrrhbkv3.js} +3 -3
- package/{index-nsj2ftxj.js → index-3pgtw35d.js} +3 -3
|
@@ -1,26 +1,37 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
AkanContextAnalyzer
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-4yp06pn1.js";
|
|
5
5
|
import {
|
|
6
6
|
Prompter
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-xwgm94zr.js";
|
|
8
8
|
import {
|
|
9
9
|
runner,
|
|
10
10
|
script
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-jk9q6pse.js";
|
|
12
|
+
import {
|
|
13
|
+
AGENT_BLOCK_END,
|
|
14
|
+
AGENT_BLOCK_START,
|
|
15
|
+
AppExecutor,
|
|
16
|
+
LibExecutor,
|
|
17
|
+
renderRecipeEntries,
|
|
18
|
+
upsertAgentBlock
|
|
19
|
+
} from "./index-jtm86byd.js";
|
|
20
|
+
import {
|
|
21
|
+
collectRecipeSources,
|
|
22
|
+
scanRecipes
|
|
23
|
+
} from "./index-xys926f2.js";
|
|
12
24
|
|
|
13
25
|
// pkgs/@akanjs/cli/agent/agent.script.ts
|
|
14
|
-
import { Logger } from "akanjs/common";
|
|
26
|
+
import { Logger as Logger2 } from "akanjs/common";
|
|
15
27
|
|
|
16
28
|
// pkgs/@akanjs/cli/agent/agent.runner.ts
|
|
29
|
+
import { Logger } from "akanjs/common";
|
|
17
30
|
var targetPaths = {
|
|
18
31
|
cursor: ".cursor/rules/akan.mdc",
|
|
19
32
|
"agents-md": "AGENTS.md",
|
|
20
33
|
claude: "CLAUDE.md"
|
|
21
34
|
};
|
|
22
|
-
var AGENT_BLOCK_START = "<!-- akan:agent:start -->";
|
|
23
|
-
var AGENT_BLOCK_END = "<!-- akan:agent:end -->";
|
|
24
35
|
var SAMPLE_ARTIFACTS = [
|
|
25
36
|
{ probe: "lib/task/task.constant.ts", target: "lib/task", label: "sample database module" },
|
|
26
37
|
{ probe: "lib/_noti/noti.service.ts", target: "lib/_noti", label: "sample service module" },
|
|
@@ -60,12 +71,44 @@ ${items.join(`
|
|
|
60
71
|
|
|
61
72
|
Keep a sample only while you are still learning its pattern; delete it once your own modules cover the same ground.
|
|
62
73
|
|
|
74
|
+
`;
|
|
75
|
+
};
|
|
76
|
+
var renderRecipeIndex = async (workspace) => {
|
|
77
|
+
const sources = await collectRecipeSources(`${workspace.workspaceRoot}/pkgs/akanjs/ui`, "akanjs/ui", "recipe");
|
|
78
|
+
const recipes = scanRecipes(sources);
|
|
79
|
+
const scopeIndexPointer = `App and lib recipes are **not** listed here. Each app/lib carries its own generated index \u2014
|
|
80
|
+
\`apps/<app>/AGENTS.md\` / \`libs/<lib>/AGENTS.md\` (\`## Recipes In Scope\`) \u2014 regenerated by \`akan sync\` and
|
|
81
|
+
verified by \`akan lint\`. When working inside an app or lib, consult that file before consuming or authoring a look.`;
|
|
82
|
+
if (recipes.length === 0)
|
|
83
|
+
return `## Recipes
|
|
84
|
+
|
|
85
|
+
${scopeIndexPointer}
|
|
86
|
+
|
|
87
|
+
`;
|
|
88
|
+
return `## Recipes
|
|
89
|
+
|
|
90
|
+
Framework UI recipes (Tailwind-variant look factories), importable from every app and lib. Consume by exact name \u2014
|
|
91
|
+
\`import { <name> } from "akanjs/ui"\`, then \`<name>(variants?, className?)\`. The second arg merges internally and
|
|
92
|
+
takes **an array too**, so never wrap it in \`cn()\`: \`<name>({}, ["h-full", isWide && "w-full", className])\`.
|
|
93
|
+
Do not guess recipe names, import paths, or variant values; the list below carries
|
|
94
|
+
the full contract (\`*\` marks the default, \`key?\` is a boolean flag), so there is no need to open the recipe file to
|
|
95
|
+
consume one. tsc still reports variant mistakes. **Before inlining a repeated surface (card, box,
|
|
96
|
+
tile, \u2026): reuse a recipe, or add one as \`apps/<app>/ui/Recipe/<name>.ts\` (one recipe per file, re-exported from
|
|
97
|
+
that folder's \`index.ts\`) \u2014 never re-implement the same look inline in several places, and never author a
|
|
98
|
+
near-duplicate.** Full authoring/consumption policy: the \`recipeRule\` guideline.
|
|
99
|
+
|
|
100
|
+
${renderRecipeEntries(recipes)}
|
|
101
|
+
|
|
102
|
+
${scopeIndexPointer}
|
|
103
|
+
|
|
63
104
|
`;
|
|
64
105
|
};
|
|
65
106
|
var renderManagedBlock = async (workspace) => {
|
|
66
107
|
const context = await AkanContextAnalyzer.analyze(workspace);
|
|
67
108
|
const frameworkGuide = await Prompter.getInstruction("framework");
|
|
68
|
-
const
|
|
109
|
+
const appNames = context.apps.map((app) => app.name);
|
|
110
|
+
const sampleCleanup = await renderSampleCleanup(workspace, appNames);
|
|
111
|
+
const recipeIndex = await renderRecipeIndex(workspace);
|
|
69
112
|
return `## Workspace
|
|
70
113
|
|
|
71
114
|
- Repo: ${context.repoName}
|
|
@@ -86,7 +129,7 @@ ${sampleCleanup}## Akan Module Abstracts
|
|
|
86
129
|
Do not hand-edit generated Akan files such as ${context.generatedFiles.map((file) => `\`${file}\``).join(", ")}.
|
|
87
130
|
If generated output is stale or broken, update the owning source file and run \`akan repair generated\` or \`akan sync <app-or-lib>\`.
|
|
88
131
|
|
|
89
|
-
## MCP Workflow Policy
|
|
132
|
+
${recipeIndex}## MCP Workflow Policy
|
|
90
133
|
|
|
91
134
|
- Prefer Akan MCP workflows before direct source edits.
|
|
92
135
|
- Direct source edits are denied when an allowlisted Akan workflow or repair tool can perform the change.
|
|
@@ -107,24 +150,10 @@ ${context.validationCommands.map((command) => `- \`${command}\``).join(`
|
|
|
107
150
|
|
|
108
151
|
${frameworkGuide.trim()}`;
|
|
109
152
|
};
|
|
110
|
-
var upsertManagedBlock = (existing, block) => {
|
|
111
|
-
const managed = `${AGENT_BLOCK_START}
|
|
112
|
-
${block}
|
|
113
|
-
${AGENT_BLOCK_END}`;
|
|
114
|
-
const startIndex = existing.indexOf(AGENT_BLOCK_START);
|
|
115
|
-
const endIndex = existing.indexOf(AGENT_BLOCK_END);
|
|
116
|
-
if (startIndex >= 0 && endIndex > startIndex) {
|
|
117
|
-
return `${existing.slice(0, startIndex)}${managed}${existing.slice(endIndex + AGENT_BLOCK_END.length)}`;
|
|
118
|
-
}
|
|
119
|
-
return `${existing.replace(/\s*$/, "")}
|
|
120
|
-
|
|
121
|
-
${managed}
|
|
122
|
-
`;
|
|
123
|
-
};
|
|
124
153
|
var renderAgentsMd = async (workspace, existing) => {
|
|
125
154
|
const block = await renderManagedBlock(workspace);
|
|
126
155
|
if (existing?.trim())
|
|
127
|
-
return
|
|
156
|
+
return upsertAgentBlock(existing, block);
|
|
128
157
|
const context = await AkanContextAnalyzer.analyze(workspace);
|
|
129
158
|
return `# ${context.repoName} Agent Guide
|
|
130
159
|
|
|
@@ -176,6 +205,26 @@ class AgentRunner extends runner("agent") {
|
|
|
176
205
|
await workspace.writeFile(filePath, content, { overwrite: true });
|
|
177
206
|
written.push(filePath);
|
|
178
207
|
}
|
|
208
|
+
if (targets.includes("agents-md"))
|
|
209
|
+
written.push(...await this.#installScopeIndexes(workspace));
|
|
210
|
+
return written;
|
|
211
|
+
}
|
|
212
|
+
async#installScopeIndexes(workspace) {
|
|
213
|
+
const [appNames, libNames] = await workspace.getExecs();
|
|
214
|
+
const written = [];
|
|
215
|
+
const syncScope = async (exec) => {
|
|
216
|
+
try {
|
|
217
|
+
await exec.syncAgentsIndex();
|
|
218
|
+
written.push(`${exec.type}s/${exec.name}/AGENTS.md`);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
221
|
+
Logger.rawLog(`Skipped ${exec.type}s/${exec.name}/AGENTS.md (${message})`, undefined, "warn");
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
for (const name of libNames)
|
|
225
|
+
await syncScope(LibExecutor.from(workspace, name));
|
|
226
|
+
for (const name of appNames)
|
|
227
|
+
await syncScope(AppExecutor.from(workspace, name));
|
|
179
228
|
return written;
|
|
180
229
|
}
|
|
181
230
|
}
|
|
@@ -194,7 +243,7 @@ class AgentScript extends script("agent", [AgentRunner]) {
|
|
|
194
243
|
if (action !== "install")
|
|
195
244
|
throw new Error(`Unknown agent action: ${action}. Use "install".`);
|
|
196
245
|
const written = await this.agentRunner.install(workspace, resolveTargets(target), { force });
|
|
197
|
-
|
|
246
|
+
Logger2.rawLog(`Agent rules written:
|
|
198
247
|
${written.map((file) => `- ${file}`).join(`
|
|
199
248
|
`)}`);
|
|
200
249
|
}
|
|
@@ -2217,4 +2217,216 @@ ${preparedCssText}`).toString(36);
|
|
|
2217
2217
|
return [basePath, { cssUrl, cssRelPath }];
|
|
2218
2218
|
}
|
|
2219
2219
|
}
|
|
2220
|
+
// pkgs/@akanjs/devkit/frontendBuild/styleGuard.ts
|
|
2221
|
+
var PREFIX = "(?:bg|text|border(?:-[tblrxy])?(?:-[se])?|ring(?:-offset)?|fill|stroke|shadow|from|to|via|divide|outline|decoration|placeholder|caret|accent)";
|
|
2222
|
+
var PALETTE = "(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)";
|
|
2223
|
+
var LEAD = "(^|[\\s\"'`{(\\[:!])";
|
|
2224
|
+
var TAIL = "(?=[\\s\"'`})\\]:/!,]|$)";
|
|
2225
|
+
var RAW_PALETTE_RE = new RegExp(`${LEAD}((?:${PREFIX})-)${PALETTE}-\\d{2,3}${TAIL}`, "g");
|
|
2226
|
+
var ARBITRARY_COLOR_RE = /\[(#[0-9a-fA-F]{3,8}|(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch|hwb|color)\([^\]]*\))\]/g;
|
|
2227
|
+
var INTERPOLATED_ARBITRARY_RE = new RegExp(`${LEAD}[a-z][a-z0-9-]*-\\[[^\\]\`]*\\$\\{`, "g");
|
|
2228
|
+
var STYLE_OBJECT_RE = /style=\{\{([\s\S]*?)\}\}/g;
|
|
2229
|
+
var STYLE_TAG_RE = /<style[^>]*>([\s\S]*?)<\/style>/g;
|
|
2230
|
+
var INLINE_COLOR_LITERAL_RE = /#[0-9a-fA-F]{3,8}\b|(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch|hwb)\(/g;
|
|
2231
|
+
var DAISYUI_LEGACY_RE = new RegExp(`${LEAD}(?:` + "btn-(?:primary|secondary|accent|neutral|info|success|warning|error|ghost|link|outline|square|circle|wide|block|xs|sm|md|lg)|" + "badge-(?:primary|secondary|accent|neutral|info|success|warning|error|ghost|outline)|" + "alert-(?:info|success|warning|error)|" + "input-(?:bordered|primary|secondary|accent|ghost|error)|" + "select-(?:bordered|primary|ghost)|textarea-(?:bordered|primary|ghost)|" + "checkbox-(?:primary|secondary|accent)|toggle-(?:primary|secondary|accent)|" + "loading-(?:spinner|dots|ring|ball|bars|infinity)|" + "card-(?:body|title|actions)|modal-(?:box|action|backdrop)|" + "collapse-(?:title|content|arrow|plus)|dropdown-(?:content|end|start|hover)|" + "stat-(?:title|value|desc)|tabs-(?:boxed|lifted|bordered)|tab-active|" + "menu-(?:title|dropdown)|steps-(?:horizontal|vertical)|join-item|" + "mockup-(?:code|phone|browser|window)|drawer-(?:side|content|toggle)" + `)${TAIL}`, "g");
|
|
2232
|
+
var ALL_RULES = [
|
|
2233
|
+
"raw-palette",
|
|
2234
|
+
"arbitrary-color",
|
|
2235
|
+
"inline-color",
|
|
2236
|
+
"daisyui-legacy",
|
|
2237
|
+
"interpolated-arbitrary"
|
|
2238
|
+
];
|
|
2239
|
+
|
|
2240
|
+
class StyleGuard {
|
|
2241
|
+
run(files) {
|
|
2242
|
+
const out = [];
|
|
2243
|
+
for (const file of files) {
|
|
2244
|
+
const directives = this.#parseDirectives(file.content);
|
|
2245
|
+
const scan = this.#stripComments(file.content);
|
|
2246
|
+
const local = [];
|
|
2247
|
+
this.#scanClassLiterals(file, scan, local);
|
|
2248
|
+
this.#scanInlineColors(file, scan, local);
|
|
2249
|
+
for (const v of local)
|
|
2250
|
+
if (!this.#isSuppressed(v, directives))
|
|
2251
|
+
out.push(v);
|
|
2252
|
+
}
|
|
2253
|
+
return out;
|
|
2254
|
+
}
|
|
2255
|
+
#stripComments(src) {
|
|
2256
|
+
const out = src.split("");
|
|
2257
|
+
const blank = (i2) => {
|
|
2258
|
+
if (src[i2] !== `
|
|
2259
|
+
`)
|
|
2260
|
+
out[i2] = " ";
|
|
2261
|
+
};
|
|
2262
|
+
let state = "code";
|
|
2263
|
+
let i = 0;
|
|
2264
|
+
while (i < src.length) {
|
|
2265
|
+
const c = src[i];
|
|
2266
|
+
const d = src[i + 1];
|
|
2267
|
+
if (state === "code") {
|
|
2268
|
+
if (c === "/" && d === "/") {
|
|
2269
|
+
blank(i);
|
|
2270
|
+
blank(i + 1);
|
|
2271
|
+
state = "line";
|
|
2272
|
+
i += 2;
|
|
2273
|
+
} else if (c === "/" && d === "*") {
|
|
2274
|
+
blank(i);
|
|
2275
|
+
blank(i + 1);
|
|
2276
|
+
state = "block";
|
|
2277
|
+
i += 2;
|
|
2278
|
+
} else {
|
|
2279
|
+
if (c === "'")
|
|
2280
|
+
state = "squote";
|
|
2281
|
+
else if (c === '"')
|
|
2282
|
+
state = "dquote";
|
|
2283
|
+
else if (c === "`")
|
|
2284
|
+
state = "template";
|
|
2285
|
+
i++;
|
|
2286
|
+
}
|
|
2287
|
+
} else if (state === "line") {
|
|
2288
|
+
if (c === `
|
|
2289
|
+
`)
|
|
2290
|
+
state = "code";
|
|
2291
|
+
else
|
|
2292
|
+
blank(i);
|
|
2293
|
+
i++;
|
|
2294
|
+
} else if (state === "block") {
|
|
2295
|
+
if (c === "*" && d === "/") {
|
|
2296
|
+
blank(i);
|
|
2297
|
+
blank(i + 1);
|
|
2298
|
+
state = "code";
|
|
2299
|
+
i += 2;
|
|
2300
|
+
} else {
|
|
2301
|
+
blank(i);
|
|
2302
|
+
i++;
|
|
2303
|
+
}
|
|
2304
|
+
} else {
|
|
2305
|
+
const close = state === "squote" ? "'" : state === "dquote" ? '"' : "`";
|
|
2306
|
+
if (c === "\\")
|
|
2307
|
+
i += 2;
|
|
2308
|
+
else {
|
|
2309
|
+
if (c === close)
|
|
2310
|
+
state = "code";
|
|
2311
|
+
i++;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
return out.join("");
|
|
2316
|
+
}
|
|
2317
|
+
#parseDirectives(content) {
|
|
2318
|
+
const directives = { file: null, nextLine: new Map };
|
|
2319
|
+
const lines = content.split(`
|
|
2320
|
+
`);
|
|
2321
|
+
for (let i = 0;i < lines.length; i++) {
|
|
2322
|
+
const line = lines[i];
|
|
2323
|
+
const nextLineMatch = line.match(/styleguard-disable-next-line\b(.*)$/);
|
|
2324
|
+
if (nextLineMatch) {
|
|
2325
|
+
directives.nextLine.set(i + 2, this.#extractRules(nextLineMatch[1]));
|
|
2326
|
+
continue;
|
|
2327
|
+
}
|
|
2328
|
+
const fileMatch = line.match(/styleguard-disable\b(.*)$/);
|
|
2329
|
+
if (fileMatch)
|
|
2330
|
+
directives.file = this.#mergeFileScope(directives.file, this.#extractRules(fileMatch[1]));
|
|
2331
|
+
}
|
|
2332
|
+
return directives;
|
|
2333
|
+
}
|
|
2334
|
+
#extractRules(tail) {
|
|
2335
|
+
const found = ALL_RULES.filter((rule) => tail.includes(rule));
|
|
2336
|
+
return found.length === 0 ? "all" : new Set(found);
|
|
2337
|
+
}
|
|
2338
|
+
#mergeFileScope(prev, next) {
|
|
2339
|
+
if (prev === "all" || next === "all")
|
|
2340
|
+
return "all";
|
|
2341
|
+
return new Set([...prev ?? [], ...next]);
|
|
2342
|
+
}
|
|
2343
|
+
#isSuppressed(v, directives) {
|
|
2344
|
+
if (directives.file === "all" || directives.file?.has(v.rule))
|
|
2345
|
+
return true;
|
|
2346
|
+
const nextLine = directives.nextLine.get(v.line);
|
|
2347
|
+
return nextLine === "all" || (nextLine?.has(v.rule) ?? false);
|
|
2348
|
+
}
|
|
2349
|
+
#scanClassLiterals(file, scan, out) {
|
|
2350
|
+
for (const m of scan.matchAll(RAW_PALETTE_RE)) {
|
|
2351
|
+
out.push(this.#violation(file, m.index ?? 0, {
|
|
2352
|
+
rule: "raw-palette",
|
|
2353
|
+
severity: "error",
|
|
2354
|
+
suggestion: "\uC2DC\uB9E8\uD2F1 \uD1A0\uD070\uC73C\uB85C \uAD50\uCCB4\uD558\uC138\uC694 \u2014 \uC608: text-gray-500\u2192text-muted-foreground, bg-red-500\u2192bg-destructive, bg-blue-500\u2192bg-info. \uBE0C\uB79C\uB4DC \uC0C9\uC740 page/styles.css \uD1A0\uD070\uC5D0\uC11C \uC870\uC815\uD569\uB2C8\uB2E4."
|
|
2355
|
+
}));
|
|
2356
|
+
}
|
|
2357
|
+
for (const m of scan.matchAll(ARBITRARY_COLOR_RE)) {
|
|
2358
|
+
out.push(this.#violation(file, m.index ?? 0, {
|
|
2359
|
+
rule: "arbitrary-color",
|
|
2360
|
+
severity: "error",
|
|
2361
|
+
suggestion: "\uC784\uC758 \uC0C9 \uB9AC\uD130\uB7F4 \uB300\uC2E0 \uD1A0\uD070\uC744 \uC4F0\uC138\uC694 \u2014 bg-[#3b82f6]\u2192bg-info, text-[rgb(...)]\u2192\uC2DC\uB9E8\uD2F1 \uD1A0\uD070. \uAF2D \uD544\uC694\uD558\uBA74 \uC2A4\uCF54\uD504 \uD1A0\uD070(.campaign-x { --primary: \u2026 })\uC744 \uC815\uC758\uD558\uC138\uC694."
|
|
2362
|
+
}));
|
|
2363
|
+
}
|
|
2364
|
+
for (const m of scan.matchAll(DAISYUI_LEGACY_RE)) {
|
|
2365
|
+
out.push(this.#violation(file, m.index ?? 0, {
|
|
2366
|
+
rule: "daisyui-legacy",
|
|
2367
|
+
severity: "error",
|
|
2368
|
+
suggestion: "daisyUI \uD074\uB798\uC2A4\uB294 \uC81C\uAC70\uB410\uC2B5\uB2C8\uB2E4. akanjs/ui \uD504\uB9AC\uBBF8\uD2F0\uBE0C(Button/Badge \uB4F1)\uB098 buttonRecipe()/badgeRecipe() + \uC2DC\uB9E8\uD2F1 \uD1A0\uD070\uC73C\uB85C \uAD50\uCCB4\uD558\uC138\uC694."
|
|
2369
|
+
}));
|
|
2370
|
+
}
|
|
2371
|
+
for (const m of scan.matchAll(INTERPOLATED_ARBITRARY_RE)) {
|
|
2372
|
+
out.push(this.#violation(file, m.index ?? 0, {
|
|
2373
|
+
rule: "interpolated-arbitrary",
|
|
2374
|
+
severity: "error",
|
|
2375
|
+
suggestion: "\uB7F0\uD0C0\uC784 \uAC12\uC73C\uB85C \uC784\uC758\uAC12 \uD074\uB798\uC2A4\uB97C \uC870\uB9BD\uD558\uBA74 CSS \uAC00 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC2A4\uCE90\uB108\uB294 \uC18C\uC2A4 \uD14D\uC2A4\uD2B8\uB97C \uC77D\uC2B5\uB2C8\uB2E4). \uD06C\uAE30/\uC704\uCE58\uB294 style prop \uC73C\uB85C \uB118\uAE30\uC138\uC694 \u2014 style={{ minHeight }}. \uAC12\uC774 enum \uC774\uBA74 \uB9AC\uD130\uB7F4 \uD074\uB798\uC2A4 \uB9F5\uC73C\uB85C \uB450\uC138\uC694."
|
|
2376
|
+
}));
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
#scanInlineColors(file, scan, out) {
|
|
2380
|
+
const push = (index) => out.push(this.#violation(file, index, {
|
|
2381
|
+
rule: "inline-color",
|
|
2382
|
+
severity: "error",
|
|
2383
|
+
suggestion: "\uC778\uB77C\uC778 \uC0C9 \uB9AC\uD130\uB7F4 \uB300\uC2E0 CSS \uBCC0\uC218/\uD1A0\uD070\uC744 \uCC38\uC870\uD558\uC138\uC694 \u2014 style={{ color: 'var(--primary)' }} \uB610\uB294 \uC2DC\uB9E8\uD2F1 \uD074\uB798\uC2A4(text-primary). \uD558\uB4DC\uCF54\uB529 hex/rgb \uB294 \uD14C\uB9C8 \uC804\uD658\uC744 \uAE68\uB728\uB9BD\uB2C8\uB2E4."
|
|
2384
|
+
}));
|
|
2385
|
+
for (const region of [STYLE_OBJECT_RE, STYLE_TAG_RE]) {
|
|
2386
|
+
for (const m of scan.matchAll(region)) {
|
|
2387
|
+
const inner = m[1] ?? "";
|
|
2388
|
+
const innerStart = (m.index ?? 0) + m[0].indexOf(inner);
|
|
2389
|
+
for (const lit of inner.matchAll(INLINE_COLOR_LITERAL_RE)) {
|
|
2390
|
+
push(innerStart + (lit.index ?? 0));
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
#violation(file, index, base) {
|
|
2396
|
+
const before = file.content.slice(0, index);
|
|
2397
|
+
const line = before.length === 0 ? 1 : before.split(`
|
|
2398
|
+
`).length;
|
|
2399
|
+
const lineStart = before.lastIndexOf(`
|
|
2400
|
+
`) + 1;
|
|
2401
|
+
const lineEnd = file.content.indexOf(`
|
|
2402
|
+
`, index);
|
|
2403
|
+
const snippet = file.content.slice(lineStart, lineEnd === -1 ? undefined : lineEnd).trim();
|
|
2404
|
+
return { ...base, path: file.path, line, snippet };
|
|
2405
|
+
}
|
|
2406
|
+
static countNamedComponentClasses(css) {
|
|
2407
|
+
const names = new Set;
|
|
2408
|
+
const layerRe = /@layer\s+components\s*\{/g;
|
|
2409
|
+
for (const layer of css.matchAll(layerRe)) {
|
|
2410
|
+
const bodyStart = (layer.index ?? 0) + layer[0].length;
|
|
2411
|
+
const body = StyleGuard.#extractBalanced(css, bodyStart);
|
|
2412
|
+
for (const sel of body.matchAll(/(^|[\s}])\.(-?[a-zA-Z_][\w-]*)/g))
|
|
2413
|
+
names.add(sel[2]);
|
|
2414
|
+
}
|
|
2415
|
+
return { count: names.size, names: [...names].sort() };
|
|
2416
|
+
}
|
|
2417
|
+
static #extractBalanced(css, start) {
|
|
2418
|
+
let depth = 1;
|
|
2419
|
+
for (let i = start;i < css.length; i++) {
|
|
2420
|
+
const ch = css[i];
|
|
2421
|
+
if (ch === "{")
|
|
2422
|
+
depth++;
|
|
2423
|
+
else if (ch === "}") {
|
|
2424
|
+
depth--;
|
|
2425
|
+
if (depth === 0)
|
|
2426
|
+
return css.slice(start, i);
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
return css.slice(start);
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2220
2432
|
export { AllRoutesBuilder, CsrArtifactBuilder, CssCompiler, FontOptimizer, PagesBundleBuilder, precompressArtifacts, SsrBaseArtifactBuilder };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
ModuleScript
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-vhv3xnjz.js";
|
|
5
5
|
import {
|
|
6
6
|
addFieldUiPolicyForType,
|
|
7
7
|
coerceFieldDefault,
|
|
@@ -34,15 +34,15 @@ import {
|
|
|
34
34
|
sourceFile,
|
|
35
35
|
validationCommandsForTarget,
|
|
36
36
|
viaBuilderParameterName
|
|
37
|
-
} from "./index-
|
|
37
|
+
} from "./index-4zgs8g1w.js";
|
|
38
38
|
import {
|
|
39
39
|
script
|
|
40
|
-
} from "./index-
|
|
40
|
+
} from "./index-jk9q6pse.js";
|
|
41
41
|
import {
|
|
42
42
|
AppExecutor,
|
|
43
43
|
LibExecutor,
|
|
44
44
|
ModuleExecutor
|
|
45
|
-
} from "./index-
|
|
45
|
+
} from "./index-jtm86byd.js";
|
|
46
46
|
|
|
47
47
|
// pkgs/@akanjs/cli/primitive/primitive.script.ts
|
|
48
48
|
import { capitalize } from "akanjs/common";
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
PageScript
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-hjzr8ja1.js";
|
|
5
5
|
import {
|
|
6
6
|
pluralizeName
|
|
7
7
|
} from "./index-ss469dec.js";
|
|
8
|
+
import {
|
|
9
|
+
AiSession
|
|
10
|
+
} from "./index-3pgtw35d.js";
|
|
8
11
|
import {
|
|
9
12
|
bilingualDescriptionForField,
|
|
10
13
|
bilingualLabelForField,
|
|
@@ -12,20 +15,17 @@ import {
|
|
|
12
15
|
generatedFilesForSync,
|
|
13
16
|
moduleSourcePaths,
|
|
14
17
|
sourceFile
|
|
15
|
-
} from "./index-
|
|
18
|
+
} from "./index-4zgs8g1w.js";
|
|
16
19
|
import {
|
|
17
20
|
Prompter
|
|
18
|
-
} from "./index-
|
|
19
|
-
import {
|
|
20
|
-
AiSession
|
|
21
|
-
} from "./index-nsj2ftxj.js";
|
|
21
|
+
} from "./index-xwgm94zr.js";
|
|
22
22
|
import {
|
|
23
23
|
runner,
|
|
24
24
|
script
|
|
25
|
-
} from "./index-
|
|
25
|
+
} from "./index-jk9q6pse.js";
|
|
26
26
|
import {
|
|
27
27
|
ModuleExecutor
|
|
28
|
-
} from "./index-
|
|
28
|
+
} from "./index-jtm86byd.js";
|
|
29
29
|
import {
|
|
30
30
|
FileSys
|
|
31
31
|
} from "./index-67546d0j.js";
|
|
@@ -352,9 +352,9 @@ var requestTemplate = ({
|
|
|
352
352
|
\uCF54\uB529 \uADDC\uCE59
|
|
353
353
|
- \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
354
354
|
- \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
355
|
-
- CSS: tailwind
|
|
355
|
+
- CSS: tailwind + akanjs/ui \uD504\uB9AC\uBBF8\uD2F0\uBE0C(Button/Badge/Input \uB4F1) + \uC2DC\uB9E8\uD2F1 \uD1A0\uD070(bg-background/text-foreground/bg-primary \uB4F1) \uC0AC\uC6A9 (DaisyUI \uAE08\uC9C0)
|
|
356
356
|
- Ui Component: @util/ui \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
357
|
-
- \uC870\uAC74\uBD80 \uD074\uB798\uC2A4:
|
|
357
|
+
- \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: cn \uD568\uC218 \uC0AC\uC6A9 (akanjs/client, cond && 'x' \uD615\uD0DC \u2014 \uAC1D\uCCB4 \uBB38\uBC95 \uC5C6\uC74C)
|
|
358
358
|
\uCF54\uB4DC \uC2A4\uD0C0\uC77C
|
|
359
359
|
- \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
|
|
360
360
|
- \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
|
|
@@ -410,9 +410,9 @@ var requestView = ({
|
|
|
410
410
|
\uCF54\uB529 \uADDC\uCE59
|
|
411
411
|
- \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
412
412
|
- \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
413
|
-
- CSS: tailwind
|
|
413
|
+
- CSS: tailwind + akanjs/ui \uD504\uB9AC\uBBF8\uD2F0\uBE0C(Button/Badge/Input \uB4F1) + \uC2DC\uB9E8\uD2F1 \uD1A0\uD070(bg-background/text-foreground/bg-primary \uB4F1) \uC0AC\uC6A9 (DaisyUI \uAE08\uC9C0)
|
|
414
414
|
- Ui Component: @util/ui \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
415
|
-
- \uC870\uAC74\uBD80 \uD074\uB798\uC2A4:
|
|
415
|
+
- \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: cn \uD568\uC218 \uC0AC\uC6A9 (akanjs/client, cond && 'x' \uD615\uD0DC \u2014 \uAC1D\uCCB4 \uBB38\uBC95 \uC5C6\uC74C)
|
|
416
416
|
\uCF54\uB4DC \uC2A4\uD0C0\uC77C
|
|
417
417
|
- \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
|
|
418
418
|
- \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
|
|
@@ -471,9 +471,9 @@ var requestUnit = ({
|
|
|
471
471
|
\uCF54\uB529 \uADDC\uCE59
|
|
472
472
|
- \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
473
473
|
- \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
474
|
-
- CSS: tailwind
|
|
474
|
+
- CSS: tailwind + akanjs/ui \uD504\uB9AC\uBBF8\uD2F0\uBE0C(Button/Badge/Input \uB4F1) + \uC2DC\uB9E8\uD2F1 \uD1A0\uD070(bg-background/text-foreground/bg-primary \uB4F1) \uC0AC\uC6A9 (DaisyUI \uAE08\uC9C0)
|
|
475
475
|
- Ui Component: @util/ui \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
476
|
-
- \uC870\uAC74\uBD80 \uD074\uB798\uC2A4:
|
|
476
|
+
- \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: cn \uD568\uC218 \uC0AC\uC6A9 (akanjs/client, cond && 'x' \uD615\uD0DC \u2014 \uAC1D\uCCB4 \uBB38\uBC95 \uC5C6\uC74C)
|
|
477
477
|
\uCF54\uB4DC \uC2A4\uD0C0\uC77C
|
|
478
478
|
- \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
|
|
479
479
|
- \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
|
|
@@ -579,7 +579,7 @@ class ModuleRunner extends runner("module") {
|
|
|
579
579
|
await module.sys.applyTemplate({
|
|
580
580
|
basePath: `./lib/${module.name}`,
|
|
581
581
|
template: `module/__Model__.${capitalize2(type)}.tsx`,
|
|
582
|
-
dict: { model: module.name,
|
|
582
|
+
dict: { model: module.name, sysName: module.sys.name, sysType: module.sys.type }
|
|
583
583
|
});
|
|
584
584
|
return {
|
|
585
585
|
component: {
|
|
@@ -612,6 +612,7 @@ class ModuleRunner extends runner("module") {
|
|
|
612
612
|
model: module.name,
|
|
613
613
|
models: names,
|
|
614
614
|
sysName: module.sys.name,
|
|
615
|
+
sysType: module.sys.type,
|
|
615
616
|
modelLabelEn: modelLabel.en,
|
|
616
617
|
modelLabelKo: modelLabel.ko,
|
|
617
618
|
modelDescEn: modelDescription.en,
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// pkgs/@akanjs/devkit/frontendBuild/styleContract.ts
|
|
3
|
+
var countBlocking = (v) => v.style.filter((s) => s.severity === "error").length + v.theme.length;
|
|
4
|
+
var formatStyleContract = (v) => {
|
|
5
|
+
const lines = [];
|
|
6
|
+
for (const s of v.style) {
|
|
7
|
+
lines.push(` [${s.severity}] ${s.rule} ${s.path}:${s.line}`);
|
|
8
|
+
lines.push(` ${s.snippet}`);
|
|
9
|
+
lines.push(` \u2192 ${s.suggestion}`);
|
|
10
|
+
}
|
|
11
|
+
for (const t of v.theme) {
|
|
12
|
+
lines.push(` [error] contrast ${t.scope} ${t.pair} = ${t.ratio}:1 (min ${t.threshold}:1)`);
|
|
13
|
+
lines.push(` \u2192 ${t.suggestion}`);
|
|
14
|
+
}
|
|
15
|
+
return lines.join(`
|
|
16
|
+
`);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// pkgs/@akanjs/devkit/frontendBuild/themeValidator.ts
|
|
20
|
+
var PAIRS = [
|
|
21
|
+
{ base: "background", fg: "foreground", threshold: 4.5 },
|
|
22
|
+
{ base: "primary", fg: "primary-foreground", threshold: 4.5 },
|
|
23
|
+
{ base: "secondary", fg: "secondary-foreground", threshold: 4.5 },
|
|
24
|
+
{ base: "accent", fg: "accent-foreground", threshold: 4.5 },
|
|
25
|
+
{ base: "neutral", fg: "neutral-foreground", threshold: 4.5 },
|
|
26
|
+
{ base: "card", fg: "card-foreground", threshold: 4.5 },
|
|
27
|
+
{ base: "popover", fg: "popover-foreground", threshold: 4.5 },
|
|
28
|
+
{ base: "muted", fg: "muted-foreground", threshold: 3 },
|
|
29
|
+
{ base: "info", fg: "info-foreground", threshold: 3 },
|
|
30
|
+
{ base: "success", fg: "success-foreground", threshold: 3 },
|
|
31
|
+
{ base: "warning", fg: "warning-foreground", threshold: 3 },
|
|
32
|
+
{ base: "destructive", fg: "destructive-foreground", threshold: 3 },
|
|
33
|
+
{ base: "open", fg: "open-foreground", threshold: 3 }
|
|
34
|
+
];
|
|
35
|
+
var THEME_SCOPES = new Set([":root", '[data-theme="dark"]', '[data-theme="light"]']);
|
|
36
|
+
|
|
37
|
+
class ThemeValidator {
|
|
38
|
+
validate(css) {
|
|
39
|
+
const tokensByScope = ThemeValidator.parseThemeTokens(css);
|
|
40
|
+
const violations = [];
|
|
41
|
+
for (const [scope, tokens] of Object.entries(tokensByScope)) {
|
|
42
|
+
if (!THEME_SCOPES.has(scope))
|
|
43
|
+
continue;
|
|
44
|
+
violations.push(...this.validateScope(tokens, scope));
|
|
45
|
+
}
|
|
46
|
+
return violations;
|
|
47
|
+
}
|
|
48
|
+
validateScope(tokens, scope) {
|
|
49
|
+
const violations = [];
|
|
50
|
+
for (const { base, fg, threshold } of PAIRS) {
|
|
51
|
+
const bg = tokens[base];
|
|
52
|
+
const front = tokens[fg];
|
|
53
|
+
if (!bg || !front)
|
|
54
|
+
continue;
|
|
55
|
+
const bgRgb = ThemeValidator.parseHex(bg);
|
|
56
|
+
const fgRgb = ThemeValidator.parseHex(front);
|
|
57
|
+
if (!bgRgb || !fgRgb)
|
|
58
|
+
continue;
|
|
59
|
+
const ratio = ThemeValidator.contrastRatio(bgRgb, fgRgb);
|
|
60
|
+
if (ratio >= threshold)
|
|
61
|
+
continue;
|
|
62
|
+
violations.push({
|
|
63
|
+
scope,
|
|
64
|
+
pair: `${base} / ${fg}`,
|
|
65
|
+
background: bg,
|
|
66
|
+
foreground: front,
|
|
67
|
+
ratio: Math.round(ratio * 100) / 100,
|
|
68
|
+
threshold,
|
|
69
|
+
suggestion: `${scope}\uC758 --${base}(${bg})\uC640 --${fg}(${front}) \uB300\uBE44\uAC00 ${ratio.toFixed(2)}:1 \uB85C \uCD5C\uC18C ${threshold}:1 \uBBF8\uB9CC\uC785\uB2C8\uB2E4. \uD55C\uCABD\uC744 \uB354 \uBC1D\uAC8C/\uC5B4\uB461\uAC8C \uC870\uC815\uD574 \uB300\uBE44\uB97C \uD655\uBCF4\uD558\uC138\uC694.`
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return violations;
|
|
73
|
+
}
|
|
74
|
+
static parseThemeTokens(css) {
|
|
75
|
+
const result = {};
|
|
76
|
+
const blockRe = /(?:^|})\s*([^{}@]+?)\s*\{([^{}]*)\}/g;
|
|
77
|
+
for (const block of css.matchAll(blockRe)) {
|
|
78
|
+
const selectors = block[1].split(",").map((s) => s.trim());
|
|
79
|
+
const relevant = selectors.filter((s) => THEME_SCOPES.has(ThemeValidator.#normalizeScope(s)));
|
|
80
|
+
if (relevant.length === 0)
|
|
81
|
+
continue;
|
|
82
|
+
const decls = {};
|
|
83
|
+
for (const decl of block[2].matchAll(/--([\w-]+)\s*:\s*([^;]+?)\s*(?:;|$)/g)) {
|
|
84
|
+
decls[decl[1]] = decl[2].trim();
|
|
85
|
+
}
|
|
86
|
+
for (const selector of relevant) {
|
|
87
|
+
const scope = ThemeValidator.#normalizeScope(selector);
|
|
88
|
+
result[scope] = { ...result[scope] ?? {}, ...decls };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
static #normalizeScope(selector) {
|
|
94
|
+
return selector.replace(/\[data-theme=['"]?([\w-]+)['"]?\]/g, '[data-theme="$1"]').trim();
|
|
95
|
+
}
|
|
96
|
+
static parseHex(value) {
|
|
97
|
+
const v = value.trim();
|
|
98
|
+
if (!v.startsWith("#"))
|
|
99
|
+
return null;
|
|
100
|
+
const hex = v.slice(1);
|
|
101
|
+
let full;
|
|
102
|
+
if (hex.length === 3 || hex.length === 4)
|
|
103
|
+
full = hex.slice(0, 3).split("").map((c) => c + c).join("");
|
|
104
|
+
else if (hex.length === 6 || hex.length === 8)
|
|
105
|
+
full = hex.slice(0, 6);
|
|
106
|
+
else
|
|
107
|
+
return null;
|
|
108
|
+
if (!/^[0-9a-fA-F]{6}$/.test(full))
|
|
109
|
+
return null;
|
|
110
|
+
const n = Number.parseInt(full, 16);
|
|
111
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
112
|
+
}
|
|
113
|
+
static #relativeLuminance([r, g, b]) {
|
|
114
|
+
const channel = (c) => {
|
|
115
|
+
const s = c / 255;
|
|
116
|
+
return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
117
|
+
};
|
|
118
|
+
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
|
|
119
|
+
}
|
|
120
|
+
static contrastRatio(a, b) {
|
|
121
|
+
const la = ThemeValidator.#relativeLuminance(a);
|
|
122
|
+
const lb = ThemeValidator.#relativeLuminance(b);
|
|
123
|
+
const hi = Math.max(la, lb);
|
|
124
|
+
const lo = Math.min(la, lb);
|
|
125
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { countBlocking, formatStyleContract, ThemeValidator };
|