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/LICENSE +21 -0
- package/README.md +170 -0
- package/dist/analyzer-Ctnj3WTI.js +424 -0
- package/dist/cli.js +581 -0
- package/dist/index.js +507 -0
- package/dist/router-DhUDyb8s.js +129 -0
- package/dist/vite.js +366 -0
- package/package.json +67 -0
- package/skills/analyzer/SKILL.md +128 -0
- package/skills/cli/SKILL.md +109 -0
- package/skills/compiler/SKILL.md +122 -0
- package/skills/components/SKILL.md +233 -0
- package/skills/runtime/SKILL.md +145 -0
- package/skills/testing/SKILL.md +169 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 underworf
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Arckode UI
|
|
2
|
+
|
|
3
|
+
Framework frontend con archivos `.ark` (Single File Components), reactividad por signals, compiler propio (Vite plugin), router file-system y analyzer estático.
|
|
4
|
+
|
|
5
|
+
> **Diseñado para máxima predictibilidad de output de IA.** Hay un solo camino correcto para cada problema. Para cada violation detectada, el analyzer sugiere el fix concreto.
|
|
6
|
+
|
|
7
|
+
**Versión:** 0.1.0 — desarrollo activo
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Instalación
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bun add arckode-ui
|
|
15
|
+
# o
|
|
16
|
+
npm install arckode-ui
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Setup mínimo
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// vite.config.ts
|
|
23
|
+
import { defineConfig } from 'vite'
|
|
24
|
+
import { arkcodeUi } from 'arckode-ui/vite'
|
|
25
|
+
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
plugins: [arkcodeUi()],
|
|
28
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// src/main.ts
|
|
33
|
+
import { mount } from 'arckode-ui'
|
|
34
|
+
import App from './App.ark'
|
|
35
|
+
|
|
36
|
+
mount(App, '#app')
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Tu primer componente — `App.ark`
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<template>
|
|
43
|
+
<div>
|
|
44
|
+
<h1>{{ state.message.value }}</h1>
|
|
45
|
+
<button @click="actions.toggle">{{ computed.buttonLabel.value }}</button>
|
|
46
|
+
</div>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<script lang="ts">
|
|
50
|
+
import { defineComponent, signal, computed } from 'arckode-ui'
|
|
51
|
+
|
|
52
|
+
export default defineComponent({
|
|
53
|
+
name: 'App',
|
|
54
|
+
props: {},
|
|
55
|
+
emits: [],
|
|
56
|
+
setup(props, { emit }) {
|
|
57
|
+
const message = signal('Hola Arckode')
|
|
58
|
+
const buttonLabel = computed(() => message.value === 'Hola Arckode' ? 'Chau' : 'Hola')
|
|
59
|
+
|
|
60
|
+
function toggle() {
|
|
61
|
+
message.value = message.value === 'Hola Arckode' ? 'Chau Arckode' : 'Hola Arckode'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
state: { message },
|
|
66
|
+
computed: { buttonLabel },
|
|
67
|
+
actions: { toggle },
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
</script>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## CLI
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
ark new mi-app # scaffold proyecto nuevo
|
|
78
|
+
ark generate component UserCard # → src/components/features/UserCard.ark
|
|
79
|
+
ark generate page about # → src/pages/about.ark
|
|
80
|
+
ark generate page users/[id] # → src/pages/users/[id].ark
|
|
81
|
+
ark generate store cart # → src/stores/cart.store.ts
|
|
82
|
+
ark generate service Product # → src/services/product.service.ts
|
|
83
|
+
ark generate layout users # → src/pages/users/_layout.ark
|
|
84
|
+
ark analyze [--json] # detecta violations en archivos .ark
|
|
85
|
+
ark routes # lista rutas detectadas en src/pages/
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Aliases del generator: `c` (component), `p` (page), `s` (store), `sv` (service), `l` (layout).
|
|
89
|
+
|
|
90
|
+
## Reactividad — Signals
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { signal, computed, watch, effect } from 'arckode-ui'
|
|
94
|
+
|
|
95
|
+
const count = signal(0)
|
|
96
|
+
count.value = 5 // set — dispara re-render
|
|
97
|
+
count.value // get — suscribe al efecto activo
|
|
98
|
+
count.peek // get sin suscribirse
|
|
99
|
+
|
|
100
|
+
const double = computed(() => count.value * 2)
|
|
101
|
+
double.value // readonly — recalcula al cambiar count
|
|
102
|
+
|
|
103
|
+
watch(count, (newVal, oldVal) => { /* ... */ })
|
|
104
|
+
effect(() => { document.title = `${count.value}` })
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Directivas del template
|
|
108
|
+
|
|
109
|
+
| Necesito | Sintaxis |
|
|
110
|
+
|----------|----------|
|
|
111
|
+
| Texto dinámico | `{{ state.x.value }}` / `{{ computed.x.value }}` / `{{ props.x }}` |
|
|
112
|
+
| Atributo dinámico | `:value="state.x.value"` / `:class="..."` |
|
|
113
|
+
| Evento | `@click="actions.handler"` |
|
|
114
|
+
| Evento con argumento | `@click="actions.toggle(item.id)"` |
|
|
115
|
+
| Tecla modificadora | `@keydown.enter="actions.search"` |
|
|
116
|
+
| Condicional | `v-if="..."` + `v-else-if="..."` + `v-else` |
|
|
117
|
+
| Visibilidad (sin desmontar) | `v-show="state.x.value"` |
|
|
118
|
+
| Lista | `v-for="item in state.items.value"` |
|
|
119
|
+
| Componente hijo | `<MiComponente :prop="state.x.value" />` |
|
|
120
|
+
| Slot (en wrapper) | `<slot />` |
|
|
121
|
+
|
|
122
|
+
## El analyzer — `ark analyze`
|
|
123
|
+
|
|
124
|
+
Detecta 16 violations comunes. Cada violation incluye un campo `fix` con la sugerencia concreta:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
[arckode-ui] UserCard.ark:5
|
|
128
|
+
HANDLER_NOT_IN_ACTIONS: El handler "handleClick" no está namespaced.
|
|
129
|
+
|
|
130
|
+
> 5 | <button @click="handleClick">
|
|
131
|
+
Fix: Reemplazar "handleClick" por "actions.handleClick" y asegurarse de que la función esté declarada en setup() y exportada en el return.actions.
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Reglas principales:
|
|
135
|
+
- `MISSING_TEMPLATE`, `MISSING_SCRIPT`, `MISSING_LANG_TS`, `WRONG_TEMPLATE_ORDER`
|
|
136
|
+
- `MISSING_COMPONENT_NAME`, `PROP_MISSING_TYPE`, `EMIT_CAMELCASE`
|
|
137
|
+
- `REF_REACTIVE_USAGE`, `PROVIDE_INJECT_USAGE`, `DIRECT_FETCH_IN_COMPONENT`
|
|
138
|
+
- `LOGIC_IN_TEMPLATE`, `ARROW_FUNCTION_ACTION`
|
|
139
|
+
- `HANDLER_NOT_IN_ACTIONS`, `VFOR_NOT_NAMESPACED`, `VIF_NOT_NAMESPACED`
|
|
140
|
+
- `SETUP_UNKNOWN_RETURN_KEY`
|
|
141
|
+
|
|
142
|
+
## Ejemplos incluidos
|
|
143
|
+
|
|
144
|
+
- `examples/kitchen-sink/` — demo de todas las directivas y primitivas
|
|
145
|
+
- `examples/tasks/` — app real de tareas con sidebar, modal, form, validación, search con debounce
|
|
146
|
+
|
|
147
|
+
Correr cualquiera:
|
|
148
|
+
```bash
|
|
149
|
+
cd examples/tasks && bun install && bun dev
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Documentación para IA
|
|
153
|
+
|
|
154
|
+
- `CLAUDE.md` (en este repo) — instrucciones para Claude/OpenCode al desarrollar el framework
|
|
155
|
+
- `~/.claude/skills/arckode-ui/SKILL.md` — guía completa para usar el framework
|
|
156
|
+
|
|
157
|
+
## Stack
|
|
158
|
+
|
|
159
|
+
- TypeScript estricto
|
|
160
|
+
- Vite (build + dev server)
|
|
161
|
+
- Vitest + happy-dom (tests)
|
|
162
|
+
- Bun (package manager recomendado)
|
|
163
|
+
|
|
164
|
+
## Estado
|
|
165
|
+
|
|
166
|
+
312 tests pasando · 16 reglas del analyzer · 7 componentes en el ejemplo `tasks`.
|
|
167
|
+
|
|
168
|
+
## Licencia
|
|
169
|
+
|
|
170
|
+
MIT
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
function u(e, i) {
|
|
2
|
+
return e.slice(0, i).split(`
|
|
3
|
+
`).length;
|
|
4
|
+
}
|
|
5
|
+
function S(e) {
|
|
6
|
+
return e.split(`
|
|
7
|
+
`);
|
|
8
|
+
}
|
|
9
|
+
function O(e) {
|
|
10
|
+
return /<template[\s>]/i.test(e) ? null : {
|
|
11
|
+
code: "MISSING_TEMPLATE",
|
|
12
|
+
severity: "error",
|
|
13
|
+
line: 1,
|
|
14
|
+
message: "El componente no tiene sección <template>.",
|
|
15
|
+
fix: "Agregar <template>...</template> al principio del archivo, antes de <script>."
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function $(e) {
|
|
19
|
+
return /<script[\s>]/i.test(e) ? null : {
|
|
20
|
+
code: "MISSING_SCRIPT",
|
|
21
|
+
severity: "error",
|
|
22
|
+
line: 1,
|
|
23
|
+
message: "El componente no tiene sección <script>.",
|
|
24
|
+
fix: 'Agregar <script lang="ts">...<\/script> después de </template>.'
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function N(e) {
|
|
28
|
+
const i = e.match(/<script([^>]*)>/i);
|
|
29
|
+
if (i) {
|
|
30
|
+
const t = i[1] ?? "";
|
|
31
|
+
if (!/lang=["']ts["']/.test(t)) {
|
|
32
|
+
const n = e.indexOf(i[0]);
|
|
33
|
+
return {
|
|
34
|
+
code: "MISSING_LANG_TS",
|
|
35
|
+
severity: "error",
|
|
36
|
+
line: u(e, n),
|
|
37
|
+
message: 'La sección <script> debe tener lang="ts".',
|
|
38
|
+
fix: 'Reemplazar <script> por <script lang="ts">.'
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function I(e) {
|
|
45
|
+
const i = e.search(/<script[\s>]/i), t = e.search(/<template[\s>]/i);
|
|
46
|
+
return i !== -1 && t !== -1 && i < t ? {
|
|
47
|
+
code: "WRONG_TEMPLATE_ORDER",
|
|
48
|
+
severity: "error",
|
|
49
|
+
line: u(e, i),
|
|
50
|
+
message: "<script> aparece antes que <template>. El orden correcto es <template> primero.",
|
|
51
|
+
fix: "Mover la sección <template>...</template> arriba, antes de <script>."
|
|
52
|
+
} : null;
|
|
53
|
+
}
|
|
54
|
+
function g(e) {
|
|
55
|
+
const i = e.match(/<template[^>]*>([\s\S]*?)<\/template>/i);
|
|
56
|
+
if (!i) return null;
|
|
57
|
+
const t = i[1], n = e.indexOf(i[0]) + i[0].indexOf(t), s = u(e, n) - 1;
|
|
58
|
+
return { content: t, lineOffset: s };
|
|
59
|
+
}
|
|
60
|
+
function E(e) {
|
|
61
|
+
const i = [];
|
|
62
|
+
let t = 0, n = 0;
|
|
63
|
+
for (; n < e.length; ) {
|
|
64
|
+
const s = e[n];
|
|
65
|
+
if (s === "/" && e[n + 1] === "/") {
|
|
66
|
+
for (; n < e.length && e[n] !== `
|
|
67
|
+
`; ) n++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (s === "/" && e[n + 1] === "*") {
|
|
71
|
+
for (n += 2; n < e.length && !(e[n] === "*" && e[n + 1] === "/"); ) n++;
|
|
72
|
+
n += 2;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (s === '"' || s === "'" || s === "`") {
|
|
76
|
+
const o = s;
|
|
77
|
+
for (n++; n < e.length && e[n] !== o; )
|
|
78
|
+
e[n] === "\\" && n++, n++;
|
|
79
|
+
n++;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (s === "{" || s === "[" || s === "(") {
|
|
83
|
+
t++, n++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (s === "}" || s === "]" || s === ")") {
|
|
87
|
+
t--, n++;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (t === 0) {
|
|
91
|
+
const o = /^([a-zA-Z_$][\w$]*)\s*:/.exec(e.slice(n));
|
|
92
|
+
if (o && o[1]) {
|
|
93
|
+
i.push(o[1]), n += o[0].length;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
n++;
|
|
98
|
+
}
|
|
99
|
+
return i;
|
|
100
|
+
}
|
|
101
|
+
function x(e, i) {
|
|
102
|
+
const t = e.indexOf("{", i);
|
|
103
|
+
if (t === -1) return null;
|
|
104
|
+
let n = 0, s = t;
|
|
105
|
+
for (; s < e.length; ) {
|
|
106
|
+
if (e[s] === "{") n++;
|
|
107
|
+
else if (e[s] === "}" && (n--, n === 0))
|
|
108
|
+
return { content: e.slice(t + 1, s), start: t, end: s };
|
|
109
|
+
s++;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
function w(e) {
|
|
114
|
+
const i = [], n = /\bprops\s*:\s*\{/g.exec(e);
|
|
115
|
+
if (!n) return i;
|
|
116
|
+
const s = n.index + n[0].length - 1, o = x(e, s);
|
|
117
|
+
if (!o) return i;
|
|
118
|
+
const c = o.content, r = /(\w+)\s*:\s*\{/g;
|
|
119
|
+
let a;
|
|
120
|
+
for (; (a = r.exec(c)) !== null; ) {
|
|
121
|
+
const p = a[1], l = o.start + 1 + a.index + a[0].length - 1, d = x(e, l);
|
|
122
|
+
if (!d) continue;
|
|
123
|
+
const h = d.content;
|
|
124
|
+
if (!/\btype\s*:/.test(h)) {
|
|
125
|
+
const f = o.start + 1 + a.index;
|
|
126
|
+
i.push({
|
|
127
|
+
code: "PROP_MISSING_TYPE",
|
|
128
|
+
severity: "error",
|
|
129
|
+
line: u(e, f),
|
|
130
|
+
message: `La prop "${p}" no tiene "type" definido.`,
|
|
131
|
+
fix: `${p}: { type: String, required: true } — reemplazar String por el tipo correcto (String, Number, Boolean, Array, Object, Function).`
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return i;
|
|
136
|
+
}
|
|
137
|
+
function C(e) {
|
|
138
|
+
const i = [], t = e.match(/emits\s*:\s*\[([\s\S]*?)\]/);
|
|
139
|
+
if (!t) return i;
|
|
140
|
+
const n = t[1], s = e.indexOf(t[0]), o = /['"]([^'"]+)['"]/g;
|
|
141
|
+
let c;
|
|
142
|
+
for (; (c = o.exec(n)) !== null; ) {
|
|
143
|
+
const r = c[1];
|
|
144
|
+
if (/^[a-z][a-zA-Z]*[A-Z][a-zA-Z]*$/.test(r)) {
|
|
145
|
+
const a = r.replace(/([A-Z])/g, (l) => `-${l.toLowerCase()}`), p = s + t[0].indexOf(c[0]);
|
|
146
|
+
i.push({
|
|
147
|
+
code: "EMIT_CAMELCASE",
|
|
148
|
+
severity: "error",
|
|
149
|
+
line: u(e, p),
|
|
150
|
+
message: `El evento "${r}" debe ser kebab-case: "${a}".`,
|
|
151
|
+
fix: `Reemplazar '${r}' por '${a}' en emits[] y en cada emit() / @${a}="..." del template.`
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return i;
|
|
156
|
+
}
|
|
157
|
+
function y(e) {
|
|
158
|
+
const i = [], t = e.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
159
|
+
if (!t) return i;
|
|
160
|
+
const n = t[1], s = e.indexOf(t[0]) + t[0].indexOf(n), o = [
|
|
161
|
+
{ re: /\bref\s*\(/g, name: "ref()" },
|
|
162
|
+
{ re: /\breactive\s*\(/g, name: "reactive()" }
|
|
163
|
+
];
|
|
164
|
+
for (const { re: c, name: r } of o) {
|
|
165
|
+
let a;
|
|
166
|
+
for (; (a = c.exec(n)) !== null; )
|
|
167
|
+
i.push({
|
|
168
|
+
code: "REF_REACTIVE_USAGE",
|
|
169
|
+
severity: "error",
|
|
170
|
+
line: u(e, s + a.index),
|
|
171
|
+
message: `Uso de ${r} detectado. Usar signal() en lugar de ref()/reactive().`,
|
|
172
|
+
fix: `Reemplazar ${r} por signal(). Si era reactive({a, b}), declarar un signal por cada propiedad. Importar signal desde 'arckode-ui'.`
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return i;
|
|
176
|
+
}
|
|
177
|
+
function A(e) {
|
|
178
|
+
const i = [], t = e.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
179
|
+
if (!t) return i;
|
|
180
|
+
const n = t[1], s = e.indexOf(t[0]) + t[0].indexOf(n), o = [
|
|
181
|
+
{ re: /\bprovide\s*\(/g, name: "provide()" },
|
|
182
|
+
{ re: /\binject\s*\(/g, name: "inject()" }
|
|
183
|
+
];
|
|
184
|
+
for (const { re: c, name: r } of o) {
|
|
185
|
+
let a;
|
|
186
|
+
for (; (a = c.exec(n)) !== null; )
|
|
187
|
+
i.push({
|
|
188
|
+
code: "PROVIDE_INJECT_USAGE",
|
|
189
|
+
severity: "error",
|
|
190
|
+
line: u(e, s + a.index),
|
|
191
|
+
message: `Uso de ${r} detectado. Arckode UI no usa provide/inject — usar conectores o props.`,
|
|
192
|
+
fix: `Eliminar la llamada a ${r}. Pasar el valor por props si es padre→hijo, o usar estado global (defineStore) si son componentes sin relación.`
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return i;
|
|
196
|
+
}
|
|
197
|
+
function b(e) {
|
|
198
|
+
const i = [], t = e.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
199
|
+
if (!t) return i;
|
|
200
|
+
const n = t[1], s = e.indexOf(t[0]) + t[0].indexOf(n), o = /\bfetch\s*\(/g;
|
|
201
|
+
let c;
|
|
202
|
+
for (; (c = o.exec(n)) !== null; )
|
|
203
|
+
i.push({
|
|
204
|
+
code: "DIRECT_FETCH_IN_COMPONENT",
|
|
205
|
+
severity: "error",
|
|
206
|
+
line: u(e, s + c.index),
|
|
207
|
+
message: "fetch() directo en el componente. Mover la lógica de red a un servicio externo.",
|
|
208
|
+
fix: "Crear un service en src/services/{dominio}.service.ts con createService() y llamarlo desde una action o desde onMount."
|
|
209
|
+
});
|
|
210
|
+
return i;
|
|
211
|
+
}
|
|
212
|
+
function _(e) {
|
|
213
|
+
const i = /defineComponent\s*\(\s*\{([\s\S]*?)\}\s*\)/, t = e.match(i);
|
|
214
|
+
if (!t) return null;
|
|
215
|
+
const n = t[1];
|
|
216
|
+
if (!/\bname\s*:/.test(n)) {
|
|
217
|
+
const s = e.indexOf(t[0]);
|
|
218
|
+
return {
|
|
219
|
+
code: "MISSING_COMPONENT_NAME",
|
|
220
|
+
severity: "error",
|
|
221
|
+
line: u(e, s),
|
|
222
|
+
message: 'defineComponent() no tiene "name" definido. Agregar name: "NombreComponente".',
|
|
223
|
+
fix: `Agregar "name: 'NombreEnPascalCase'," como primera propiedad de defineComponent({...}). Usar el mismo nombre del archivo.`
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
function M(e) {
|
|
229
|
+
const i = [], t = e.match(/<template[^>]*>([\s\S]*?)<\/template>/i);
|
|
230
|
+
if (!t) return i;
|
|
231
|
+
const n = t[1], s = e.indexOf(t[0]) + t[0].indexOf(n), o = n.split(`
|
|
232
|
+
`);
|
|
233
|
+
let c = u(e, s) - 1;
|
|
234
|
+
const r = /(?:@\w[\w.]*|v-if|v-show|v-for|:[a-z][\w-]*)="([^"]+)"/g;
|
|
235
|
+
for (let a = 0; a < o.length; a++) {
|
|
236
|
+
const p = o[a];
|
|
237
|
+
let l;
|
|
238
|
+
for (r.lastIndex = 0; (l = r.exec(p)) !== null; ) {
|
|
239
|
+
const d = l[1];
|
|
240
|
+
if (/\+\+|--|\?[^:]|[^?]:/.test(d) && /\+\+|--|[^=!<>?]\s*\?/.test(d)) {
|
|
241
|
+
i.push({
|
|
242
|
+
code: "LOGIC_IN_TEMPLATE",
|
|
243
|
+
severity: "error",
|
|
244
|
+
line: c + a + 1,
|
|
245
|
+
message: `Expresión compleja en directiva: "${d}". Mover la lógica a computed o action.`,
|
|
246
|
+
fix: "Crear un action (function declaration) en setup() que ejecute esta lógica y referenciarlo como actions.nombreDelAction. Si es un valor derivado, usar computed."
|
|
247
|
+
});
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return i;
|
|
253
|
+
}
|
|
254
|
+
function R(e) {
|
|
255
|
+
const i = [], t = e.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
256
|
+
if (!t) return i;
|
|
257
|
+
const n = t[1], s = e.indexOf(t[0]) + t[0].indexOf(n), o = n.match(/actions\s*:\s*\{([\s\S]*?)\}/);
|
|
258
|
+
if (!o) return i;
|
|
259
|
+
const c = o[1], r = n.indexOf(o[0]), a = /(\w+)\s*:\s*(?:\([^)]*\)|[a-zA-Z_$][\w$]*)\s*=>/g;
|
|
260
|
+
let p;
|
|
261
|
+
for (; (p = a.exec(c)) !== null; ) {
|
|
262
|
+
const f = s + r + o[0].indexOf(p[0]), m = p[1];
|
|
263
|
+
i.push({
|
|
264
|
+
code: "ARROW_FUNCTION_ACTION",
|
|
265
|
+
severity: "warning",
|
|
266
|
+
line: u(e, f),
|
|
267
|
+
message: `La action "${m}" está definida como arrow function. Usar function declaration.`,
|
|
268
|
+
fix: `Declarar como "function ${m}(...) { ... }" en setup() y pasarla por shorthand: actions: { ${m} }.`
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
const l = /const\s+(\w+)\s*=\s*(?:\([^)]*\)|[a-zA-Z_$][\w$]*)\s*=>/g;
|
|
272
|
+
l.lastIndex = 0;
|
|
273
|
+
const d = n.match(/actions\s*:\s*\{([^}]*)\}/);
|
|
274
|
+
if (!d) return i;
|
|
275
|
+
const h = d[1].split(",").map((f) => f.trim()).filter(Boolean);
|
|
276
|
+
for (; (p = l.exec(n)) !== null; ) {
|
|
277
|
+
const f = p[1];
|
|
278
|
+
if (h.includes(f)) {
|
|
279
|
+
const m = s + p.index;
|
|
280
|
+
i.push({
|
|
281
|
+
code: "ARROW_FUNCTION_ACTION",
|
|
282
|
+
severity: "warning",
|
|
283
|
+
line: u(e, m),
|
|
284
|
+
message: `La action "${f}" está definida como arrow function. Usar function declaration.`,
|
|
285
|
+
fix: `Reemplazar "const ${f} = (...) => ..." por "function ${f}(...) { ... }".`
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return i;
|
|
290
|
+
}
|
|
291
|
+
function T(e) {
|
|
292
|
+
const i = [], t = g(e);
|
|
293
|
+
if (!t) return i;
|
|
294
|
+
const { content: n, lineOffset: s } = t, o = n.split(`
|
|
295
|
+
`), c = /@[\w.]+="([^"]+)"/g;
|
|
296
|
+
for (let r = 0; r < o.length; r++) {
|
|
297
|
+
const a = o[r];
|
|
298
|
+
c.lastIndex = 0;
|
|
299
|
+
let p;
|
|
300
|
+
for (; (p = c.exec(a)) !== null; ) {
|
|
301
|
+
const l = p[1].trim();
|
|
302
|
+
l.startsWith("actions.") || i.push({
|
|
303
|
+
code: "HANDLER_NOT_IN_ACTIONS",
|
|
304
|
+
severity: "error",
|
|
305
|
+
line: s + r + 1,
|
|
306
|
+
message: `El handler "${l}" no está namespaced. Usar actions.handlerName en lugar de llamadas directas.`,
|
|
307
|
+
fix: k(l)
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return i;
|
|
312
|
+
}
|
|
313
|
+
function k(e) {
|
|
314
|
+
if (/\+\+|--|=(?!=)/.test(e))
|
|
315
|
+
return `Crear una function action en setup() (ej: function handleClick() { ${e} }) y referenciarla como actions.handleClick.`;
|
|
316
|
+
const i = e.startsWith("actions.") ? e : `actions.${e}`;
|
|
317
|
+
return `Reemplazar "${e}" por "${i}" y asegurarse de que la función esté declarada en setup() y exportada en el return.actions.`;
|
|
318
|
+
}
|
|
319
|
+
function L(e) {
|
|
320
|
+
const i = [], t = g(e);
|
|
321
|
+
if (!t) return i;
|
|
322
|
+
const { content: n, lineOffset: s } = t, o = n.split(`
|
|
323
|
+
`), c = /v-for="([^"]+)"/g, r = ["state.", "computed.", "props."];
|
|
324
|
+
for (let a = 0; a < o.length; a++) {
|
|
325
|
+
const p = o[a];
|
|
326
|
+
c.lastIndex = 0;
|
|
327
|
+
let l;
|
|
328
|
+
for (; (l = c.exec(p)) !== null; ) {
|
|
329
|
+
const d = l[1].trim(), h = /^(?:\([^)]+\)|\w+)\s+in\s+(.+)$/.exec(d);
|
|
330
|
+
if (!h) continue;
|
|
331
|
+
const f = h[1].trim();
|
|
332
|
+
if (!r.some((m) => f.startsWith(m))) {
|
|
333
|
+
const m = d.slice(0, d.indexOf(" in")).trim(), v = /^\w+$/.test(f) ? `v-for="${m} in state.${f}.value" // o computed.${f}.value / props.${f}` : `v-for="${m} in state.<nombreDelSignal>.value" // mover "${f}" a un signal o computed en setup()`;
|
|
334
|
+
i.push({
|
|
335
|
+
code: "VFOR_NOT_NAMESPACED",
|
|
336
|
+
severity: "error",
|
|
337
|
+
line: s + a + 1,
|
|
338
|
+
message: `v-for colección "${f}" no está namespaced. Usar state.xxx, computed.xxx o props.xxx.`,
|
|
339
|
+
fix: v
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return i;
|
|
345
|
+
}
|
|
346
|
+
function U(e) {
|
|
347
|
+
const i = [], t = g(e);
|
|
348
|
+
if (!t) return i;
|
|
349
|
+
const { content: n, lineOffset: s } = t, o = n.split(`
|
|
350
|
+
`), c = /v-(?:if|else-if)="([^"]+)"/g;
|
|
351
|
+
for (let r = 0; r < o.length; r++) {
|
|
352
|
+
const a = o[r];
|
|
353
|
+
c.lastIndex = 0;
|
|
354
|
+
let p;
|
|
355
|
+
for (; (p = c.exec(a)) !== null; ) {
|
|
356
|
+
const l = p[1].trim();
|
|
357
|
+
if (!l.includes(".")) {
|
|
358
|
+
const d = l.startsWith("!") ? l.slice(1).trim() : l, h = l.startsWith("!"), f = /^\w+$/.test(d) ? `v-if="${h ? "!" : ""}state.${d}.value" // o computed.${d}.value` : 'v-if="state.<nombreDelSignal>.value" // mover la expresión a un computed en setup()';
|
|
359
|
+
i.push({
|
|
360
|
+
code: "VIF_NOT_NAMESPACED",
|
|
361
|
+
severity: "warning",
|
|
362
|
+
line: s + r + 1,
|
|
363
|
+
message: `v-if "${l}" parece un identificador sin namespace. Usar state.xxx.value, computed.xxx.value o props.xxx.`,
|
|
364
|
+
fix: f
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return i;
|
|
370
|
+
}
|
|
371
|
+
function P(e) {
|
|
372
|
+
const i = [], t = e.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
373
|
+
if (!t) return i;
|
|
374
|
+
const n = t[1], s = e.indexOf(t[0]) + t[0].indexOf(n), o = /* @__PURE__ */ new Set(["state", "computed", "actions"]), c = /\breturn\s*\{/g;
|
|
375
|
+
let r;
|
|
376
|
+
for (; (r = c.exec(n)) !== null; ) {
|
|
377
|
+
const a = r.index + r[0].lastIndexOf("{"), p = s + a, l = x(e, p);
|
|
378
|
+
if (!l) continue;
|
|
379
|
+
const d = E(l.content), h = d.some((m) => o.has(m)), f = d.filter((m) => !o.has(m));
|
|
380
|
+
if (h && f.length > 0)
|
|
381
|
+
for (const m of f)
|
|
382
|
+
i.push({
|
|
383
|
+
code: "SETUP_UNKNOWN_RETURN_KEY",
|
|
384
|
+
severity: "error",
|
|
385
|
+
line: u(e, l.start),
|
|
386
|
+
message: `La llave "${m}" no es válida en el return de setup(). Solo se permiten: state, computed, actions.`,
|
|
387
|
+
fix: `Quitar "${m}: ..." del return. Si era estado, mover a state. Si era función, mover a actions. Si era valor derivado, mover a computed.`
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
return i;
|
|
391
|
+
}
|
|
392
|
+
function D(e, i) {
|
|
393
|
+
const t = [], n = {
|
|
394
|
+
template: !/<template[\s>]/i.test(e),
|
|
395
|
+
script: !/<script[\s>]/i.test(e)
|
|
396
|
+
}, s = O(e);
|
|
397
|
+
s && t.push(s);
|
|
398
|
+
const o = $(e);
|
|
399
|
+
if (o && t.push(o), !n.script) {
|
|
400
|
+
const c = N(e);
|
|
401
|
+
c && t.push(c);
|
|
402
|
+
}
|
|
403
|
+
if (!n.template && !n.script) {
|
|
404
|
+
const c = I(e);
|
|
405
|
+
c && t.push(c);
|
|
406
|
+
}
|
|
407
|
+
if (!n.script) {
|
|
408
|
+
const c = _(e);
|
|
409
|
+
c && t.push(c), t.push(...w(e)), t.push(...C(e)), t.push(...y(e)), t.push(...A(e)), t.push(...b(e)), t.push(...R(e));
|
|
410
|
+
}
|
|
411
|
+
return n.template || (t.push(...M(e)), t.push(...T(e)), t.push(...L(e)), t.push(...U(e))), n.script || t.push(...P(e)), t;
|
|
412
|
+
}
|
|
413
|
+
function F(e, i, t) {
|
|
414
|
+
const n = i.split("/").pop() ?? i, o = S(t)[e.line - 1] ?? "", c = e.fix !== void 0 ? `
|
|
415
|
+
Fix: ${e.fix}` : "";
|
|
416
|
+
return `[arckode-ui] ${n}:${e.line}
|
|
417
|
+
${e.code}: ${e.message}
|
|
418
|
+
|
|
419
|
+
> ${e.line} | ${o}` + c;
|
|
420
|
+
}
|
|
421
|
+
export {
|
|
422
|
+
D as a,
|
|
423
|
+
F as f
|
|
424
|
+
};
|