silgi 0.27.5 → 0.27.7

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.
@@ -1,25 +1,27 @@
1
- import { genObjectFromRawEntries, genObjectFromRaw, genObjectFromValues } from 'knitwork';
2
- import { join, resolve, relative, dirname, basename, extname, isAbsolute } from 'pathe';
3
- import { writeFile, hasSilgiModule, addTemplate, relativeWithDot, hash, resolveAlias, directoryToURL, hasError, parseServices, normalizeTemplate, useLogger, resolveSilgiPath, genEnsureSafeVar, isDirectory } from 'silgi/kit';
4
1
  import { generateDTS } from 'apiful/openapi';
5
2
  import consola$1, { consola } from 'consola';
6
- import { existsSync, promises, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
3
+ import { join, resolve, dirname, isAbsolute, relative, basename, extname } from 'pathe';
4
+ import { hasSilgiModule, addTemplate, normalizeTemplate, useLogger, genEnsureSafeVar, hash, relativeWithDot, writeFile, isDirectory, resolveAlias as resolveAlias$1, directoryToURL, hasError, parseServices, resolveSilgiPath } from 'silgi/kit';
5
+ import { mkdirSync, existsSync, writeFileSync, promises, readFileSync } from 'node:fs';
7
6
  import { readdir, readFile } from 'node:fs/promises';
7
+ import { genObjectFromRawEntries, genObjectFromRaw, genObjectFromValues } from 'knitwork';
8
+ import { resolvePath, parseNodeModulePath, lookupNodeModuleSubpath, resolveModuleExportNames, findTypeExports, findExports } from 'mlly';
9
+ import { resolveAlias } from 'pathe/utils';
10
+ import { runtimeDir } from 'silgi/runtime/meta';
11
+ import { toExports, scanExports, createUnimport } from 'unimport';
12
+ import { generateTypes, resolveSchema } from 'untyped';
13
+ import { s as silgiGenerateType, l as loadOptions } from './types.mjs';
8
14
  import { createHooks, createDebugger } from 'hookable';
9
15
  import { useSilgiCLI, replaceRuntimeValues, silgiCLICtx, autoImportTypes } from 'silgi';
10
- import { runtimeDir } from 'silgi/runtime/meta';
11
- import { scanExports, createUnimport, toExports } from 'unimport';
12
16
  import { c as createRouteRules } from '../_chunks/routeRules.mjs';
13
17
  import * as p from '@clack/prompts';
14
18
  import * as dotenv from 'dotenv';
15
- import { resolveModuleExportNames, findTypeExports, findExports, resolvePath, parseNodeModulePath, lookupNodeModuleSubpath } from 'mlly';
16
19
  import { createJiti } from 'dev-jiti';
17
20
  import { h as hasInstalledModule } from './compatibility.mjs';
18
21
  import { fileURLToPath } from 'node:url';
19
22
  import { defu } from 'defu';
20
23
  import { resolveModuleURL } from 'exsolve';
21
24
  import { isRelative, withTrailingSlash } from 'ufo';
22
- import { generateTypes, resolveSchema } from 'untyped';
23
25
  import { globby } from 'globby';
24
26
  import ignore from 'ignore';
25
27
  import { parseSync } from '@oxc-parser/wasm';
@@ -28,46 +30,6 @@ import { useSilgiRuntimeConfig, initRuntimeConfig } from 'silgi/runtime';
28
30
  import { createStorage, builtinDrivers } from 'unstorage';
29
31
  import { peerDependencies } from 'silgi/meta';
30
32
  import { snakeCase } from 'scule';
31
- import { l as loadOptions, s as silgiGenerateType } from './types.mjs';
32
- import { resolveAlias as resolveAlias$1 } from 'pathe/utils';
33
-
34
- async function prepareBuild(silgi) {
35
- try {
36
- if (!silgi?.routeRules?.exportRules) {
37
- throw new Error("Invalid silgi configuration: routeRules or exportRules is undefined");
38
- }
39
- const exportedRules = silgi.routeRules.exportRules();
40
- if (!exportedRules || typeof exportedRules !== "object") {
41
- throw new Error("No valid route rules to export");
42
- }
43
- const content = `/* eslint-disable */
44
- // @ts-nocheck
45
- // This file is auto-generated at build time by Silgi
46
- // Contains route rules with preserved functions
47
- // DO NOT MODIFY THIS FILE DIRECTLY
48
-
49
- export const routeRules = ${genObjectFromRawEntries(
50
- Object.entries(exportedRules).map(([key, value]) => {
51
- if (typeof value === "function") {
52
- return [key, genObjectFromRaw(value)];
53
- }
54
- return [key, genObjectFromValues(value)];
55
- })
56
- )}
57
- `;
58
- const serverDir = silgi.options.silgi.serverDir;
59
- if (!serverDir) {
60
- throw new Error("Server directory not defined in configuration");
61
- }
62
- const file = join(serverDir, "rules.ts");
63
- if (!silgi.errors.length) {
64
- await writeFile(file, content);
65
- }
66
- } catch (error) {
67
- console.error("\u274C Failed to prepare build:", error instanceof Error ? error.message : String(error));
68
- throw error;
69
- }
70
- }
71
33
 
72
34
  async function generateApiFul(silgi) {
73
35
  const config = silgi.options.apiFul;
@@ -103,2311 +65,2358 @@ async function generateApiFul(silgi) {
103
65
  });
104
66
  }
105
67
 
