@meekojs/meeko 0.0.2 → 0.0.4

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.
Files changed (146) hide show
  1. package/README.md +40 -2
  2. package/dist/autoimports-ts.d.ts +26 -0
  3. package/dist/autoimports-ts.js +127 -0
  4. package/dist/autoimports-ts.js.map +1 -0
  5. package/dist/autoimports.d.ts +21 -0
  6. package/dist/autoimports.js +285 -0
  7. package/dist/autoimports.js.map +1 -0
  8. package/dist/check.js +35 -23
  9. package/dist/check.js.map +1 -0
  10. package/dist/cli.js +59 -19
  11. package/dist/cli.js.map +1 -0
  12. package/dist/compiler/import-graph.d.ts +8 -0
  13. package/dist/compiler/import-graph.js +33 -0
  14. package/dist/compiler/import-graph.js.map +1 -0
  15. package/dist/compiler/mapping.js +1 -0
  16. package/dist/compiler/mapping.js.map +1 -0
  17. package/dist/compiler/project-root.d.ts +2 -0
  18. package/dist/compiler/project-root.js +18 -0
  19. package/dist/compiler/project-root.js.map +1 -0
  20. package/dist/compiler/sfc.js +1 -0
  21. package/dist/compiler/sfc.js.map +1 -0
  22. package/dist/compiler/sourcemap.d.ts +3 -0
  23. package/dist/compiler/sourcemap.js +75 -0
  24. package/dist/compiler/sourcemap.js.map +1 -0
  25. package/dist/compiler/template.js +1 -0
  26. package/dist/compiler/template.js.map +1 -0
  27. package/dist/compiler.js +22 -11
  28. package/dist/compiler.js.map +1 -0
  29. package/dist/devtools-ui/index.js +56 -6
  30. package/dist/devtools-ui/index.js.map +1 -0
  31. package/dist/devtools.d.ts +9 -0
  32. package/dist/devtools.js +16 -6
  33. package/dist/devtools.js.map +1 -0
  34. package/dist/dom.d.ts +17 -6
  35. package/dist/dom.js +86 -19
  36. package/dist/dom.js.map +1 -0
  37. package/dist/framework/check-app.d.ts +5 -0
  38. package/dist/framework/check-app.js +59 -0
  39. package/dist/framework/check-app.js.map +1 -0
  40. package/dist/framework/client.d.ts +45 -0
  41. package/dist/framework/client.js +146 -0
  42. package/dist/framework/client.js.map +1 -0
  43. package/dist/framework/clients.d.ts +82 -0
  44. package/dist/framework/clients.js +182 -0
  45. package/dist/framework/clients.js.map +1 -0
  46. package/dist/framework/config-type.d.ts +10 -0
  47. package/dist/framework/config-type.js +4 -0
  48. package/dist/framework/config-type.js.map +1 -0
  49. package/dist/framework/config.d.ts +15 -0
  50. package/dist/framework/config.js +138 -0
  51. package/dist/framework/config.js.map +1 -0
  52. package/dist/framework/contracts-generate.d.ts +8 -0
  53. package/dist/framework/contracts-generate.js +227 -0
  54. package/dist/framework/contracts-generate.js.map +1 -0
  55. package/dist/framework/generate.d.ts +7 -0
  56. package/dist/framework/generate.js +67 -0
  57. package/dist/framework/generate.js.map +1 -0
  58. package/dist/framework/init.d.ts +2 -0
  59. package/dist/framework/init.js +362 -0
  60. package/dist/framework/init.js.map +1 -0
  61. package/dist/framework/modules.d.ts +22 -0
  62. package/dist/framework/modules.js +29 -0
  63. package/dist/framework/modules.js.map +1 -0
  64. package/dist/framework/page.d.ts +60 -0
  65. package/dist/framework/page.js +42 -0
  66. package/dist/framework/page.js.map +1 -0
  67. package/dist/framework/prerender.d.ts +14 -0
  68. package/dist/framework/prerender.js +104 -0
  69. package/dist/framework/prerender.js.map +1 -0
  70. package/dist/framework/router.d.ts +39 -0
  71. package/dist/framework/router.js +173 -0
  72. package/dist/framework/router.js.map +1 -0
  73. package/dist/framework/server.d.ts +51 -0
  74. package/dist/framework/server.js +192 -0
  75. package/dist/framework/server.js.map +1 -0
  76. package/dist/framework/socket-contracts-generate.d.ts +5 -0
  77. package/dist/framework/socket-contracts-generate.js +159 -0
  78. package/dist/framework/socket-contracts-generate.js.map +1 -0
  79. package/dist/framework/socket.d.ts +70 -0
  80. package/dist/framework/socket.js +150 -0
  81. package/dist/framework/socket.js.map +1 -0
  82. package/dist/framework/state.d.ts +15 -0
  83. package/dist/framework/state.js +72 -0
  84. package/dist/framework/state.js.map +1 -0
  85. package/dist/framework-server/auto-clients.d.ts +3 -0
  86. package/dist/framework-server/auto-clients.js +27 -0
  87. package/dist/framework-server/auto-clients.js.map +1 -0
  88. package/dist/framework-server/context.d.ts +21 -0
  89. package/dist/framework-server/context.js +22 -0
  90. package/dist/framework-server/context.js.map +1 -0
  91. package/dist/framework-server/contracts.d.ts +64 -0
  92. package/dist/framework-server/contracts.js +129 -0
  93. package/dist/framework-server/contracts.js.map +1 -0
  94. package/dist/framework-server/discovery.d.ts +40 -0
  95. package/dist/framework-server/discovery.js +252 -0
  96. package/dist/framework-server/discovery.js.map +1 -0
  97. package/dist/framework-server/local.d.ts +19 -0
  98. package/dist/framework-server/local.js +98 -0
  99. package/dist/framework-server/local.js.map +1 -0
  100. package/dist/framework-server/node-upgrade.d.ts +23 -0
  101. package/dist/framework-server/node-upgrade.js +78 -0
  102. package/dist/framework-server/node-upgrade.js.map +1 -0
  103. package/dist/framework-server/node-websocket.d.ts +16 -0
  104. package/dist/framework-server/node-websocket.js +125 -0
  105. package/dist/framework-server/node-websocket.js.map +1 -0
  106. package/dist/framework-server/openapi.d.ts +17 -0
  107. package/dist/framework-server/openapi.js +118 -0
  108. package/dist/framework-server/openapi.js.map +1 -0
  109. package/dist/framework-server/session-stores.d.ts +66 -0
  110. package/dist/framework-server/session-stores.js +96 -0
  111. package/dist/framework-server/session-stores.js.map +1 -0
  112. package/dist/framework-server/sessions.d.ts +64 -0
  113. package/dist/framework-server/sessions.js +136 -0
  114. package/dist/framework-server/sessions.js.map +1 -0
  115. package/dist/framework-server/socket.d.ts +83 -0
  116. package/dist/framework-server/socket.js +161 -0
  117. package/dist/framework-server/socket.js.map +1 -0
  118. package/dist/framework-server.d.ts +21 -0
  119. package/dist/framework-server.js +12 -0
  120. package/dist/framework-server.js.map +1 -0
  121. package/dist/framework.d.ts +29 -0
  122. package/dist/framework.js +13 -0
  123. package/dist/framework.js.map +1 -0
  124. package/dist/hmr.js +1 -0
  125. package/dist/hmr.js.map +1 -0
  126. package/dist/index.d.ts +3 -3
  127. package/dist/index.js +2 -1
  128. package/dist/index.js.map +1 -0
  129. package/dist/init.d.ts +2 -0
  130. package/dist/init.js +209 -0
  131. package/dist/init.js.map +1 -0
  132. package/dist/internal.js +1 -0
  133. package/dist/internal.js.map +1 -0
  134. package/dist/node.d.ts +12 -0
  135. package/dist/node.js +52 -0
  136. package/dist/node.js.map +1 -0
  137. package/dist/reactivity.d.ts +25 -1
  138. package/dist/reactivity.js +69 -10
  139. package/dist/reactivity.js.map +1 -0
  140. package/dist/server.js +1 -0
  141. package/dist/server.js.map +1 -0
  142. package/dist/types.js +23 -8
  143. package/dist/types.js.map +1 -0
  144. package/dist/vite.js +91 -6
  145. package/dist/vite.js.map +1 -0
  146. package/package.json +19 -3
