arckode-ui 0.1.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/dist/vite.js ADDED
@@ -0,0 +1,366 @@
1
+ var y = Object.defineProperty;
2
+ var C = (r, t, e) => t in r ? y(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
3
+ var x = (r, t, e) => C(r, typeof t != "symbol" ? t + "" : t, e);
4
+ import { transformWithEsbuild as b } from "vite";
5
+ import { a as N, f as v } from "./analyzer-Ctnj3WTI.js";
6
+ class h extends Error {
7
+ constructor(e, n, s) {
8
+ super(n);
9
+ x(this, "code");
10
+ x(this, "line");
11
+ this.name = "ArkParseError", this.code = e, this.line = s, Object.setPrototypeOf(this, h.prototype);
12
+ }
13
+ }
14
+ const k = /<(template|script|style)((?:\s+[^>]*)??)>([\s\S]*?)<\/\1>/g;
15
+ function O(r, t) {
16
+ let e = 1;
17
+ for (let n = 0; n < t; n++)
18
+ r[n] === `
19
+ ` && e++;
20
+ return e;
21
+ }
22
+ function T(r, t) {
23
+ const n = new RegExp(`${t}=["']([^"']*)["']`).exec(r);
24
+ return n != null ? n[1] ?? null : null;
25
+ }
26
+ function R(r, t) {
27
+ return new RegExp(`(?:^|\\s)${t}(?:\\s|$|=)`).test(r);
28
+ }
29
+ function L(r) {
30
+ const t = [];
31
+ let e;
32
+ for (k.lastIndex = 0; (e = k.exec(r)) !== null; ) {
33
+ const i = e[1], u = e[2] ?? "", d = e[3] ?? "", m = O(r, e.index);
34
+ i !== void 0 && t.push({ tag: i, attrs: u, content: d, openLine: m });
35
+ }
36
+ const n = t.filter((i) => i.tag === "template"), s = t.filter((i) => i.tag === "script"), c = t.filter((i) => i.tag === "style");
37
+ if (n.length > 1 || s.length > 1) {
38
+ const i = n.length > 1 ? n[1] : s[1];
39
+ throw new h(
40
+ "DUPLICATE_SECTION",
41
+ `Duplicate <${i.tag}> section`,
42
+ i.openLine
43
+ );
44
+ }
45
+ if (n.length === 0)
46
+ throw new h(
47
+ "MISSING_TEMPLATE",
48
+ "Missing <template> section",
49
+ 1
50
+ );
51
+ if (s.length === 0)
52
+ throw new h(
53
+ "MISSING_SCRIPT",
54
+ "Missing <script> section",
55
+ 1
56
+ );
57
+ const p = n[0], o = s[0];
58
+ if (o.openLine < p.openLine)
59
+ throw new h(
60
+ "WRONG_ORDER",
61
+ "<script> must appear after <template>",
62
+ o.openLine
63
+ );
64
+ const l = T(o.attrs, "lang");
65
+ if (l !== "ts")
66
+ throw new h(
67
+ "MISSING_LANG_TS",
68
+ '<script> must have lang="ts"',
69
+ o.openLine
70
+ );
71
+ let f;
72
+ if (c.length > 0) {
73
+ const i = c[0], u = R(i.attrs, "scoped");
74
+ f = { content: i.content, scoped: u };
75
+ }
76
+ return {
77
+ template: {
78
+ content: p.content,
79
+ start: p.openLine
80
+ },
81
+ script: {
82
+ content: o.content,
83
+ lang: l
84
+ },
85
+ ...f !== void 0 ? { style: f } : {}
86
+ };
87
+ }
88
+ const E = /(?:(@[\w.]+|:[a-zA-Z][\w-]*|v-else-if|v-if|v-for|v-show|v-else|[\w-]+)(?:="([^"]*)")?)/g;
89
+ function j(r) {
90
+ const t = [];
91
+ E.lastIndex = 0;
92
+ let e;
93
+ for (; (e = E.exec(r)) !== null; )
94
+ t.push({ name: e[1] ?? "", value: e[2] ?? "", raw: e[0] });
95
+ return t;
96
+ }
97
+ const I = /<(\/?)([a-zA-Z][a-zA-Z0-9-]*)((?:"[^"]*"|[^>])*)(\/?)>|([^<]+)/g;
98
+ function W(r) {
99
+ const t = [], e = [];
100
+ I.lastIndex = 0;
101
+ let n;
102
+ const s = /* @__PURE__ */ new Set([
103
+ "area",
104
+ "base",
105
+ "br",
106
+ "col",
107
+ "embed",
108
+ "hr",
109
+ "img",
110
+ "input",
111
+ "link",
112
+ "meta",
113
+ "param",
114
+ "source",
115
+ "track",
116
+ "wbr"
117
+ ]);
118
+ for (; (n = I.exec(r)) !== null; ) {
119
+ const c = n[1] === "/", p = n[2], o = (n[3] ?? "").trim(), l = o.endsWith("/"), f = l ? o.slice(0, -1).trim() : o, i = l || n[4] === "/" || p !== void 0 && s.has(p.toLowerCase()), u = n[5];
120
+ if (u !== void 0) {
121
+ const d = u;
122
+ d.length > 0 && (e.length > 0 ? e[e.length - 1].children : t).push({ kind: "text", text: d });
123
+ continue;
124
+ }
125
+ if (p !== void 0)
126
+ if (c) {
127
+ if (e.length > 0) {
128
+ const d = e.pop();
129
+ (e.length > 0 ? e[e.length - 1].children : t).push({ kind: "element", el: d });
130
+ }
131
+ } else {
132
+ const d = {
133
+ tag: p,
134
+ attrs: j(f),
135
+ children: [],
136
+ selfClosing: i
137
+ };
138
+ i ? (e.length > 0 ? e[e.length - 1].children : t).push({ kind: "element", el: d }) : e.push(d);
139
+ }
140
+ }
141
+ for (; e.length > 0; ) {
142
+ const c = e.pop();
143
+ (e.length > 0 ? e[e.length - 1].children : t).push({ kind: "element", el: c });
144
+ }
145
+ return t;
146
+ }
147
+ const $ = /\{\{\s*([\s\S]+?)\s*\}\}/g;
148
+ function z(r) {
149
+ return $.lastIndex = 0, $.test(r);
150
+ }
151
+ function M(r) {
152
+ $.lastIndex = 0;
153
+ const t = [];
154
+ let e = 0, n;
155
+ for ($.lastIndex = 0; (n = $.exec(r)) !== null; )
156
+ n.index > e && t.push({ kind: "text", value: r.slice(e, n.index) }), t.push({ kind: "expr", value: n[1] }), e = n.index + n[0].length;
157
+ return e < r.length && t.push({ kind: "text", value: r.slice(e) }), t.length === 0 ? JSON.stringify(r) : t.length === 1 && t[0].kind === "expr" ? t[0].value : "`" + t.map((c) => c.kind === "text" ? c.value : `\${${c.value}}`).join("") + "`";
158
+ }
159
+ const D = {
160
+ enter: "Enter",
161
+ escape: "Escape",
162
+ esc: "Escape",
163
+ tab: "Tab",
164
+ space: " ",
165
+ up: "ArrowUp",
166
+ down: "ArrowDown",
167
+ left: "ArrowLeft",
168
+ right: "ArrowRight",
169
+ delete: "Delete",
170
+ backspace: "Backspace"
171
+ };
172
+ function J(r, t) {
173
+ const e = r.slice(1), [n, ...s] = e.split("."), p = `on${n.charAt(0).toUpperCase() + n.slice(1)}`;
174
+ if (s.length === 0)
175
+ return `${p}: ${t}`;
176
+ const o = s[0].toLowerCase(), l = D[o] ?? o.charAt(0).toUpperCase() + o.slice(1);
177
+ return `${p}: (e) => e.key === '${l}' && ${t}(e)`;
178
+ }
179
+ function P(r) {
180
+ const { name: t, value: e } = r;
181
+ return t.startsWith("@") ? J(t, e) : t.startsWith(":") ? `${t.slice(1)}: ${e}` : ["v-if", "v-for", "v-else", "v-else-if"].includes(t) ? null : t === "v-show" ? `style: { display: ${e} ? '' : 'none' }` : e === "" ? `${t}: true` : `${t}: ${JSON.stringify(e)}`;
182
+ }
183
+ function V(r) {
184
+ const t = r;
185
+ return z(t) ? `h('span', null, ${M(t)})` : /^\s+$/.test(t) ? "" : JSON.stringify(t);
186
+ }
187
+ function w(r) {
188
+ const t = r.attrs.find((f) => f.name === "v-for");
189
+ if (r.tag === "slot")
190
+ return "(props.__slot_default ? props.__slot_default() : [])";
191
+ const e = r.attrs.map(P).filter((f) => f !== null && f !== ""), n = e.length === 0 ? "null" : `{ ${e.join(", ")} }`, s = A(r.children);
192
+ let l = `h(${[/^[A-Z]/.test(r.tag) ? r.tag : JSON.stringify(r.tag), n, ...s].join(", ")})`;
193
+ return t !== void 0 && (l = G(t.value, l)), l;
194
+ }
195
+ function A(r) {
196
+ const t = [];
197
+ let e = 0;
198
+ for (; e < r.length; ) {
199
+ const n = r[e];
200
+ if (n.kind === "text") {
201
+ const o = V(n.text);
202
+ o !== "" && t.push(o), e++;
203
+ continue;
204
+ }
205
+ const s = n.el, c = s.attrs.find((o) => o.name === "v-if");
206
+ if (!c && s.attrs.some((o) => o.name === "v-else" || o.name === "v-else-if")) {
207
+ e++;
208
+ continue;
209
+ }
210
+ if (c) {
211
+ let o = `${c.value} ? ${w(s)} : `;
212
+ for (e++; e < r.length; ) {
213
+ const l = r[e];
214
+ if (l.kind === "text" && /^\s+$/.test(l.text)) {
215
+ e++;
216
+ continue;
217
+ }
218
+ if (l.kind !== "element") break;
219
+ const f = l.el.attrs.find((u) => u.name === "v-else-if"), i = l.el.attrs.some((u) => u.name === "v-else");
220
+ if (f)
221
+ o += `${f.value} ? ${w(l.el)} : `, e++;
222
+ else if (i) {
223
+ o += w(l.el), e++;
224
+ break;
225
+ } else
226
+ break;
227
+ }
228
+ o.endsWith(" : ") && (o += "null"), t.push(o);
229
+ continue;
230
+ }
231
+ t.push(w(s)), e++;
232
+ }
233
+ return t;
234
+ }
235
+ function G(r, t) {
236
+ const e = /^\s*(?:\(([^)]+)\)|(\w+))\s+in\s+(.+?)\s*$/.exec(r);
237
+ if (!e)
238
+ return `(${r}).map((item) => ${t})`;
239
+ const n = e[1] ?? e[2] ?? "item";
240
+ return `${e[3].trim()}.map((${n}) => ${t})`;
241
+ }
242
+ function F(r) {
243
+ const t = W(r.trim()), e = A(t).filter((s) => s !== "");
244
+ let n;
245
+ return e.length === 0 ? n = "null" : e.length === 1 ? n = e[0] : n = `[${e.join(", ")}]`, `function h(tag, props, ...children) {
246
+ return { tag, props: props ?? {}, children: children.flat().filter(c => c != null) }
247
+ }
248
+
249
+ export default function render(props, state, computed, actions) {
250
+ return ${n}
251
+ }
252
+ `;
253
+ }
254
+ function U(r) {
255
+ let t = 5381;
256
+ for (let n = 0; n < r.length; n++)
257
+ t = (t << 5) + t ^ r.charCodeAt(n), t = t | 0;
258
+ return (t >>> 0).toString(36).padStart(6, "0").slice(-6);
259
+ }
260
+ function Z(r) {
261
+ const t = /export\s+default\s+(defineComponent\s*\([\s\S]*\))\s*;?\s*$/, e = r.trim(), n = t.exec(e);
262
+ return n ? {
263
+ preamble: e.slice(0, n.index).trim(),
264
+ defineComponentExpr: n[1]
265
+ } : {
266
+ preamble: e,
267
+ defineComponentExpr: null
268
+ };
269
+ }
270
+ function K(r, t, e) {
271
+ const s = F(t).replace(/export default function render/, "function render").replace(/function h\(tag, props, \.\.\.children\) \{\n return [^\n]+\n\}\n+/, "").trim(), { preamble: c, defineComponentExpr: p } = Z(r);
272
+ return p ? [
273
+ "import { defineComponent, h } from 'arckode-ui'",
274
+ "",
275
+ c,
276
+ "",
277
+ "// [compiled render function — generated from <template>]",
278
+ s,
279
+ "",
280
+ `// [scope id: ${e}]`,
281
+ `const __scopeId = ${JSON.stringify(e)}`,
282
+ "",
283
+ `const __component = ${p}`,
284
+ "__component.__scopeId = __scopeId",
285
+ "__component.__render = render",
286
+ "",
287
+ "export default __component"
288
+ ].filter((o) => o !== void 0).join(`
289
+ `) : [
290
+ "import { defineComponent } from 'arckode-ui'",
291
+ "",
292
+ c,
293
+ "",
294
+ "// [compiled render function — generated from <template>]",
295
+ s,
296
+ "",
297
+ `export default { __render: render, __scopeId: ${JSON.stringify(e)} }`
298
+ ].join(`
299
+ `);
300
+ }
301
+ function H(r = {}) {
302
+ let t = "development";
303
+ return {
304
+ name: "arckode-ui",
305
+ enforce: "pre",
306
+ configResolved(e) {
307
+ t = e.mode;
308
+ },
309
+ transform(e, n) {
310
+ var S, _;
311
+ if (!n.endsWith(".ark")) return null;
312
+ let s;
313
+ try {
314
+ s = L(e);
315
+ } catch (a) {
316
+ if (a instanceof h) {
317
+ const g = [
318
+ `[arckode-ui] Parse error in ${n}`,
319
+ ` ${a.code} (line ${a.line}): ${a.message}`,
320
+ "",
321
+ " Fix the .ark file structure before continuing."
322
+ ].join(`
323
+ `);
324
+ throw new Error(g);
325
+ }
326
+ throw a;
327
+ }
328
+ const c = ((S = r.analyzer) == null ? void 0 : S.ignore) ?? [], p = ((_ = r.analyzer) == null ? void 0 : _.failOnWarnings) ?? !1, l = N(e).filter((a) => !c.includes(a.code)), f = l.filter((a) => a.severity === "error"), i = l.filter((a) => a.severity === "warning"), u = t === "production";
329
+ for (const a of i)
330
+ console.warn(v(a, n, e));
331
+ if (f.length > 0) {
332
+ const a = f.map((g) => v(g, n, e)).join(`
333
+
334
+ `);
335
+ if (u)
336
+ throw new Error(
337
+ `[arckode-ui] ${f.length} error(s) in ${n}:
338
+
339
+ ${a}`
340
+ );
341
+ console.warn(`[arckode-ui] Analyzer errors in ${n} (not blocking in dev):
342
+
343
+ ${a}`);
344
+ }
345
+ if (p && u && i.length > 0) {
346
+ const a = i.map((g) => v(g, n, e)).join(`
347
+
348
+ `);
349
+ throw new Error(
350
+ `[arckode-ui] ${i.length} warning(s) treated as errors (failOnWarnings=true) in ${n}:
351
+
352
+ ${a}`
353
+ );
354
+ }
355
+ const d = U(n), m = K(s.script.content, s.template.content, d);
356
+ return b(m, n + ".ts", {
357
+ loader: "ts",
358
+ target: "esnext"
359
+ }).then((a) => ({ code: a.code, map: a.map }));
360
+ }
361
+ };
362
+ }
363
+ export {
364
+ H as arkcodeUi,
365
+ U as generateScopeId
366
+ };
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "arckode-ui",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Frontend framework con .ark SFCs, signals, file-system router y analyzer estático con sugerencias concretas de fix. Diseñado para máxima predictibilidad de output de IA.",
6
+ "keywords": [
7
+ "framework",
8
+ "frontend",
9
+ "signals",
10
+ "reactivity",
11
+ "sfc",
12
+ "vite-plugin",
13
+ "ai-friendly",
14
+ "typescript",
15
+ "ark",
16
+ "arckode"
17
+ ],
18
+ "author": "underworf",
19
+ "license": "MIT",
20
+ "homepage": "https://gitlab.com/underworf/arckode-ui",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://gitlab.com/underworf/arckode-ui.git"
24
+ },
25
+ "bugs": {
26
+ "url": "https://gitlab.com/underworf/arckode-ui/-/issues"
27
+ },
28
+ "exports": {
29
+ ".": {
30
+ "import": "./dist/index.js",
31
+ "types": "./dist/index.d.ts"
32
+ },
33
+ "./vite": {
34
+ "import": "./dist/vite.js",
35
+ "types": "./dist/vite.d.ts"
36
+ }
37
+ },
38
+ "main": "./dist/index.js",
39
+ "types": "./dist/index.d.ts",
40
+ "bin": {
41
+ "ark": "./dist/cli.js"
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "skills",
46
+ "README.md",
47
+ "LICENSE"
48
+ ],
49
+ "scripts": {
50
+ "build": "vite build && chmod +x dist/cli.js",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest",
53
+ "type-check": "tsc --noEmit",
54
+ "prepublishOnly": "bun run build && bun run test && bun run type-check"
55
+ },
56
+ "peerDependencies": {
57
+ "typescript": ">=5.0.0",
58
+ "vite": ">=5.0.0"
59
+ },
60
+ "devDependencies": {
61
+ "@types/bun": "latest",
62
+ "happy-dom": "^20.9.0",
63
+ "typescript": "^5.4.0",
64
+ "vite": "^5.4.0",
65
+ "vitest": "^2.0.0"
66
+ }
67
+ }
@@ -0,0 +1,128 @@
1
+ # SKILL: Analyzer — reglas, fix suggestions, agregar nueva
2
+
3
+ > Cargá este skill cuando trabajés en `src/compiler/analyzer.ts` o cuando necesites agregar una nueva regla de validación estática.
4
+
5
+ ## Interfaz Violation
6
+
7
+ ```typescript
8
+ export interface Violation {
9
+ code: string // ej: 'HANDLER_NOT_IN_ACTIONS'
10
+ severity: 'error' | 'warning' // error bloquea build en prod
11
+ line: number // 1-based
12
+ message: string // descripción en español
13
+ fix?: string // sugerencia concreta (sino, undefined)
14
+ }
15
+ ```
16
+
17
+ **Toda violation nueva DEBE incluir `fix` cuando el fix es determinístico.** La IA lee el fix y aplica directo, sin cruzar tablas externas.
18
+
19
+ ## Las 16 reglas implementadas
20
+
21
+ | Código | Severidad | Detecta |
22
+ |--------|-----------|---------|
23
+ | MISSING_TEMPLATE | error | sin `<template>` |
24
+ | MISSING_SCRIPT | error | sin `<script>` |
25
+ | MISSING_LANG_TS | error | `<script>` sin `lang="ts"` |
26
+ | WRONG_TEMPLATE_ORDER | error | `<script>` antes que `<template>` |
27
+ | MISSING_COMPONENT_NAME | error | `defineComponent()` sin `name` |
28
+ | PROP_MISSING_TYPE | error | prop sin `type` |
29
+ | EMIT_CAMELCASE | error | evento en camelCase |
30
+ | REF_REACTIVE_USAGE | error | uso de `ref()` o `reactive()` |
31
+ | PROVIDE_INJECT_USAGE | error | uso de `provide()` o `inject()` |
32
+ | DIRECT_FETCH_IN_COMPONENT | error | `fetch()` en script de componente |
33
+ | LOGIC_IN_TEMPLATE | error | `++`, `--`, ternario en directiva |
34
+ | HANDLER_NOT_IN_ACTIONS | error | `@event` sin prefijo `actions.` |
35
+ | VFOR_NOT_NAMESPACED | error | colección de `v-for` sin `state.`/`computed.`/`props.` |
36
+ | SETUP_UNKNOWN_RETURN_KEY | error | llave extra en `return` de setup() |
37
+ | ARROW_FUNCTION_ACTION | warning | action como arrow function |
38
+ | VIF_NOT_NAMESPACED | warning | `v-if` con bare identifier (sin `.`) |
39
+
40
+ ## formatViolation — output
41
+
42
+ ```
43
+ [arckode-ui] UserCard.ark:5
44
+ HANDLER_NOT_IN_ACTIONS: El handler "handleClick" no está namespaced.
45
+
46
+ > 5 | <button @click="handleClick">
47
+ Fix: Reemplazar "handleClick" por "actions.handleClick" y asegurarse...
48
+ ```
49
+
50
+ La línea `Fix: ...` aparece solo si `violation.fix !== undefined`.
51
+
52
+ ## Cómo agregar una regla nueva
53
+
54
+ ### Paso 1: definir el checker
55
+
56
+ ```typescript
57
+ function checkMyRule(source: string): Violation[] {
58
+ const violations: Violation[] = []
59
+ // Extraer template/script según corresponda
60
+ const template = extractTemplateContent(source)
61
+ if (!template) return violations
62
+
63
+ // ... detectar pattern, push violations con fix
64
+
65
+ return violations
66
+ }
67
+ ```
68
+
69
+ ### Paso 2: registrar en `analyze()`
70
+
71
+ ```typescript
72
+ if (!missing.template) {
73
+ violations.push(...checkMyRule(source))
74
+ }
75
+ ```
76
+
77
+ ### Paso 3: testear
78
+
79
+ ```typescript
80
+ describe('MY_RULE', () => {
81
+ test('detecta el problema', () => { ... })
82
+ test('no flaga código válido', () => { ... })
83
+ test('fix tiene la sugerencia esperada', () => {
84
+ const v = findViolation(violations, 'MY_RULE')
85
+ expect(v?.fix).toContain('lo que sugieres')
86
+ })
87
+ })
88
+ ```
89
+
90
+ ### Paso 4: documentar
91
+
92
+ - Skill principal (`~/.claude/skills/arckode-ui/SKILL.md`) → tabla de violations
93
+ - README del repo → tabla rápida
94
+ - Este skill → sumar a "Las 16 reglas implementadas"
95
+
96
+ ## Helpers disponibles
97
+
98
+ ```typescript
99
+ extractTemplateContent(source) // { content, lineOffset } | null
100
+ extractBalancedBlock(source, startIdx) // { content, start, end } | null
101
+ getTopLevelObjectKeys(content) // string[]
102
+ getLineNumber(source, index) // number (1-based)
103
+ getLines(source) // string[]
104
+ ```
105
+
106
+ ## Convenciones de mensaje y fix
107
+
108
+ **Message** — describe QUÉ está mal en lenguaje natural, con el valor problemático entre comillas:
109
+ ```
110
+ 'El handler "handleClick" no está namespaced.'
111
+ ```
112
+
113
+ **Fix** — describe EXACTAMENTE qué cambiar, idealmente con el código de reemplazo:
114
+ ```
115
+ 'Reemplazar "handleClick" por "actions.handleClick" y asegurarse de que la función esté declarada en setup() y exportada en el return.actions.'
116
+ ```
117
+
118
+ Para fixes ambiguos (varias opciones válidas), enumerar:
119
+ ```
120
+ 'v-for="item in state.items.value" // o computed.items.value / props.items'
121
+ ```
122
+
123
+ ## Regla de retrocompatibilidad
124
+
125
+ - **NUNCA** cambiar el `code` de una regla existente
126
+ - **NUNCA** eliminar una regla (consumidores parsean output del analyzer en CI)
127
+ - **NUNCA** quitar el campo `fix` de una violation que ya lo tenía
128
+ - Solo agregar reglas nuevas; modificar mensaje OK; modificar fix para mejorarlo OK
@@ -0,0 +1,109 @@
1
+ # SKILL: CLI — comandos `ark` y generators
2
+
3
+ > Cargá este skill cuando trabajés en `src/cli/` o cuando uses `ark new/generate/analyze/routes`.
4
+
5
+ ## Comandos disponibles
6
+
7
+ ```bash
8
+ ark new <nombre> # scaffold proyecto nuevo
9
+ ark generate <tipo> <nombre> # alias: ark g
10
+ ark analyze [--json] # analiza .ark files
11
+ ark routes # lista rutas detectadas en src/pages/
12
+ ark help | --help | -h # ayuda
13
+ ```
14
+
15
+ ## Tipos del generator
16
+
17
+ | Tipo | Alias | Naming | Output |
18
+ |------|-------|--------|--------|
19
+ | `component` | `c` | PascalCase | `src/components/features/<Name>.ark` |
20
+ | `page` | `p` | kebab/path | `src/pages/<path>.ark` |
21
+ | `store` | `s` | camelCase | `src/stores/<name>.store.ts` |
22
+ | `service` | `sv` | PascalCase | `src/services/<name>.service.ts` |
23
+ | `layout` | `l` | name | `src/pages/<name>/_layout.ark` |
24
+
25
+ Errores del generator:
26
+ - `INVALID_COMPONENT_NAME` — component no en PascalCase
27
+ - `INVALID_STORE_NAME` — store no en camelCase
28
+ - `INVALID_SERVICE_NAME` — service no en PascalCase
29
+ - `UNKNOWN_GENERATE_TYPE` — tipo no reconocido
30
+ - `FILE_ALREADY_EXISTS` — el destino ya existe
31
+
32
+ ## Plantillas (`src/cli/templates/index.ts`)
33
+
34
+ Cada tipo tiene su función pura que retorna el contenido:
35
+ - `componentTemplate(name)` → `.ark` con shape estándar
36
+ - `pageTemplate(name)` → `.ark` para `src/pages/`
37
+ - `layoutTemplate(name)` → `.ark` con slot
38
+ - `storeTemplate(name)` → `.ts` con defineStore
39
+ - `serviceTemplate(name)` → `.ts` con createService
40
+
41
+ **Garantía**: el output de cada plantilla pasa el analyzer con 0 errors. Hay test que lo valida (`src/cli/__tests__/templates.test.ts`).
42
+
43
+ ## Cómo agregar un tipo nuevo al generator
44
+
45
+ 1. Crear función `xxxTemplate(name)` en `src/cli/templates/index.ts`
46
+ 2. Agregar al `TYPE_ALIASES` en `src/cli/commands/generate.ts`
47
+ 3. Agregar `buildXxxPath()` función
48
+ 4. Agregar `case 'xxx':` al switch
49
+ 5. Validación de naming si aplica
50
+ 6. Test en `src/cli/__tests__/generate.test.ts` + `templates.test.ts`
51
+
52
+ ## ark new — scaffold de proyecto
53
+
54
+ Crea estructura completa en `<projectName>/`:
55
+ - `src/pages/index.ark`, `src/pages/_layout.ark`
56
+ - `src/components/{ui,features}/` (con .gitkeep)
57
+ - `src/{stores,services,types,shared/{helpers,constants}}/`
58
+ - `src/styles/global.css` (Tailwind entry)
59
+ - `vite.config.ts` (con `arkcodeUi`)
60
+ - `tsconfig.json`, `tailwind.config.js`, `postcss.config.js`
61
+ - `package.json` con scripts dev/build/preview
62
+
63
+ **IMPORTANTE**: el `vite.config.ts` generado importa de `'arckode-ui/vite'` con el nombre real `arkcodeUi` (no `arckodePlugin`). Test cubre esto en `src/cli/__tests__/new.test.ts`.
64
+
65
+ Hay test que verifica que **ninguna** plantilla del scaffold use nombres rotos (`arckodePlugin`, `defineConfig from 'arckode-ui/vite'`).
66
+
67
+ ## Anti-patterns
68
+
69
+ ```typescript
70
+ // ❌ generar templates con imports inexistentes
71
+ const TEMPLATE = `import { arckodePlugin } from 'arckode-ui/vite'`
72
+ // → 'arkcodeUi' (es el nombre real)
73
+
74
+ // ❌ olvidar el test del template
75
+ // si agregás un tipo nuevo, agregar test que corra analyze() sobre el output
76
+
77
+ // ❌ shebang en src/cli/index.ts
78
+ // esbuild lo rechaza. El shebang se inyecta vía rollupOptions.output.banner
79
+ ```
80
+
81
+ ## Cómo se construye el bin
82
+
83
+ ```typescript
84
+ // vite.config.ts
85
+ build: {
86
+ lib: {
87
+ entry: {
88
+ index: 'src/index.ts',
89
+ vite: 'src/compiler/vite-plugin.ts',
90
+ cli: 'src/cli/index.ts',
91
+ },
92
+ },
93
+ rollupOptions: {
94
+ output: {
95
+ banner: (chunk) => chunk.name === 'cli' ? '#!/usr/bin/env node' : '',
96
+ },
97
+ },
98
+ }
99
+ ```
100
+
101
+ Y en package.json:
102
+ ```json
103
+ {
104
+ "bin": { "ark": "./dist/cli.js" },
105
+ "scripts": { "build": "vite build && chmod +x dist/cli.js" }
106
+ }
107
+ ```
108
+
109
+ El `chmod +x` es obligatorio porque el shebang solo se respeta si el archivo es ejecutable.