106
- async function prepare(_silgi) {
107
- }
108
-
109
- async function setupDotenv(options) {
110
- const targetEnvironment = options.env ?? process.env;
111
- const environment = await loadDotenv({
112
- cwd: options.cwd,
113
- fileName: options.fileName ?? ".env",
114
- env: targetEnvironment,
115
- interpolate: options.interpolate ?? true
116
- });
117
- for (const key in environment) {
118
- if (!key.startsWith("_") && targetEnvironment[key] === void 0) {
119
- targetEnvironment[key] = environment[key];
68
+ const vueShim = {
69
+ filename: "delete/testtest.d.ts",
70
+ where: ".silgi",
71
+ getContents: ({ app }) => {
72
+ if (!app.options.typescript.shim) {
73
+ return "";
120
74
  }
75
+ return [
76
+ "declare module '*.vue' {",
77
+ " import { DefineComponent } from 'vue'",
78
+ " const component: DefineComponent<{}, {}, any>",
79
+ " export default component",
80
+ "}"
81
+ ].join("\n");
121
82
  }
122
- return environment;
123
- }
124
- async function loadDotenv(options) {
125
- const environment = /* @__PURE__ */ Object.create(null);
126
- const dotenvFile = resolve(options.cwd, options.fileName);
127
- if (existsSync(dotenvFile)) {
128
- const parsed = dotenv.parse(await promises.readFile(dotenvFile, "utf8"));
129
- Object.assign(environment, parsed);
83
+ };
84
+ const pluginsDeclaration = {
85
+ filename: "delete/testtest1.d.ts",
86
+ where: ".silgi",
87
+ getContents: async () => {
88
+ return `
89
+ declare module 'nuxt' {
90
+ interface NuxtApp {
91
+ $myPlugin: any;
92
+ }
93
+ }
94
+ `;
130
95
  }
131
- if (!options.env?._applied) {
132
- Object.assign(environment, options.env);
133
- environment._applied = true;
96
+ };
97
+
98
+ const defaultTemplates = {
99
+ __proto__: null,
100
+ pluginsDeclaration: pluginsDeclaration,
101
+ vueShim: vueShim
102
+ };
103
+
104
+ const postTemplates = [
105
+ pluginsDeclaration.filename
106
+ ];
107
+ const logger = useLogger("silgi");
108
+ async function generateApp(app, options = {}) {
109
+ app.templates = Object.values(defaultTemplates).concat(app.options.build.templates);
110
+ await app.callHook("app:templates", app);
111
+ app.templates = app.templates.map((tmpl) => {
112
+ const dir = tmpl.where === ".silgi" ? app.options.build.dir : tmpl.where === "server" ? app.options.silgi.serverDir : tmpl.where === "client" ? app.options.silgi.clientDir : app.options.silgi.vfsDir;
113
+ return normalizeTemplate(tmpl, dir);
114
+ });
115
+ const filteredTemplates = {
116
+ pre: [],
117
+ post: []
118
+ };
119
+ for (const template of app.templates) {
120
+ if (options.filter && !options.filter(template)) {
121
+ continue;
122
+ }
123
+ const key = template.filename && postTemplates.includes(template.filename) ? "post" : "pre";
124
+ filteredTemplates[key].push(template);
134
125
  }
135
- if (options.interpolate) {
136
- interpolate(environment);
126
+ const templateContext = { app };
127
+ const writes = [];
128
+ const dirs = /* @__PURE__ */ new Set();
129
+ const changedTemplates = [];
130
+ async function processTemplate(template) {
131
+ const dir = template.where === ".silgi" ? app.options.build.dir : template.where === "server" ? app.options.silgi.serverDir : template.where === "client" ? app.options.silgi.clientDir : app.options.silgi.vfsDir;
132
+ const fullPath = template.dst || resolve(dir, template.filename);
133
+ const start = performance.now();
134
+ const contents = await compileTemplate(template, templateContext).catch((e) => {
135
+ logger.error(`Could not compile template \`${template.filename}\`.`);
136
+ logger.error(e);
137
+ throw e;
138
+ });
139
+ template.modified = true;
140
+ if (template.modified) {
141
+ changedTemplates.push(template);
142
+ }
143
+ const perf = performance.now() - start;
144
+ const setupTime = Math.round(perf * 100) / 100;
145
+ if (app.options.debug || setupTime > 500) {
146
+ logger.info(`Compiled \`${template.filename}\` in ${setupTime}ms`);
147
+ }
148
+ if (template.modified && template.write) {
149
+ dirs.add(dirname(fullPath));
150
+ if (template.skipIfExists && existsSync(fullPath)) {
151
+ return;
152
+ }
153
+ writes.push(() => writeFileSync(fullPath, contents, "utf8"));
154
+ }
137
155
  }
138
- return environment;
139
- }
140
- function interpolate(target, source = {}, parse = (v) => v) {
141
- function getValue(key) {
142
- return source[key] === void 0 ? target[key] : source[key];
156
+ await Promise.allSettled(filteredTemplates.pre.map(processTemplate));
157
+ await Promise.allSettled(filteredTemplates.post.map(processTemplate));
158
+ for (const dir of dirs) {
159
+ mkdirSync(dir, { recursive: true });
143
160
  }
144
- function interpolate2(value, parents = []) {
145
- if (typeof value !== "string") {
146
- return value;
161
+ for (const write of writes) {
162
+ if (!app.errors.length) {
163
+ write();
147
164
  }
148
- const matches = value.match(/(.?\$\{?[\w:]*\}?)/g) || [];
149
- return parse(
150
- matches.reduce((newValue, match) => {
151
- const parts = /(.?)\$\{?([\w:]+)?\}?/.exec(match) || [];
152
- const prefix = parts[1];
153
- let value2, replacePart;
154
- if (prefix === "\\") {
155
- replacePart = parts[0] || "";
156
- value2 = replacePart.replace(String.raw`\$`, "$");
157
- } else {
158
- const key = parts[2];
159
- replacePart = (parts[0] || "").slice(prefix.length);
160
- if (parents.includes(key)) {
161
- console.warn(
162
- `Please avoid recursive environment variables ( loop: ${parents.join(
163
- " > "
164
- )} > ${key} )`
165
- );
166
- return "";
167
- }
168
- value2 = getValue(key);
169
- value2 = interpolate2(value2, [...parents, key]);
170
- }
171
- return value2 === void 0 ? newValue : newValue.replace(replacePart, value2);
172
- }, value)
173
- );
174
165
  }
175
- for (const key in target) {
176
- target[key] = interpolate2(getValue(key));
166
+ if (changedTemplates.length) {
167
+ await app.callHook("app:templatesGenerated", app, changedTemplates, options);
177
168
  }
178
169
  }
179
-
180
- let initialized = false;
181
- async function prepareEnv(silgiConfig) {
182
- if (initialized)
183
- return;
184
- initialized = true;
185
- const customEnvironments = silgiConfig.environments;
186
- const environment = silgiConfig.activeEnvironment ? silgiConfig.activeEnvironment : await p.select({
187
- message: "Select an environment",
188
- options: customEnvironments?.length > 0 ? customEnvironments.map((env) => ({
189
- label: env.fileName,
190
- value: env.fileName
191
- })) : [
192
- { label: "Development (.env)", value: ".env" },
193
- { label: "Docker (.env.docker)", value: "docker" },
194
- { label: "Staging (.env.staging)", value: "staging" },
195
- { label: "Testing (.env.testing)", value: "testing" },
196
- { label: "Production (.env.prod)", value: "prod" }
197
- ]
198
- });
199
- const findEnv = customEnvironments?.find((env) => env.fileName === environment);
200
- if (findEnv) {
201
- await setupDotenv({
202
- cwd: findEnv.cwd || silgiConfig.rootDir,
203
- interpolate: findEnv.interpolate,
204
- fileName: findEnv.fileName,
205
- env: findEnv.env
206
- });
207
- } else {
208
- await setupDotenv({
209
- cwd: silgiConfig.rootDir,
210
- interpolate: true,
211
- fileName: environment === ".env" ? ".env" : `.env.${environment}`
170
+ async function compileTemplate(template, ctx) {
171
+ delete ctx.utils;
172
+ if (template.src) {
173
+ try {
174
+ return await promises.readFile(template.src, "utf-8");
175
+ } catch (err) {
176
+ logger.error(`[nuxt] Error reading template from \`${template.src}\``);
177
+ throw err;
178
+ }
179
+ }
180
+ if (template.getContents) {
181
+ return template.getContents({
182
+ ...ctx,
183
+ options: template.options
212
184
  });
213
185
  }
186
+ throw new Error(`[nuxt] Invalid template. Templates must have either \`src\` or \`getContents\`: ${JSON.stringify(template)}`);
214
187
  }
215
188
 
216
- async function emptyFramework(silgi) {
217
- if (silgi.options.preset === "npm-package" || !silgi.options.preset) {
218
- silgi.hook("after:prepare:schema.ts", (data) => {
219
- data.unshift("type FrameworkContextExtends = {}");
220
- });
221
- }
189
+ async function prepare(_silgi) {
222
190
  }
223
191
 
224
- async function h3Framework(silgi, skip = false) {
225
- if (silgi.options.preset !== "h3" && skip === false)
226
- return;
227
- if (silgi.options.preset === "h3") {
228
- silgi.hook("after:prepare:schema.ts", (data) => {
229
- data.unshift("type FrameworkContextExtends = NitroApp");
230
- });
192
+ async function prepareConfigs(silgi) {
193
+ const _data = {
194
+ runtimeConfig: {}
195
+ };
196
+ for (const module of silgi.scanModules) {
197
+ if (module.meta.cliToRuntimeOptionsKeys && module.meta.cliToRuntimeOptionsKeys?.length > 0) {
198
+ for (const key of module.meta.cliToRuntimeOptionsKeys) {
199
+ _data[module.meta.configKey] = {
200
+ ..._data[module.meta.configKey],
201
+ [key]: module.options[key]
202
+ };
203
+ }
204
+ } else {
205
+ _data[module.meta.configKey] = {};
206
+ }
231
207
  }
232
- silgi.hook("prepare:schema.ts", (data) => {
233
- data.importItems.nitropack = {
208
+ await silgi.callHook("prepare:configs.ts", _data);
209
+ const importData = [
210
+ "import type { SilgiRuntimeOptions, SilgiRuntimeConfig, SilgiOptions } from 'silgi/types'",
211
+ "import { useSilgiRuntimeConfig } from 'silgi/runtime'",
212
+ "",
213
+ `export const runtimeConfig: Partial<SilgiRuntimeConfig> = ${genObjectFromRawEntries(
214
+ Object.entries(_data.runtimeConfig).map(([key, value]) => [key, genEnsureSafeVar(value)]),
215
+ ""
216
+ )}`,
217
+ "",
218
+ "const runtime = useSilgiRuntimeConfig(undefined, runtimeConfig)",
219
+ ""
220
+ ];
221
+ delete _data.runtimeConfig;
222
+ importData.push(`export const cliConfigs: Partial<SilgiRuntimeOptions & SilgiOptions> = ${genObjectFromRawEntries(
223
+ Object.entries(_data).map(
224
+ ([key, value]) => [key, genEnsureSafeVar(value)]
225
+ ).concat(
226
+ [
227
+ ["runtimeConfig", "runtime"]
228
+ ]
229
+ )
230
+ )}`);
231
+ return importData;
232
+ }
233
+
234
+ async function prepareCoreFile(data, frameworkContext, silgi) {
235
+ let importItems = {
236
+ "silgi": {
234
237
  import: [
235
238
  {
236
- name: "NitroApp",
237
- type: true,
238
- key: "NitroApp"
239
+ name: "createSilgi",
240
+ key: "createSilgi"
239
241
  }
240
242
  ],
241
- from: "nitropack/types"
242
- };
243
- data.importItems.h3 = {
243
+ from: "silgi"
244
+ },
245
+ "silgi/types": {
244
246
  import: [
245
247
  {
246
- name: "H3Event",
248
+ name: "SilgiRuntimeOptions",
247
249
  type: true,
248
- key: "H3Event"
249
- }
250
- ],
251
- from: "h3"
252
- };
253
- data.events.push({
254
- key: "H3Event",
255
- value: "H3Event",
256
- extends: true,
257
- isSilgiContext: false
258
- });
259
- });
260
- silgi.hook("prepare:createDTSFramework", (data) => {
261
- data.importItems["silgi/types"] = {
262
- import: [
250
+ key: "SilgiRuntimeOptions"
251
+ },
263
252
  {
264
- name: "SilgiRuntimeContext",
253
+ name: "FrameworkContext",
265
254
  type: true,
266
- key: "SilgiRuntimeContext"
255
+ key: "FrameworkContext"
256
+ },
257
+ {
258
+ name: "SilgiOptions",
259
+ type: true,
260
+ key: "SilgiOptions"
267
261
  }
268
262
  ],
269
263
  from: "silgi/types"
270
- };
271
- data.customContent?.push(
272
- "",
273
- 'declare module "h3" {',
274
- " interface H3EventContext extends SilgiRuntimeContext {}",
275
- "}",
276
- ""
277
- );
278
- });
279
- silgi.hook("prepare:core.ts", (data) => {
280
- data._silgiConfigs.push(`captureError: (silgi, error, context = {}) => {
281
- const promise = silgi.hooks
282
- .callHookParallel('error', silgi, error, context)
283
- .catch((error_) => {
284
- console.error('Error while capturing another error', error_)
285
- })
286
-
287
- if (context.event && isEvent(context.event)) {
288
- const errors = context.event.context.nitro?.errors
289
- if (errors) {
290
- errors.push({ error, context })
291
- }
292
- if (context.event.waitUntil) {
293
- context.event.waitUntil(promise)
294
- }
264
+ },
265
+ "#silgi/vfs": {
266
+ import: [],
267
+ from: "./vfs"
268
+ },
269
+ "scan.ts": {
270
+ import: [
271
+ {
272
+ name: "uris",
273
+ type: false,
274
+ key: "uris"
275
+ },
276
+ {
277
+ name: "services",
278
+ type: false,
279
+ key: "services"
280
+ },
281
+ {
282
+ name: "shareds",
283
+ type: false,
284
+ key: "shareds"
285
+ },
286
+ {
287
+ name: "schemas",
288
+ type: false,
289
+ key: "schemas"
290
+ },
291
+ {
292
+ name: "modulesURIs",
293
+ type: false,
294
+ key: "modulesURIs"
295
295
  }
296
- }`);
297
- });
298
- if (silgi.options.imports !== false) {
299
- const h3Exports = await resolveModuleExportNames("h3", {
300
- url: import.meta.url
301
- });
302
- silgi.options.imports.presets ??= [];
303
- silgi.options.imports.presets.push({
304
- from: "h3",
305
- imports: h3Exports.filter((n) => !/^[A-Z]/.test(n) && n !== "use")
306
- });
307
- }
308
- }
309
-
310
- async function nitroFramework(silgi, skip = false) {
311
- if (silgi.options.preset !== "nitro" && skip === false)
312
- return;
313
- silgi.hook("prepare:schema.ts", (data) => {
314
- data.importItems.nitropack = {
296
+ ],
297
+ from: "./scan.ts"
298
+ },
299
+ "configs.ts": {
315
300
  import: [
316
301
  {
317
- name: "NitroApp",
318
- type: true,
319
- key: "NitroApp"
302
+ name: "cliConfigs",
303
+ type: false,
304
+ key: "cliConfigs"
320
305
  }
321
306
  ],
322
- from: "nitropack/types"
323
- };
324
- });
325
- silgi.hook("after:prepare:schema.ts", (data) => {
326
- data.unshift("type FrameworkContextExtends = NitroApp");
327
- });
328
- silgi.options.plugins.push({
329
- packageImport: "silgi/runtime/internal/nitro",
330
- path: join(runtimeDir, "internal/nitro")
331
- });
332
- silgi.hook("prepare:createDTSFramework", (data) => {
333
- data.importItems["nitropack/types"] = {
307
+ from: "./configs.ts"
308
+ },
309
+ "rules.ts": {
334
310
  import: [
335
311
  {
336
- name: "NitroRuntimeConfig",
337
- type: true,
338
- key: "NitroRuntimeConfig"
312
+ name: "routeRules",
313
+ key: "routeRules"
339
314
  }
340
315
  ],
341
- from: "nitropack/types"
342
- };
343
- data.customContent?.push(
344
- "",
345
- 'declare module "silgi/types" {',
346
- " interface SilgiRuntimeConfig extends NitroRuntimeConfig {}",
347
- "}",
348
- ""
349
- );
350
- });
351
- if (silgi.options.imports !== false) {
352
- silgi.options.imports.presets ??= [];
316
+ from: "./rules.ts"
317
+ }
318
+ };
319
+ importItems = { ...data._importItems, ...importItems };
320
+ const _data = {
321
+ customImports: data._customImports || [],
322
+ buildSilgiExtraContent: [],
323
+ beforeBuildSilgiExtraContent: [],
324
+ afterCliOptions: [],
325
+ _silgiConfigs: [],
326
+ customContent: [],
327
+ importItems
328
+ };
329
+ await silgi.callHook("prepare:core.ts", _data);
330
+ if (importItems["#silgi/vfs"].import.length === 0) {
331
+ delete importItems["#silgi/vfs"];
353
332
  }
354
- await h3Framework(silgi, true);
355
- }
356
-
357
- async function nuxtFramework(silgi, skip = false) {
358
- if (silgi.options.preset !== "nuxt" && skip === false)
359
- return;
360
- await nitroFramework(silgi, true);
361
- }
362
-
363
- const frameworkSetup = [
364
- emptyFramework,
365
- h3Framework,
366
- nitroFramework,
367
- nuxtFramework
368
- ];
369
-
370
- async function registerModuleExportScan(silgi) {
371
- silgi.hook("prepare:schema.ts", async (options) => {
372
- for (const module of silgi.scanModules) {
373
- const moduleReExports = [];
374
- if (!module.entryPath) {
375
- continue;
333
+ const plugins = [];
334
+ for (const plugin of silgi.options.plugins) {
335
+ const pluginImportName = `_${hash(plugin.packageImport)}`;
336
+ _data.customImports.push(`import ${pluginImportName} from '${plugin.packageImport}'`);
337
+ plugins.push(pluginImportName);
338
+ }
339
+ const importsContent = [
340
+ 'import { mergeDeep } from "silgi/runtime"',
341
+ ...Object.entries(importItems).map(([_name, { from, import: imports }]) => {
342
+ if (silgi.options.typescript.removeFileExtension) {
343
+ from = from.replace(/\.(js|ts|mjs|cjs|jsx|tsx)$/, "");
376
344
  }
377
- const moduleTypes = await promises.readFile(module.entryPath.replace(/\.mjs$/, "Types.d.ts"), "utf8").catch(() => "");
378
- const normalisedModuleTypes = moduleTypes.replace(/export\s*\{.*?\}/gs, (match) => match.replace(/\b(type|interface)\b/g, ""));
379
- for (const e of findTypeExports(normalisedModuleTypes)) {
380
- moduleReExports.push(e);
381
- }
382
- for (const e of findExports(normalisedModuleTypes)) {
383
- moduleReExports.push(e);
384
- }
385
- const hasTypeExport = (name) => moduleReExports.find((exp) => exp.names?.includes(name));
386
- const configKey = module.meta.configKey;
387
- const moduleName = module.meta.name || module.meta._packageName;
388
- options.importItems[configKey] = {
389
- import: [],
390
- from: module.meta._packageName ? moduleName : relativeWithDot(silgi.options.build.typesDir, module.entryPath)
391
- };
392
- if (hasTypeExport("ModuleOptions")) {
393
- const importName = `_${hash(`${configKey}ModuleOptions`)}`;
394
- options.importItems[configKey].import.push({
395
- name: `ModuleOptions as ${importName}`,
396
- type: true,
397
- key: importName
398
- });
399
- options.options.push({ key: configKey, value: importName });
400
- }
401
- if (hasTypeExport("ModuleRuntimeOptions")) {
402
- const importName = `_${hash(`${configKey}ModuleRuntimeOptions`)}`;
403
- options.importItems[configKey].import.push({
404
- name: `ModuleRuntimeOptions as ${importName}`,
405
- type: true,
406
- key: importName
407
- });
408
- options.runtimeOptions.push({ key: configKey, value: importName });
409
- }
410
- if (hasTypeExport("ModuleRuntimeShareds")) {
411
- const importName = `_${hash(`${configKey}ModuleRuntimeShareds`)}`;
412
- options.importItems[configKey].import.push({
413
- name: `ModuleRuntimeShareds as ${importName}`,
414
- type: true,
415
- key: importName
416
- });
417
- options.shareds.push({ key: configKey, value: importName });
418
- }
419
- if (hasTypeExport("ModuleEvents")) {
420
- const importName = `_${hash(`${configKey}ModuleEvents`)}`;
421
- options.importItems[configKey].import.push({
422
- name: `ModuleEvents as ${importName}`,
423
- type: true,
424
- key: importName
425
- });
426
- options.events.push({ key: configKey, value: importName });
427
- }
428
- if (hasTypeExport("ModuleRuntimeContexts")) {
429
- const importName = `_${hash(`${configKey}ModuleRuntimeContexts`)}`;
430
- options.importItems[configKey].import.push({
431
- name: `ModuleRuntimeContexts as ${importName}`,
432
- type: true,
433
- key: importName
434
- });
435
- options.contexts.push({ key: configKey, value: importName });
436
- }
437
- if (hasTypeExport("ModuleHooks")) {
438
- const importName = `_${hash(`${configKey}ModuleHooks`)}`;
439
- options.importItems[configKey].import.push({
440
- name: `ModuleHooks as ${importName}`,
441
- type: true,
442
- key: importName
443
- });
444
- options.hooks.push({ key: configKey, value: importName });
445
- }
446
- if (hasTypeExport("ModuleRuntimeHooks")) {
447
- const importName = `_${hash(`${configKey}RuntimeHooks`)}`;
448
- options.importItems[configKey].import.push({
449
- name: `ModuleRuntimeHooks as ${importName}`,
450
- type: true,
451
- key: importName
452
- });
453
- options.runtimeHooks.push({ key: configKey, value: importName });
454
- }
455
- if (hasTypeExport("ModuleRuntimeActions")) {
456
- const importName = `_${hash(`${configKey}ModuleRuntimeActions`)}`;
457
- options.importItems[configKey].import.push({
458
- name: `ModuleRuntimeActions as ${importName}`,
459
- type: true,
460
- key: importName
461
- });
462
- options.actions.push({ key: configKey, value: importName });
463
- }
464
- if (hasTypeExport("ModuleRuntimeMethods")) {
465
- const importName = `_${hash(`${configKey}ModuleRuntimeMethods`)}`;
466
- options.importItems[configKey].import.push({
467
- name: `ModuleRuntimeMethods as ${importName}`,
468
- type: true,
469
- key: importName
470
- });
471
- options.methods.push({ key: configKey, value: importName });
472
- }
473
- if (hasTypeExport("ModuleRuntimeRouteRules")) {
474
- const importName = `_${hash(`${configKey}ModuleRuntimeRouteRules`)}`;
475
- options.importItems[configKey].import.push({
476
- name: `ModuleRuntimeRouteRules as ${importName}`,
345
+ return `import { ${imports.map(({ type, name }) => type ? `type ${name}` : name).join(", ")} } from '${from}'`;
346
+ }),
347
+ "",
348
+ ..._data.customImports,
349
+ ""
350
+ ];
351
+ const importData = [
352
+ "",
353
+ "export async function buildSilgi(framework: FrameworkContext, moduleOptions?: Partial<SilgiRuntimeOptions>,buildOptions?: Partial<SilgiOptions>) {",
354
+ "",
355
+ _data.beforeBuildSilgiExtraContent.length > 0 ? _data.beforeBuildSilgiExtraContent.map(({ value, type }) => {
356
+ return type === "function" ? value : `const ${value}`;
357
+ }) : "",
358
+ "",
359
+ " const silgi = await createSilgi({",
360
+ " framework,",
361
+ " shared: shareds as any,",
362
+ " services: services as any,",
363
+ " schemas: schemas as any,",
364
+ " uris,",
365
+ " modulesURIs,",
366
+ ` plugins: [${plugins.join(", ")}],`,
367
+ _data._silgiConfigs.length > 0 ? ` ${_data._silgiConfigs.map((config) => typeof config === "string" ? config : typeof config === "object" ? Object.entries(config).map(([key, value]) => `${key}: ${value}`).join(",\n ") : "").join(",\n ")},` : "",
368
+ " options: mergeDeep(cliConfigs, {",
369
+ " runtimeConfig: {} as SilgiRuntimeOptions,",
370
+ " routeRules: routeRules as any,",
371
+ ` present: '${silgi.options.preset}',`,
372
+ " ...moduleOptions,",
373
+ " ...buildOptions,",
374
+ " }) as any,",
375
+ " })",
376
+ "",
377
+ ...frameworkContext,
378
+ "",
379
+ ..._data.buildSilgiExtraContent,
380
+ "",
381
+ " return silgi",
382
+ "}",
383
+ ""
384
+ ];
385
+ await silgi.callHook("after:prepare:core.ts", importData);
386
+ importData.unshift(...importsContent);
387
+ return importData;
388
+ }
389
+
390
+ async function prepareFramework(silgi) {
391
+ const importItems = {
392
+ "silgi/types": {
393
+ import: [
394
+ {
395
+ name: "SilgiRuntimeContext",
477
396
  type: true,
478
- key: importName
479
- });
480
- options.routeRules.push({ key: configKey, value: importName });
481
- }
482
- if (hasTypeExport("ModuleRuntimeRouteRulesConfig")) {
483
- const importName = `_${hash(`${configKey}ModuleRuntimeRouteRulesConfig`)}`;
484
- options.importItems[configKey].import.push({
485
- name: `ModuleRuntimeRouteRulesConfig as ${importName}`,
397
+ key: "SilgiRuntimeContext"
398
+ }
399
+ ],
400
+ from: "silgi/types"
401
+ }
402
+ };
403
+ const customImports = [];
404
+ const functions = [];
405
+ await silgi.callHook("prepare:createCoreFramework", {
406
+ importItems,
407
+ customImports,
408
+ functions
409
+ });
410
+ const content = [
411
+ ...functions.map((f) => f.params?.length ? ` await ${f.name}(framework, ${f.params.join(",")})` : ` await ${f.name}(framework)`)
412
+ ];
413
+ return {
414
+ content,
415
+ importItems,
416
+ customImports
417
+ };
418
+ }
419
+ async function createDTSFramework(silgi) {
420
+ const importItems = {
421
+ "silgi/types": {
422
+ import: [
423
+ {
424
+ name: "SilgiRuntimeContext",
486
425
  type: true,
487
- key: importName
488
- });
489
- options.routeRulesConfig.push({ key: configKey, value: importName });
490
- }
426
+ key: "SilgiRuntimeContext"
427
+ }
428
+ ],
429
+ from: "silgi/types"
491
430
  }
431
+ };
432
+ const customImports = [];
433
+ const customContent = [];
434
+ await silgi.callHook("prepare:createDTSFramework", {
435
+ importItems,
436
+ customImports,
437
+ customContent
492
438
  });
439
+ const content = [
440
+ ...Object.entries(importItems).map(([_name, { from, import: imports }]) => {
441
+ const path = isAbsolute(from) ? relativeWithDot(silgi.options.build.typesDir, from) : from;
442
+ if (silgi.options.typescript.removeFileExtension) {
443
+ from = from.replace(/\.(js|ts|mjs|cjs|jsx|tsx)$/, "");
444
+ }
445
+ return `import { ${imports.map(({ type, name }) => type ? `type ${name}` : name).join(", ")} } from '${path}'`;
446
+ }),
447
+ "",
448
+ ...customImports,
449
+ "",
450
+ ...customContent,
451
+ ""
452
+ ];
453
+ return {
454
+ content,
455
+ importItems
456
+ };
493
457
  }
494
458
 
495
- async function loadSilgiModuleInstance(silgiModule) {
496
- if (typeof silgiModule === "string") {
497
- throw new TypeError(`Could not load \`${silgiModule}\`. Is it installed?`);
498
- }
499
- if (typeof silgiModule !== "function") {
500
- throw new TypeError(`Nuxt module should be a function: ${silgiModule}`);
459
+ async function writeCoreFile(silgi) {
460
+ const data = await prepareFramework(silgi);
461
+ const coreContent = await prepareCoreFile({
462
+ _importItems: data?.importItems ?? {},
463
+ _customImports: data?.customImports ?? []
464
+ }, data?.content ?? [], silgi);
465
+ const configs = await prepareConfigs(silgi);
466
+ const silgiDir = resolve(silgi.options.silgi.serverDir);
467
+ const buildFiles = [];
468
+ buildFiles.push({
469
+ path: join(silgiDir, "core.ts"),
470
+ contents: coreContent.join("\n")
471
+ });
472
+ buildFiles.push({
473
+ path: join(silgiDir, "configs.ts"),
474
+ contents: configs.join("\n")
475
+ });
476
+ for await (const file of buildFiles) {
477
+ if (!silgi.errors.length) {
478
+ await writeFile(
479
+ resolve(silgi.options.build.dir, file.path),
480
+ file.contents
481
+ );
482
+ }
501
483
  }
502
- return { silgiModule };
503
484
  }
504
- async function installModules(silgi, prepare = false) {
505
- silgi.options.isPreparingModules = prepare;
506
- const jiti = createJiti(silgi.options.rootDir, {
507
- alias: silgi.options.alias,
508
- fsCache: true,
509
- moduleCache: true
510
- });
511
- for (const module of silgi.scanModules) {
512
- if (hasInstalledModule(module.meta.configKey) && !silgi.options.dev) {
513
- silgi.logger.info(`Module ${module.meta.configKey} installed`);
485
+
486
+ async function generateRouterDTS(silgi) {
487
+ const uris = silgi.uris;
488
+ const subPath = "srn";
489
+ const groupedPaths = /* @__PURE__ */ new Map();
490
+ Object.entries(uris || {}).forEach(([key, params]) => {
491
+ const [service, resource, method, action] = key.split("/");
492
+ const basePath = params ? `${subPath}/${service}/${resource}/${action}/${params}` : `${subPath}/${service}/${resource}/${action}`;
493
+ const fullPath = `${subPath}/${service}/${resource}/${action}`;
494
+ if (!groupedPaths.has(basePath)) {
495
+ groupedPaths.set(basePath, /* @__PURE__ */ new Map());
514
496
  }
515
- try {
516
- const silgiModule = module.entryPath !== void 0 ? await jiti.import(module.entryPath, {
517
- default: true,
518
- conditions: silgi.options.conditions
519
- }) : module.module;
520
- if (silgiModule.name !== "silgiNormalizedModule") {
521
- silgi.scanModules = silgi.scanModules.filter((m) => m.entryPath !== module.entryPath);
522
- continue;
523
- }
524
- await installModule(silgiModule, silgi, prepare);
525
- } catch (err) {
526
- silgi.logger.error(err);
497
+ groupedPaths.get(basePath)?.set(method.toLowerCase(), fullPath);
498
+ });
499
+ const keys = [
500
+ " keys: {",
501
+ Array.from(groupedPaths.entries()).map(([basePath, methods]) => {
502
+ return ` '/${basePath}': {${Array.from(methods.entries()).map(([method, path]) => `
503
+ ${method}: '/${path}'`).join(",")}
504
+ }`;
505
+ }).join(",\n"),
506
+ " }",
507
+ ""
508
+ ].join("\n");
509
+ const groupedRoutes = Object.entries(uris || {}).reduce((acc, [key, _params]) => {
510
+ const [service, resource, method, action] = key.split("/");
511
+ const routePath = `${subPath}/${service}/${resource}/${action}`;
512
+ if (!acc[routePath]) {
513
+ acc[routePath] = {};
527
514
  }
528
- }
529
- silgi.options.isPreparingModules = false;
530
- }
531
- async function installModule(moduleToInstall, silgi = useSilgiCLI(), inlineOptions, prepare = false) {
532
- const { silgiModule } = await loadSilgiModuleInstance(moduleToInstall);
533
- const res = await silgiModule(inlineOptions || {}, silgi) ?? {};
534
- if (res === false) {
535
- return false;
536
- }
537
- const metaData = await silgiModule.getMeta?.();
538
- if (prepare) {
539
- return metaData;
540
- }
541
- const installedModule = silgi.scanModules.find((m) => m.meta.configKey === metaData?.configKey);
542
- if (installedModule) {
543
- installedModule.installed = true;
544
- } else {
545
- throw new Error(`Module ${metaData?.name} not found`);
546
- }
515
+ acc[routePath][method] = {
516
+ input: `ExtractInputFromURI<'${key}'>`,
517
+ output: `ExtractOutputFromURI<'${key}'>`,
518
+ queryParams: `ExtractQueryParamsFromURI<'${key}'>`,
519
+ pathParams: `ExtractPathParamsFromURI<'${key}'>`
520
+ };
521
+ return acc;
522
+ }, {});
523
+ const routerTypes = Object.entries(groupedRoutes).map(([path, methods]) => {
524
+ const methodEntries = Object.entries(methods).map(([method, { input, output, queryParams, pathParams }]) => {
525
+ return ` '${method}': {
526
+ input: ${input},
527
+ output: ${output},
528
+ queryParams: ${queryParams},
529
+ pathParams: ${pathParams}
530
+ }`;
531
+ }).join(",\n");
532
+ return ` '/${path}': {
533
+ ${methodEntries}
534
+ }`;
535
+ });
536
+ const nitro = [
537
+ "declare module 'nitropack/types' {",
538
+ " interface InternalApi extends RouterTypes {}",
539
+ "}"
540
+ ];
541
+ const content = [
542
+ keys.slice(0, -1),
543
+ // son satırdaki boş satırı kaldır
544
+ ...routerTypes
545
+ ].join(",\n");
546
+ const context = [
547
+ "import type { ExtractInputFromURI, ExtractOutputFromURI, ExtractQueryParamsFromURI, ExtractPathParamsFromURI } from 'silgi/types'",
548
+ "",
549
+ "export interface RouterTypes {",
550
+ content,
551
+ "}",
552
+ "",
553
+ "declare module 'silgi/types' {",
554
+ " interface SilgiRouterTypes extends RouterTypes {",
555
+ " }",
556
+ "}",
557
+ "",
558
+ silgi.options.preset === "h3" || silgi.options.preset === "nitro" ? nitro.join("\n") : "",
559
+ "",
560
+ "export {}"
561
+ ];
562
+ return context;
547
563
  }
548
564
 
549
- const MissingModuleMatcher = /Cannot find module\s+['"]?([^'")\s]+)['"]?/i;
550
- async function _resolveSilgiModule(silgiModule, silgi) {
551
- let resolvedModulePath;
552
- let buildTimeModuleMeta = {};
553
- const jiti = createJiti(silgi.options.rootDir, {
554
- alias: silgi.options.alias,
555
- fsCache: true,
556
- moduleCache: true
557
- });
558
- if (typeof silgiModule === "string") {
559
- silgiModule = resolveAlias(silgiModule, silgi.options.alias);
560
- if (isRelative(silgiModule)) {
561
- silgiModule = resolve(silgi.options.rootDir, silgiModule);
562
- }
563
- try {
564
- const src = resolveModuleURL(silgiModule, {
565
- from: silgi.options.modulesDir.map((m) => directoryToURL(m.replace(/\/node_modules\/?$/, "/"))),
566
- suffixes: ["silgi", "silgi/index", "module", "module/index", "", "index"],
567
- extensions: [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"]
568
- // Maybe add https://github.com/unjs/exsolve/blob/dfff3e9bbc4a3a173a2d56b9b9ff731ab15598be/src/resolve.ts#L7
569
- // conditions: silgi.options.conditions,
570
- });
571
- resolvedModulePath = fileURLToPath(src);
572
- const resolvedSilgiModule = await jiti.import(src, { default: true });
573
- if (typeof resolvedSilgiModule !== "function") {
574
- throw new TypeError(`Nuxt module should be a function: ${silgiModule}.`);
575
- }
576
- silgiModule = await jiti.import(src, {
577
- default: true,
578
- conditions: silgi.options.conditions
579
- });
580
- const moduleMetadataPath = new URL("module.json", src);
581
- if (existsSync(moduleMetadataPath)) {
582
- buildTimeModuleMeta = JSON.parse(await promises.readFile(moduleMetadataPath, "utf-8"));
583
- } else {
584
- if (typeof silgiModule === "function") {
585
- const meta = await silgiModule.getMeta?.();
586
- const _exports = await scanExports(resolvedModulePath, true);
587
- buildTimeModuleMeta = {
588
- ...meta,
589
- exports: _exports.map(({ from, ...rest }) => rest)
590
- };
565
+ async function prepareSchema(silgi) {
566
+ const importItems = {
567
+ "silgi/types": {
568
+ import: [
569
+ {
570
+ name: "URIsTypes",
571
+ type: true,
572
+ key: "URIsTypes"
573
+ },
574
+ {
575
+ name: "Namespaces",
576
+ type: true,
577
+ key: "Namespaces"
578
+ },
579
+ {
580
+ name: "SilgiRuntimeContext",
581
+ type: true,
582
+ key: "SilgiRuntimeContext"
591
583
  }
584
+ ],
585
+ from: "silgi/types"
586
+ },
587
+ "silgi/scan": {
588
+ import: [{
589
+ key: "modulesURIs",
590
+ name: "modulesURIs",
591
+ type: false
592
+ }],
593
+ from: relativeWithDot(silgi.options.build.typesDir, `${silgi.options.silgi.serverDir}/scan.ts`)
594
+ }
595
+ };
596
+ const data = {
597
+ importItems,
598
+ customImports: [],
599
+ options: [],
600
+ contexts: [],
601
+ actions: [],
602
+ shareds: [
603
+ {
604
+ key: "modulesURIs",
605
+ value: "{ modulesURIs: typeof modulesURIs }"
592
606
  }
593
- } catch (error) {
594
- const code = error.code;
595
- if (code === "MODULE_NOT_FOUND" || code === "ERR_PACKAGE_PATH_NOT_EXPORTED" || code === "ERR_MODULE_NOT_FOUND" || code === "ERR_UNSUPPORTED_DIR_IMPORT" || code === "ENOTDIR") {
596
- throw new TypeError(`Could not load \`${silgiModule}\`. Is it installed?`);
607
+ ],
608
+ events: [],
609
+ hooks: [],
610
+ runtimeHooks: [],
611
+ runtimeOptions: [],
612
+ methods: [],
613
+ routeRules: [],
614
+ routeRulesConfig: []
615
+ };
616
+ await silgi.callHook("prepare:schema.ts", data);
617
+ relativeWithDot(silgi.options.build.typesDir, `${silgi.options.silgi.serverDir}/core.ts`);
618
+ const silgiScanTS = relativeWithDot(silgi.options.build.typesDir, `${silgi.options.silgi.serverDir}/scan.ts`);
619
+ let addSilgiContext = false;
620
+ const importsContent = [
621
+ ...Object.entries(importItems).map(([_name, { from, import: imports }]) => {
622
+ const path = isAbsolute(from) ? relativeWithDot(silgi.options.build.typesDir, from) : from;
623
+ if (silgi.options.typescript.removeFileExtension) {
624
+ from = from.replace(/\.(js|ts|mjs|cjs|jsx|tsx)$/, "");
597
625
  }
598
- if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") {
599
- const module = MissingModuleMatcher.exec(error.message)?.[1];
600
- if (module && !module.includes(silgiModule)) {
601
- throw new TypeError(`Error while importing module \`${silgiModule}\`: ${error}`);
602
- }
626
+ return `import { ${imports.map(({ type, name }) => type ? `type ${name}` : name).join(", ")} } from '${path}'`;
627
+ }),
628
+ "",
629
+ ...data.customImports,
630
+ ""
631
+ ];
632
+ const importData = [
633
+ "interface InferredNamespaces {",
634
+ ...(silgi.options.namespaces || []).map((key) => ` ${key}: string,`),
635
+ "}",
636
+ "",
637
+ `type SchemaExtends = Namespaces<typeof import('${silgiScanTS}')['schemas']>`,
638
+ "",
639
+ `type SilgiURIsMerge = URIsTypes<typeof import('${silgiScanTS}')['uris']>`,
640
+ "",
641
+ `type SilgiModuleContextExtends = ${data.contexts.length ? data.contexts.map(({ value }) => value).join(" & ") : "{}"}`,
642
+ "",
643
+ data.events.length ? `interface SilgiModuleEventsExtends extends ${data.events.map((item) => item.extends ? item.value : "").join(", ")} {
644
+ ${data.events.map((item) => {
645
+ if (item.isSilgiContext) {
646
+ addSilgiContext = true;
603
647
  }
604
- }
605
- }
606
- if (!buildTimeModuleMeta) {
607
- throw new Error(`Module ${silgiModule} is not a valid Silgi module`);
608
- }
609
- if (typeof silgiModule === "function") {
610
- if (!buildTimeModuleMeta.configKey) {
611
- const meta = await silgiModule.getMeta?.();
612
- buildTimeModuleMeta = {
613
- ...meta,
614
- exports: []
615
- };
616
- }
617
- if (silgi.scanModules.some((m) => m.meta?.configKey === buildTimeModuleMeta.configKey)) {
618
- throw new Error(`Module with key \`${buildTimeModuleMeta.configKey}\` already exists`);
619
- }
620
- const options = await silgiModule.getOptions?.() || {};
621
- if (options) {
622
- silgi.options._c12.config[buildTimeModuleMeta.configKey] = defu(
623
- silgi.options._c12.config[buildTimeModuleMeta.configKey] || {},
624
- options || {}
625
- );
626
- } else {
627
- throw new TypeError(`Could not load \`${silgiModule}\`. Is it installed?`);
628
- }
629
- silgi.scanModules.push({
630
- meta: buildTimeModuleMeta,
631
- entryPath: resolvedModulePath || void 0,
632
- installed: false,
633
- options,
634
- module: silgiModule
635
- });
636
- }
637
- }
638
- async function scanModules$1(silgi) {
639
- const _modules = [
640
- ...silgi.options._modules,
641
- ...silgi.options.modules
648
+ return !item.extends && !addSilgiContext ? ` ${item.key}: ${item.value}` : item.isSilgiContext ? " context: SilgiRuntimeContext" : "";
649
+ }).join(",\n")}
650
+ }` : "interface SilgiModuleEventsExtends {}",
651
+ "",
652
+ `type RuntimeActionExtends = ${data.actions?.length ? data.actions.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
653
+ "",
654
+ `type RuntimeMethodExtends = ${data.methods?.length ? data.methods.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
655
+ "",
656
+ `type RuntimeRouteRulesExtends = ${data.routeRules?.length ? data.routeRules.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
657
+ "",
658
+ `type RuntimeRouteRulesConfigExtends = ${data.routeRulesConfig?.length ? data.routeRulesConfig.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
659
+ "",
660
+ `type SilgiModuleSharedExtends = ${data.shareds.length ? data.shareds.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
661
+ "",
662
+ `type SilgiModuleOptionExtend = ${data.options?.length ? data.options.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
663
+ "",
664
+ `type SilgiRuntimeOptionExtends = ${data.runtimeOptions?.length ? data.runtimeOptions.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
665
+ "",
666
+ silgi.options.typescript.generateRuntimeConfigTypes ? generateTypes(
667
+ await resolveSchema(
668
+ {
669
+ ...Object.fromEntries(
670
+ Object.entries(silgi.options.runtimeConfig).filter(
671
+ ([key]) => !["app", "nitro", "nuxt"].includes(key)
672
+ )
673
+ )
674
+ }
675
+ ),
676
+ {
677
+ interfaceName: "SilgiRuntimeConfigExtends",
678
+ addExport: false,
679
+ addDefaults: false,
680
+ allowExtraKeys: false,
681
+ indentation: 0
682
+ }
683
+ ) : "",
684
+ "",
685
+ generateTypes(
686
+ await resolveSchema(
687
+ {
688
+ ...silgi.options.storages?.reduce((acc, key) => ({ ...acc, [key]: "" }), {}) || {},
689
+ // 'redis': {} -> 'redis': string
690
+ ...Object.entries(silgi.options.storage).map(([key]) => ({
691
+ [key]: ""
692
+ })).reduce((acc, obj) => ({ ...acc, ...obj }), {})
693
+ }
694
+ ),
695
+ {
696
+ interfaceName: "SilgiStorageBaseExtends",
697
+ addExport: false,
698
+ addDefaults: false,
699
+ allowExtraKeys: false,
700
+ indentation: 0
701
+ }
702
+ ),
703
+ "",
704
+ `type ModuleHooksExtend = ${data.hooks?.length ? data.hooks.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
705
+ "",
706
+ `type SilgiRuntimeHooksExtends = ${data.runtimeHooks?.length ? data.runtimeHooks.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
707
+ "",
708
+ "declare module 'silgi/types' {",
709
+ " interface FrameworkContext extends FrameworkContextExtends {}",
710
+ " interface SilgiSchema extends SchemaExtends {}",
711
+ " interface SilgiNamespaces extends InferredNamespaces {}",
712
+ " interface SilgiStorageBase extends SilgiStorageBaseExtends {}",
713
+ " interface SilgiURIs extends SilgiURIsMerge {}",
714
+ " interface SilgiRuntimeContext extends SilgiModuleContextExtends {}",
715
+ " interface SilgiEvents extends SilgiModuleEventsExtends {}",
716
+ " interface SilgiRuntimeSharedsExtend extends SilgiModuleSharedExtends {}",
717
+ " interface SilgiRuntimeActions extends RuntimeActionExtends {}",
718
+ " interface SilgiModuleOptions extends SilgiModuleOptionExtend {}",
719
+ " interface SilgiRuntimeOptions extends SilgiRuntimeOptionExtends {}",
720
+ " interface SilgiRuntimeHooks extends SilgiRuntimeHooksExtends {}",
721
+ " interface SilgiRuntimeConfig extends SilgiRuntimeConfigExtends {}",
722
+ " interface SilgiHooks extends ModuleHooksExtend {}",
723
+ " interface SilgiRuntimeMethods extends RuntimeMethodExtends {}",
724
+ " interface SilgiRuntimeRouteRules extends RuntimeRouteRulesExtends {}",
725
+ " interface SilgiRuntimeRouteRulesConfig extends RuntimeRouteRulesConfigExtends {}",
726
+ " interface SilgiCommands extends SilgiCommandsExtended {}",
727
+ "",
728
+ "}",
729
+ "",
730
+ "export {}"
642
731
  ];
643
- for await (const mod of _modules) {
644
- await _resolveSilgiModule(mod, silgi);
645
- }
646
- const moduleMap = new Map(
647
- silgi.scanModules.map((m) => [m.meta?.configKey, m])
648
- );
649
- const graphData = createDependencyGraph(silgi.scanModules);
650
- const sortedKeys = topologicalSort(graphData);
651
- const modules = sortedKeys.map((key) => moduleMap.get(key)).filter((module) => Boolean(module));
652
- silgi.scanModules = modules;
732
+ await silgi.callHook("after:prepare:schema.ts", importData);
733
+ importData.unshift(...importsContent);
734
+ return importData;
653
735
  }
654
- function createDependencyGraph(modules) {
655
- const graph = /* @__PURE__ */ new Map();
656
- const inDegree = /* @__PURE__ */ new Map();
657
- modules.forEach((module) => {
658
- const key = module.meta?.configKey;
659
- if (key) {
660
- graph.set(key, /* @__PURE__ */ new Set());
661
- inDegree.set(key, 0);
662
- }
736
+
737
+ async function writeTypesAndFiles(silgi) {
738
+ const routerDTS = await generateRouterDTS(silgi);
739
+ silgi.hook("prepare:types", (opts) => {
740
+ opts.references.push({ path: "./schema.d.ts" });
741
+ opts.references.push({ path: "./silgi-routes.d.ts" });
742
+ opts.references.push({ path: "./framework.d.ts" });
663
743
  });
664
- modules.forEach((module) => {
665
- const key = module.meta?.configKey;
666
- if (!key) {
667
- return;
668
- }
669
- const requiredDeps = module.meta?.requiredDependencies || [];
670
- const beforeDeps = module.meta?.beforeDependencies || [];
671
- const afterDeps = module.meta?.afterDependencies || [];
672
- const processedDeps = /* @__PURE__ */ new Set();
673
- requiredDeps.forEach((dep) => {
674
- if (!graph.has(dep)) {
675
- throw new Error(`Required dependency "${dep}" for module "${key}" is missing`);
676
- }
677
- graph.get(dep)?.add(key);
678
- inDegree.set(key, (inDegree.get(key) || 0) + 1);
679
- processedDeps.add(dep);
680
- });
681
- beforeDeps.forEach((dep) => {
682
- if (!graph.has(dep)) {
683
- return;
744
+ const schemaContent = await prepareSchema(silgi);
745
+ const frameworkDTS = await createDTSFramework(silgi);
746
+ const { declarations, tsConfig } = await silgiGenerateType(silgi);
747
+ const tsConfigPath = resolve(
748
+ silgi.options.rootDir,
749
+ silgi.options.typescript.tsconfigPath
750
+ );
751
+ const typesDir = resolve(silgi.options.build.typesDir);
752
+ let autoImportedTypes = [];
753
+ let autoImportExports = "";
754
+ if (silgi.unimport) {
755
+ await silgi.unimport.init();
756
+ const allImports = await silgi.unimport.getImports();
757
+ autoImportExports = toExports(allImports).replace(
758
+ /#internal\/nitro/g,
759
+ relative(typesDir, runtimeDir)
760
+ );
761
+ const resolvedImportPathMap = /* @__PURE__ */ new Map();
762
+ for (const i of allImports.filter((i2) => !i2.type)) {
763
+ if (resolvedImportPathMap.has(i.from)) {
764
+ continue;
684
765
  }
685
- graph.get(key)?.add(dep);
686
- inDegree.set(dep, (inDegree.get(dep) || 0) + 1);
687
- });
688
- afterDeps.forEach((dep) => {
689
- if (processedDeps.has(dep)) {
690
- return;
766
+ let path = resolveAlias(i.from, silgi.options.alias);
767
+ if (isAbsolute(path)) {
768
+ const resolvedPath = await resolvePath(i.from, {
769
+ url: silgi.options.nodeModulesDirs
770
+ }).catch(() => null);
771
+ if (resolvedPath) {
772
+ const { dir, name } = parseNodeModulePath(resolvedPath);
773
+ if (!dir || !name) {
774
+ path = resolvedPath;
775
+ } else {
776
+ const subpath = await lookupNodeModuleSubpath(resolvedPath);
777
+ path = join(dir, name, subpath || "");
778
+ }
779
+ }
691
780
  }
692
- if (!graph.has(dep)) {
693
- return;
781
+ if (existsSync(path) && !await isDirectory(path)) {
782
+ path = path.replace(/\.[a-z]+$/, "");
694
783
  }
695
- graph.get(dep)?.add(key);
696
- inDegree.set(key, (inDegree.get(key) || 0) + 1);
697
- });
698
- });
699
- return { graph, inDegree };
700
- }
701
- function findCyclicDependencies(graph) {
702
- const visited = /* @__PURE__ */ new Set();
703
- const recursionStack = /* @__PURE__ */ new Set();
704
- const cycles = [];
705
- function dfs(node, path = []) {
706
- visited.add(node);
707
- recursionStack.add(node);
708
- path.push(node);
709
- for (const neighbor of graph.get(node) || []) {
710
- if (recursionStack.has(neighbor)) {
711
- const cycleStart = path.indexOf(neighbor);
712
- if (cycleStart !== -1) {
713
- cycles.push([...path.slice(cycleStart), neighbor]);
714
- }
715
- } else if (!visited.has(neighbor)) {
716
- dfs(neighbor, [...path]);
784
+ if (isAbsolute(path)) {
785
+ path = relative(typesDir, path);
717
786
  }
787
+ resolvedImportPathMap.set(i.from, path);
718
788
  }
719
- recursionStack.delete(node);
720
- path.pop();
721
- }
722
- for (const node of graph.keys()) {
723
- if (!visited.has(node)) {
724
- dfs(node, []);
725
- }
726
- }
727
- return cycles;
728
- }
729
- function topologicalSort(graphData) {
730
- const { graph, inDegree } = graphData;
731
- const order = [];
732
- const queue = [];
733
- for (const [node, degree] of inDegree.entries()) {
734
- if (degree === 0) {
735
- queue.push(node);
736
- }
789
+ autoImportedTypes = [
790
+ silgi.options.imports && silgi.options.imports.autoImport !== false ? (await silgi.unimport.generateTypeDeclarations({
791
+ exportHelper: false,
792
+ resolvePath: (i) => resolvedImportPathMap.get(i.from) ?? i.from
793
+ })).trim() : ""
794
+ ];
737
795
  }
738
- while (queue.length > 0) {
739
- const node = queue.shift();
740
- order.push(node);
741
- const neighbors = Array.from(graph.get(node) || []);
742
- for (const neighbor of neighbors) {
743
- const newDegree = (inDegree.get(neighbor) || 0) - 1;
744
- inDegree.set(neighbor, newDegree);
745
- if (newDegree === 0) {
746
- queue.push(neighbor);
747
- }
796
+ const buildFiles = [];
797
+ buildFiles.push({
798
+ path: join(typesDir, "silgi-routes.d.ts"),
799
+ contents: routerDTS.join("\n")
800
+ });
801
+ buildFiles.push({
802
+ path: join(typesDir, "silgi-imports.d.ts"),
803
+ contents: [...autoImportedTypes, autoImportExports || "export {}"].join(
804
+ "\n"
805
+ )
806
+ });
807
+ buildFiles.push({
808
+ path: join(typesDir, "schema.d.ts"),
809
+ contents: schemaContent.join("\n")
810
+ });
811
+ buildFiles.push({
812
+ path: join(typesDir, "silgi.d.ts"),
813
+ contents: declarations.join("\n")
814
+ });
815
+ buildFiles.push({
816
+ path: tsConfigPath,
817
+ contents: JSON.stringify(tsConfig, null, 2)
818
+ });
819
+ buildFiles.push({
820
+ path: join(typesDir, "framework.d.ts"),
821
+ contents: frameworkDTS.content.join("\n")
822
+ });
823
+ for await (const file of buildFiles) {
824
+ if (!silgi.errors.length) {
825
+ await writeFile(
826
+ resolve(silgi.options.build.dir, file.path),
827
+ file.contents
828
+ );
748
829
  }
749
830
  }
750
- if (order.length !== graph.size) {
751
- const cycles = findCyclicDependencies(graph);
752
- if (cycles.length > 0) {
753
- const cycleStr = cycles.map((cycle) => ` ${cycle.join(" -> ")}`).join("\n");
754
- throw new Error(`Circular dependencies detected:
755
- ${cycleStr}`);
756
- } else {
757
- const unresolvedModules = Array.from(graph.keys()).filter((key) => !order.includes(key));
758
- throw new Error(`Unable to resolve dependencies for modules: ${unresolvedModules.join(", ")}`);
831
+ }
832
+
833
+ async function prepareBuild(silgi) {
834
+ try {
835
+ if (!silgi?.routeRules?.exportRules) {
836
+ throw new Error("Invalid silgi configuration: routeRules or exportRules is undefined");
837
+ }
838
+ const exportedRules = silgi.routeRules.exportRules();
839
+ if (!exportedRules || typeof exportedRules !== "object") {
840
+ throw new Error("No valid route rules to export");
841
+ }
842
+ const content = `/* eslint-disable */
843
+ // @ts-nocheck
844
+ // This file is auto-generated at build time by Silgi
845
+ // Contains route rules with preserved functions
846
+ // DO NOT MODIFY THIS FILE DIRECTLY
847
+
848
+ export const routeRules = ${genObjectFromRawEntries(
849
+ Object.entries(exportedRules).map(([key, value]) => {
850
+ if (typeof value === "function") {
851
+ return [key, genObjectFromRaw(value)];
852
+ }
853
+ return [key, genObjectFromValues(value)];
854
+ })
855
+ )}
856
+ `;
857
+ const serverDir = silgi.options.silgi.serverDir;
858
+ if (!serverDir) {
859
+ throw new Error("Server directory not defined in configuration");
860
+ }
861
+ const file = join(serverDir, "rules.ts");
862
+ if (!silgi.errors.length) {
863
+ await writeFile(file, content);
759
864
  }
865
+ } catch (error) {
866
+ console.error("\u274C Failed to prepare build:", error instanceof Error ? error.message : String(error));
867
+ throw error;
760
868
  }
761
- return order;
762
869
  }
763
870
 
764
- async function commands(silgi) {
765
- const commands2 = {
766
- ...silgi.options.commands
767
- };
768
- await silgi.callHook("prepare:commands", commands2);
769
- addTemplate({
770
- filename: "cli.json",
771
- where: ".silgi",
772
- write: true,
773
- getContents: () => JSON.stringify(commands2, null, 2)
774
- });
775
- silgi.commands = commands2;
776
- silgi.hook("prepare:schema.ts", async (object) => {
777
- const allTags = Object.values(commands2).reduce((acc, commandGroup) => {
778
- Object.values(commandGroup).forEach((command) => {
779
- if (command.tags) {
780
- command.tags.forEach((tag) => acc.add(tag));
781
- }
782
- });
783
- return acc;
784
- }, /* @__PURE__ */ new Set());
785
- const data = [
786
- "",
787
- generateTypes(
788
- await resolveSchema(
789
- {
790
- ...Object.fromEntries(Array.from(allTags.values()).map((tag) => [tag, "string"]))
791
- }
792
- ),
793
- {
794
- interfaceName: "SilgiCommandsExtended",
795
- addExport: false,
796
- addDefaults: false,
797
- allowExtraKeys: false,
798
- indentation: 0
799
- }
800
- ),
801
- ""
802
- ];
803
- object.customImports?.push(...data);
804
- });
871
+ async function build(silgi) {
872
+ await prepare();
873
+ await generateApiFul(silgi);
874
+ await writeTypesAndFiles(silgi);
875
+ await writeCoreFile(silgi);
876
+ await prepareBuild(silgi);
877
+ await generateApp(silgi);
805
878
  }
806
879
 
807
- function resolveIgnorePatterns(silgi, relativePath) {
808
- if (!silgi) {
809
- return [];
880
+ async function setupDotenv(options) {
881
+ const targetEnvironment = options.env ?? process.env;
882
+ const environment = await loadDotenv({
883
+ cwd: options.cwd,
884
+ fileName: options.fileName ?? ".env",
885
+ env: targetEnvironment,
886
+ interpolate: options.interpolate ?? true
887
+ });
888
+ for (const key in environment) {
889
+ if (!key.startsWith("_") && targetEnvironment[key] === void 0) {
890
+ targetEnvironment[key] = environment[key];
891
+ }
810
892
  }
811
- const ignorePatterns = silgi.options.ignore.flatMap((s) => resolveGroupSyntax(s));
812
- const nuxtignoreFile = join(silgi.options.rootDir, ".nuxtignore");
813
- if (existsSync(nuxtignoreFile)) {
814
- const contents = readFileSync(nuxtignoreFile, "utf-8");
815
- ignorePatterns.push(...contents.trim().split(/\r?\n/));
893
+ return environment;
894
+ }
895
+ async function loadDotenv(options) {
896
+ const environment = /* @__PURE__ */ Object.create(null);
897
+ const dotenvFile = resolve(options.cwd, options.fileName);
898
+ if (existsSync(dotenvFile)) {
899
+ const parsed = dotenv.parse(await promises.readFile(dotenvFile, "utf8"));
900
+ Object.assign(environment, parsed);
816
901
  }
817
- return ignorePatterns;
902
+ if (!options.env?._applied) {
903
+ Object.assign(environment, options.env);
904
+ environment._applied = true;
905
+ }
906
+ if (options.interpolate) {
907
+ interpolate(environment);
908
+ }
909
+ return environment;
818
910
  }
819
- function isIgnored(pathname, silgi, _stats) {
820
- if (!silgi) {
821
- return false;
911
+ function interpolate(target, source = {}, parse = (v) => v) {
912
+ function getValue(key) {
913
+ return source[key] === void 0 ? target[key] : source[key];
822
914
  }
823
- if (!silgi._ignore) {
824
- silgi._ignore = ignore(silgi.options.ignoreOptions);
825
- silgi._ignore.add(resolveIgnorePatterns(silgi));
915
+ function interpolate2(value, parents = []) {
916
+ if (typeof value !== "string") {
917
+ return value;
918
+ }
919
+ const matches = value.match(/(.?\$\{?[\w:]*\}?)/g) || [];
920
+ return parse(
921
+ matches.reduce((newValue, match) => {
922
+ const parts = /(.?)\$\{?([\w:]+)?\}?/.exec(match) || [];
923
+ const prefix = parts[1];
924
+ let value2, replacePart;
925
+ if (prefix === "\\") {
926
+ replacePart = parts[0] || "";
927
+ value2 = replacePart.replace(String.raw`\$`, "$");
928
+ } else {
929
+ const key = parts[2];
930
+ replacePart = (parts[0] || "").slice(prefix.length);
931
+ if (parents.includes(key)) {
932
+ console.warn(
933
+ `Please avoid recursive environment variables ( loop: ${parents.join(
934
+ " > "
935
+ )} > ${key} )`
936
+ );
937
+ return "";
938
+ }
939
+ value2 = getValue(key);
940
+ value2 = interpolate2(value2, [...parents, key]);
941
+ }
942
+ return value2 === void 0 ? newValue : newValue.replace(replacePart, value2);
943
+ }, value)
944
+ );
826
945
  }
827
- const relativePath = relative(silgi.options.rootDir, pathname);
828
- if (relativePath[0] === "." && relativePath[1] === ".") {
829
- return false;
946
+ for (const key in target) {
947
+ target[key] = interpolate2(getValue(key));
830
948
  }
831
- return !!(relativePath && silgi._ignore.ignores(relativePath));
832
949
  }
833
- function resolveGroupSyntax(group) {
834
- let groups = [group];
835
- while (groups.some((group2) => group2.includes("{"))) {
836
- groups = groups.flatMap((group2) => {
837
- const [head, ...tail] = group2.split("{");
838
- if (tail.length) {
839
- const [body = "", ...rest] = tail.join("{").split("}");
840
- return body.split(",").map((part) => `${head}${part}${rest.join("")}`);
841
- }
842
- return group2;
950
+
951
+ let initialized = false;
952
+ async function prepareEnv(silgiConfig) {
953
+ if (initialized)
954
+ return;
955
+ initialized = true;
956
+ const customEnvironments = silgiConfig.environments;
957
+ const environment = silgiConfig.activeEnvironment ? silgiConfig.activeEnvironment : await p.select({
958
+ message: "Select an environment",
959
+ options: customEnvironments?.length > 0 ? customEnvironments.map((env) => ({
960
+ label: env.fileName,
961
+ value: env.fileName
962
+ })) : [
963
+ { label: "Development (.env)", value: ".env" },
964
+ { label: "Docker (.env.docker)", value: "docker" },
965
+ { label: "Staging (.env.staging)", value: "staging" },
966
+ { label: "Testing (.env.testing)", value: "testing" },
967
+ { label: "Production (.env.prod)", value: "prod" }
968
+ ]
969
+ });
970
+ const findEnv = customEnvironments?.find((env) => env.fileName === environment);
971
+ if (findEnv) {
972
+ await setupDotenv({
973
+ cwd: findEnv.cwd || silgiConfig.rootDir,
974
+ interpolate: findEnv.interpolate,
975
+ fileName: findEnv.fileName,
976
+ env: findEnv.env
977
+ });
978
+ } else {
979
+ await setupDotenv({
980
+ cwd: silgiConfig.rootDir,
981
+ interpolate: true,
982
+ fileName: environment === ".env" ? ".env" : `.env.${environment}`
843
983
  });
844
984
  }
845
- return groups;
846
985
  }
847
986
 
848
- const safeFiles = [
849
- "silgi/configs",
850
- "silgi",
851
- "silgi/rules",
852
- "silgi/scan",
853
- "silgi/vfs"
854
- ];
855
- class SchemaParser {
856
- options = {
857
- debug: false
858
- };
859
- /**
860
- *
861
- */
862
- constructor(options) {
863
- this.options = {
864
- ...this.options,
865
- ...options
866
- };
867
- }
868
- parseExports(content, filePath) {
869
- const ast = parseSync(content, { sourceType: "module", sourceFilename: filePath });
870
- if (this.options.debug)
871
- writeFileSync(`${filePath}.ast.json`, JSON.stringify(ast.program, null, 2));
872
- return {
873
- exportVariables: (search, path) => this.parseTypeDeclarations(ast, search, path),
874
- parseInterfaceDeclarations: (search, path) => this.parseInterfaceDeclarations(ast, search, path)
875
- // parsePlugin: (path: string) => this.parsePlugin(ast, path),
876
- };
877
- }
878
- parseVariableDeclaration(ast, path) {
879
- const silgi = useSilgiCLI();
880
- if (ast.program.body.length === 0) {
881
- if (safeFiles.find((i) => path.includes(i)))
882
- return [];
883
- silgi.errors.push({
884
- type: "Parser",
885
- path
886
- });
887
- consola$1.warn("This file has a problem:", path);
888
- }
889
- const variableDeclarations = ast.program.body.filter((i) => i.type === "ExportNamedDeclaration").filter((i) => i.declaration?.type === "VariableDeclaration");
890
- return variableDeclarations;
987
+ async function emptyFramework(silgi) {
988
+ if (silgi.options.preset === "npm-package" || !silgi.options.preset) {
989
+ silgi.hook("after:prepare:schema.ts", (data) => {
990
+ data.unshift("type FrameworkContextExtends = {}");
991
+ });
891
992
  }
892
- parseTSInterfaceDeclaration(ast, path = "") {
893
- const silgi = useSilgiCLI();
894
- if (ast.program.body.length === 0) {
895
- if (safeFiles.find((i) => path.includes(i)))
896
- return [];
897
- silgi.errors.push({
898
- type: "Parser",
899
- path
900
- });
901
- consola$1.warn("This file has a problem:", path);
902
- }
903
- const interfaceDeclarations = ast.program.body.filter((i) => i.type === "ExportNamedDeclaration").filter((i) => i.declaration?.type === "TSInterfaceDeclaration");
904
- return interfaceDeclarations;
993
+ }
994
+
995
+ async function h3Framework(silgi, skip = false) {
996
+ if (silgi.options.preset !== "h3" && skip === false)
997
+ return;
998
+ if (silgi.options.preset === "h3") {
999
+ silgi.hook("after:prepare:schema.ts", (data) => {
1000
+ data.unshift("type FrameworkContextExtends = NitroApp");
1001
+ });
905
1002
  }
906
- parseTypeDeclarations(ast, find = "", path = "") {
907
- const data = [];
908
- const variableDeclarations = this.parseVariableDeclaration(ast, path);
909
- for (const item of variableDeclarations) {
910
- for (const declaration of item.declaration.declarations) {
911
- if (declaration.init?.callee?.name === find) {
912
- const options = {};
913
- if (declaration.init.arguments) {
914
- for (const argument of declaration.init.arguments) {
915
- for (const propertie of argument.properties) {
916
- if (propertie.key.name === "name")
917
- options.pluginName = propertie.value.value;
918
- }
919
- }
1003
+ silgi.hook("prepare:schema.ts", (data) => {
1004
+ data.importItems.nitropack = {
1005
+ import: [
1006
+ {
1007
+ name: "NitroApp",
1008
+ type: true,
1009
+ key: "NitroApp"
1010
+ }
1011
+ ],
1012
+ from: "nitropack/types"
1013
+ };
1014
+ data.importItems.h3 = {
1015
+ import: [
1016
+ {
1017
+ name: "H3Event",
1018
+ type: true,
1019
+ key: "H3Event"
1020
+ }
1021
+ ],
1022
+ from: "h3"
1023
+ };
1024
+ data.events.push({
1025
+ key: "H3Event",
1026
+ value: "H3Event",
1027
+ extends: true,
1028
+ isSilgiContext: false
1029
+ });
1030
+ });
1031
+ silgi.hook("prepare:createDTSFramework", (data) => {
1032
+ data.importItems["silgi/types"] = {
1033
+ import: [
1034
+ {
1035
+ name: "SilgiRuntimeContext",
1036
+ type: true,
1037
+ key: "SilgiRuntimeContext"
1038
+ }
1039
+ ],
1040
+ from: "silgi/types"
1041
+ };
1042
+ data.customContent?.push(
1043
+ "",
1044
+ 'declare module "h3" {',
1045
+ " interface H3EventContext extends SilgiRuntimeContext {}",
1046
+ "}",
1047
+ ""
1048
+ );
1049
+ });
1050
+ silgi.hook("prepare:core.ts", (data) => {
1051
+ data._silgiConfigs.push(`captureError: (silgi, error, context = {}) => {
1052
+ const promise = silgi.hooks
1053
+ .callHookParallel('error', silgi, error, context)
1054
+ .catch((error_) => {
1055
+ console.error('Error while capturing another error', error_)
1056
+ })
1057
+
1058
+ if (context.event && isEvent(context.event)) {
1059
+ const errors = context.event.context.nitro?.errors
1060
+ if (errors) {
1061
+ errors.push({ error, context })
920
1062
  }
921
- for (const key in declaration.init.properties) {
922
- const property = declaration.init.properties[key];
923
- if (property.type === "ObjectProperty") {
924
- if (property.key.name === "options") {
925
- for (const key2 in property.value.properties) {
926
- const option = property.value.properties[key2];
927
- if (option.type === "ObjectProperty") {
928
- options[option.key.name] = option.value.value;
929
- }
930
- }
931
- }
932
- }
1063
+ if (context.event.waitUntil) {
1064
+ context.event.waitUntil(promise)
933
1065
  }
934
- options.type = false;
935
- data.push({
936
- exportName: declaration.id.name,
937
- options,
938
- // object: declaration.init,
939
- path
940
- });
941
- }
942
- }
943
- }
944
- return data;
945
- }
946
- parseInterfaceDeclarations(ast, find = "", path = "") {
947
- const data = [];
948
- for (const item of this.parseTSInterfaceDeclaration(ast, path)) {
949
- if (!item?.declaration?.extends)
950
- continue;
951
- for (const declaration of item?.declaration?.extends) {
952
- if (declaration.expression.name === find) {
953
- const options = {};
954
- options.type = true;
955
- data.push({
956
- exportName: item.declaration.id.name,
957
- options,
958
- // object: declaration.init,
959
- path
960
- });
961
1066
  }
962
- }
963
- }
964
- return data;
1067
+ }`);
1068
+ });
1069
+ if (silgi.options.imports !== false) {
1070
+ const h3Exports = await resolveModuleExportNames("h3", {
1071
+ url: import.meta.url
1072
+ });
1073
+ silgi.options.imports.presets ??= [];
1074
+ silgi.options.imports.presets.push({
1075
+ from: "h3",
1076
+ imports: h3Exports.filter((n) => !/^[A-Z]/.test(n) && n !== "use")
1077
+ });
965
1078
  }
966
- // private parsePlugin(ast: any, path: string = '') {
967
- // const data = {
968
- // export: [],
969
- // name: '',
970
- // path: '',
971
- // } as DataTypePlugin
972
- // for (const item of this.parseVariableDeclaration(ast)) {
973
- // for (const declaration of item.declaration.declarations) {
974
- // if (declaration.init.callee?.name === 'defineSilgiModule') {
975
- // if (declaration.init.arguments) {
976
- // for (const argument of declaration.init.arguments) {
977
- // for (const propertie of argument.properties) {
978
- // if (propertie.key.name === 'name')
979
- // data.name = propertie.value.value
980
- // }
981
- // }
982
- // }
983
- // data.export.push({
984
- // name: data.name,
985
- // as: camelCase(`${data.name}DefineSilgiModule`),
986
- // type: false,
987
- // })
988
- // }
989
- // }
990
- // }
991
- // for (const item of this.parseTSInterfaceDeclaration(ast)) {
992
- // if (!item?.declaration?.extends)
993
- // continue
994
- // for (const declaration of item?.declaration?.extends) {
995
- // if (declaration.expression.name === 'ModuleOptions') {
996
- // data.export.push({
997
- // name: item.declaration.id.name,
998
- // as: camelCase(`${data.name}ModuleOptions`),
999
- // type: true,
1000
- // })
1001
- // }
1002
- // // TODO add other plugins
1003
- // }
1004
- // }
1005
- // data.path = path
1006
- // return data
1007
- // }
1008
1079
  }
1009
1080
 
1010
- async function scanExportFile(silgi) {
1011
- const filePaths = /* @__PURE__ */ new Set();
1012
- const scannedPaths = [];
1013
- const dir = silgi.options.serverDir;
1014
- const files = (await globby(dir, { cwd: silgi.options.rootDir, ignore: silgi.options.ignore })).sort();
1015
- if (files.length) {
1016
- const siblings = await readdir(dirname(dir)).catch(() => []);
1017
- const directory = basename(dir);
1018
- if (!siblings.includes(directory)) {
1019
- const directoryLowerCase = directory.toLowerCase();
1020
- const caseCorrected = siblings.find((sibling) => sibling.toLowerCase() === directoryLowerCase);
1021
- if (caseCorrected) {
1022
- const original = relative(silgi.options.serverDir, dir);
1023
- const corrected = relative(silgi.options.serverDir, join(dirname(dir), caseCorrected));
1024
- consola.warn(`Components not scanned from \`~/${corrected}\`. Did you mean to name the directory \`~/${original}\` instead?`);
1025
- }
1026
- }
1027
- }
1028
- for (const _file of files) {
1029
- const filePath = resolve(dir, _file);
1030
- if (scannedPaths.find((d) => filePath.startsWith(withTrailingSlash(d))) || isIgnored(filePath, silgi)) {
1031
- continue;
1032
- }
1033
- if (filePaths.has(filePath)) {
1034
- continue;
1035
- }
1036
- filePaths.add(filePath);
1037
- if (silgi.options.extensions.includes(extname(filePath))) {
1038
- const parser = new SchemaParser({
1039
- debug: false
1040
- });
1041
- const readfile = await readFile(filePath, "utf-8");
1042
- const { exportVariables, parseInterfaceDeclarations } = parser.parseExports(readfile, filePath);
1043
- const createServices = exportVariables("createService", filePath);
1044
- if (hasError("Parser", silgi)) {
1045
- return;
1081
+ async function nitroFramework(silgi, skip = false) {
1082
+ if (silgi.options.preset !== "nitro" && skip === false)
1083
+ return;
1084
+ silgi.hook("prepare:schema.ts", (data) => {
1085
+ data.importItems.nitropack = {
1086
+ import: [
1087
+ {
1088
+ name: "NitroApp",
1089
+ type: true,
1090
+ key: "NitroApp"
1091
+ }
1092
+ ],
1093
+ from: "nitropack/types"
1094
+ };
1095
+ });
1096
+ silgi.hook("after:prepare:schema.ts", (data) => {
1097
+ data.unshift("type FrameworkContextExtends = NitroApp");
1098
+ });
1099
+ silgi.options.plugins.push({
1100
+ packageImport: "silgi/runtime/internal/nitro",
1101
+ path: join(runtimeDir, "internal/nitro")
1102
+ });
1103
+ silgi.hook("prepare:createDTSFramework", (data) => {
1104
+ data.importItems["nitropack/types"] = {
1105
+ import: [
1106
+ {
1107
+ name: "NitroRuntimeConfig",
1108
+ type: true,
1109
+ key: "NitroRuntimeConfig"
1110
+ }
1111
+ ],
1112
+ from: "nitropack/types"
1113
+ };
1114
+ data.customContent?.push(
1115
+ "",
1116
+ 'declare module "silgi/types" {',
1117
+ " interface SilgiRuntimeConfig extends NitroRuntimeConfig {}",
1118
+ "}",
1119
+ ""
1120
+ );
1121
+ });
1122
+ if (silgi.options.imports !== false) {
1123
+ silgi.options.imports.presets ??= [];
1124
+ }
1125
+ await h3Framework(silgi, true);
1126
+ }
1127
+
1128
+ async function nuxtFramework(silgi, skip = false) {
1129
+ if (silgi.options.preset !== "nuxt" && skip === false)
1130
+ return;
1131
+ await nitroFramework(silgi, true);
1132
+ }
1133
+
1134
+ const frameworkSetup = [
1135
+ emptyFramework,
1136
+ h3Framework,
1137
+ nitroFramework,
1138
+ nuxtFramework
1139
+ ];
1140
+
1141
+ async function registerModuleExportScan(silgi) {
1142
+ silgi.hook("prepare:schema.ts", async (options) => {
1143
+ for (const module of silgi.scanModules) {
1144
+ const moduleReExports = [];
1145
+ if (!module.entryPath) {
1146
+ continue;
1046
1147
  }
1047
- const scanTS = [];
1048
- const schemaTS = [];
1049
- if (createServices.length > 0) {
1050
- scanTS.push(...createServices.map(({ exportName, path }) => {
1051
- const randomString = hash(basename(path) + exportName);
1052
- const _name = `_v${randomString}`;
1053
- return { exportName, path, _name, type: "service" };
1054
- }));
1148
+ const moduleTypes = await promises.readFile(module.entryPath.replace(/\.mjs$/, "Types.d.ts"), "utf8").catch(() => "");
1149
+ const normalisedModuleTypes = moduleTypes.replace(/export\s*\{.*?\}/gs, (match) => match.replace(/\b(type|interface)\b/g, ""));
1150
+ for (const e of findTypeExports(normalisedModuleTypes)) {
1151
+ moduleReExports.push(e);
1055
1152
  }
1056
- const createSchemas = exportVariables("createSchema", filePath);
1057
- if (hasError("Parser", silgi)) {
1058
- return;
1153
+ for (const e of findExports(normalisedModuleTypes)) {
1154
+ moduleReExports.push(e);
1059
1155
  }
1060
- if (createSchemas.length > 0) {
1061
- scanTS.push(...createSchemas.map(({ exportName, path }) => {
1062
- const randomString = hash(basename(path) + exportName);
1063
- const _name = `_v${randomString}`;
1064
- return { exportName, path, _name, type: "schema" };
1065
- }));
1156
+ const hasTypeExport = (name) => moduleReExports.find((exp) => exp.names?.includes(name));
1157
+ const configKey = module.meta.configKey;
1158
+ const moduleName = module.meta.name || module.meta._packageName;
1159
+ options.importItems[configKey] = {
1160
+ import: [],
1161
+ from: module.meta._packageName ? moduleName : relativeWithDot(silgi.options.build.typesDir, module.entryPath)
1162
+ };
1163
+ if (hasTypeExport("ModuleOptions")) {
1164
+ const importName = `_${hash(`${configKey}ModuleOptions`)}`;
1165
+ options.importItems[configKey].import.push({
1166
+ name: `ModuleOptions as ${importName}`,
1167
+ type: true,
1168
+ key: importName
1169
+ });
1170
+ options.options.push({ key: configKey, value: importName });
1066
1171
  }
1067
- const createShareds = exportVariables("createShared", filePath);
1068
- if (hasError("Parser", silgi)) {
1069
- return;
1172
+ if (hasTypeExport("ModuleRuntimeOptions")) {
1173
+ const importName = `_${hash(`${configKey}ModuleRuntimeOptions`)}`;
1174
+ options.importItems[configKey].import.push({
1175
+ name: `ModuleRuntimeOptions as ${importName}`,
1176
+ type: true,
1177
+ key: importName
1178
+ });
1179
+ options.runtimeOptions.push({ key: configKey, value: importName });
1070
1180
  }
1071
- if (createShareds.length > 0) {
1072
- scanTS.push(...createShareds.map(({ exportName, path }) => {
1073
- const randomString = hash(basename(path) + exportName);
1074
- const _name = `_v${randomString}`;
1075
- return { exportName, path, _name, type: "shared" };
1076
- }));
1181
+ if (hasTypeExport("ModuleRuntimeShareds")) {
1182
+ const importName = `_${hash(`${configKey}ModuleRuntimeShareds`)}`;
1183
+ options.importItems[configKey].import.push({
1184
+ name: `ModuleRuntimeShareds as ${importName}`,
1185
+ type: true,
1186
+ key: importName
1187
+ });
1188
+ options.shareds.push({ key: configKey, value: importName });
1077
1189
  }
1078
- const sharedsTypes = parseInterfaceDeclarations("ExtendShared", filePath);
1079
- if (hasError("Parser", silgi)) {
1080
- return;
1190
+ if (hasTypeExport("ModuleEvents")) {
1191
+ const importName = `_${hash(`${configKey}ModuleEvents`)}`;
1192
+ options.importItems[configKey].import.push({
1193
+ name: `ModuleEvents as ${importName}`,
1194
+ type: true,
1195
+ key: importName
1196
+ });
1197
+ options.events.push({ key: configKey, value: importName });
1081
1198
  }
1082
- if (sharedsTypes.length > 0) {
1083
- schemaTS.push(...sharedsTypes.map(({ exportName, path }) => {
1084
- const randomString = hash(basename(path) + exportName);
1085
- const _name = `_v${randomString}`;
1086
- return { exportName, path, _name, type: "shared" };
1087
- }));
1199
+ if (hasTypeExport("ModuleRuntimeContexts")) {
1200
+ const importName = `_${hash(`${configKey}ModuleRuntimeContexts`)}`;
1201
+ options.importItems[configKey].import.push({
1202
+ name: `ModuleRuntimeContexts as ${importName}`,
1203
+ type: true,
1204
+ key: importName
1205
+ });
1206
+ options.contexts.push({ key: configKey, value: importName });
1088
1207
  }
1089
- const contextTypes = parseInterfaceDeclarations("ExtendContext", filePath);
1090
- if (hasError("Parser", silgi)) {
1091
- return;
1208
+ if (hasTypeExport("ModuleHooks")) {
1209
+ const importName = `_${hash(`${configKey}ModuleHooks`)}`;
1210
+ options.importItems[configKey].import.push({
1211
+ name: `ModuleHooks as ${importName}`,
1212
+ type: true,
1213
+ key: importName
1214
+ });
1215
+ options.hooks.push({ key: configKey, value: importName });
1092
1216
  }
1093
- if (contextTypes.length > 0) {
1094
- schemaTS.push(...contextTypes.map(({ exportName, path }) => {
1095
- const randomString = hash(basename(path) + exportName);
1096
- const _name = `_v${randomString}`;
1097
- return { exportName, path, _name, type: "context" };
1098
- }));
1217
+ if (hasTypeExport("ModuleRuntimeHooks")) {
1218
+ const importName = `_${hash(`${configKey}RuntimeHooks`)}`;
1219
+ options.importItems[configKey].import.push({
1220
+ name: `ModuleRuntimeHooks as ${importName}`,
1221
+ type: true,
1222
+ key: importName
1223
+ });
1224
+ options.runtimeHooks.push({ key: configKey, value: importName });
1099
1225
  }
1100
- silgi.hook("prepare:scan.ts", (options) => {
1101
- for (const { exportName, path, _name, type } of scanTS) {
1102
- if (!path.includes("vfs")) {
1103
- silgi.options.devServer.watch.push(path);
1104
- }
1105
- if (type === "service") {
1106
- options.services.push(_name);
1107
- }
1108
- if (type === "shared") {
1109
- options.shareds.push(_name);
1110
- }
1111
- if (type === "schema") {
1112
- options.schemas.push(_name);
1113
- }
1114
- options.importItems[path] ??= {
1115
- import: [],
1116
- from: relativeWithDot(silgi.options.silgi.serverDir, path)
1117
- };
1118
- options.importItems[path].import.push({
1119
- name: `${exportName} as ${_name}`,
1120
- key: _name
1121
- });
1122
- }
1123
- });
1124
- silgi.hook("prepare:schema.ts", (options) => {
1125
- for (const { exportName, path, _name, type } of schemaTS) {
1126
- if (!path.includes("vfs")) {
1127
- silgi.options.devServer.watch.push(path);
1128
- }
1129
- if (type === "shared") {
1130
- options.shareds.push({
1131
- key: _name,
1132
- value: _name
1133
- });
1134
- }
1135
- if (type === "context") {
1136
- options.contexts.push({
1137
- key: _name,
1138
- value: _name
1139
- });
1140
- }
1141
- options.importItems[path] ??= {
1142
- import: [],
1143
- from: relativeWithDot(silgi.options.build.typesDir, path)
1144
- };
1145
- options.importItems[path].import.push({
1146
- name: `${exportName} as ${_name}`,
1147
- key: _name
1148
- });
1149
- }
1150
- });
1151
- }
1152
- }
1153
- }
1154
-
1155
- function buildUriMap(silgi, currentPath = []) {
1156
- const uriMap = /* @__PURE__ */ new Map();
1157
- function traverse(node, path = []) {
1158
- if (!node || typeof node !== "object")
1159
- return;
1160
- if (path.length === 4) {
1161
- const basePath = path.join("/");
1162
- let pathString = "";
1163
- if (node.pathParams) {
1164
- let paths = null;
1165
- if (node.pathParams?._def?.typeName !== void 0) {
1166
- try {
1167
- const shape = node.pathParams?.shape;
1168
- paths = shape ? Object.keys(shape) : null;
1169
- } catch {
1170
- paths = null;
1171
- }
1172
- }
1173
- if (paths?.length) {
1174
- pathString = paths.map((p) => `:${p}`).join("/");
1175
- }
1226
+ if (hasTypeExport("ModuleRuntimeActions")) {
1227
+ const importName = `_${hash(`${configKey}ModuleRuntimeActions`)}`;
1228
+ options.importItems[configKey].import.push({
1229
+ name: `ModuleRuntimeActions as ${importName}`,
1230
+ type: true,
1231
+ key: importName
1232
+ });
1233
+ options.actions.push({ key: configKey, value: importName });
1176
1234
  }
1177
- uriMap.set(basePath, pathString);
1178
- return;
1179
- }
1180
- for (const key in node) {
1181
- if (!["_type", "fields"].includes(key)) {
1182
- traverse(node[key], [...path, key]);
1235
+ if (hasTypeExport("ModuleRuntimeMethods")) {
1236
+ const importName = `_${hash(`${configKey}ModuleRuntimeMethods`)}`;
1237
+ options.importItems[configKey].import.push({
1238
+ name: `ModuleRuntimeMethods as ${importName}`,
1239
+ type: true,
1240
+ key: importName
1241
+ });
1242
+ options.methods.push({ key: configKey, value: importName });
1243
+ }
1244
+ if (hasTypeExport("ModuleRuntimeRouteRules")) {
1245
+ const importName = `_${hash(`${configKey}ModuleRuntimeRouteRules`)}`;
1246
+ options.importItems[configKey].import.push({
1247
+ name: `ModuleRuntimeRouteRules as ${importName}`,
1248
+ type: true,
1249
+ key: importName
1250
+ });
1251
+ options.routeRules.push({ key: configKey, value: importName });
1252
+ }
1253
+ if (hasTypeExport("ModuleRuntimeRouteRulesConfig")) {
1254
+ const importName = `_${hash(`${configKey}ModuleRuntimeRouteRulesConfig`)}`;
1255
+ options.importItems[configKey].import.push({
1256
+ name: `ModuleRuntimeRouteRulesConfig as ${importName}`,
1257
+ type: true,
1258
+ key: importName
1259
+ });
1260
+ options.routeRulesConfig.push({ key: configKey, value: importName });
1183
1261
  }
1184
1262
  }
1185
- }
1186
- traverse(silgi.schemas, currentPath);
1187
- silgi.uris = defu(silgi.uris, Object.fromEntries(uriMap));
1188
- return uriMap;
1263
+ });
1189
1264
  }
1190
1265
 
1191
- async function readScanFile(silgi) {
1192
- const path = resolve(silgi.options.silgi.serverDir, "scan.ts");
1193
- const context = await promises.readFile(path, { encoding: "utf-8" });
1194
- silgi.unimport = createUnimport(silgi.options.imports || {});
1195
- await silgi.unimport.init();
1196
- const injectedResult = await silgi.unimport.injectImports(context, path);
1197
- if (!injectedResult) {
1198
- throw new Error("Failed to inject imports");
1266
+ async function loadSilgiModuleInstance(silgiModule) {
1267
+ if (typeof silgiModule === "string") {
1268
+ throw new TypeError(`Could not load \`${silgiModule}\`. Is it installed?`);
1199
1269
  }
1270
+ if (typeof silgiModule !== "function") {
1271
+ throw new TypeError(`Nuxt module should be a function: ${silgiModule}`);
1272
+ }
1273
+ return { silgiModule };
1274
+ }
1275
+ async function installModules(silgi, prepare = false) {
1276
+ silgi.options.isPreparingModules = prepare;
1200
1277
  const jiti = createJiti(silgi.options.rootDir, {
1278
+ alias: silgi.options.alias,
1201
1279
  fsCache: true,
1202
- moduleCache: false,
1203
- debug: silgi.options.debug,
1204
- alias: silgi.options.alias
1280
+ moduleCache: true
1205
1281
  });
1206
- try {
1207
- if (silgi.options.commandType === "prepare") {
1208
- globalThis._silgi_runtime = silgi.options.runtimeConfig;
1209
- injectedResult.code = `globalThis._silgi_runtime = ${JSON.stringify(silgi.options.runtimeConfig)};
1210
- ${injectedResult.code}`;
1211
- injectedResult.code = injectedResult.code.replace(/runtimeConfig: \{\}/, `runtimeConfig: ${JSON.stringify(silgi.options.runtimeConfig)}`);
1282
+ for (const module of silgi.scanModules) {
1283
+ if (hasInstalledModule(module.meta.configKey) && !silgi.options.dev) {
1284
+ silgi.logger.info(`Module ${module.meta.configKey} installed`);
1212
1285
  }
1213
- const scanFile = await jiti.evalModule(
1214
- injectedResult.code,
1215
- {
1216
- filename: path,
1217
- async: true,
1286
+ try {
1287
+ const silgiModule = module.entryPath !== void 0 ? await jiti.import(module.entryPath, {
1288
+ default: true,
1218
1289
  conditions: silgi.options.conditions
1219
- },
1220
- async (data, name) => {
1221
- return (await silgi.unimport.injectImports(data, name)).code;
1290
+ }) : module.module;
1291
+ if (silgiModule.name !== "silgiNormalizedModule") {
1292
+ silgi.scanModules = silgi.scanModules.filter((m) => m.entryPath !== module.entryPath);
1293
+ continue;
1222
1294
  }
1223
- );
1224
- silgi.uris = defu(silgi.uris, scanFile.uris) || {};
1225
- silgi.schemas = defu(scanFile.schemas, scanFile.uris) || {};
1226
- silgi.services = defu(scanFile.services, scanFile.uris) || {};
1227
- silgi.shareds = defu(scanFile.shareds, scanFile.shareds) || {};
1228
- silgi.modulesURIs = defu(scanFile.modulesURIs, scanFile.modulesURIs) || {};
1229
- return {
1230
- context,
1231
- object: {
1232
- schemas: scanFile.schemas,
1233
- uris: scanFile.uris,
1234
- services: scanFile.services,
1235
- shareds: scanFile.shareds,
1236
- modulesURIs: scanFile.modulesURIs
1237
- },
1238
- path
1239
- };
1240
- } catch (error) {
1241
- if (silgi.options.debug) {
1242
- console.error("Failed to read scan.ts file:", error);
1243
- } else {
1244
- consola.withTag("silgi").error(error);
1295
+ await installModule(silgiModule, silgi, prepare);
1296
+ } catch (err) {
1297
+ silgi.logger.error(err);
1245
1298
  }
1246
- return {
1247
- context,
1248
- object: {
1249
- schemas: {},
1250
- uris: {},
1251
- services: {},
1252
- shareds: {},
1253
- modulesURIs: {}
1254
- },
1255
- path
1256
- };
1257
1299
  }
1300
+ silgi.options.isPreparingModules = false;
1258
1301
  }
1259
-
1260
- async function prepareServerFiles(silgi) {
1261
- const importItems = {
1262
- "silgi": {
1263
- import: [
1264
- { name: "createSilgi", key: "createSilgi" },
1265
- { name: "createShared", key: "createShared" }
1266
- ],
1267
- from: "silgi"
1268
- },
1269
- "silgi/types": {
1270
- import: [
1271
- { name: "SilgiRuntimeOptions", type: true, key: "SilgiRuntimeOptions" },
1272
- { name: "FrameworkContext", type: true, key: "FrameworkContext" }
1273
- ],
1274
- from: "silgi/types"
1275
- },
1276
- "#silgi/vfs": {
1277
- import: [],
1278
- from: "./vfs"
1279
- },
1280
- "configs.ts": {
1281
- import: [
1282
- {
1283
- name: "cliConfigs",
1284
- type: false,
1285
- key: "cliConfigs"
1286
- }
1287
- ],
1288
- from: "./configs.ts"
1289
- }
1290
- };
1291
- const scanned = {
1292
- uris: {},
1293
- services: [],
1294
- shareds: [
1295
- `createShared({
1296
- modulesURIs,
1297
- })`
1298
- ],
1299
- schemas: [],
1300
- modulesURIs: {},
1301
- customImports: [],
1302
- importItems
1303
- };
1304
- if (silgi.uris) {
1305
- defu(scanned.uris, silgi.uris);
1306
- }
1307
- if (silgi.modulesURIs) {
1308
- defu(scanned.modulesURIs, silgi.modulesURIs);
1309
- }
1310
- await silgi.callHook("prepare:scan.ts", scanned);
1311
- if (importItems["#silgi/vfs"].import.length === 0) {
1312
- delete importItems["#silgi/vfs"];
1313
- }
1314
- if (scanned.services.length > 0) {
1315
- importItems.silgi.import.push({ name: "mergeServices", key: "mergeServices" });
1316
- }
1317
- if (scanned.shareds.length > 0) {
1318
- importItems.silgi.import.push({ name: "mergeShared", key: "mergeShared" });
1302
+ async function installModule(moduleToInstall, silgi = useSilgiCLI(), inlineOptions, prepare = false) {
1303
+ const { silgiModule } = await loadSilgiModuleInstance(moduleToInstall);
1304
+ const res = await silgiModule(inlineOptions || {}, silgi) ?? {};
1305
+ if (res === false) {
1306
+ return false;
1319
1307
  }
1320
- if (scanned.schemas.length > 0) {
1321
- importItems.silgi.import.push({ name: "mergeSchemas", key: "mergeSchemas" });
1308
+ const metaData = await silgiModule.getMeta?.();
1309
+ if (prepare) {
1310
+ return metaData;
1322
1311
  }
1323
- for (const key in importItems) {
1324
- importItems[key].import = deduplicateImportsByKey(importItems[key].import);
1312
+ const installedModule = silgi.scanModules.find((m) => m.meta.configKey === metaData?.configKey);
1313
+ if (installedModule) {
1314
+ installedModule.installed = true;
1315
+ } else {
1316
+ throw new Error(`Module ${metaData?.name} not found`);
1325
1317
  }
1326
- const importsContent = [
1327
- ...Object.entries(importItems).map(([_name, { from, import: imports }]) => {
1328
- if (silgi.options.typescript.removeFileExtension) {
1329
- from = from.replace(/\.(js|ts|mjs|cjs|jsx|tsx)$/, "");
1330
- }
1331
- return `import { ${imports.map(({ type, name }) => type ? `type ${name}` : name).join(", ")} } from '${from}'`;
1332
- }),
1333
- "",
1334
- ...scanned.customImports,
1335
- ""
1336
- ];
1337
- const importData = [
1338
- `export const uris = ${JSON.stringify(scanned.uris, null, 2)}`,
1339
- "",
1340
- `export const modulesURIs = ${JSON.stringify(scanned.modulesURIs, null, 2)}`,
1341
- "",
1342
- scanned.schemas.length > 0 ? "export const schemas = mergeSchemas([" : "export const schemas = {",
1343
- ...scanned.schemas.map((name) => {
1344
- return ` ${name},`;
1345
- }),
1346
- scanned.schemas.length > 0 ? "])" : "}",
1347
- "",
1348
- scanned.services.length > 0 ? "export const services = mergeServices([" : "export const services = {",
1349
- ...scanned.services.map((name) => {
1350
- return ` ${name},`;
1351
- }),
1352
- scanned.services.length > 0 ? "])" : "}",
1353
- "",
1354
- scanned.shareds.length > 0 ? "export const shareds = mergeShared([" : "export const shareds = {",
1355
- ...scanned.shareds.map((name) => {
1356
- return ` ${name},`;
1357
- }),
1358
- scanned.shareds.length > 0 ? "])" : "}",
1359
- ""
1360
- ];
1361
- await silgi.callHook("after:prepare:scan.ts", importData);
1362
- importData.unshift(...importsContent);
1363
- return importData;
1364
- }
1365
- function deduplicateImportsByKey(imports) {
1366
- const seenKeys = /* @__PURE__ */ new Map();
1367
- return imports.filter((item) => {
1368
- if (seenKeys.has(item.key)) {
1369
- return false;
1370
- }
1371
- seenKeys.set(item.key, true);
1372
- return true;
1373
- });
1374
- }
1375
-
1376
- async function writeScanFiles(silgi) {
1377
- const data = await prepareServerFiles(silgi);
1378
- if (!silgi.errors.length) {
1379
- await writeFile(
1380
- resolve(silgi.options.silgi.serverDir, "scan.ts"),
1381
- data.join("\n")
1382
- );
1383
- }
1384
- await readScanFile(silgi);
1385
- buildUriMap(silgi);
1386
- parseServices(silgi);
1387
- silgi.hook("prepare:scan.ts", (file) => {
1388
- file.uris = {
1389
- ...file.uris,
1390
- ...silgi.uris
1391
- };
1392
- file.modulesURIs = {
1393
- ...file.modulesURIs,
1394
- ...silgi.modulesURIs
1395
- };
1396
- });
1397
1318
  }
1398
1319
 
1399
- async function createStorageCLI(silgi) {
1400
- const storage = createStorage();
1401
- const runtime = useSilgiRuntimeConfig();
1402
- const mounts = klona({
1403
- ...silgi.options.storage,
1404
- ...silgi.options.devStorage
1320
+ const MissingModuleMatcher = /Cannot find module\s+['"]?([^'")\s]+)['"]?/i;
1321
+ async function _resolveSilgiModule(silgiModule, silgi) {
1322
+ let resolvedModulePath;
1323
+ let buildTimeModuleMeta = {};
1324
+ const jiti = createJiti(silgi.options.rootDir, {
1325
+ alias: silgi.options.alias,
1326
+ fsCache: true,
1327
+ moduleCache: true
1405
1328
  });
1406
- for (const [path, opts] of Object.entries(mounts)) {
1407
- if (opts.driver) {
1408
- const driver = await import(builtinDrivers[opts.driver] || opts.driver).then((r) => r.default || r);
1409
- const processedOpts = replaceRuntimeValues({ ...opts }, runtime);
1410
- storage.mount(path, driver(processedOpts));
1411
- } else {
1412
- silgi.logger.warn(`No \`driver\` set for storage mount point "${path}".`);
1413
- }
1414
- }
1415
- return storage;
1416
- }
1417
-
1418
- const vueShim = {
1419
- filename: "delete/testtest.d.ts",
1420
- where: ".silgi",
1421
- getContents: ({ app }) => {
1422
- if (!app.options.typescript.shim) {
1423
- return "";
1329
+ if (typeof silgiModule === "string") {
1330
+ silgiModule = resolveAlias$1(silgiModule, silgi.options.alias);
1331
+ if (isRelative(silgiModule)) {
1332
+ silgiModule = resolve(silgi.options.rootDir, silgiModule);
1424
1333
  }
1425
- return [
1426
- "declare module '*.vue' {",
1427
- " import { DefineComponent } from 'vue'",
1428
- " const component: DefineComponent<{}, {}, any>",
1429
- " export default component",
1430
- "}"
1431
- ].join("\n");
1432
- }
1433
- };
1434
- const pluginsDeclaration = {
1435
- filename: "delete/testtest1.d.ts",
1436
- where: ".silgi",
1437
- getContents: async () => {
1438
- return `
1439
- declare module 'nuxt' {
1440
- interface NuxtApp {
1441
- $myPlugin: any;
1334
+ try {
1335
+ const src = resolveModuleURL(silgiModule, {
1336
+ from: silgi.options.modulesDir.map((m) => directoryToURL(m.replace(/\/node_modules\/?$/, "/"))),
1337
+ suffixes: ["silgi", "silgi/index", "module", "module/index", "", "index"],
1338
+ extensions: [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"]
1339
+ // Maybe add https://github.com/unjs/exsolve/blob/dfff3e9bbc4a3a173a2d56b9b9ff731ab15598be/src/resolve.ts#L7
1340
+ // conditions: silgi.options.conditions,
1341
+ });
1342
+ resolvedModulePath = fileURLToPath(src);
1343
+ const resolvedSilgiModule = await jiti.import(src, { default: true });
1344
+ if (typeof resolvedSilgiModule !== "function") {
1345
+ throw new TypeError(`Nuxt module should be a function: ${silgiModule}.`);
1346
+ }
1347
+ silgiModule = await jiti.import(src, {
1348
+ default: true,
1349
+ conditions: silgi.options.conditions
1350
+ });
1351
+ const moduleMetadataPath = new URL("module.json", src);
1352
+ if (existsSync(moduleMetadataPath)) {
1353
+ buildTimeModuleMeta = JSON.parse(await promises.readFile(moduleMetadataPath, "utf-8"));
1354
+ } else {
1355
+ if (typeof silgiModule === "function") {
1356
+ const meta = await silgiModule.getMeta?.();
1357
+ const _exports = await scanExports(resolvedModulePath, true);
1358
+ buildTimeModuleMeta = {
1359
+ ...meta,
1360
+ exports: _exports.map(({ from, ...rest }) => rest)
1361
+ };
1442
1362
  }
1443
1363
  }
1444
- `;
1445
- }
1446
- };
1447
-
1448
- const defaultTemplates = {
1449
- __proto__: null,
1450
- pluginsDeclaration: pluginsDeclaration,
1451
- vueShim: vueShim
1452
- };
1453
-
1454
- const postTemplates = [
1455
- pluginsDeclaration.filename
1456
- ];
1457
- const logger = useLogger("silgi");
1458
- async function generateApp(app, options = {}) {
1459
- app.templates = Object.values(defaultTemplates).concat(app.options.build.templates);
1460
- await app.callHook("app:templates", app);
1461
- app.templates = app.templates.map((tmpl) => {
1462
- const dir = tmpl.where === ".silgi" ? app.options.build.dir : tmpl.where === "server" ? app.options.silgi.serverDir : tmpl.where === "client" ? app.options.silgi.clientDir : app.options.silgi.vfsDir;
1463
- return normalizeTemplate(tmpl, dir);
1464
- });
1465
- const filteredTemplates = {
1466
- pre: [],
1467
- post: []
1468
- };
1469
- for (const template of app.templates) {
1470
- if (options.filter && !options.filter(template)) {
1471
- continue;
1472
- }
1473
- const key = template.filename && postTemplates.includes(template.filename) ? "post" : "pre";
1474
- filteredTemplates[key].push(template);
1475
- }
1476
- const templateContext = { app };
1477
- const writes = [];
1478
- const dirs = /* @__PURE__ */ new Set();
1479
- const changedTemplates = [];
1480
- async function processTemplate(template) {
1481
- const dir = template.where === ".silgi" ? app.options.build.dir : template.where === "server" ? app.options.silgi.serverDir : template.where === "client" ? app.options.silgi.clientDir : app.options.silgi.vfsDir;
1482
- const fullPath = template.dst || resolve(dir, template.filename);
1483
- const start = performance.now();
1484
- const contents = await compileTemplate(template, templateContext).catch((e) => {
1485
- logger.error(`Could not compile template \`${template.filename}\`.`);
1486
- logger.error(e);
1487
- throw e;
1488
- });
1489
- template.modified = true;
1490
- if (template.modified) {
1491
- changedTemplates.push(template);
1492
- }
1493
- const perf = performance.now() - start;
1494
- const setupTime = Math.round(perf * 100) / 100;
1495
- if (app.options.debug || setupTime > 500) {
1496
- logger.info(`Compiled \`${template.filename}\` in ${setupTime}ms`);
1497
- }
1498
- if (template.modified && template.write) {
1499
- dirs.add(dirname(fullPath));
1500
- if (template.skipIfExists && existsSync(fullPath)) {
1501
- return;
1364
+ } catch (error) {
1365
+ const code = error.code;
1366
+ if (code === "MODULE_NOT_FOUND" || code === "ERR_PACKAGE_PATH_NOT_EXPORTED" || code === "ERR_MODULE_NOT_FOUND" || code === "ERR_UNSUPPORTED_DIR_IMPORT" || code === "ENOTDIR") {
1367
+ throw new TypeError(`Could not load \`${silgiModule}\`. Is it installed?`);
1368
+ }
1369
+ if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") {
1370
+ const module = MissingModuleMatcher.exec(error.message)?.[1];
1371
+ if (module && !module.includes(silgiModule)) {
1372
+ throw new TypeError(`Error while importing module \`${silgiModule}\`: ${error}`);
1373
+ }
1502
1374
  }
1503
- writes.push(() => writeFileSync(fullPath, contents, "utf8"));
1504
1375
  }
1505
1376
  }
1506
- await Promise.allSettled(filteredTemplates.pre.map(processTemplate));
1507
- await Promise.allSettled(filteredTemplates.post.map(processTemplate));
1508
- for (const dir of dirs) {
1509
- mkdirSync(dir, { recursive: true });
1377
+ if (!buildTimeModuleMeta) {
1378
+ throw new Error(`Module ${silgiModule} is not a valid Silgi module`);
1510
1379
  }
1511
- for (const write of writes) {
1512
- if (!app.errors.length) {
1513
- write();
1380
+ if (typeof silgiModule === "function") {
1381
+ if (!buildTimeModuleMeta.configKey) {
1382
+ const meta = await silgiModule.getMeta?.();
1383
+ buildTimeModuleMeta = {
1384
+ ...meta,
1385
+ exports: []
1386
+ };
1514
1387
  }
1515
- }
1516
- if (changedTemplates.length) {
1517
- await app.callHook("app:templatesGenerated", app, changedTemplates, options);
1518
- }
1519
- }
1520
- async function compileTemplate(template, ctx) {
1521
- delete ctx.utils;
1522
- if (template.src) {
1523
- try {
1524
- return await promises.readFile(template.src, "utf-8");
1525
- } catch (err) {
1526
- logger.error(`[nuxt] Error reading template from \`${template.src}\``);
1527
- throw err;
1388
+ if (silgi.scanModules.some((m) => m.meta?.configKey === buildTimeModuleMeta.configKey)) {
1389
+ throw new Error(`Module with key \`${buildTimeModuleMeta.configKey}\` already exists`);
1528
1390
  }
1529
- }
1530
- if (template.getContents) {
1531
- return template.getContents({
1532
- ...ctx,
1533
- options: template.options
1391
+ const options = await silgiModule.getOptions?.() || {};
1392
+ if (options) {
1393
+ silgi.options._c12.config[buildTimeModuleMeta.configKey] = defu(
1394
+ silgi.options._c12.config[buildTimeModuleMeta.configKey] || {},
1395
+ options || {}
1396
+ );
1397
+ } else {
1398
+ throw new TypeError(`Could not load \`${silgiModule}\`. Is it installed?`);
1399
+ }
1400
+ silgi.scanModules.push({
1401
+ meta: buildTimeModuleMeta,
1402
+ entryPath: resolvedModulePath || void 0,
1403
+ installed: false,
1404
+ options,
1405
+ module: silgiModule
1534
1406
  });
1535
1407
  }
1536
- throw new Error(`[nuxt] Invalid template. Templates must have either \`src\` or \`getContents\`: ${JSON.stringify(template)}`);
1537
1408
  }
1538
-
1539
- async function installPackages(silgi) {
1540
- const packages = {
1541
- dependencies: {
1542
- "@fastify/deepmerge": peerDependencies["@fastify/deepmerge"],
1543
- "@silgi/ecosystem": peerDependencies["@silgi/ecosystem"],
1544
- ...silgi.options.installPackages?.dependencies
1545
- },
1546
- devDependencies: {
1547
- ...silgi.options.installPackages?.devDependencies
1548
- }
1549
- };
1550
- await silgi.callHook("prepare:installPackages", packages);
1551
- if (silgi.options.preset === "npm-package") {
1552
- packages.devDependencies = {
1553
- ...packages.devDependencies,
1554
- ...packages.dependencies
1555
- };
1556
- packages.dependencies = {};
1557
- }
1558
- addTemplate({
1559
- filename: "install.json",
1560
- where: ".silgi",
1561
- write: true,
1562
- getContents: () => JSON.stringify(packages, null, 2)
1563
- });
1409
+ async function scanModules$1(silgi) {
1410
+ const _modules = [
1411
+ ...silgi.options._modules,
1412
+ ...silgi.options.modules
1413
+ ];
1414
+ for await (const mod of _modules) {
1415
+ await _resolveSilgiModule(mod, silgi);
1416
+ }
1417
+ const moduleMap = new Map(
1418
+ silgi.scanModules.map((m) => [m.meta?.configKey, m])
1419
+ );
1420
+ const graphData = createDependencyGraph(silgi.scanModules);
1421
+ const sortedKeys = topologicalSort(graphData);
1422
+ const modules = sortedKeys.map((key) => moduleMap.get(key)).filter((module) => Boolean(module));
1423
+ silgi.scanModules = modules;
1564
1424
  }
1565
-
1566
- function useCLIRuntimeConfig(silgi) {
1567
- const safeRuntimeConfig = JSON.parse(JSON.stringify(silgi.options.runtimeConfig));
1568
- silgi.hook("prepare:configs.ts", (data) => {
1569
- data.runtimeConfig = safeRuntimeConfig;
1570
- silgi.options.envOptions = silgi.options.envOptions;
1425
+ function createDependencyGraph(modules) {
1426
+ const graph = /* @__PURE__ */ new Map();
1427
+ const inDegree = /* @__PURE__ */ new Map();
1428
+ modules.forEach((module) => {
1429
+ const key = module.meta?.configKey;
1430
+ if (key) {
1431
+ graph.set(key, /* @__PURE__ */ new Set());
1432
+ inDegree.set(key, 0);
1433
+ }
1571
1434
  });
1572
- addTemplate({
1573
- filename: "env.example",
1574
- write: true,
1575
- where: ".silgi",
1576
- getContents() {
1577
- console.log("Generating env.example");
1578
- const flattenedConfig = flattenObject(safeRuntimeConfig);
1579
- const groupedVars = {};
1580
- Object.entries(flattenedConfig).forEach(([key, { value, originalPath }]) => {
1581
- const shouldExclude = silgi.options.codegen.env.safeList.some((safePath) => {
1582
- if (safePath.length !== originalPath.length)
1583
- return false;
1584
- return safePath.every((segment, index) => segment.toLowerCase() === originalPath[index].toLowerCase());
1585
- });
1586
- if (shouldExclude)
1587
- return;
1588
- if (originalPath.length > 0) {
1589
- const firstKey = originalPath[0];
1590
- const categoryName = extractCategoryFromCamel(firstKey);
1591
- if (!groupedVars[categoryName]) {
1592
- groupedVars[categoryName] = {};
1593
- }
1594
- groupedVars[categoryName][key] = value;
1595
- } else {
1596
- if (!groupedVars.OTHER) {
1597
- groupedVars.OTHER = {};
1598
- }
1599
- groupedVars.OTHER[key] = value;
1600
- }
1601
- });
1602
- return Object.entries(groupedVars).map(([category, vars]) => {
1603
- const varsContent = Object.entries(vars).map(([key, value]) => `${key}=${value}`).join("\n");
1604
- return `# ${category}
1605
- ${varsContent}`;
1606
- }).join("\n\n");
1435
+ modules.forEach((module) => {
1436
+ const key = module.meta?.configKey;
1437
+ if (!key) {
1438
+ return;
1607
1439
  }
1440
+ const requiredDeps = module.meta?.requiredDependencies || [];
1441
+ const beforeDeps = module.meta?.beforeDependencies || [];
1442
+ const afterDeps = module.meta?.afterDependencies || [];
1443
+ const processedDeps = /* @__PURE__ */ new Set();
1444
+ requiredDeps.forEach((dep) => {
1445
+ if (!graph.has(dep)) {
1446
+ throw new Error(`Required dependency "${dep}" for module "${key}" is missing`);
1447
+ }
1448
+ graph.get(dep)?.add(key);
1449
+ inDegree.set(key, (inDegree.get(key) || 0) + 1);
1450
+ processedDeps.add(dep);
1451
+ });
1452
+ beforeDeps.forEach((dep) => {
1453
+ if (!graph.has(dep)) {
1454
+ return;
1455
+ }
1456
+ graph.get(key)?.add(dep);
1457
+ inDegree.set(dep, (inDegree.get(dep) || 0) + 1);
1458
+ });
1459
+ afterDeps.forEach((dep) => {
1460
+ if (processedDeps.has(dep)) {
1461
+ return;
1462
+ }
1463
+ if (!graph.has(dep)) {
1464
+ return;
1465
+ }
1466
+ graph.get(dep)?.add(key);
1467
+ inDegree.set(key, (inDegree.get(key) || 0) + 1);
1468
+ });
1608
1469
  });
1609
- const _sharedRuntimeConfig = initRuntimeConfig(silgi.options.envOptions, silgi.options.runtimeConfig);
1610
- silgi.options.runtimeConfig = safeRuntimeConfig;
1611
- return _sharedRuntimeConfig;
1470
+ return { graph, inDegree };
1612
1471
  }
1613
- function extractCategoryFromCamel(str) {
1614
- if (/^[a-z]+[A-Z]/.test(str)) {
1615
- const matches = str.match(/^([a-z]+)([A-Z][a-z0-9]*)?/);
1616
- if (matches && matches[2]) {
1617
- return `${matches[1].toUpperCase()}_${matches[2].toUpperCase()}`;
1472
+ function findCyclicDependencies(graph) {
1473
+ const visited = /* @__PURE__ */ new Set();
1474
+ const recursionStack = /* @__PURE__ */ new Set();
1475
+ const cycles = [];
1476
+ function dfs(node, path = []) {
1477
+ visited.add(node);
1478
+ recursionStack.add(node);
1479
+ path.push(node);
1480
+ for (const neighbor of graph.get(node) || []) {
1481
+ if (recursionStack.has(neighbor)) {
1482
+ const cycleStart = path.indexOf(neighbor);
1483
+ if (cycleStart !== -1) {
1484
+ cycles.push([...path.slice(cycleStart), neighbor]);
1485
+ }
1486
+ } else if (!visited.has(neighbor)) {
1487
+ dfs(neighbor, [...path]);
1488
+ }
1618
1489
  }
1490
+ recursionStack.delete(node);
1491
+ path.pop();
1619
1492
  }
1620
- return str.toUpperCase();
1621
- }
1622
- function flattenObject(obj, prefix = "SILGI", originalPath = []) {
1623
- return Object.entries(obj).reduce((acc, [key, value]) => {
1624
- const formattedKey = snakeCase(key).toUpperCase();
1625
- const newKey = `${prefix}_${formattedKey}`;
1626
- const newPath = [...originalPath, key];
1627
- if (typeof value === "object" && value !== null) {
1628
- Object.assign(acc, flattenObject(value, newKey, newPath));
1629
- } else {
1630
- acc[newKey] = { value, originalPath: newPath };
1493
+ for (const node of graph.keys()) {
1494
+ if (!visited.has(node)) {
1495
+ dfs(node, []);
1631
1496
  }
1632
- return acc;
1633
- }, {});
1497
+ }
1498
+ return cycles;
1634
1499
  }
1635
-
1636
- const GLOB_SCAN_PATTERN = "**/*.{js,mjs,cjs,ts,mts,cts,tsx,jsx}";
1637
- async function scanAndSyncOptions(silgi) {
1638
- const scannedModules = await scanModules(silgi);
1639
- silgi.options.modules = silgi.options.modules || [];
1640
- for (const modPath of scannedModules) {
1641
- if (!silgi.options.modules.includes(modPath)) {
1642
- silgi.options.modules.push(modPath);
1500
+ function topologicalSort(graphData) {
1501
+ const { graph, inDegree } = graphData;
1502
+ const order = [];
1503
+ const queue = [];
1504
+ for (const [node, degree] of inDegree.entries()) {
1505
+ if (degree === 0) {
1506
+ queue.push(node);
1643
1507
  }
1644
1508
  }
1509
+ while (queue.length > 0) {
1510
+ const node = queue.shift();
1511
+ order.push(node);
1512
+ const neighbors = Array.from(graph.get(node) || []);
1513
+ for (const neighbor of neighbors) {
1514
+ const newDegree = (inDegree.get(neighbor) || 0) - 1;
1515
+ inDegree.set(neighbor, newDegree);
1516
+ if (newDegree === 0) {
1517
+ queue.push(neighbor);
1518
+ }
1519
+ }
1520
+ }
1521
+ if (order.length !== graph.size) {
1522
+ const cycles = findCyclicDependencies(graph);
1523
+ if (cycles.length > 0) {
1524
+ const cycleStr = cycles.map((cycle) => ` ${cycle.join(" -> ")}`).join("\n");
1525
+ throw new Error(`Circular dependencies detected:
1526
+ ${cycleStr}`);
1527
+ } else {
1528
+ const unresolvedModules = Array.from(graph.keys()).filter((key) => !order.includes(key));
1529
+ throw new Error(`Unable to resolve dependencies for modules: ${unresolvedModules.join(", ")}`);
1530
+ }
1531
+ }
1532
+ return order;
1645
1533
  }
1646
- async function scanModules(silgi) {
1647
- const files = await scanFiles(silgi, "silgi/modules");
1648
- return files.map((f) => f.fullPath);
1649
- }
1650
- async function scanFiles(silgi, name) {
1651
- const files = await Promise.all(
1652
- silgi.options.scanDirs.map((dir) => scanDir(silgi, dir, name))
1653
- ).then((r) => r.flat());
1654
- return files;
1655
- }
1656
- async function scanDir(silgi, dir, name) {
1657
- const fileNames = await globby(join(name, GLOB_SCAN_PATTERN), {
1658
- cwd: dir,
1659
- dot: true,
1660
- ignore: silgi.options.ignore,
1661
- absolute: true
1534
+
1535
+ async function commands(silgi) {
1536
+ const commands2 = {
1537
+ ...silgi.options.commands
1538
+ };
1539
+ await silgi.callHook("prepare:commands", commands2);
1540
+ addTemplate({
1541
+ filename: "cli.json",
1542
+ where: ".silgi",
1543
+ write: true,
1544
+ getContents: () => JSON.stringify(commands2, null, 2)
1545
+ });
1546
+ silgi.commands = commands2;
1547
+ silgi.hook("prepare:schema.ts", async (object) => {
1548
+ const allTags = Object.values(commands2).reduce((acc, commandGroup) => {
1549
+ Object.values(commandGroup).forEach((command) => {
1550
+ if (command.tags) {
1551
+ command.tags.forEach((tag) => acc.add(tag));
1552
+ }
1553
+ });
1554
+ return acc;
1555
+ }, /* @__PURE__ */ new Set());
1556
+ const data = [
1557
+ "",
1558
+ generateTypes(
1559
+ await resolveSchema(
1560
+ {
1561
+ ...Object.fromEntries(Array.from(allTags.values()).map((tag) => [tag, "string"]))
1562
+ }
1563
+ ),
1564
+ {
1565
+ interfaceName: "SilgiCommandsExtended",
1566
+ addExport: false,
1567
+ addDefaults: false,
1568
+ allowExtraKeys: false,
1569
+ indentation: 0
1570
+ }
1571
+ ),
1572
+ ""
1573
+ ];
1574
+ object.customImports?.push(...data);
1662
1575
  });
1663
- return fileNames.map((fullPath) => {
1664
- return {
1665
- fullPath,
1666
- path: relative(join(dir, name), fullPath)
1667
- };
1668
- }).sort((a, b) => a.path.localeCompare(b.path));
1669
1576
  }
1670
1577
 
1671
- async function createSilgiCLI(config = {}, opts = {}) {
1672
- const options = await loadOptions(config, opts);
1673
- const hooks = createHooks();
1674
- const silgi = {
1675
- modulesURIs: {},
1676
- scannedURIs: /* @__PURE__ */ new Map(),
1677
- services: {},
1678
- uris: {},
1679
- shareds: {},
1680
- schemas: {},
1681
- unimport: void 0,
1682
- options,
1683
- hooks,
1684
- errors: [],
1685
- commands: {},
1686
- _requiredModules: {},
1687
- logger: consola.withTag("silgi"),
1688
- close: () => silgi.hooks.callHook("close", silgi),
1689
- storage: void 0,
1690
- scanModules: [],
1691
- templates: [],
1692
- callHook: hooks.callHook,
1693
- addHooks: hooks.addHooks,
1694
- hook: hooks.hook,
1695
- async updateConfig(_config) {
1696
- },
1697
- routeRules: void 0
1698
- };
1699
- await prepareEnv(options);
1700
- const routeRules = createRouteRules();
1701
- routeRules.importRules(options.routeRules ?? {});
1702
- silgi.routeRules = routeRules;
1703
- if (silgiCLICtx.tryUse()) {
1704
- silgiCLICtx.unset();
1705
- silgiCLICtx.set(silgi);
1706
- } else {
1707
- silgiCLICtx.set(silgi);
1708
- silgi.hook("close", () => silgiCLICtx.unset());
1578
+ function resolveIgnorePatterns(silgi, relativePath) {
1579
+ if (!silgi) {
1580
+ return [];
1709
1581
  }
1710
- if (silgi.options.debug) {
1711
- createDebugger(silgi.hooks, { tag: "silgi" });
1712
- silgi.options.plugins.push({
1713
- path: join(runtimeDir, "internal/debug"),
1714
- packageImport: "silgi/runtime/internal/debug"
1715
- });
1582
+ const ignorePatterns = silgi.options.ignore.flatMap((s) => resolveGroupSyntax(s));
1583
+ const nuxtignoreFile = join(silgi.options.rootDir, ".nuxtignore");
1584
+ if (existsSync(nuxtignoreFile)) {
1585
+ const contents = readFileSync(nuxtignoreFile, "utf-8");
1586
+ ignorePatterns.push(...contents.trim().split(/\r?\n/));
1716
1587
  }
1717
- for (const framework of frameworkSetup) {
1718
- await framework(silgi);
1588
+ return ignorePatterns;
1589
+ }
1590
+ function isIgnored(pathname, silgi, _stats) {
1591
+ if (!silgi) {
1592
+ return false;
1719
1593
  }
1720
- await scanAndSyncOptions(silgi);
1721
- await scanModules$1(silgi);
1722
- await scanExportFile(silgi);
1723
- await installModules(silgi, true);
1724
- useCLIRuntimeConfig(silgi);
1725
- await writeScanFiles(silgi);
1726
- silgi.storage = await createStorageCLI(silgi);
1727
- silgi.hooks.hook("close", async () => {
1728
- await silgi.storage.dispose();
1729
- });
1730
- if (silgi.options.logLevel !== void 0) {
1731
- silgi.logger.level = silgi.options.logLevel;
1594
+ if (!silgi._ignore) {
1595
+ silgi._ignore = ignore(silgi.options.ignoreOptions);
1596
+ silgi._ignore.add(resolveIgnorePatterns(silgi));
1732
1597
  }
1733
- silgi.hooks.addHooks(silgi.options.hooks);
1734
- await installModules(silgi);
1735
- await silgi.hooks.callHook("scanFiles:done", silgi);
1736
- await commands(silgi);
1737
- await installPackages(silgi);
1738
- await generateApp(silgi);
1739
- if (silgi.options.imports) {
1740
- silgi.options.imports.dirs ??= [];
1741
- silgi.options.imports.dirs = silgi.options.imports.dirs.map((dir) => {
1742
- if (typeof dir === "string") {
1743
- if (dir.startsWith("!")) {
1744
- return `!${resolveSilgiPath(dir.slice(1), options, silgi.options.rootDir)}`;
1745
- }
1746
- return resolveSilgiPath(dir, options, silgi.options.rootDir);
1598
+ const relativePath = relative(silgi.options.rootDir, pathname);
1599
+ if (relativePath[0] === "." && relativePath[1] === ".") {
1600
+ return false;
1601
+ }
1602
+ return !!(relativePath && silgi._ignore.ignores(relativePath));
1603
+ }
1604
+ function resolveGroupSyntax(group) {
1605
+ let groups = [group];
1606
+ while (groups.some((group2) => group2.includes("{"))) {
1607
+ groups = groups.flatMap((group2) => {
1608
+ const [head, ...tail] = group2.split("{");
1609
+ if (tail.length) {
1610
+ const [body = "", ...rest] = tail.join("{").split("}");
1611
+ return body.split(",").map((part) => `${head}${part}${rest.join("")}`);
1747
1612
  }
1748
- return dir;
1749
- });
1750
- silgi.options.imports.presets.push({
1751
- from: "silgi/types",
1752
- imports: autoImportTypes.map((type) => type),
1753
- type: true
1754
- });
1755
- silgi.options.imports.presets.push({
1756
- from: "silgi/types",
1757
- imports: autoImportTypes.map((type) => type),
1758
- type: true
1759
- });
1760
- silgi.options.imports.presets.push({
1761
- from: "silgi/runtime/internal/ofetch",
1762
- imports: ["createSilgiFetch", "silgi$fetch"]
1613
+ return group2;
1763
1614
  });
1764
- silgi.unimport = createUnimport(silgi.options.imports);
1765
- await silgi.unimport.init();
1766
1615
  }
1767
- await registerModuleExportScan(silgi);
1768
- await writeScanFiles(silgi);
1769
- return silgi;
1616
+ return groups;
1770
1617
  }
1771
1618
 
1772
- async function prepareConfigs(silgi) {
1773
- const _data = {
1774
- runtimeConfig: {}
1619
+ const safeFiles = [
1620
+ "silgi/configs",
1621
+ "silgi",
1622
+ "silgi/rules",
1623
+ "silgi/scan",
1624
+ "silgi/vfs"
1625
+ ];
1626
+ class SchemaParser {
1627
+ options = {
1628
+ debug: false
1775
1629
  };
1776
- for (const module of silgi.scanModules) {
1777
- if (module.meta.cliToRuntimeOptionsKeys && module.meta.cliToRuntimeOptionsKeys?.length > 0) {
1778
- for (const key of module.meta.cliToRuntimeOptionsKeys) {
1779
- _data[module.meta.configKey] = {
1780
- ..._data[module.meta.configKey],
1781
- [key]: module.options[key]
1782
- };
1783
- }
1784
- } else {
1785
- _data[module.meta.configKey] = {};
1630
+ /**
1631
+ *
1632
+ */
1633
+ constructor(options) {
1634
+ this.options = {
1635
+ ...this.options,
1636
+ ...options
1637
+ };
1638
+ }
1639
+ parseExports(content, filePath) {
1640
+ const ast = parseSync(content, { sourceType: "module", sourceFilename: filePath });
1641
+ if (this.options.debug)
1642
+ writeFileSync(`${filePath}.ast.json`, JSON.stringify(ast.program, null, 2));
1643
+ return {
1644
+ exportVariables: (search, path) => this.parseTypeDeclarations(ast, search, path),
1645
+ parseInterfaceDeclarations: (search, path) => this.parseInterfaceDeclarations(ast, search, path)
1646
+ // parsePlugin: (path: string) => this.parsePlugin(ast, path),
1647
+ };
1648
+ }
1649
+ parseVariableDeclaration(ast, path) {
1650
+ const silgi = useSilgiCLI();
1651
+ if (ast.program.body.length === 0) {
1652
+ if (safeFiles.find((i) => path.includes(i)))
1653
+ return [];
1654
+ silgi.errors.push({
1655
+ type: "Parser",
1656
+ path
1657
+ });
1658
+ consola$1.warn("This file has a problem:", path);
1786
1659
  }
1660
+ const variableDeclarations = ast.program.body.filter((i) => i.type === "ExportNamedDeclaration").filter((i) => i.declaration?.type === "VariableDeclaration");
1661
+ return variableDeclarations;
1787
1662
  }
1788
- await silgi.callHook("prepare:configs.ts", _data);
1789
- const importData = [
1790
- "import type { SilgiRuntimeOptions, SilgiRuntimeConfig, SilgiOptions } from 'silgi/types'",
1791
- "import { useSilgiRuntimeConfig } from 'silgi/runtime'",
1792
- "",
1793
- `export const runtimeConfig: Partial<SilgiRuntimeConfig> = ${genObjectFromRawEntries(
1794
- Object.entries(_data.runtimeConfig).map(([key, value]) => [key, genEnsureSafeVar(value)]),
1795
- ""
1796
- )}`,
1797
- "",
1798
- "const runtime = useSilgiRuntimeConfig(undefined, runtimeConfig)",
1799
- ""
1800
- ];
1801
- delete _data.runtimeConfig;
1802
- importData.push(`export const cliConfigs: Partial<SilgiRuntimeOptions & SilgiOptions> = ${genObjectFromRawEntries(
1803
- Object.entries(_data).map(
1804
- ([key, value]) => [key, genEnsureSafeVar(value)]
1805
- ).concat(
1806
- [
1807
- ["runtimeConfig", "runtime"]
1808
- ]
1809
- )
1810
- )}`);
1811
- return importData;
1812
- }
1813
-
1814
- async function prepareCoreFile(data, frameworkContext, silgi) {
1815
- let importItems = {
1816
- "silgi": {
1817
- import: [
1818
- {
1819
- name: "createSilgi",
1820
- key: "createSilgi"
1663
+ parseTSInterfaceDeclaration(ast, path = "") {
1664
+ const silgi = useSilgiCLI();
1665
+ if (ast.program.body.length === 0) {
1666
+ if (safeFiles.find((i) => path.includes(i)))
1667
+ return [];
1668
+ silgi.errors.push({
1669
+ type: "Parser",
1670
+ path
1671
+ });
1672
+ consola$1.warn("This file has a problem:", path);
1673
+ }
1674
+ const interfaceDeclarations = ast.program.body.filter((i) => i.type === "ExportNamedDeclaration").filter((i) => i.declaration?.type === "TSInterfaceDeclaration");
1675
+ return interfaceDeclarations;
1676
+ }
1677
+ parseTypeDeclarations(ast, find = "", path = "") {
1678
+ const data = [];
1679
+ const variableDeclarations = this.parseVariableDeclaration(ast, path);
1680
+ for (const item of variableDeclarations) {
1681
+ for (const declaration of item.declaration.declarations) {
1682
+ if (declaration.init?.callee?.name === find) {
1683
+ const options = {};
1684
+ if (declaration.init.arguments) {
1685
+ for (const argument of declaration.init.arguments) {
1686
+ for (const propertie of argument.properties) {
1687
+ if (propertie.key.name === "name")
1688
+ options.pluginName = propertie.value.value;
1689
+ }
1690
+ }
1691
+ }
1692
+ for (const key in declaration.init.properties) {
1693
+ const property = declaration.init.properties[key];
1694
+ if (property.type === "ObjectProperty") {
1695
+ if (property.key.name === "options") {
1696
+ for (const key2 in property.value.properties) {
1697
+ const option = property.value.properties[key2];
1698
+ if (option.type === "ObjectProperty") {
1699
+ options[option.key.name] = option.value.value;
1700
+ }
1701
+ }
1702
+ }
1703
+ }
1704
+ }
1705
+ options.type = false;
1706
+ data.push({
1707
+ exportName: declaration.id.name,
1708
+ options,
1709
+ // object: declaration.init,
1710
+ path
1711
+ });
1821
1712
  }
1822
- ],
1823
- from: "silgi"
1824
- },
1825
- "silgi/types": {
1826
- import: [
1827
- {
1828
- name: "SilgiRuntimeOptions",
1829
- type: true,
1830
- key: "SilgiRuntimeOptions"
1831
- },
1832
- {
1833
- name: "FrameworkContext",
1834
- type: true,
1835
- key: "FrameworkContext"
1836
- },
1837
- {
1838
- name: "SilgiOptions",
1839
- type: true,
1840
- key: "SilgiOptions"
1841
- }
1842
- ],
1843
- from: "silgi/types"
1844
- },
1845
- "#silgi/vfs": {
1846
- import: [],
1847
- from: "./vfs"
1848
- },
1849
- "scan.ts": {
1850
- import: [
1851
- {
1852
- name: "uris",
1853
- type: false,
1854
- key: "uris"
1855
- },
1856
- {
1857
- name: "services",
1858
- type: false,
1859
- key: "services"
1860
- },
1861
- {
1862
- name: "shareds",
1863
- type: false,
1864
- key: "shareds"
1865
- },
1866
- {
1867
- name: "schemas",
1868
- type: false,
1869
- key: "schemas"
1870
- },
1871
- {
1872
- name: "modulesURIs",
1873
- type: false,
1874
- key: "modulesURIs"
1875
- }
1876
- ],
1877
- from: "./scan.ts"
1878
- },
1879
- "configs.ts": {
1880
- import: [
1881
- {
1882
- name: "cliConfigs",
1883
- type: false,
1884
- key: "cliConfigs"
1885
- }
1886
- ],
1887
- from: "./configs.ts"
1888
- },
1889
- "rules.ts": {
1890
- import: [
1891
- {
1892
- name: "routeRules",
1893
- key: "routeRules"
1894
- }
1895
- ],
1896
- from: "./rules.ts"
1713
+ }
1897
1714
  }
1898
- };
1899
- importItems = { ...data._importItems, ...importItems };
1900
- const _data = {
1901
- customImports: data._customImports || [],
1902
- buildSilgiExtraContent: [],
1903
- beforeBuildSilgiExtraContent: [],
1904
- afterCliOptions: [],
1905
- _silgiConfigs: [],
1906
- customContent: [],
1907
- importItems
1908
- };
1909
- await silgi.callHook("prepare:core.ts", _data);
1910
- if (importItems["#silgi/vfs"].import.length === 0) {
1911
- delete importItems["#silgi/vfs"];
1912
- }
1913
- const plugins = [];
1914
- for (const plugin of silgi.options.plugins) {
1915
- const pluginImportName = `_${hash(plugin.packageImport)}`;
1916
- _data.customImports.push(`import ${pluginImportName} from '${plugin.packageImport}'`);
1917
- plugins.push(pluginImportName);
1715
+ return data;
1918
1716
  }
1919
- const importsContent = [
1920
- 'import { mergeDeep } from "silgi/runtime"',
1921
- ...Object.entries(importItems).map(([_name, { from, import: imports }]) => {
1922
- if (silgi.options.typescript.removeFileExtension) {
1923
- from = from.replace(/\.(js|ts|mjs|cjs|jsx|tsx)$/, "");
1717
+ parseInterfaceDeclarations(ast, find = "", path = "") {
1718
+ const data = [];
1719
+ for (const item of this.parseTSInterfaceDeclaration(ast, path)) {
1720
+ if (!item?.declaration?.extends)
1721
+ continue;
1722
+ for (const declaration of item?.declaration?.extends) {
1723
+ if (declaration.expression.name === find) {
1724
+ const options = {};
1725
+ options.type = true;
1726
+ data.push({
1727
+ exportName: item.declaration.id.name,
1728
+ options,
1729
+ // object: declaration.init,
1730
+ path
1731
+ });
1732
+ }
1924
1733
  }
1925
- return `import { ${imports.map(({ type, name }) => type ? `type ${name}` : name).join(", ")} } from '${from}'`;
1926
- }),
1927
- "",
1928
- ..._data.customImports,
1929
- ""
1930
- ];
1931
- const importData = [
1932
- "",
1933
- "export async function buildSilgi(framework: FrameworkContext, moduleOptions?: Partial<SilgiRuntimeOptions>,buildOptions?: Partial<SilgiOptions>) {",
1934
- "",
1935
- _data.beforeBuildSilgiExtraContent.length > 0 ? _data.beforeBuildSilgiExtraContent.map(({ value, type }) => {
1936
- return type === "function" ? value : `const ${value}`;
1937
- }) : "",
1938
- "",
1939
- " const silgi = await createSilgi({",
1940
- " framework,",
1941
- " shared: shareds as any,",
1942
- " services: services as any,",
1943
- " schemas: schemas as any,",
1944
- " uris,",
1945
- " modulesURIs,",
1946
- ` plugins: [${plugins.join(", ")}],`,
1947
- _data._silgiConfigs.length > 0 ? ` ${_data._silgiConfigs.map((config) => typeof config === "string" ? config : typeof config === "object" ? Object.entries(config).map(([key, value]) => `${key}: ${value}`).join(",\n ") : "").join(",\n ")},` : "",
1948
- " options: mergeDeep(cliConfigs, {",
1949
- " runtimeConfig: {} as SilgiRuntimeOptions,",
1950
- " routeRules: routeRules as any,",
1951
- ` present: '${silgi.options.preset}',`,
1952
- " ...moduleOptions,",
1953
- " ...buildOptions,",
1954
- " }) as any,",
1955
- " })",
1956
- "",
1957
- ...frameworkContext,
1958
- "",
1959
- ..._data.buildSilgiExtraContent,
1960
- "",
1961
- " return silgi",
1962
- "}",
1963
- ""
1964
- ];
1965
- await silgi.callHook("after:prepare:core.ts", importData);
1966
- importData.unshift(...importsContent);
1967
- return importData;
1734
+ }
1735
+ return data;
1736
+ }
1737
+ // private parsePlugin(ast: any, path: string = '') {
1738
+ // const data = {
1739
+ // export: [],
1740
+ // name: '',
1741
+ // path: '',
1742
+ // } as DataTypePlugin
1743
+ // for (const item of this.parseVariableDeclaration(ast)) {
1744
+ // for (const declaration of item.declaration.declarations) {
1745
+ // if (declaration.init.callee?.name === 'defineSilgiModule') {
1746
+ // if (declaration.init.arguments) {
1747
+ // for (const argument of declaration.init.arguments) {
1748
+ // for (const propertie of argument.properties) {
1749
+ // if (propertie.key.name === 'name')
1750
+ // data.name = propertie.value.value
1751
+ // }
1752
+ // }
1753
+ // }
1754
+ // data.export.push({
1755
+ // name: data.name,
1756
+ // as: camelCase(`${data.name}DefineSilgiModule`),
1757
+ // type: false,
1758
+ // })
1759
+ // }
1760
+ // }
1761
+ // }
1762
+ // for (const item of this.parseTSInterfaceDeclaration(ast)) {
1763
+ // if (!item?.declaration?.extends)
1764
+ // continue
1765
+ // for (const declaration of item?.declaration?.extends) {
1766
+ // if (declaration.expression.name === 'ModuleOptions') {
1767
+ // data.export.push({
1768
+ // name: item.declaration.id.name,
1769
+ // as: camelCase(`${data.name}ModuleOptions`),
1770
+ // type: true,
1771
+ // })
1772
+ // }
1773
+ // // TODO add other plugins
1774
+ // }
1775
+ // }
1776
+ // data.path = path
1777
+ // return data
1778
+ // }
1968
1779
  }
1969
1780
 
1970
- async function prepareFramework(silgi) {
1971
- const importItems = {
1972
- "silgi/types": {
1973
- import: [
1974
- {
1975
- name: "SilgiRuntimeContext",
1976
- type: true,
1977
- key: "SilgiRuntimeContext"
1978
- }
1979
- ],
1980
- from: "silgi/types"
1781
+ async function scanExportFile(silgi) {
1782
+ const filePaths = /* @__PURE__ */ new Set();
1783
+ const scannedPaths = [];
1784
+ const dir = silgi.options.serverDir;
1785
+ const files = (await globby(dir, { cwd: silgi.options.rootDir, ignore: silgi.options.ignore })).sort();
1786
+ if (files.length) {
1787
+ const siblings = await readdir(dirname(dir)).catch(() => []);
1788
+ const directory = basename(dir);
1789
+ if (!siblings.includes(directory)) {
1790
+ const directoryLowerCase = directory.toLowerCase();
1791
+ const caseCorrected = siblings.find((sibling) => sibling.toLowerCase() === directoryLowerCase);
1792
+ if (caseCorrected) {
1793
+ const original = relative(silgi.options.serverDir, dir);
1794
+ const corrected = relative(silgi.options.serverDir, join(dirname(dir), caseCorrected));
1795
+ consola.warn(`Components not scanned from \`~/${corrected}\`. Did you mean to name the directory \`~/${original}\` instead?`);
1796
+ }
1981
1797
  }
1982
- };
1983
- const customImports = [];
1984
- const functions = [];
1985
- await silgi.callHook("prepare:createCoreFramework", {
1986
- importItems,
1987
- customImports,
1988
- functions
1989
- });
1990
- const content = [
1991
- ...functions.map((f) => f.params?.length ? ` await ${f.name}(framework, ${f.params.join(",")})` : ` await ${f.name}(framework)`)
1992
- ];
1993
- return {
1994
- content,
1995
- importItems,
1996
- customImports
1997
- };
1998
- }
1999
- async function createDTSFramework(silgi) {
2000
- const importItems = {
2001
- "silgi/types": {
2002
- import: [
2003
- {
2004
- name: "SilgiRuntimeContext",
2005
- type: true,
2006
- key: "SilgiRuntimeContext"
2007
- }
2008
- ],
2009
- from: "silgi/types"
1798
+ }
1799
+ for (const _file of files) {
1800
+ const filePath = resolve(dir, _file);
1801
+ if (scannedPaths.find((d) => filePath.startsWith(withTrailingSlash(d))) || isIgnored(filePath, silgi)) {
1802
+ continue;
2010
1803
  }
2011
- };
2012
- const customImports = [];
2013
- const customContent = [];
2014
- await silgi.callHook("prepare:createDTSFramework", {
2015
- importItems,
2016
- customImports,
2017
- customContent
2018
- });
2019
- const content = [
2020
- ...Object.entries(importItems).map(([_name, { from, import: imports }]) => {
2021
- const path = isAbsolute(from) ? relativeWithDot(silgi.options.build.typesDir, from) : from;
2022
- if (silgi.options.typescript.removeFileExtension) {
2023
- from = from.replace(/\.(js|ts|mjs|cjs|jsx|tsx)$/, "");
1804
+ if (filePaths.has(filePath)) {
1805
+ continue;
1806
+ }
1807
+ filePaths.add(filePath);
1808
+ if (silgi.options.extensions.includes(extname(filePath))) {
1809
+ const parser = new SchemaParser({
1810
+ debug: false
1811
+ });
1812
+ const readfile = await readFile(filePath, "utf-8");
1813
+ const { exportVariables, parseInterfaceDeclarations } = parser.parseExports(readfile, filePath);
1814
+ const createServices = exportVariables("createService", filePath);
1815
+ if (hasError("Parser", silgi)) {
1816
+ return;
2024
1817
  }
2025
- return `import { ${imports.map(({ type, name }) => type ? `type ${name}` : name).join(", ")} } from '${path}'`;
2026
- }),
2027
- "",
2028
- ...customImports,
2029
- "",
2030
- ...customContent,
2031
- ""
2032
- ];
2033
- return {
2034
- content,
2035
- importItems
2036
- };
2037
- }
2038
-
2039
- async function writeCoreFile(silgi) {
2040
- const data = await prepareFramework(silgi);
2041
- const coreContent = await prepareCoreFile({
2042
- _importItems: data?.importItems ?? {},
2043
- _customImports: data?.customImports ?? []
2044
- }, data?.content ?? [], silgi);
2045
- const configs = await prepareConfigs(silgi);
2046
- const silgiDir = resolve(silgi.options.silgi.serverDir);
2047
- const buildFiles = [];
2048
- buildFiles.push({
2049
- path: join(silgiDir, "core.ts"),
2050
- contents: coreContent.join("\n")
2051
- });
2052
- buildFiles.push({
2053
- path: join(silgiDir, "configs.ts"),
2054
- contents: configs.join("\n")
2055
- });
2056
- for await (const file of buildFiles) {
2057
- if (!silgi.errors.length) {
2058
- await writeFile(
2059
- resolve(silgi.options.build.dir, file.path),
2060
- file.contents
2061
- );
1818
+ const scanTS = [];
1819
+ const schemaTS = [];
1820
+ if (createServices.length > 0) {
1821
+ scanTS.push(...createServices.map(({ exportName, path }) => {
1822
+ const randomString = hash(basename(path) + exportName);
1823
+ const _name = `_v${randomString}`;
1824
+ return { exportName, path, _name, type: "service" };
1825
+ }));
1826
+ }
1827
+ const createSchemas = exportVariables("createSchema", filePath);
1828
+ if (hasError("Parser", silgi)) {
1829
+ return;
1830
+ }
1831
+ if (createSchemas.length > 0) {
1832
+ scanTS.push(...createSchemas.map(({ exportName, path }) => {
1833
+ const randomString = hash(basename(path) + exportName);
1834
+ const _name = `_v${randomString}`;
1835
+ return { exportName, path, _name, type: "schema" };
1836
+ }));
1837
+ }
1838
+ const createShareds = exportVariables("createShared", filePath);
1839
+ if (hasError("Parser", silgi)) {
1840
+ return;
1841
+ }
1842
+ if (createShareds.length > 0) {
1843
+ scanTS.push(...createShareds.map(({ exportName, path }) => {
1844
+ const randomString = hash(basename(path) + exportName);
1845
+ const _name = `_v${randomString}`;
1846
+ return { exportName, path, _name, type: "shared" };
1847
+ }));
1848
+ }
1849
+ const sharedsTypes = parseInterfaceDeclarations("ExtendShared", filePath);
1850
+ if (hasError("Parser", silgi)) {
1851
+ return;
1852
+ }
1853
+ if (sharedsTypes.length > 0) {
1854
+ schemaTS.push(...sharedsTypes.map(({ exportName, path }) => {
1855
+ const randomString = hash(basename(path) + exportName);
1856
+ const _name = `_v${randomString}`;
1857
+ return { exportName, path, _name, type: "shared" };
1858
+ }));
1859
+ }
1860
+ const contextTypes = parseInterfaceDeclarations("ExtendContext", filePath);
1861
+ if (hasError("Parser", silgi)) {
1862
+ return;
1863
+ }
1864
+ if (contextTypes.length > 0) {
1865
+ schemaTS.push(...contextTypes.map(({ exportName, path }) => {
1866
+ const randomString = hash(basename(path) + exportName);
1867
+ const _name = `_v${randomString}`;
1868
+ return { exportName, path, _name, type: "context" };
1869
+ }));
1870
+ }
1871
+ silgi.hook("prepare:scan.ts", (options) => {
1872
+ for (const { exportName, path, _name, type } of scanTS) {
1873
+ if (!path.includes("vfs")) {
1874
+ silgi.options.devServer.watch.push(path);
1875
+ }
1876
+ if (type === "service") {
1877
+ options.services.push(_name);
1878
+ }
1879
+ if (type === "shared") {
1880
+ options.shareds.push(_name);
1881
+ }
1882
+ if (type === "schema") {
1883
+ options.schemas.push(_name);
1884
+ }
1885
+ options.importItems[path] ??= {
1886
+ import: [],
1887
+ from: relativeWithDot(silgi.options.silgi.serverDir, path)
1888
+ };
1889
+ options.importItems[path].import.push({
1890
+ name: `${exportName} as ${_name}`,
1891
+ key: _name
1892
+ });
1893
+ }
1894
+ });
1895
+ silgi.hook("prepare:schema.ts", (options) => {
1896
+ for (const { exportName, path, _name, type } of schemaTS) {
1897
+ if (!path.includes("vfs")) {
1898
+ silgi.options.devServer.watch.push(path);
1899
+ }
1900
+ if (type === "shared") {
1901
+ options.shareds.push({
1902
+ key: _name,
1903
+ value: _name
1904
+ });
1905
+ }
1906
+ if (type === "context") {
1907
+ options.contexts.push({
1908
+ key: _name,
1909
+ value: _name
1910
+ });
1911
+ }
1912
+ options.importItems[path] ??= {
1913
+ import: [],
1914
+ from: relativeWithDot(silgi.options.build.typesDir, path)
1915
+ };
1916
+ options.importItems[path].import.push({
1917
+ name: `${exportName} as ${_name}`,
1918
+ key: _name
1919
+ });
1920
+ }
1921
+ });
2062
1922
  }
2063
1923
  }
2064
1924
  }
2065
1925
 
2066
- async function generateRouterDTS(silgi) {
2067
- const uris = silgi.uris;
2068
- const subPath = "srn";
2069
- const groupedPaths = /* @__PURE__ */ new Map();
2070
- Object.entries(uris || {}).forEach(([key, params]) => {
2071
- const [service, resource, method, action] = key.split("/");
2072
- const basePath = params ? `${subPath}/${service}/${resource}/${action}/${params}` : `${subPath}/${service}/${resource}/${action}`;
2073
- const fullPath = `${subPath}/${service}/${resource}/${action}`;
2074
- if (!groupedPaths.has(basePath)) {
2075
- groupedPaths.set(basePath, /* @__PURE__ */ new Map());
1926
+ function buildUriMap(silgi, currentPath = []) {
1927
+ const uriMap = /* @__PURE__ */ new Map();
1928
+ function traverse(node, path = []) {
1929
+ if (!node || typeof node !== "object")
1930
+ return;
1931
+ if (path.length === 4) {
1932
+ const basePath = path.join("/");
1933
+ let pathString = "";
1934
+ if (node.pathParams) {
1935
+ let paths = null;
1936
+ if (node.pathParams?._def?.typeName !== void 0) {
1937
+ try {
1938
+ const shape = node.pathParams?.shape;
1939
+ paths = shape ? Object.keys(shape) : null;
1940
+ } catch {
1941
+ paths = null;
1942
+ }
1943
+ }
1944
+ if (paths?.length) {
1945
+ pathString = paths.map((p) => `:${p}`).join("/");
1946
+ }
1947
+ }
1948
+ uriMap.set(basePath, pathString);
1949
+ return;
2076
1950
  }
2077
- groupedPaths.get(basePath)?.set(method.toLowerCase(), fullPath);
1951
+ for (const key in node) {
1952
+ if (!["_type", "fields"].includes(key)) {
1953
+ traverse(node[key], [...path, key]);
1954
+ }
1955
+ }
1956
+ }
1957
+ traverse(silgi.schemas, currentPath);
1958
+ silgi.uris = defu(silgi.uris, Object.fromEntries(uriMap));
1959
+ return uriMap;
1960
+ }
1961
+
1962
+ async function readScanFile(silgi) {
1963
+ const path = resolve(silgi.options.silgi.serverDir, "scan.ts");
1964
+ const context = await promises.readFile(path, { encoding: "utf-8" });
1965
+ silgi.unimport = createUnimport(silgi.options.imports || {});
1966
+ await silgi.unimport.init();
1967
+ const injectedResult = await silgi.unimport.injectImports(context, path);
1968
+ if (!injectedResult) {
1969
+ throw new Error("Failed to inject imports");
1970
+ }
1971
+ const jiti = createJiti(silgi.options.rootDir, {
1972
+ fsCache: true,
1973
+ moduleCache: false,
1974
+ debug: silgi.options.debug,
1975
+ alias: silgi.options.alias
2078
1976
  });
2079
- const keys = [
2080
- " keys: {",
2081
- Array.from(groupedPaths.entries()).map(([basePath, methods]) => {
2082
- return ` '/${basePath}': {${Array.from(methods.entries()).map(([method, path]) => `
2083
- ${method}: '/${path}'`).join(",")}
2084
- }`;
2085
- }).join(",\n"),
2086
- " }",
2087
- ""
2088
- ].join("\n");
2089
- const groupedRoutes = Object.entries(uris || {}).reduce((acc, [key, _params]) => {
2090
- const [service, resource, method, action] = key.split("/");
2091
- const routePath = `${subPath}/${service}/${resource}/${action}`;
2092
- if (!acc[routePath]) {
2093
- acc[routePath] = {};
1977
+ try {
1978
+ if (silgi.options.commandType === "prepare") {
1979
+ globalThis._silgi_runtime = silgi.options.runtimeConfig;
1980
+ injectedResult.code = `globalThis._silgi_runtime = ${JSON.stringify(silgi.options.runtimeConfig)};
1981
+ ${injectedResult.code}`;
1982
+ injectedResult.code = injectedResult.code.replace(/runtimeConfig: \{\}/, `runtimeConfig: ${JSON.stringify(silgi.options.runtimeConfig)}`);
2094
1983
  }
2095
- acc[routePath][method] = {
2096
- input: `ExtractInputFromURI<'${key}'>`,
2097
- output: `ExtractOutputFromURI<'${key}'>`,
2098
- queryParams: `ExtractQueryParamsFromURI<'${key}'>`,
2099
- pathParams: `ExtractPathParamsFromURI<'${key}'>`
1984
+ const scanFile = await jiti.evalModule(
1985
+ injectedResult.code,
1986
+ {
1987
+ filename: path,
1988
+ async: true,
1989
+ conditions: silgi.options.conditions
1990
+ },
1991
+ async (data, name) => {
1992
+ return (await silgi.unimport.injectImports(data, name)).code;
1993
+ }
1994
+ );
1995
+ silgi.uris = defu(silgi.uris, scanFile.uris) || {};
1996
+ silgi.schemas = defu(scanFile.schemas, scanFile.uris) || {};
1997
+ silgi.services = defu(scanFile.services, scanFile.uris) || {};
1998
+ silgi.shareds = defu(scanFile.shareds, scanFile.shareds) || {};
1999
+ silgi.modulesURIs = defu(scanFile.modulesURIs, scanFile.modulesURIs) || {};
2000
+ return {
2001
+ context,
2002
+ object: {
2003
+ schemas: scanFile.schemas,
2004
+ uris: scanFile.uris,
2005
+ services: scanFile.services,
2006
+ shareds: scanFile.shareds,
2007
+ modulesURIs: scanFile.modulesURIs
2008
+ },
2009
+ path
2100
2010
  };
2101
- return acc;
2102
- }, {});
2103
- const routerTypes = Object.entries(groupedRoutes).map(([path, methods]) => {
2104
- const methodEntries = Object.entries(methods).map(([method, { input, output, queryParams, pathParams }]) => {
2105
- return ` '${method}': {
2106
- input: ${input},
2107
- output: ${output},
2108
- queryParams: ${queryParams},
2109
- pathParams: ${pathParams}
2110
- }`;
2111
- }).join(",\n");
2112
- return ` '/${path}': {
2113
- ${methodEntries}
2114
- }`;
2115
- });
2116
- const nitro = [
2117
- "declare module 'nitropack/types' {",
2118
- " interface InternalApi extends RouterTypes {}",
2119
- "}"
2120
- ];
2121
- const content = [
2122
- keys.slice(0, -1),
2123
- // son satırdaki boş satırı kaldır
2124
- ...routerTypes
2125
- ].join(",\n");
2126
- const context = [
2127
- "import type { ExtractInputFromURI, ExtractOutputFromURI, ExtractQueryParamsFromURI, ExtractPathParamsFromURI } from 'silgi/types'",
2128
- "",
2129
- "export interface RouterTypes {",
2130
- content,
2131
- "}",
2132
- "",
2133
- "declare module 'silgi/types' {",
2134
- " interface SilgiRouterTypes extends RouterTypes {",
2135
- " }",
2136
- "}",
2137
- "",
2138
- silgi.options.preset === "h3" || silgi.options.preset === "nitro" ? nitro.join("\n") : "",
2139
- "",
2140
- "export {}"
2141
- ];
2142
- return context;
2011
+ } catch (error) {
2012
+ if (silgi.options.debug) {
2013
+ console.error("Failed to read scan.ts file:", error);
2014
+ } else {
2015
+ consola.withTag("silgi").error(error);
2016
+ }
2017
+ return {
2018
+ context,
2019
+ object: {
2020
+ schemas: {},
2021
+ uris: {},
2022
+ services: {},
2023
+ shareds: {},
2024
+ modulesURIs: {}
2025
+ },
2026
+ path
2027
+ };
2028
+ }
2143
2029
  }
2144
2030
 
2145
- async function prepareSchema(silgi) {
2031
+ async function prepareServerFiles(silgi) {
2146
2032
  const importItems = {
2033
+ "silgi": {
2034
+ import: [
2035
+ { name: "createSilgi", key: "createSilgi" },
2036
+ { name: "createShared", key: "createShared" }
2037
+ ],
2038
+ from: "silgi"
2039
+ },
2147
2040
  "silgi/types": {
2148
2041
  import: [
2149
- {
2150
- name: "URIsTypes",
2151
- type: true,
2152
- key: "URIsTypes"
2153
- },
2154
- {
2155
- name: "Namespaces",
2156
- type: true,
2157
- key: "Namespaces"
2158
- },
2159
- {
2160
- name: "SilgiRuntimeContext",
2161
- type: true,
2162
- key: "SilgiRuntimeContext"
2163
- }
2042
+ { name: "SilgiRuntimeOptions", type: true, key: "SilgiRuntimeOptions" },
2043
+ { name: "FrameworkContext", type: true, key: "FrameworkContext" }
2164
2044
  ],
2165
2045
  from: "silgi/types"
2166
2046
  },
2167
- "silgi/scan": {
2168
- import: [{
2169
- key: "modulesURIs",
2170
- name: "modulesURIs",
2171
- type: false
2172
- }],
2173
- from: relativeWithDot(silgi.options.build.typesDir, `${silgi.options.silgi.serverDir}/scan.ts`)
2047
+ "#silgi/vfs": {
2048
+ import: [],
2049
+ from: "./vfs"
2050
+ },
2051
+ "configs.ts": {
2052
+ import: [
2053
+ {
2054
+ name: "cliConfigs",
2055
+ type: false,
2056
+ key: "cliConfigs"
2057
+ }
2058
+ ],
2059
+ from: "./configs.ts"
2174
2060
  }
2175
2061
  };
2176
- const data = {
2177
- importItems,
2178
- customImports: [],
2179
- options: [],
2180
- contexts: [],
2181
- actions: [],
2182
- shareds: [
2183
- {
2184
- key: "modulesURIs",
2185
- value: "{ modulesURIs: typeof modulesURIs }"
2186
- }
2187
- ],
2188
- events: [],
2189
- hooks: [],
2190
- runtimeHooks: [],
2191
- runtimeOptions: [],
2192
- methods: [],
2193
- routeRules: [],
2194
- routeRulesConfig: []
2062
+ const scanned = {
2063
+ uris: {},
2064
+ services: [],
2065
+ shareds: [
2066
+ `createShared({
2067
+ modulesURIs,
2068
+ })`
2069
+ ],
2070
+ schemas: [],
2071
+ modulesURIs: {},
2072
+ customImports: [],
2073
+ importItems
2195
2074
  };
2196
- await silgi.callHook("prepare:schema.ts", data);
2197
- relativeWithDot(silgi.options.build.typesDir, `${silgi.options.silgi.serverDir}/core.ts`);
2198
- const silgiScanTS = relativeWithDot(silgi.options.build.typesDir, `${silgi.options.silgi.serverDir}/scan.ts`);
2199
- let addSilgiContext = false;
2075
+ if (silgi.uris) {
2076
+ defu(scanned.uris, silgi.uris);
2077
+ }
2078
+ if (silgi.modulesURIs) {
2079
+ defu(scanned.modulesURIs, silgi.modulesURIs);
2080
+ }
2081
+ await silgi.callHook("prepare:scan.ts", scanned);
2082
+ if (importItems["#silgi/vfs"].import.length === 0) {
2083
+ delete importItems["#silgi/vfs"];
2084
+ }
2085
+ if (scanned.services.length > 0) {
2086
+ importItems.silgi.import.push({ name: "mergeServices", key: "mergeServices" });
2087
+ }
2088
+ if (scanned.shareds.length > 0) {
2089
+ importItems.silgi.import.push({ name: "mergeShared", key: "mergeShared" });
2090
+ }
2091
+ if (scanned.schemas.length > 0) {
2092
+ importItems.silgi.import.push({ name: "mergeSchemas", key: "mergeSchemas" });
2093
+ }
2094
+ for (const key in importItems) {
2095
+ importItems[key].import = deduplicateImportsByKey(importItems[key].import);
2096
+ }
2200
2097
  const importsContent = [
2201
2098
  ...Object.entries(importItems).map(([_name, { from, import: imports }]) => {
2202
- const path = isAbsolute(from) ? relativeWithDot(silgi.options.build.typesDir, from) : from;
2203
2099
  if (silgi.options.typescript.removeFileExtension) {
2204
2100
  from = from.replace(/\.(js|ts|mjs|cjs|jsx|tsx)$/, "");
2205
2101
  }
2206
- return `import { ${imports.map(({ type, name }) => type ? `type ${name}` : name).join(", ")} } from '${path}'`;
2102
+ return `import { ${imports.map(({ type, name }) => type ? `type ${name}` : name).join(", ")} } from '${from}'`;
2207
2103
  }),
2208
2104
  "",
2209
- ...data.customImports,
2105
+ ...scanned.customImports,
2210
2106
  ""
2211
2107
  ];
2212
2108
  const importData = [
2213
- "interface InferredNamespaces {",
2214
- ...(silgi.options.namespaces || []).map((key) => ` ${key}: string,`),
2215
- "}",
2216
- "",
2217
- `type SchemaExtends = Namespaces<typeof import('${silgiScanTS}')['schemas']>`,
2218
- "",
2219
- `type SilgiURIsMerge = URIsTypes<typeof import('${silgiScanTS}')['uris']>`,
2220
- "",
2221
- `type SilgiModuleContextExtends = ${data.contexts.length ? data.contexts.map(({ value }) => value).join(" & ") : "{}"}`,
2222
- "",
2223
- data.events.length ? `interface SilgiModuleEventsExtends extends ${data.events.map((item) => item.extends ? item.value : "").join(", ")} {
2224
- ${data.events.map((item) => {
2225
- if (item.isSilgiContext) {
2226
- addSilgiContext = true;
2227
- }
2228
- return !item.extends && !addSilgiContext ? ` ${item.key}: ${item.value}` : item.isSilgiContext ? " context: SilgiRuntimeContext" : "";
2229
- }).join(",\n")}
2230
- }` : "interface SilgiModuleEventsExtends {}",
2231
- "",
2232
- `type RuntimeActionExtends = ${data.actions?.length ? data.actions.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
2233
- "",
2234
- `type RuntimeMethodExtends = ${data.methods?.length ? data.methods.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
2235
- "",
2236
- `type RuntimeRouteRulesExtends = ${data.routeRules?.length ? data.routeRules.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
2237
- "",
2238
- `type RuntimeRouteRulesConfigExtends = ${data.routeRulesConfig?.length ? data.routeRulesConfig.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
2239
- "",
2240
- `type SilgiModuleSharedExtends = ${data.shareds.length ? data.shareds.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
2241
- "",
2242
- `type SilgiModuleOptionExtend = ${data.options?.length ? data.options.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
2243
- "",
2244
- `type SilgiRuntimeOptionExtends = ${data.runtimeOptions?.length ? data.runtimeOptions.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
2245
- "",
2246
- silgi.options.typescript.generateRuntimeConfigTypes ? generateTypes(
2247
- await resolveSchema(
2248
- {
2249
- ...Object.fromEntries(
2250
- Object.entries(silgi.options.runtimeConfig).filter(
2251
- ([key]) => !["app", "nitro", "nuxt"].includes(key)
2252
- )
2253
- )
2254
- }
2255
- ),
2256
- {
2257
- interfaceName: "SilgiRuntimeConfigExtends",
2258
- addExport: false,
2259
- addDefaults: false,
2260
- allowExtraKeys: false,
2261
- indentation: 0
2262
- }
2263
- ) : "",
2264
- "",
2265
- generateTypes(
2266
- await resolveSchema(
2267
- {
2268
- ...silgi.options.storages?.reduce((acc, key) => ({ ...acc, [key]: "" }), {}) || {},
2269
- // 'redis': {} -> 'redis': string
2270
- ...Object.entries(silgi.options.storage).map(([key]) => ({
2271
- [key]: ""
2272
- })).reduce((acc, obj) => ({ ...acc, ...obj }), {})
2273
- }
2274
- ),
2275
- {
2276
- interfaceName: "SilgiStorageBaseExtends",
2277
- addExport: false,
2278
- addDefaults: false,
2279
- allowExtraKeys: false,
2280
- indentation: 0
2281
- }
2282
- ),
2283
- "",
2284
- `type ModuleHooksExtend = ${data.hooks?.length ? data.hooks.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
2109
+ `export const uris = ${JSON.stringify(scanned.uris, null, 2)}`,
2285
2110
  "",
2286
- `type SilgiRuntimeHooksExtends = ${data.runtimeHooks?.length ? data.runtimeHooks.map(({ value }) => `${value}`).join(" & ") : "{}"}`,
2111
+ `export const modulesURIs = ${JSON.stringify(scanned.modulesURIs, null, 2)}`,
2287
2112
  "",
2288
- "declare module 'silgi/types' {",
2289
- " interface FrameworkContext extends FrameworkContextExtends {}",
2290
- " interface SilgiSchema extends SchemaExtends {}",
2291
- " interface SilgiNamespaces extends InferredNamespaces {}",
2292
- " interface SilgiStorageBase extends SilgiStorageBaseExtends {}",
2293
- " interface SilgiURIs extends SilgiURIsMerge {}",
2294
- " interface SilgiRuntimeContext extends SilgiModuleContextExtends {}",
2295
- " interface SilgiEvents extends SilgiModuleEventsExtends {}",
2296
- " interface SilgiRuntimeSharedsExtend extends SilgiModuleSharedExtends {}",
2297
- " interface SilgiRuntimeActions extends RuntimeActionExtends {}",
2298
- " interface SilgiModuleOptions extends SilgiModuleOptionExtend {}",
2299
- " interface SilgiRuntimeOptions extends SilgiRuntimeOptionExtends {}",
2300
- " interface SilgiRuntimeHooks extends SilgiRuntimeHooksExtends {}",
2301
- " interface SilgiRuntimeConfig extends SilgiRuntimeConfigExtends {}",
2302
- " interface SilgiHooks extends ModuleHooksExtend {}",
2303
- " interface SilgiRuntimeMethods extends RuntimeMethodExtends {}",
2304
- " interface SilgiRuntimeRouteRules extends RuntimeRouteRulesExtends {}",
2305
- " interface SilgiRuntimeRouteRulesConfig extends RuntimeRouteRulesConfigExtends {}",
2306
- " interface SilgiCommands extends SilgiCommandsExtended {}",
2113
+ scanned.schemas.length > 0 ? "export const schemas = mergeSchemas([" : "export const schemas = {",
2114
+ ...scanned.schemas.map((name) => {
2115
+ return ` ${name},`;
2116
+ }),
2117
+ scanned.schemas.length > 0 ? "])" : "}",
2307
2118
  "",
2308
- "}",
2119
+ scanned.services.length > 0 ? "export const services = mergeServices([" : "export const services = {",
2120
+ ...scanned.services.map((name) => {
2121
+ return ` ${name},`;
2122
+ }),
2123
+ scanned.services.length > 0 ? "])" : "}",
2309
2124
  "",
2310
- "export {}"
2125
+ scanned.shareds.length > 0 ? "export const shareds = mergeShared([" : "export const shareds = {",
2126
+ ...scanned.shareds.map((name) => {
2127
+ return ` ${name},`;
2128
+ }),
2129
+ scanned.shareds.length > 0 ? "])" : "}",
2130
+ ""
2311
2131
  ];
2312
- await silgi.callHook("after:prepare:schema.ts", importData);
2132
+ await silgi.callHook("after:prepare:scan.ts", importData);
2313
2133
  importData.unshift(...importsContent);
2314
2134
  return importData;
2315
2135
  }
2316
-
2317
- async function writeTypesAndFiles(silgi) {
2318
- const routerDTS = await generateRouterDTS(silgi);
2319
- silgi.hook("prepare:types", (opts) => {
2320
- opts.references.push({ path: "./schema.d.ts" });
2321
- opts.references.push({ path: "./silgi-routes.d.ts" });
2322
- opts.references.push({ path: "./framework.d.ts" });
2136
+ function deduplicateImportsByKey(imports) {
2137
+ const seenKeys = /* @__PURE__ */ new Map();
2138
+ return imports.filter((item) => {
2139
+ if (seenKeys.has(item.key)) {
2140
+ return false;
2141
+ }
2142
+ seenKeys.set(item.key, true);
2143
+ return true;
2323
2144
  });
2324
- const schemaContent = await prepareSchema(silgi);
2325
- const frameworkDTS = await createDTSFramework(silgi);
2326
- const { declarations, tsConfig } = await silgiGenerateType(silgi);
2327
- const tsConfigPath = resolve(
2328
- silgi.options.rootDir,
2329
- silgi.options.typescript.tsconfigPath
2330
- );
2331
- const typesDir = resolve(silgi.options.build.typesDir);
2332
- let autoImportedTypes = [];
2333
- let autoImportExports = "";
2334
- if (silgi.unimport) {
2335
- await silgi.unimport.init();
2336
- const allImports = await silgi.unimport.getImports();
2337
- autoImportExports = toExports(allImports).replace(
2338
- /#internal\/nitro/g,
2339
- relative(typesDir, runtimeDir)
2145
+ }
2146
+
2147
+ async function writeScanFiles(silgi) {
2148
+ const data = await prepareServerFiles(silgi);
2149
+ if (!silgi.errors.length) {
2150
+ await writeFile(
2151
+ resolve(silgi.options.silgi.serverDir, "scan.ts"),
2152
+ data.join("\n")
2340
2153
  );
2341
- const resolvedImportPathMap = /* @__PURE__ */ new Map();
2342
- for (const i of allImports.filter((i2) => !i2.type)) {
2343
- if (resolvedImportPathMap.has(i.from)) {
2344
- continue;
2345
- }
2346
- let path = resolveAlias$1(i.from, silgi.options.alias);
2347
- if (isAbsolute(path)) {
2348
- const resolvedPath = await resolvePath(i.from, {
2349
- url: silgi.options.nodeModulesDirs
2350
- }).catch(() => null);
2351
- if (resolvedPath) {
2352
- const { dir, name } = parseNodeModulePath(resolvedPath);
2353
- if (!dir || !name) {
2354
- path = resolvedPath;
2355
- } else {
2356
- const subpath = await lookupNodeModuleSubpath(resolvedPath);
2357
- path = join(dir, name, subpath || "");
2358
- }
2359
- }
2360
- }
2361
- if (existsSync(path) && !await isDirectory(path)) {
2362
- path = path.replace(/\.[a-z]+$/, "");
2363
- }
2364
- if (isAbsolute(path)) {
2365
- path = relative(typesDir, path);
2366
- }
2367
- resolvedImportPathMap.set(i.from, path);
2154
+ }
2155
+ await readScanFile(silgi);
2156
+ buildUriMap(silgi);
2157
+ parseServices(silgi);
2158
+ silgi.hook("prepare:scan.ts", (file) => {
2159
+ file.uris = {
2160
+ ...file.uris,
2161
+ ...silgi.uris
2162
+ };
2163
+ file.modulesURIs = {
2164
+ ...file.modulesURIs,
2165
+ ...silgi.modulesURIs
2166
+ };
2167
+ });
2168
+ }
2169
+
2170
+ async function createStorageCLI(silgi) {
2171
+ const storage = createStorage();
2172
+ const runtime = useSilgiRuntimeConfig();
2173
+ const mounts = klona({
2174
+ ...silgi.options.storage,
2175
+ ...silgi.options.devStorage
2176
+ });
2177
+ for (const [path, opts] of Object.entries(mounts)) {
2178
+ if (opts.driver) {
2179
+ const driver = await import(builtinDrivers[opts.driver] || opts.driver).then((r) => r.default || r);
2180
+ const processedOpts = replaceRuntimeValues({ ...opts }, runtime);
2181
+ storage.mount(path, driver(processedOpts));
2182
+ } else {
2183
+ silgi.logger.warn(`No \`driver\` set for storage mount point "${path}".`);
2184
+ }
2185
+ }
2186
+ return storage;
2187
+ }
2188
+
2189
+ async function installPackages(silgi) {
2190
+ const packages = {
2191
+ dependencies: {
2192
+ "@fastify/deepmerge": peerDependencies["@fastify/deepmerge"],
2193
+ "@silgi/ecosystem": peerDependencies["@silgi/ecosystem"],
2194
+ ...silgi.options.installPackages?.dependencies
2195
+ },
2196
+ devDependencies: {
2197
+ ...silgi.options.installPackages?.devDependencies
2368
2198
  }
2369
- autoImportedTypes = [
2370
- silgi.options.imports && silgi.options.imports.autoImport !== false ? (await silgi.unimport.generateTypeDeclarations({
2371
- exportHelper: false,
2372
- resolvePath: (i) => resolvedImportPathMap.get(i.from) ?? i.from
2373
- })).trim() : ""
2374
- ];
2199
+ };
2200
+ await silgi.callHook("prepare:installPackages", packages);
2201
+ if (silgi.options.preset === "npm-package") {
2202
+ packages.devDependencies = {
2203
+ ...packages.devDependencies,
2204
+ ...packages.dependencies
2205
+ };
2206
+ packages.dependencies = {};
2375
2207
  }
2376
- const buildFiles = [];
2377
- buildFiles.push({
2378
- path: join(typesDir, "silgi-routes.d.ts"),
2379
- contents: routerDTS.join("\n")
2380
- });
2381
- buildFiles.push({
2382
- path: join(typesDir, "silgi-imports.d.ts"),
2383
- contents: [...autoImportedTypes, autoImportExports || "export {}"].join(
2384
- "\n"
2385
- )
2208
+ addTemplate({
2209
+ filename: "install.json",
2210
+ where: ".silgi",
2211
+ write: true,
2212
+ getContents: () => JSON.stringify(packages, null, 2)
2386
2213
  });
2387
- buildFiles.push({
2388
- path: join(typesDir, "schema.d.ts"),
2389
- contents: schemaContent.join("\n")
2214
+ }
2215
+
2216
+ function useCLIRuntimeConfig(silgi) {
2217
+ const safeRuntimeConfig = JSON.parse(JSON.stringify(silgi.options.runtimeConfig));
2218
+ silgi.hook("prepare:configs.ts", (data) => {
2219
+ data.runtimeConfig = safeRuntimeConfig;
2220
+ silgi.options.envOptions = silgi.options.envOptions;
2390
2221
  });
2391
- buildFiles.push({
2392
- path: join(typesDir, "silgi.d.ts"),
2393
- contents: declarations.join("\n")
2222
+ addTemplate({
2223
+ filename: "env.example",
2224
+ write: true,
2225
+ where: ".silgi",
2226
+ getContents() {
2227
+ console.log("Generating env.example");
2228
+ const flattenedConfig = flattenObject(safeRuntimeConfig);
2229
+ const groupedVars = {};
2230
+ Object.entries(flattenedConfig).forEach(([key, { value, originalPath }]) => {
2231
+ const shouldExclude = silgi.options.codegen.env.safeList.some((safePath) => {
2232
+ if (safePath.length !== originalPath.length)
2233
+ return false;
2234
+ return safePath.every((segment, index) => segment.toLowerCase() === originalPath[index].toLowerCase());
2235
+ });
2236
+ if (shouldExclude)
2237
+ return;
2238
+ if (originalPath.length > 0) {
2239
+ const firstKey = originalPath[0];
2240
+ const categoryName = extractCategoryFromCamel(firstKey);
2241
+ if (!groupedVars[categoryName]) {
2242
+ groupedVars[categoryName] = {};
2243
+ }
2244
+ groupedVars[categoryName][key] = value;
2245
+ } else {
2246
+ if (!groupedVars.OTHER) {
2247
+ groupedVars.OTHER = {};
2248
+ }
2249
+ groupedVars.OTHER[key] = value;
2250
+ }
2251
+ });
2252
+ return Object.entries(groupedVars).map(([category, vars]) => {
2253
+ const varsContent = Object.entries(vars).map(([key, value]) => `${key}=${value}`).join("\n");
2254
+ return `# ${category}
2255
+ ${varsContent}`;
2256
+ }).join("\n\n");
2257
+ }
2394
2258
  });
2395
- buildFiles.push({
2396
- path: tsConfigPath,
2397
- contents: JSON.stringify(tsConfig, null, 2)
2259
+ const _sharedRuntimeConfig = initRuntimeConfig(silgi.options.envOptions, silgi.options.runtimeConfig);
2260
+ silgi.options.runtimeConfig = safeRuntimeConfig;
2261
+ return _sharedRuntimeConfig;
2262
+ }
2263
+ function extractCategoryFromCamel(str) {
2264
+ if (/^[a-z]+[A-Z]/.test(str)) {
2265
+ const matches = str.match(/^([a-z]+)([A-Z][a-z0-9]*)?/);
2266
+ if (matches && matches[2]) {
2267
+ return `${matches[1].toUpperCase()}_${matches[2].toUpperCase()}`;
2268
+ }
2269
+ }
2270
+ return str.toUpperCase();
2271
+ }
2272
+ function flattenObject(obj, prefix = "SILGI", originalPath = []) {
2273
+ return Object.entries(obj).reduce((acc, [key, value]) => {
2274
+ const formattedKey = snakeCase(key).toUpperCase();
2275
+ const newKey = `${prefix}_${formattedKey}`;
2276
+ const newPath = [...originalPath, key];
2277
+ if (typeof value === "object" && value !== null) {
2278
+ Object.assign(acc, flattenObject(value, newKey, newPath));
2279
+ } else {
2280
+ acc[newKey] = { value, originalPath: newPath };
2281
+ }
2282
+ return acc;
2283
+ }, {});
2284
+ }
2285
+
2286
+ const GLOB_SCAN_PATTERN = "**/*.{js,mjs,cjs,ts,mts,cts,tsx,jsx}";
2287
+ async function scanAndSyncOptions(silgi) {
2288
+ const scannedModules = await scanModules(silgi);
2289
+ silgi.options.modules = silgi.options.modules || [];
2290
+ for (const modPath of scannedModules) {
2291
+ if (!silgi.options.modules.includes(modPath)) {
2292
+ silgi.options.modules.push(modPath);
2293
+ }
2294
+ }
2295
+ }
2296
+ async function scanModules(silgi) {
2297
+ const files = await scanFiles(silgi, "silgi/modules");
2298
+ return files.map((f) => f.fullPath);
2299
+ }
2300
+ async function scanFiles(silgi, name) {
2301
+ const files = await Promise.all(
2302
+ silgi.options.scanDirs.map((dir) => scanDir(silgi, dir, name))
2303
+ ).then((r) => r.flat());
2304
+ return files;
2305
+ }
2306
+ async function scanDir(silgi, dir, name) {
2307
+ const fileNames = await globby(join(name, GLOB_SCAN_PATTERN), {
2308
+ cwd: dir,
2309
+ dot: true,
2310
+ ignore: silgi.options.ignore,
2311
+ absolute: true
2398
2312
  });
2399
- buildFiles.push({
2400
- path: join(typesDir, "framework.d.ts"),
2401
- contents: frameworkDTS.content.join("\n")
2313
+ return fileNames.map((fullPath) => {
2314
+ return {
2315
+ fullPath,
2316
+ path: relative(join(dir, name), fullPath)
2317
+ };
2318
+ }).sort((a, b) => a.path.localeCompare(b.path));
2319
+ }
2320
+
2321
+ async function createSilgiCLI(config = {}, opts = {}) {
2322
+ const options = await loadOptions(config, opts);
2323
+ const hooks = createHooks();
2324
+ const silgi = {
2325
+ modulesURIs: {},
2326
+ scannedURIs: /* @__PURE__ */ new Map(),
2327
+ services: {},
2328
+ uris: {},
2329
+ shareds: {},
2330
+ schemas: {},
2331
+ unimport: void 0,
2332
+ options,
2333
+ hooks,
2334
+ errors: [],
2335
+ commands: {},
2336
+ _requiredModules: {},
2337
+ logger: consola.withTag("silgi"),
2338
+ close: () => silgi.hooks.callHook("close", silgi),
2339
+ storage: void 0,
2340
+ scanModules: [],
2341
+ templates: [],
2342
+ callHook: hooks.callHook,
2343
+ addHooks: hooks.addHooks,
2344
+ hook: hooks.hook,
2345
+ async updateConfig(_config) {
2346
+ },
2347
+ routeRules: void 0
2348
+ };
2349
+ await prepareEnv(options);
2350
+ const routeRules = createRouteRules();
2351
+ routeRules.importRules(options.routeRules ?? {});
2352
+ silgi.routeRules = routeRules;
2353
+ if (silgiCLICtx.tryUse()) {
2354
+ silgiCLICtx.unset();
2355
+ silgiCLICtx.set(silgi);
2356
+ } else {
2357
+ silgiCLICtx.set(silgi);
2358
+ silgi.hook("close", () => silgiCLICtx.unset());
2359
+ }
2360
+ if (silgi.options.debug) {
2361
+ createDebugger(silgi.hooks, { tag: "silgi" });
2362
+ silgi.options.plugins.push({
2363
+ path: join(runtimeDir, "internal/debug"),
2364
+ packageImport: "silgi/runtime/internal/debug"
2365
+ });
2366
+ }
2367
+ for (const framework of frameworkSetup) {
2368
+ await framework(silgi);
2369
+ }
2370
+ await scanAndSyncOptions(silgi);
2371
+ await scanModules$1(silgi);
2372
+ await scanExportFile(silgi);
2373
+ await installModules(silgi, true);
2374
+ useCLIRuntimeConfig(silgi);
2375
+ await writeScanFiles(silgi);
2376
+ silgi.storage = await createStorageCLI(silgi);
2377
+ silgi.hooks.hook("close", async () => {
2378
+ await silgi.storage.dispose();
2402
2379
  });
2403
- for await (const file of buildFiles) {
2404
- if (!silgi.errors.length) {
2405
- await writeFile(
2406
- resolve(silgi.options.build.dir, file.path),
2407
- file.contents
2408
- );
2409
- }
2380
+ if (silgi.options.logLevel !== void 0) {
2381
+ silgi.logger.level = silgi.options.logLevel;
2382
+ }
2383
+ silgi.hooks.addHooks(silgi.options.hooks);
2384
+ await installModules(silgi);
2385
+ await silgi.hooks.callHook("scanFiles:done", silgi);
2386
+ await commands(silgi);
2387
+ await installPackages(silgi);
2388
+ await generateApp(silgi);
2389
+ if (silgi.options.imports) {
2390
+ silgi.options.imports.dirs ??= [];
2391
+ silgi.options.imports.dirs = silgi.options.imports.dirs.map((dir) => {
2392
+ if (typeof dir === "string") {
2393
+ if (dir.startsWith("!")) {
2394
+ return `!${resolveSilgiPath(dir.slice(1), options, silgi.options.rootDir)}`;
2395
+ }
2396
+ return resolveSilgiPath(dir, options, silgi.options.rootDir);
2397
+ }
2398
+ return dir;
2399
+ });
2400
+ silgi.options.imports.presets.push({
2401
+ from: "silgi/types",
2402
+ imports: autoImportTypes.map((type) => type),
2403
+ type: true
2404
+ });
2405
+ silgi.options.imports.presets.push({
2406
+ from: "silgi/types",
2407
+ imports: autoImportTypes.map((type) => type),
2408
+ type: true
2409
+ });
2410
+ silgi.options.imports.presets.push({
2411
+ from: "silgi/runtime/internal/ofetch",
2412
+ imports: ["createSilgiFetch", "silgi$fetch"]
2413
+ });
2414
+ silgi.unimport = createUnimport(silgi.options.imports);
2415
+ await silgi.unimport.init();
2410
2416
  }
2417
+ await registerModuleExportScan(silgi);
2418
+ await writeScanFiles(silgi);
2419
+ return silgi;
2411
2420
  }
2412
2421
 
2413
- export { prepare as a, writeTypesAndFiles as b, createSilgiCLI as c, prepareEnv as d, generateApp as e, generateApiFul as g, prepareBuild as p, writeCoreFile as w };
2422
+ export { build as b, createSilgiCLI as c, prepareEnv as p };