package/README.md CHANGED
@@ -14,9 +14,11 @@ El proyecto está en etapa temprana. Ya funciona el recorrido completo de un con
14
14
 
15
15
  - [Arquitectura y contratos](docs/architecture.md)
16
16
  - [API y límites de compatibilidad](docs/api.md)
17
+ - [Aplicaciones full-stack con Meeko](docs/framework.md)
17
18
  - [Plan de desarrollo y criterios de aceptación](docs/roadmap.md)
18
19
  - [Comparativa reproducible con Vue](docs/benchmarks.md)
19
20
  - [DevTools para depurar componentes](docs/devtools.md)
21
+ - [Aplicación piloto SSR](examples/pilot/README.md)
20
22
  - [Publicación y verificaciones](docs/releasing.md)
21
23
  - [Historial de cambios](CHANGELOG.md)
22
24
  - [Guía para contribuir](CONTRIBUTING.md)
@@ -45,6 +47,37 @@ export default defineConfig({ plugins: [meeko()] });
45
47
 
46
48
  El paquete incluye TypeScript 7 y la CLI `meeko`. Puedes comprobar un componente con `pnpm exec meeko check ruta/Componente.meeko`. Vite es opcional si sólo usas el compilador y SSR.
47
49
 
50
+ ### Crear una aplicación SSR
51
+
52
+ Con el paquete instalado, `meeko init` crea un proyecto independiente. El servidor renderiza el componente `.meeko` y transfiere props y señales para que el navegador hidrate el HTML existente.
53
+
54
+ ```powershell
55
+ pnpm exec meeko init mi-app
56
+ cd mi-app
57
+ npm install
58
+ npm run dev
59
+ ```
60
+
61
+ Abre `http://127.0.0.1:4173`. `npm run check` genera los tipos del componente y ejecuta TypeScript 7. `npm run build` compila cliente y servidor; `npm start` sirve la versión compilada con SSR. La CLI no sobrescribe archivos de aplicación existentes.
62
+
63
+ Desde `0.0.3`, si ya ejecutaste `pnpm add @meekojs/meeko` en una carpeta sin código, puedes inicializarla directamente con `pnpm exec meeko init .`. Se conservan el manifiesto, los lockfiles, el workspace y `node_modules`; la CLI rechaza archivos de aplicación o scripts que entrarían en conflicto. Después ejecuta `pnpm install` y `pnpm run dev`. Para actualizar desde `0.0.2`, usa `pnpm add @meekojs/meeko@0.0.3` antes de `init .`.
64
+
65
+ ### Crear una aplicación full-stack
66
+
67
+ Meeko Engine permite usar componentes `.meeko` de forma independiente. Meeko Framework añade rutas por archivos, layouts, SSR, estado por petición, API, RPC, sesiones y WebSocket con el mismo compilador y runtime. **La versión `0.0.4` que incluye este framework está preparada, pero aún no se ha publicado en npm.** Una vez publicada e instalada, crea una aplicación con:
68
+
69
+ ```powershell
70
+ pnpm exec meeko init-framework my-app
71
+ cd my-app
72
+ npm install
73
+ npm run dev
74
+ npm run check
75
+ npm run build
76
+ npm start
77
+ ```
78
+
79
+ `check` genera contratos y comprueba tipos; `build` produce cliente, servidor y prerender. `start` sirve SSR de producción con Node 24 y requiere configurar sesiones persistentes. La [guía del framework](docs/framework.md) explica configuración, autoimports, rutas, despliegue y seguridad.
80
+
48
81
  ## Estructura
49
82
 
50
83
  | Ruta | Responsabilidad |
@@ -73,11 +106,12 @@ El paquete incluye TypeScript 7 y la CLI `meeko`. Puedes comprobar un componente
73
106
  pnpm install
74
107
  pnpm run verify
75
108
  pnpm run e2e
109
+ pnpm run pilot:e2e
76
110
  pnpm run package:smoke
77
111
  pnpm run example:serve
