@akanjs/cli 2.4.2-rc.3 → 3.0.0-alpha.1
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-e5yq60qz.js} +4 -4
- package/{agent.command-de4r6vk8.js → agent.command-yv00p0s0.js} +8 -7
- package/{application.command-nfsfm96y.js → application.command-0qsczf4k.js} +6 -6
- package/{applicationBuildRunner-554a2vnk.js → applicationBuildRunner-ct2pxyvw.js} +3 -2
- package/{applicationReleasePackager-pjbxc793.js → applicationReleasePackager-gg8ccbeh.js} +3 -3
- package/buildBatch.proc.js +8 -7
- package/{capacitorApp-yg9b4ajz.js → capacitorApp-4r9sm3qg.js} +3 -3
- package/{cloud.command-1qf95x67.js → cloud.command-dxfv18sa.js} +10 -10
- package/{context.command-hhjyjtp7.js → context.command-8d9e3k6b.js} +16 -15
- package/{guideline.command-vj956kfr.js → guideline.command-3jbkxzh6.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/guidelines/ssrRule/ssrRule.generate.json +31 -0
- package/guidelines/ssrRule/ssrRule.instruction.md +108 -0
- package/incrementalBuilder.proc.js +6 -6
- package/{index-nsj2ftxj.js → index-0cj2zxbm.js} +3 -3
- package/{index-y1e79mz3.js → index-1pvex4az.js} +13 -13
- package/{index-h971erkm.js → index-2q1f5j9k.js} +3 -3
- package/{index-s9s0n5fz.js → index-7c6k4yvg.js} +2 -2
- package/{index-d0h48d2f.js → index-8hsbnyes.js} +1 -1
- package/{index-v9jqrdd1.js → index-a00jg8x8.js} +100 -2
- package/{index-dgeaqwmw.js → index-bbkek1bp.js} +1 -1
- package/{index-acwxfncv.js → index-bekddz9q.js} +4 -4
- package/{index-fbv96xaw.js → index-e16gnn26.js} +39 -16
- package/{index-z5v766nx.js → index-ec09pws3.js} +6 -6
- package/{index-zn9rh7sh.js → index-ej426mcr.js} +73 -24
- package/{index-xwfg9bam.js → index-hgna5rjy.js} +120 -4
- package/{index-z36qzgzp.js → index-k95xkh4p.js} +3 -4
- package/{index-7ppn9v4n.js → index-mr5s7mqd.js} +3 -3
- package/{index-6pz1j0zj.js → index-mxvakhsm.js} +6 -2
- package/{index-h6eav7zx.js → index-nm6wghb6.js} +6 -6
- package/{index-7k9sj7j5.js → index-pyt2b4nw.js} +212 -0
- package/{index-pfnh87f9.js → index-rzwcpcc3.js} +1 -1
- package/{index-zfp1gz9f.js → index-t3723f99.js} +1 -1
- package/{index-xftstr5s.js → index-tdbas9r0.js} +1 -1
- package/index-xjd3rqzj.js +129 -0
- package/index-xys926f2.js +136 -0
- package/{index-04as43rp.js → index-yzmhasdb.js} +16 -15
- package/{index-h5cvg9z9.js → index-zasydjxd.js} +4 -4
- package/index.js +19 -19
- package/{library.command-68ah07a3.js → library.command-ebdj1vnm.js} +4 -4
- package/{localRegistry.command-84dz000g.js → localRegistry.command-gb178ne8.js} +9 -9
- package/{module.command-t68xhamx.js → module.command-9c1bnsx4.js} +8 -8
- package/{package.command-00ncttab.js → package.command-t93p4jkj.js} +4 -4
- package/package.json +2 -3
- package/{page.command-1096s18n.js → page.command-k3k0meh2.js} +4 -4
- package/{primitive.command-02eqpx6t.js → primitive.command-de6nktdt.js} +9 -9
- package/{quality.command-r9cageqb.js → quality.command-z5kth2wa.js} +451 -48
- package/recipeScanner-tghkzrrt.js +12 -0
- package/{repair.command-7dynpnf1.js → repair.command-ehf2g112.js} +7 -6
- package/{scalar.command-3kxy3fkh.js → scalar.command-7a2jtv33.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/.cursor/rules/client-server-boundaries.mdc.template +1 -0
- package/templates/workspaceRoot/.cursor/rules/ssr-first.mdc.template +59 -0
- package/templates/workspaceRoot/AGENTS.md.template +139 -20
- package/templates/workspaceRoot/biome.json.template +1 -1
- package/{workflow.command-yy0d9gk3.js → workflow.command-e0tt0vk2.js} +15 -14
- package/{workspace.command-stv9g4a0.js → workspace.command-fpda0evj.js} +87 -27
|
@@ -1,26 +1,37 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
AkanContextAnalyzer
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-a00jg8x8.js";
|
|
5
5
|
import {
|
|
6
6
|
Prompter
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-tdbas9r0.js";
|
|
8
8
|
import {
|
|
9
9
|
runner,
|
|
10
10
|
script
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-8hsbnyes.js";
|
|
12
|
+
import {
|
|
13
|
+
AGENT_BLOCK_END,
|
|
14
|
+
AGENT_BLOCK_START,
|
|
15
|
+
AppExecutor,
|
|
16
|
+
LibExecutor,
|
|
17
|
+
renderRecipeEntries,
|
|
18
|
+
upsertAgentBlock
|
|
19
|
+
} from "./index-hgna5rjy.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
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
import {
|
|
3
|
+
Spinner
|
|
4
|
+
} from "./index-mxvakhsm.js";
|
|
2
5
|
import {
|
|
3
6
|
FileSys
|
|
4
7
|
} from "./index-67546d0j.js";
|
|
5
|
-
import {
|
|
6
|
-
Spinner
|
|
7
|
-
} from "./index-6pz1j0zj.js";
|
|
8
8
|
import {
|
|
9
9
|
__require
|
|
10
10
|
} from "./index-r24hmh0q.js";
|
|
@@ -39,6 +39,109 @@ import {
|
|
|
39
39
|
var {$ } = globalThis.Bun;
|
|
40
40
|
import chalk2 from "chalk";
|
|
41
41
|
|
|
42
|
+
// pkgs/@akanjs/devkit/agentsIndex.ts
|
|
43
|
+
var recipeScannerLoad = null;
|
|
44
|
+
var loadRecipeScanner = () => recipeScannerLoad ??= import("./recipeScanner-tghkzrrt.js");
|
|
45
|
+
var AGENT_BLOCK_START = "<!-- akan:agent:start -->";
|
|
46
|
+
var AGENT_BLOCK_END = "<!-- akan:agent:end -->";
|
|
47
|
+
var upsertAgentBlock = (existing, block) => {
|
|
48
|
+
const managed = `${AGENT_BLOCK_START}
|
|
49
|
+
${block}
|
|
50
|
+
${AGENT_BLOCK_END}`;
|
|
51
|
+
const startIndex = existing.indexOf(AGENT_BLOCK_START);
|
|
52
|
+
const endIndex = existing.indexOf(AGENT_BLOCK_END);
|
|
53
|
+
if (startIndex >= 0 && endIndex > startIndex) {
|
|
54
|
+
return `${existing.slice(0, startIndex)}${managed}${existing.slice(endIndex + AGENT_BLOCK_END.length)}`;
|
|
55
|
+
}
|
|
56
|
+
return `${existing.replace(/\s*$/, "")}
|
|
57
|
+
|
|
58
|
+
${managed}
|
|
59
|
+
`;
|
|
60
|
+
};
|
|
61
|
+
var extractAgentBlock = (content) => {
|
|
62
|
+
const startIndex = content.indexOf(AGENT_BLOCK_START);
|
|
63
|
+
const endIndex = content.indexOf(AGENT_BLOCK_END);
|
|
64
|
+
if (startIndex < 0 || endIndex <= startIndex)
|
|
65
|
+
return null;
|
|
66
|
+
return content.slice(startIndex + AGENT_BLOCK_START.length, endIndex).trim();
|
|
67
|
+
};
|
|
68
|
+
var signatureOf = (recipe) => {
|
|
69
|
+
const entries = Object.entries(recipe.variants);
|
|
70
|
+
if (entries.length === 0)
|
|
71
|
+
return "";
|
|
72
|
+
const parts = entries.map(([key, values]) => {
|
|
73
|
+
if (values.length === 1 && values[0] === "true")
|
|
74
|
+
return `${key}?`;
|
|
75
|
+
const def = recipe.defaultVariants?.[key];
|
|
76
|
+
return `${key}: ${values.map((value) => value === def ? `${value}*` : value).join("|")}`;
|
|
77
|
+
});
|
|
78
|
+
return `(${parts.join(" \xB7 ")})`;
|
|
79
|
+
};
|
|
80
|
+
var renderRecipeEntries = (recipes) => {
|
|
81
|
+
const groups = new Map;
|
|
82
|
+
for (const recipe of recipes)
|
|
83
|
+
groups.set(recipe.importFrom, [...groups.get(recipe.importFrom) ?? [], recipe]);
|
|
84
|
+
return [...groups.entries()].map(([importFrom, list]) => {
|
|
85
|
+
const items = list.sort((a, b) => a.name.localeCompare(b.name)).map((recipe) => `- \`${recipe.name}\`${signatureOf(recipe)}${recipe.doc ? ` \u2014 ${recipe.doc}` : ""}`).join(`
|
|
86
|
+
`);
|
|
87
|
+
return `Import from \`${importFrom}\`:
|
|
88
|
+
${items}`;
|
|
89
|
+
}).join(`
|
|
90
|
+
|
|
91
|
+
`);
|
|
92
|
+
};
|
|
93
|
+
var collectScopeRecipeSources = async (workspaceRoot, scope, libDeps) => {
|
|
94
|
+
const { collectRecipeSources } = await loadRecipeScanner();
|
|
95
|
+
const sources = [
|
|
96
|
+
...await collectRecipeSources(`${workspaceRoot}/${scope.type}s/${scope.name}/ui`, `@${scope.type}s/${scope.name}/ui`)
|
|
97
|
+
];
|
|
98
|
+
for (const lib of [...new Set(libDeps)].sort()) {
|
|
99
|
+
if (scope.type === "lib" && lib === scope.name)
|
|
100
|
+
continue;
|
|
101
|
+
sources.push(...await collectRecipeSources(`${workspaceRoot}/libs/${lib}/ui`, `@libs/${lib}/ui`));
|
|
102
|
+
}
|
|
103
|
+
return sources;
|
|
104
|
+
};
|
|
105
|
+
var scanScopeRecipes = async (workspaceRoot, scope, libDeps) => {
|
|
106
|
+
const { scanRecipes } = await loadRecipeScanner();
|
|
107
|
+
return scanRecipes(await collectScopeRecipeSources(workspaceRoot, scope, libDeps));
|
|
108
|
+
};
|
|
109
|
+
var renderScopeAgentBlock = (scope, recipes) => {
|
|
110
|
+
const scopePath = `${scope.type}s/${scope.name}`;
|
|
111
|
+
const intro = `## Recipes In Scope
|
|
112
|
+
|
|
113
|
+
UI recipes importable from \`${scopePath}\` code, **in addition to** the framework recipes indexed in the root
|
|
114
|
+
\`AGENTS.md\` \`## Recipes\`. Same contract: import by exact name, then \`<name>(variants?, className?)\` \u2014 the second
|
|
115
|
+
arg merges internally and takes an array too, so never wrap it in \`cn()\`. \`*\` marks the default, \`key?\` is a
|
|
116
|
+
boolean flag. Do not guess recipe names or import paths; this index is regenerated by \`akan sync ${scope.name}\`
|
|
117
|
+
and verified by \`akan lint ${scope.name}\`.`;
|
|
118
|
+
if (recipes.length === 0) {
|
|
119
|
+
return `${intro}
|
|
120
|
+
|
|
121
|
+
No scope recipes yet. Before inlining a repeated surface (card, box, tile, \u2026), reuse a framework recipe from the
|
|
122
|
+
root \`AGENTS.md\` or author one as \`${scopePath}/ui/Recipe/<name>.ts\` (one recipe per file, re-exported from that
|
|
123
|
+
folder's \`index.ts\`) \u2014 see the \`recipeRule\` guideline.`;
|
|
124
|
+
}
|
|
125
|
+
return `${intro}
|
|
126
|
+
|
|
127
|
+
${renderRecipeEntries(recipes)}`;
|
|
128
|
+
};
|
|
129
|
+
var renderScopeAgentsMd = (scope, block) => `# ${scope.name} \u2014 Agent Guide
|
|
130
|
+
|
|
131
|
+
Scoped guide for coding agents working in \`${scope.type}s/${scope.name}\`. Workspace-wide conventions live in the
|
|
132
|
+
root \`AGENTS.md\`; this file carries what is importable from this ${scope.type === "app" ? "app" : "library"}. The
|
|
133
|
+
section between the \`akan:agent\` markers is regenerated by \`akan sync ${scope.name}\`; edit anything outside the
|
|
134
|
+
markers freely.
|
|
135
|
+
|
|
136
|
+
${AGENT_BLOCK_START}
|
|
137
|
+
${block}
|
|
138
|
+
${AGENT_BLOCK_END}
|
|
139
|
+
`;
|
|
140
|
+
var renderScopeClaudeMd = (scope) => `# ${scope.name} \u2014 Claude Code Guide
|
|
141
|
+
|
|
142
|
+
@AGENTS.md
|
|
143
|
+
`;
|
|
144
|
+
|
|
42
145
|
// pkgs/@akanjs/devkit/akanConfig/akanConfig.ts
|
|
43
146
|
import fs from "fs";
|
|
44
147
|
import path from "path";
|
|
@@ -824,6 +927,8 @@ var databaseFileTypes = [
|
|
|
824
927
|
"zone"
|
|
825
928
|
];
|
|
826
929
|
var appRootAllowedFiles = new Set([
|
|
930
|
+
"AGENTS.md",
|
|
931
|
+
"CLAUDE.md",
|
|
827
932
|
"akan.app.json",
|
|
828
933
|
"akan.config.ts",
|
|
829
934
|
"capacitor.config.ts",
|
|
@@ -2252,10 +2357,21 @@ class SysExecutor extends Executor {
|
|
|
2252
2357
|
await this.#updateDependencies(scanInfo);
|
|
2253
2358
|
await Promise.all(libInfos.flatMap((libInfo) => libInfo.exec.#getScanTemplateTasks(libInfo)));
|
|
2254
2359
|
}
|
|
2360
|
+
await this.syncAgentsIndex(scanInfo);
|
|
2255
2361
|
}
|
|
2256
2362
|
this.#scanInfo = scanInfo;
|
|
2257
2363
|
return scanInfo;
|
|
2258
2364
|
}
|
|
2365
|
+
async syncAgentsIndex(scanInfo) {
|
|
2366
|
+
const info = scanInfo ?? await this.scan({ write: false });
|
|
2367
|
+
const scope = { type: this.type, name: this.name };
|
|
2368
|
+
const recipes = await scanScopeRecipes(this.workspace.workspaceRoot, scope, info.getScanResult().libDeps);
|
|
2369
|
+
const block = renderScopeAgentBlock(scope, recipes);
|
|
2370
|
+
const existing = await this.exists("AGENTS.md") ? await this.readFile("AGENTS.md") : null;
|
|
2371
|
+
await this.writeFile("AGENTS.md", existing?.trim() ? upsertAgentBlock(existing, block) : renderScopeAgentsMd(scope, block));
|
|
2372
|
+
if (!await this.exists("CLAUDE.md"))
|
|
2373
|
+
await this.writeFile("CLAUDE.md", renderScopeClaudeMd(scope));
|
|
2374
|
+
}
|
|
2259
2375
|
async#updateDependencies(scanInfo) {
|
|
2260
2376
|
const rootPackageJson = await this.workspace.getPackageJson();
|
|
2261
2377
|
const libPackageJson = await this.getPackageJson();
|
|
@@ -2887,4 +3003,4 @@ class ModuleExecutor extends Executor {
|
|
|
2887
3003
|
}
|
|
2888
3004
|
}
|
|
2889
3005
|
|
|
2890
|
-
export { getDirname, CommandExecutionError, Executor, WorkspaceExecutor, AppExecutor, LibExecutor, PkgExecutor, ModuleExecutor };
|
|
3006
|
+
export { AGENT_BLOCK_START, AGENT_BLOCK_END, upsertAgentBlock, extractAgentBlock, renderRecipeEntries, collectScopeRecipeSources, renderScopeAgentBlock, getDirname, CommandExecutionError, Executor, WorkspaceExecutor, SysExecutor, AppExecutor, LibExecutor, PkgExecutor, ModuleExecutor };
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
runner,
|
|
4
4
|
script
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-8hsbnyes.js";
|
|
6
6
|
import {
|
|
7
7
|
PkgExecutor
|
|
8
|
-
} from "./index-
|
|
8
|
+
} from "./index-hgna5rjy.js";
|
|
9
9
|
import {
|
|
10
10
|
TypeScriptDependencyScanner
|
|
11
11
|
} from "./index-46tjzh6s.js";
|
|
@@ -153,14 +153,13 @@ class PackageRunner extends runner("package") {
|
|
|
153
153
|
const scanner = await TypeScriptDependencyScanner.from(pkg);
|
|
154
154
|
const { npmDeps, npmDevDeps, missingDeps } = await scanner.getPackageBuildDependencies(pkg.name);
|
|
155
155
|
const packageRuntimeDependencies = {
|
|
156
|
-
"@akanjs/devkit": ["
|
|
156
|
+
"@akanjs/devkit": ["tailwind-scrollbar"]
|
|
157
157
|
};
|
|
158
158
|
const packageRuntimeDevDependencies = { akanjs: ["@biomejs/biome", "@types/bun"] };
|
|
159
159
|
if (pkg.name === "@akanjs/cli") {
|
|
160
160
|
const devkitPackageJson = await pkg.workspace.readJson("pkgs/@akanjs/devkit/package.json");
|
|
161
161
|
packageRuntimeDependencies[pkg.name] = [
|
|
162
162
|
...Object.keys(devkitPackageJson.dependencies ?? {}),
|
|
163
|
-
"daisyui",
|
|
164
163
|
"tailwind-scrollbar"
|
|
165
164
|
].filter((dep) => dep !== "akanjs" && dep !== "@akanjs/devkit");
|
|
166
165
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
AkanContextAnalyzer
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-a00jg8x8.js";
|
|
5
5
|
import {
|
|
6
6
|
createRepairReport,
|
|
7
7
|
generatedFilePathsForTarget,
|
|
8
8
|
renderRepairReport,
|
|
9
9
|
writeGeneratedSyncState,
|
|
10
10
|
writeWorkflowRunArtifact
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-rzwcpcc3.js";
|
|
12
12
|
import {
|
|
13
13
|
runner
|
|
14
|
-
} from "./index-
|
|
14
|
+
} from "./index-8hsbnyes.js";
|
|
15
15
|
|
|
16
16
|
// pkgs/@akanjs/cli/repair/repair.runner.ts
|
|
17
17
|
var commandForShell = (command) => command.startsWith("akan ") ? `bun run ${command}` : command;
|
|
@@ -4,6 +4,10 @@ import ora from "ora";
|
|
|
4
4
|
|
|
5
5
|
class Spinner {
|
|
6
6
|
static padding = 12;
|
|
7
|
+
static oraOptions = { discardStdin: false };
|
|
8
|
+
static canAnimate(stream = process.stderr) {
|
|
9
|
+
return !stream.isTTY || stream.columns > 0;
|
|
10
|
+
}
|
|
7
11
|
spinner;
|
|
8
12
|
stopWatch = null;
|
|
9
13
|
startAt = new Date;
|
|
@@ -14,10 +18,10 @@ class Spinner {
|
|
|
14
18
|
Spinner.padding = Math.max(Spinner.padding, prefix.length);
|
|
15
19
|
this.prefix = prefix;
|
|
16
20
|
this.message = message;
|
|
17
|
-
this.spinner = ora(message);
|
|
21
|
+
this.spinner = ora({ ...Spinner.oraOptions, text: message });
|
|
18
22
|
this.spinner.prefixText = prefix.padStart(Spinner.padding, " ");
|
|
19
23
|
this.spinner.indent = indent;
|
|
20
|
-
this.enableSpin = enableSpin;
|
|
24
|
+
this.enableSpin = enableSpin && Spinner.canAnimate();
|
|
21
25
|
}
|
|
22
26
|
start() {
|
|
23
27
|
this.startAt = new Date;
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
CloudRunner
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-zasydjxd.js";
|
|
5
5
|
import {
|
|
6
6
|
PackageScript
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-k95xkh4p.js";
|
|
8
8
|
import {
|
|
9
9
|
AiSession,
|
|
10
10
|
CloudApi,
|
|
11
11
|
GlobalConfig
|
|
12
|
-
} from "./index-
|
|
12
|
+
} from "./index-0cj2zxbm.js";
|
|
13
13
|
import {
|
|
14
14
|
ApplicationScript
|
|
15
|
-
} from "./index-
|
|
15
|
+
} from "./index-e16gnn26.js";
|
|
16
16
|
import {
|
|
17
17
|
script
|
|
18
|
-
} from "./index-
|
|
18
|
+
} from "./index-8hsbnyes.js";
|
|
19
19
|
import {
|
|
20
20
|
PkgExecutor
|
|
21
|
-
} from "./index-
|
|
21
|
+
} from "./index-hgna5rjy.js";
|
|
22
22
|
|
|
23
23
|
// pkgs/@akanjs/cli/cloud/cloud.script.ts
|
|
24
24
|
import { Logger } from "akanjs/common";
|
|
@@ -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 };
|