78
112
  ```
79
113
 
80
- `e2e` usa Chrome instalado localmente; `MEEKO_CHROME_PATH` permite indicar otro ejecutable compatible. Prueba Vite en desarrollo y producción, además de una página SSR con navegación. `package:smoke` instala el tarball en un proyecto temporal externo y prueba CLI, SSR, tipos y Vite. La última orden sirve el ejemplo en `http://127.0.0.1:4173`. El servidor envía HTML y las props iniciales como JSON escapado; el navegador hidrata el botón sin reemplazarlo.
114
+ `e2e` usa Chrome instalado localmente; `MEEKO_CHROME_PATH` permite indicar otro ejecutable compatible. Prueba Vite en desarrollo y producción, además de una página SSR con navegación. `pilot:e2e` prueba la aplicación piloto en Chromium, Firefox y WebKit instalados por Playwright, tanto en desarrollo como en producción. `package:smoke` instala el tarball en un proyecto temporal externo y prueba CLI, SSR, tipos, Vite y `meeko init`. La última orden sirve el contador en `http://127.0.0.1:4173`.
81
115
 
82
116
  Para probar la compilación automática con Vite:
83
117
 
@@ -108,13 +142,17 @@ node dist/cli.js types ruta/Componente.meeko
108
142
 
109
143
  La aplicación debe pasar **las mismas props** al render y a `hydrate`. `renderWithState(props)` entrega HTML y señales editables serializables; el ejemplo SSR transfiere ambos junto con las props mediante `serializeJson` y llama `hydrate(root, props, state)`. Meeko comprueba que el texto existente coincida durante la hidratación y devuelve una función que desconecta eventos y observadores. El HTML dinámico se escapa en el servidor.
110
144
 
145
+ ## Aplicación piloto
146
+
147
+ [`examples/pilot`](examples/pilot/README.md) reúne SSR, hidratación, componentes anidados, listas, formularios, rutas y una API asíncrona. Se ejecuta con `pnpm run pilot:dev`; `pnpm run pilot:build` y `pnpm run pilot:serve` prueban los artefactos de producción sin middleware de Vite. La prueba `pnpm run pilot:e2e` recorre ambos modos en Chromium, Firefox y WebKit. WebKit de Playwright no equivale a una prueba en Safari real.
148
+
111
149
  ## Sintaxis inicial
112
150
 
113
151
  Un archivo tiene `<script lang="ts">`, `<template>` y un `<style>` opcional; `scoped` limita las reglas CSS al componente. `defineProps<Props>()` declara las props. La plantilla acepta expresiones de texto `{expression}`, eventos `on:click="handler"` y atributos dinámicos `bind:title="expression"`. Los atributos booleanos, como `bind:disabled`, se agregan o eliminan según el valor.
114
152
 
115
153
  `if:when="expression"` muestra u oculta un elemento. Una lista usa `each:of="items"`, `each:as="item"` y `each:key="item.id"`; `each:index="position"` permite nombrar el índice, que se llama `index` por defecto. Las claves deben ser cadenas o números finitos y no pueden repetirse. Meeko conserva los nodos de las claves existentes al reordenar la lista. [Regions.meeko](examples/regions/Regions.meeko) muestra condiciones, listas anidadas, eventos y atributos dinámicos.
116
154
 
117
- Un componente importado en el script se inserta con `<component is="Child" bind:label="expression"></component>`. Las props se comprueban con TypeScript y cada hijo se hidrata dentro de un rango de comentarios, sin un elemento envolvente. El cierre explícito es obligatorio; la sintaxis autocerrada de HTML no se admite para `<component>`. [Parent.meeko](examples/nested/Parent.meeko) muestra hijos y nietos dentro de condiciones y listas.
155
+ Un componente importado en el script se inserta con `<component is="Child" bind:label="expression"></component>`. Las props se comprueban con TypeScript y cada hijo se hidrata dentro de un rango de comentarios, sin un elemento envolvente. `is` también acepta un componente recibido como prop tipada `Component<Props>`; así se puede insertar contenido reutilizable y cambiarlo reactivamente. La comunicación hacia el padre usa callbacks tipados en las props, y `createContext<T>()` permite compartir valores con descendientes. El cierre explícito es obligatorio; la sintaxis autocerrada de HTML no se admite para `<component>`. [Parent.meeko](examples/nested/Parent.meeko) muestra hijos y nietos dentro de condiciones y listas.
118
156
 
119
157
  Los errores de sintaxis de script, plantilla y CSS, así como los errores de tipos en declaraciones y expresiones, tienen ubicación en `.meeko`. Los errores atribuibles sólo al código auxiliar generado se identifican explícitamente.
120
158
 
@@ -0,0 +1,26 @@
1
+ import type { AutoImport, ImportScope } from "./autoimports.js";
2
+ export type TypeScriptScope = ImportScope;
3
+ /** Selects the application scope of a source file without executing it. */
4
+ export declare function typeScriptScope(root: string, filename: string): TypeScriptScope | undefined;
5
+ /** Produces global editor declarations for isolated app, server and shared TS projects. */
6
+ export declare function generateAutoImportTypes(projectRoot: string): Promise<{
7
+ client: string;
8
+ server: string;
9
+ shared: string;
10
+ }>;
11
+ /** Vite can call this for app/shared/server TypeScript before its normal TS transform. */
12
+ export declare function transformTypeScriptAutoImports(source: string, filename: string, root: string, registry: readonly AutoImport[]): string | undefined;
13
+ /** Maps the unchanged source body after imports inserted at the start of a TS module. */
14
+ export declare function typeScriptAutoImportMap(source: string, transformed: string, filename: string): {
15
+ version: 3;
16
+ sources: string[];
17
+ sourcesContent: string[];
18
+ names: string[];
19
+ mappings: string;
20
+ };
21
+ /** Convenience entrypoint for CLI generators. */
22
+ export declare function generateTypeScriptAutoImports(projectRoot: string): Promise<{
23
+ client: string;
24
+ server: string;
25
+ shared: string;
26
+ }>;
@@ -0,0 +1,127 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { join, relative, resolve, sep } from "node:path";
3
+ import ts from "@typescript/typescript6";
4
+ import { applyAutoImports, discoverAutoImports } from "./autoimports.js";
5
+ function within(root, filename) {
6
+ const path = relative(resolve(root), resolve(filename));
7
+ return path === ".." || path.startsWith(`..${sep}`) || path.startsWith("/") ? undefined : path.replaceAll("\\", "/");
8
+ }
9
+ /** Selects the application scope of a source file without executing it. */
10
+ export function typeScriptScope(root, filename) {
11
+ const path = within(root, filename);
12
+ if (!path || !/\.[cm]?tsx?$/.test(path) || /\.(?:test|spec|d)\.[cm]?tsx?$/.test(path))
13
+ return undefined;
14
+ if (/\.shared\.[cm]?tsx?$/.test(path))
15
+ return "shared";
16
+ if (/\.server\.[cm]?tsx?$/.test(path))
17
+ return "server";
18
+ if (/\.route\.[cm]?tsx?$/.test(path) && /(?:^|\/)app\/pages\//.test(path))
19
+ return "server";
20
+ if (/\.client\.[cm]?tsx?$/.test(path))
21
+ return "client";
22
+ if (path.startsWith("server/"))
23
+ return "server";
24
+ if (path.startsWith("app/"))
25
+ return "client";
26
+ if (path.startsWith("shared/"))
27
+ return "shared";
28
+ return undefined;
29
+ }
30
+ function publicSpecifier(outputDirectory, entry) {
31
+ if (entry.specifier)
32
+ return entry.specifier;
33
+ const path = relative(outputDirectory, entry.file).replaceAll("\\", "/");
34
+ const prefixed = path.startsWith(".") ? path : `./${path}`;
35
+ return entry.component ? prefixed : prefixed.replace(/\.[cm]?ts$/, (extension) => extension === ".mts" ? ".mjs" : extension === ".cts" ? ".cjs" : ".js");
36
+ }
37
+ function scopeEntries(registry, scope) {
38
+ return registry.filter((entry) => entry.scope === "shared" || (scope !== "shared" && entry.scope === scope));
39
+ }
40
+ function assertNoCollisions(entries, scope) {
41
+ const symbols = new Map();
42
+ for (const entry of entries) {
43
+ const previous = symbols.get(entry.name);
44
+ if (previous)
45
+ throw new Error(`Ambiguous autoimport ${entry.name} in ${scope} scope: ${previous.file}, ${entry.file}`);
46
+ symbols.set(entry.name, entry);
47
+ }
48
+ }
49
+ async function typeDeclaration(entry, specifier) {
50
+ if (entry.specifier)
51
+ return ` type ${entry.name} = import(${specifier}).${entry.imported};`;
52
+ const source = await readFile(entry.file, "utf8");
53
+ const file = ts.createSourceFile(entry.file, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
54
+ const declaration = file.statements.find((statement) => (ts.isTypeAliasDeclaration(statement) || ts.isInterfaceDeclaration(statement)) && statement.name.text === entry.imported);
55
+ if (!declaration || (!ts.isTypeAliasDeclaration(declaration) && !ts.isInterfaceDeclaration(declaration)) || !declaration.typeParameters?.length) {
56
+ return ` type ${entry.name} = import(${specifier}).${entry.imported};`;
57
+ }
58
+ const parameters = `<${declaration.typeParameters.map((parameter) => parameter.getText(file)).join(", ")}>`;
59
+ const arguments_ = `<${declaration.typeParameters.map((parameter) => parameter.name.text).join(", ")}>`;
60
+ return ` type ${entry.name}${parameters} = import(${specifier}).${entry.imported}${arguments_};`;
61
+ }
62
+ /** Produces global editor declarations for isolated app, server and shared TS projects. */
63
+ export async function generateAutoImportTypes(projectRoot) {
64
+ const root = resolve(projectRoot);
65
+ const outputDirectory = join(root, ".meeko", "types", "autoimports");
66
+ const registry = await discoverAutoImports(root);
67
+ await mkdir(outputDirectory, { recursive: true });
68
+ const outputs = {};
69
+ for (const scope of ["client", "server", "shared"]) {
70
+ const entries = scopeEntries(registry, scope);
71
+ assertNoCollisions(entries, scope);
72
+ const declarations = await Promise.all(entries.sort((a, b) => a.name.localeCompare(b.name)).map(async (entry) => {
73
+ const specifier = JSON.stringify(publicSpecifier(outputDirectory, entry));
74
+ if (entry.typeOnly)
75
+ return typeDeclaration(entry, specifier);
76
+ const value = entry.component ? `typeof import(${specifier})` : `typeof import(${specifier})[${JSON.stringify(entry.imported)}]`;
77
+ return ` const ${entry.name}: ${value};`;
78
+ }));
79
+ const path = join(outputDirectory, `${scope}.d.ts`);
80
+ const source = ["// Generated by Meeko. Scope-specific editor declarations.", "export {};", "declare global {", ...declarations, "}", ""].join("\n");
81
+ let previous;
82
+ try {
83
+ previous = await readFile(path, "utf8");
84
+ }
85
+ catch (error) {
86
+ if (error.code !== "ENOENT")
87
+ throw error;
88
+ }
89
+ if (previous !== source)
90
+ await writeFile(path, source, "utf8");
91
+ outputs[scope] = path;
92
+ }
93
+ return outputs;
94
+ }
95
+ /** Vite can call this for app/shared/server TypeScript before its normal TS transform. */
96
+ export function transformTypeScriptAutoImports(source, filename, root, registry) {
97
+ const scope = typeScriptScope(root, filename);
98
+ if (!scope)
99
+ return undefined;
100
+ if (scope === "shared") {
101
+ // A shared module must resolve identically from both client and server views.
102
+ const client = applyAutoImports(source, filename, registry, "client");
103
+ const server = applyAutoImports(source, filename, registry, "server");
104
+ if (client !== server)
105
+ throw new Error(`Shared module ${filename} references an app or server autoimport`);
106
+ return client === source ? undefined : client;
107
+ }
108
+ const transformed = applyAutoImports(source, filename, registry, scope);
109
+ return transformed === source ? undefined : transformed;
110
+ }
111
+ /** Maps the unchanged source body after imports inserted at the start of a TS module. */
112
+ export function typeScriptAutoImportMap(source, transformed, filename) {
113
+ if (!transformed.endsWith(source))
114
+ throw new TypeError("Autoimport transform must preserve the original TypeScript body");
115
+ const prefix = transformed.slice(0, -source.length);
116
+ const insertedLines = (prefix.match(/\n/g) ?? []).length;
117
+ const sourceLines = source.split(/\r\n|\r|\n/).length;
118
+ return {
119
+ version: 3, sources: [filename], sourcesContent: [source], names: [],
120
+ mappings: `${";".repeat(insertedLines)}AAAA${";AACA".repeat(Math.max(0, sourceLines - 1))}`,
121
+ };
122
+ }
123
+ /** Convenience entrypoint for CLI generators. */
124
+ export async function generateTypeScriptAutoImports(projectRoot) {
125
+ return generateAutoImportTypes(projectRoot);
126
+ }
127
+ //# sourceMappingURL=autoimports-ts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoimports-ts.js","sourceRoot":"","sources":["../src/autoimports-ts.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join, relative, resolve, sep } from \"node:path\";\nimport ts from \"@typescript/typescript6\";\nimport { applyAutoImports, discoverAutoImports } from \"./autoimports.js\";\nimport type { AutoImport, ImportScope } from \"./autoimports.js\";\n\nexport type TypeScriptScope = ImportScope;\n\nfunction within(root: string, filename: string): string | undefined {\n const path = relative(resolve(root), resolve(filename));\n return path === \"..\" || path.startsWith(`..${sep}`) || path.startsWith(\"/\") ? undefined : path.replaceAll(\"\\\\\", \"/\");\n}\n\n/** Selects the application scope of a source file without executing it. */\nexport function typeScriptScope(root: string, filename: string): TypeScriptScope | undefined {\n const path = within(root, filename);\n if (!path || !/\\.[cm]?tsx?$/.test(path) || /\\.(?:test|spec|d)\\.[cm]?tsx?$/.test(path)) return undefined;\n if (/\\.shared\\.[cm]?tsx?$/.test(path)) return \"shared\";\n if (/\\.server\\.[cm]?tsx?$/.test(path)) return \"server\";\n if (/\\.route\\.[cm]?tsx?$/.test(path) && /(?:^|\\/)app\\/pages\\//.test(path)) return \"server\";\n if (/\\.client\\.[cm]?tsx?$/.test(path)) return \"client\";\n if (path.startsWith(\"server/\")) return \"server\";\n if (path.startsWith(\"app/\")) return \"client\";\n if (path.startsWith(\"shared/\")) return \"shared\";\n return undefined;\n}\n\nfunction publicSpecifier(outputDirectory: string, entry: AutoImport): string {\n if (entry.specifier) return entry.specifier;\n const path = relative(outputDirectory, entry.file).replaceAll(\"\\\\\", \"/\");\n const prefixed = path.startsWith(\".\") ? path : `./${path}`;\n return entry.component ? prefixed : prefixed.replace(/\\.[cm]?ts$/, (extension) => extension === \".mts\" ? \".mjs\" : extension === \".cts\" ? \".cjs\" : \".js\");\n}\n\nfunction scopeEntries(registry: readonly AutoImport[], scope: TypeScriptScope): AutoImport[] {\n return registry.filter((entry) => entry.scope === \"shared\" || (scope !== \"shared\" && entry.scope === scope));\n}\n\nfunction assertNoCollisions(entries: readonly AutoImport[], scope: TypeScriptScope): void {\n const symbols = new Map<string, AutoImport>();\n for (const entry of entries) {\n const previous = symbols.get(entry.name);\n if (previous) throw new Error(`Ambiguous autoimport ${entry.name} in ${scope} scope: ${previous.file}, ${entry.file}`);\n symbols.set(entry.name, entry);\n }\n}\n\nasync function typeDeclaration(entry: AutoImport, specifier: string): Promise<string> {\n if (entry.specifier) return ` type ${entry.name} = import(${specifier}).${entry.imported};`;\n const source = await readFile(entry.file, \"utf8\");\n const file = ts.createSourceFile(entry.file, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);\n const declaration = file.statements.find((statement) =>\n (ts.isTypeAliasDeclaration(statement) || ts.isInterfaceDeclaration(statement)) && statement.name.text === entry.imported);\n if (!declaration || (!ts.isTypeAliasDeclaration(declaration) && !ts.isInterfaceDeclaration(declaration)) || !declaration.typeParameters?.length) {\n return ` type ${entry.name} = import(${specifier}).${entry.imported};`;\n }\n const parameters = `<${declaration.typeParameters.map((parameter) => parameter.getText(file)).join(\", \")}>`;\n const arguments_ = `<${declaration.typeParameters.map((parameter) => parameter.name.text).join(\", \")}>`;\n return ` type ${entry.name}${parameters} = import(${specifier}).${entry.imported}${arguments_};`;\n}\n\n/** Produces global editor declarations for isolated app, server and shared TS projects. */\nexport async function generateAutoImportTypes(projectRoot: string): Promise<{ client: string; server: string; shared: string }> {\n const root = resolve(projectRoot);\n const outputDirectory = join(root, \".meeko\", \"types\", \"autoimports\");\n const registry = await discoverAutoImports(root);\n await mkdir(outputDirectory, { recursive: true });\n const outputs = {} as { client: string; server: string; shared: string };\n for (const scope of [\"client\", \"server\", \"shared\"] as const) {\n const entries = scopeEntries(registry, scope);\n assertNoCollisions(entries, scope);\n const declarations = await Promise.all(entries.sort((a, b) => a.name.localeCompare(b.name)).map(async (entry) => {\n const specifier = JSON.stringify(publicSpecifier(outputDirectory, entry));\n if (entry.typeOnly) return typeDeclaration(entry, specifier);\n const value = entry.component ? `typeof import(${specifier})` : `typeof import(${specifier})[${JSON.stringify(entry.imported)}]`;\n return ` const ${entry.name}: ${value};`;\n }));\n const path = join(outputDirectory, `${scope}.d.ts`);\n const source = [\"// Generated by Meeko. Scope-specific editor declarations.\", \"export {};\", \"declare global {\", ...declarations, \"}\", \"\"].join(\"\\n\");\n let previous: string | undefined;\n try { previous = await readFile(path, \"utf8\"); }\n catch (error) { if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error; }\n if (previous !== source) await writeFile(path, source, \"utf8\");\n outputs[scope] = path;\n }\n return outputs;\n}\n\n/** Vite can call this for app/shared/server TypeScript before its normal TS transform. */\nexport function transformTypeScriptAutoImports(source: string, filename: string, root: string, registry: readonly AutoImport[]): string | undefined {\n const scope = typeScriptScope(root, filename);\n if (!scope) return undefined;\n if (scope === \"shared\") {\n // A shared module must resolve identically from both client and server views.\n const client = applyAutoImports(source, filename, registry, \"client\");\n const server = applyAutoImports(source, filename, registry, \"server\");\n if (client !== server) throw new Error(`Shared module ${filename} references an app or server autoimport`);\n return client === source ? undefined : client;\n }\n const transformed = applyAutoImports(source, filename, registry, scope);\n return transformed === source ? undefined : transformed;\n}\n\n/** Maps the unchanged source body after imports inserted at the start of a TS module. */\nexport function typeScriptAutoImportMap(source: string, transformed: string, filename: string): {\n version: 3; sources: string[]; sourcesContent: string[]; names: string[]; mappings: string;\n} {\n if (!transformed.endsWith(source)) throw new TypeError(\"Autoimport transform must preserve the original TypeScript body\");\n const prefix = transformed.slice(0, -source.length);\n const insertedLines = (prefix.match(/\\n/g) ?? []).length;\n const sourceLines = source.split(/\\r\\n|\\r|\\n/).length;\n return {\n version: 3, sources: [filename], sourcesContent: [source], names: [],\n mappings: `${\";\".repeat(insertedLines)}AAAA${\";AACA\".repeat(Math.max(0, sourceLines - 1))}`,\n };\n}\n\n/** Convenience entrypoint for CLI generators. */\nexport async function generateTypeScriptAutoImports(projectRoot: string): Promise<{ client: string; server: string; shared: string }> {\n return generateAutoImportTypes(projectRoot);\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import type { CompileOptions, CompileResult } from "./compiler.js";
2
+ export type ImportScope = "client" | "server" | "shared";
3
+ export interface AutoImport {
4
+ name: string;
5
+ imported: string;
6
+ file: string;
7
+ scope: ImportScope;
8
+ typeOnly: boolean;
9
+ component: boolean;
10
+ specifier?: string;
11
+ }
12
+ /** Construye el registro a partir de exports sintácticos, sin ejecutar módulos de la aplicación. */
13
+ export declare function discoverAutoImports(root: string): Promise<AutoImport[]>;
14
+ /** Same static registry used by the synchronous VS Code Language Service. */
15
+ export declare function discoverAutoImportsSync(root: string): AutoImport[];
16
+ /** Añade imports ESM para referencias libres del módulo generado. */
17
+ export declare function applyAutoImports(code: string, filename: string, registry: readonly AutoImport[], target: "client" | "server"): string;
18
+ /** Transforma un SFC usando exactamente el mismo compilador que el plugin Vite. */
19
+ export declare function transformAutoImports(source: string, filename: string, code: string, registry: readonly AutoImport[], target: "client" | "server"): string;
20
+ /** Compile result shared by Vite, CLI checks and declaration generation. */
21
+ export declare function compileWithAutoImports(source: string, filename: string, options?: CompileOptions, target?: "client" | "server"): Promise<CompileResult>;
@@ -0,0 +1,285 @@
1
+ import { readdir, readFile } from "node:fs/promises";
2
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
3
+ import { extname, isAbsolute, join, relative, resolve, sep } from "node:path";
4
+ import ts from "@typescript/typescript6";
5
+ import { compile } from "./compiler.js";
6
+ import { projectRoot } from "./compiler/project-root.js";
7
+ import { parseSfc } from "./compiler/sfc.js";
8
+ import { loadMeekoConfig, readMeekoConfigSync, resolveConfigSources } from "./framework/config.js";
9
+ const directories = [
10
+ "app/components", "app/composables", "app/utils", "app/stores",
11
+ "shared/types", "shared/schemas", "shared/utils", "shared/constants", "shared/enums",
12
+ "server/utils",
13
+ ];
14
+ function scopeOf(file) {
15
+ if (/\.shared\.[cm]?tsx?$/.test(file))
16
+ return "shared";
17
+ if (/\.server\.[cm]?tsx?$/.test(file))
18
+ return "server";
19
+ if (/\.client\.[cm]?tsx?$/.test(file))
20
+ return "client";
21
+ if (file.includes(`${sep}server${sep}`))
22
+ return "server";
23
+ if (file.includes(`${sep}app${sep}`))
24
+ return "client";
25
+ return "shared";
26
+ }
27
+ async function filesIn(path) {
28
+ const entries = await readdir(path, { withFileTypes: true }).catch(() => []);
29
+ const files = [];
30
+ for (const entry of entries) {
31
+ const filename = join(path, entry.name);
32
+ if (entry.isDirectory())
33
+ files.push(...await filesIn(filename));
34
+ else if (/\.(?:meeko|[cm]?ts)$/.test(entry.name) && !/\.(?:test|spec|d)\.[cm]?ts$/.test(entry.name))
35
+ files.push(filename);
36
+ }
37
+ return files;
38
+ }
39
+ function filesInSync(path) {
40
+ let entries;
41
+ try {
42
+ entries = readdirSync(path, { withFileTypes: true });
43
+ }
44
+ catch (error) {
45
+ if (error.code === "ENOENT")
46
+ return [];
47
+ throw error;
48
+ }
49
+ const files = [];
50
+ for (const entry of entries) {
51
+ const filename = join(path, entry.name);
52
+ if (entry.isDirectory())
53
+ files.push(...filesInSync(filename));
54
+ else if (/\.(?:meeko|[cm]?ts)$/.test(entry.name) && !/\.(?:test|spec|d)\.[cm]?ts$/.test(entry.name))
55
+ files.push(filename);
56
+ }
57
+ return files;
58
+ }
59
+ function builtins(root) {
60
+ const entries = [
61
+ ...["signal", "computed", "effect", "batch", "onCleanup", "provide", "inject", "createContext"].map((name) => ({ name, imported: name, file: "@meekojs/meeko", specifier: "@meekojs/meeko", scope: "shared", typeOnly: false, component: false })),
62
+ ...["useState", "href"].map((name) => ({ name, imported: name, file: "@meekojs/meeko/framework", specifier: "@meekojs/meeko/framework", scope: "shared", typeOnly: false, component: false })),
63
+ ];
64
+ for (const scope of ["client", "server"]) {
65
+ const file = join(root, ".meeko", "generated", `clients.${scope}.ts`);
66
+ if (!existsSync(file))
67
+ continue;
68
+ for (const name of ["api", "rpc", "socket", "useSocket"])
69
+ entries.push({ name, imported: name, file, scope, typeOnly: false, component: false });
70
+ }
71
+ if (!entries.some((entry) => entry.name === "useSocket")) {
72
+ entries.push({ name: "useSocket", imported: "useSocket", file: "@meekojs/meeko/framework", specifier: "@meekojs/meeko/framework", scope: "shared", typeOnly: false, component: false });
73
+ }
74
+ return entries;
75
+ }
76
+ function addFileImports(result, root, directory, file, source) {
77
+ const scope = scopeOf(file);
78
+ if (extname(file) === ".meeko") {
79
+ if (directory !== "app/components")
80
+ return;
81
+ const path = relative(join(root, directory), file).replace(/\.meeko$/, "");
82
+ const name = path.split(/[\\/]/).map((part) => part.replace(/(^|[-_])(\w)/g, (_, _dash, letter) => letter.toUpperCase())).join("");
83
+ result.push({ name, imported: "*", file, scope, typeOnly: false, component: true });
84
+ return;
85
+ }
86
+ const ast = ts.createSourceFile(file, source ?? "", ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
87
+ for (const statement of ast.statements) {
88
+ const exported = ts.canHaveModifiers(statement) && ts.getModifiers(statement)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
89
+ if (exported && (ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement) || ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement) || ts.isEnumDeclaration(statement)) && statement.name) {
90
+ result.push({ name: statement.name.text, imported: statement.name.text, file, scope, typeOnly: ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement), component: false });
91
+ }
92
+ if (exported && ts.isVariableStatement(statement)) {
93
+ for (const declaration of statement.declarationList.declarations)
94
+ if (ts.isIdentifier(declaration.name)) {
95
+ result.push({ name: declaration.name.text, imported: declaration.name.text, file, scope, typeOnly: false, component: false });
96
+ }
97
+ }
98
+ if (ts.isExportDeclaration(statement) && statement.exportClause && ts.isNamedExports(statement.exportClause)) {
99
+ for (const element of statement.exportClause.elements)
100
+ result.push({ name: element.name.text, imported: element.name.text, file, scope, typeOnly: statement.isTypeOnly || element.isTypeOnly, component: false });
101
+ }
102
+ }
103
+ }
104
+ /** Construye el registro a partir de exports sintácticos, sin ejecutar módulos de la aplicación. */
105
+ export async function discoverAutoImports(root) {
106
+ const result = builtins(root);
107
+ const config = await loadMeekoConfig(root);
108
+ const sources = await resolveConfigSources(root, config);
109
+ for (const source of sources) {
110
+ for (const directory of configuredDirectories(config.imports?.serverDirs)) {
111
+ for (const file of await filesIn(join(source.root, directory))) {
112
+ addFileImports(result, source.root, directory, file, extname(file) === ".meeko" ? undefined : await readFile(file, "utf8"));
113
+ }
114
+ }
115
+ }
116
+ return result;
117
+ }
118
+ /** Same static registry used by the synchronous VS Code Language Service. */
119
+ export function discoverAutoImportsSync(root) {
120
+ const result = builtins(root);
121
+ const config = readMeekoConfigSync(root);
122
+ const sourceRoots = [resolve(root)];
123
+ for (const entry of [...(config.extends ?? []), ...(config.modules ?? [])]) {
124
+ const sourceRoot = resolve(root, entry);
125
+ const path = relative(root, sourceRoot);
126
+ if (path === ".." || path.startsWith(`..${sep}`) || isAbsolute(path))
127
+ throw new Error(`Configured autoimport source escapes project root: ${entry}`);
128
+ sourceRoots.push(sourceRoot);
129
+ }
130
+ for (const sourceRoot of sourceRoots) {
131
+ for (const directory of configuredDirectories(config.imports?.serverDirs)) {
132
+ for (const file of filesInSync(join(sourceRoot, directory))) {
133
+ addFileImports(result, sourceRoot, directory, file, extname(file) === ".meeko" ? undefined : readFileSync(file, "utf8"));
134
+ }
135
+ }
136
+ }
137
+ return result;
138
+ }
139
+ function configuredDirectories(serverDirs) {
140
+ const result = new Set(directories);
141
+ for (const raw of serverDirs ?? []) {
142
+ const directory = raw.replaceAll("\\", "/").replace(/\/$/, "");
143
+ const parts = directory.split("/");
144
+ if (parts[0] !== "server" || parts.length < 2 || parts.some((part) => !part || part === "." || part === "..")) {
145
+ throw new Error(`Autoimport serverDir must be a relative path under server/: ${raw}`);
146
+ }
147
+ result.add(directory);
148
+ }
149
+ return [...result];
150
+ }
151
+ function bindingNames(name, names) {
152
+ if (ts.isIdentifier(name))
153
+ names.add(name.text);
154
+ else
155
+ for (const element of name.elements)
156
+ if (ts.isBindingElement(element))
157
+ bindingNames(element.name, names);
158
+ }
159
+ function scopeNames(node) {
160
+ const names = new Set();
161
+ if (ts.isFunctionLike(node)) {
162
+ for (const parameter of node.parameters)
163
+ bindingNames(parameter.name, names);
164
+ for (const parameter of node.typeParameters ?? [])
165
+ names.add(parameter.name.text);
166
+ }
167
+ if (ts.isCatchClause(node) && node.variableDeclaration)
168
+ bindingNames(node.variableDeclaration.name, names);
169
+ if (ts.isForStatement(node) && node.initializer && ts.isVariableDeclarationList(node.initializer)) {
170
+ for (const declaration of node.initializer.declarations)
171
+ bindingNames(declaration.name, names);
172
+ }
173
+ if ((ts.isForOfStatement(node) || ts.isForInStatement(node)) && ts.isVariableDeclarationList(node.initializer)) {
174
+ for (const declaration of node.initializer.declarations)
175
+ bindingNames(declaration.name, names);
176
+ }
177
+ if (ts.isSourceFile(node) || ts.isBlock(node)) {
178
+ for (const statement of node.statements) {
179
+ if (ts.isVariableStatement(statement))
180
+ for (const declaration of statement.declarationList.declarations)
181
+ bindingNames(declaration.name, names);
182
+ if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement) || ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement) || ts.isEnumDeclaration(statement)) && statement.name)
183
+ names.add(statement.name.text);
184
+ if (ts.isImportDeclaration(statement)) {
185
+ const clause = statement.importClause;
186
+ if (clause?.name)
187
+ names.add(clause.name.text);
188
+ if (clause?.namedBindings) {
189
+ if (ts.isNamespaceImport(clause.namedBindings))
190
+ names.add(clause.namedBindings.name.text);
191
+ else
192
+ for (const element of clause.namedBindings.elements)
193
+ names.add(element.name.text);
194
+ }
195
+ }
196
+ }
197
+ }
198
+ return names;
199
+ }
200
+ function introducesScope(node) {
201
+ return ts.isSourceFile(node) || ts.isBlock(node) || ts.isFunctionLike(node) || ts.isCatchClause(node)
202
+ || ts.isForStatement(node) || ts.isForOfStatement(node) || ts.isForInStatement(node);
203
+ }
204
+ function isReference(node) {
205
+ const parent = node.parent;
206
+ if (!parent)
207
+ return false;
208
+ if (ts.isPropertyAccessExpression(parent) && parent.name === node)
209
+ return false;
210
+ if (ts.isQualifiedName(parent) && parent.right === node)
211
+ return false;
212
+ if (ts.isPropertyAssignment(parent) && parent.name === node)
213
+ return false;
214
+ if (ts.isPropertyDeclaration(parent) && parent.name === node)
215
+ return false;
216
+ if (ts.isMethodDeclaration(parent) && parent.name === node)
217
+ return false;
218
+ if (ts.isImportSpecifier(parent) || ts.isExportSpecifier(parent) || ts.isImportClause(parent) || ts.isNamespaceImport(parent))
219
+ return false;
220
+ if (ts.isTypeReferenceNode(parent) && parent.typeName === node)
221
+ return true;
222
+ if (ts.isParameter(parent) && parent.name === node)
223
+ return false;
224
+ if (ts.isTypeParameterDeclaration(parent) && parent.name === node)
225
+ return false;
226
+ if (ts.isVariableDeclaration(parent) && parent.name === node)
227
+ return false;
228
+ if ((ts.isFunctionDeclaration(parent) || ts.isClassDeclaration(parent) || ts.isInterfaceDeclaration(parent) || ts.isTypeAliasDeclaration(parent)) && parent.name === node)
229
+ return false;
230
+ if (ts.isPropertySignature(parent) && parent.name === node)
231
+ return false;
232
+ return true;
233
+ }
234
+ /** Añade imports ESM para referencias libres del módulo generado. */
235
+ export function applyAutoImports(code, filename, registry, target) {
236
+ const ast = ts.createSourceFile(filename, code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
237
+ const referenced = new Set();
238
+ const visit = (node, scopes) => {
239
+ const active = introducesScope(node) ? [...scopes, scopeNames(node)] : scopes;
240
+ if (ts.isIdentifier(node) && isReference(node) && !active.some((scope) => scope.has(node.text)))
241
+ referenced.add(node.text);
242
+ ts.forEachChild(node, (child) => visit(child, active));
243
+ };
244
+ visit(ast, []);
245
+ const imports = [];
246
+ for (const name of [...referenced].sort()) {
247
+ const candidates = registry.filter((entry) => entry.name === name);
248
+ if (candidates.length === 0)
249
+ continue;
250
+ const allowed = candidates.filter((entry) => entry.scope === "shared" || entry.scope === target);
251
+ if (allowed.length === 0)
252
+ throw new Error(`Autoimport ${name} is unavailable in ${target} scope`);
253
+ if (allowed.length > 1)
254
+ throw new Error(`Ambiguous autoimport ${name}: ${allowed.map((entry) => entry.file).join(", ")}`);
255
+ const entry = allowed[0];
256
+ let specifier = entry.specifier ?? relative(resolve(filename, ".."), entry.file).replaceAll("\\", "/");
257
+ if (!entry.specifier && !specifier.startsWith("."))
258
+ specifier = `./${specifier}`;
259
+ if (!entry.component && !entry.specifier)
260
+ specifier = specifier.replace(/\.[cm]?ts$/, ".js");
261
+ if (entry.component)
262
+ imports.push(`import * as ${name} from ${JSON.stringify(specifier)};`);
263
+ else
264
+ imports.push(`import ${entry.typeOnly ? "type " : ""}{ ${entry.imported} as ${name} } from ${JSON.stringify(specifier)};`);
265
+ }
266
+ return imports.length ? `${imports.join("\n")}\n${code}` : code;
267
+ }
268
+ /** Transforma un SFC usando exactamente el mismo compilador que el plugin Vite. */
269
+ export function transformAutoImports(source, filename, code, registry, target) {
270
+ parseSfc(source, filename);
271
+ return applyAutoImports(code, filename, registry, target);
272
+ }
273
+ /** Compile result shared by Vite, CLI checks and declaration generation. */
274
+ export async function compileWithAutoImports(source, filename, options = {}, target = "client") {
275
+ const compiled = compile(source, filename, options);
276
+ const registry = await discoverAutoImports(projectRoot(filename));
277
+ const code = transformAutoImports(source, filename, compiled.code, registry, target);
278
+ const offset = code.length - compiled.code.length;
279
+ return {
280
+ ...compiled,
281
+ code,
282
+ mappings: offset ? compiled.mappings.map((mapping) => ({ ...mapping, generatedStart: mapping.generatedStart + offset, generatedEnd: mapping.generatedEnd + offset })) : compiled.mappings,
283
+ };
284
+ }
285
+ //# sourceMappingURL=autoimports.js.map