@vobs/cli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,576 @@
1
+ /** @license MIT
2
+ * Copyright (c) 2026 vobsjs
3
+ * @vobs/cli
4
+ */
5
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
6
+ import * as path from 'node:path';
7
+ import * as ts from 'typescript';
8
+ import { analyzeView, checkViewProject } from '@vobs/compiler-dom';
9
+ const ignoredDirectories = new Set(['.git', '.cache', '.tmp', 'coverage', 'dist', 'node_modules']);
10
+ export function inspectVobsProject(options) {
11
+ const tsconfigPath = path.resolve(options.tsconfigPath);
12
+ const projectRoot = path.dirname(tsconfigPath);
13
+ const workspaceRoot = findWorkspaceRoot(projectRoot);
14
+ const rootPackage = readJsonFile(path.join(workspaceRoot, 'package.json'));
15
+ const projectPackage = readJsonFile(path.join(projectRoot, 'package.json'));
16
+ const config = readTsConfig(tsconfigPath, projectRoot);
17
+ const files = collectProjectFiles(projectRoot);
18
+ const routeRoot = path.join(projectRoot, 'src', 'pages');
19
+ const routes = collectRoutes(routeRoot, projectRoot);
20
+ const checkResult = checkViewProject({ tsconfigPath });
21
+ return {
22
+ schemaVersion: '0.1',
23
+ generatedBy: 'vobs inspect',
24
+ workspace: {
25
+ root: manifestPath(workspaceRoot, workspaceRoot),
26
+ ...(typeof rootPackage?.packageManager === 'string'
27
+ ? { packageManager: rootPackage.packageManager }
28
+ : {}),
29
+ },
30
+ project: {
31
+ root: manifestPath(projectRoot, workspaceRoot),
32
+ ...(typeof projectPackage?.name === 'string' ? { name: projectPackage.name } : {}),
33
+ ...(typeof projectPackage?.name === 'string' ? { packageName: projectPackage.name } : {}),
34
+ ...(typeof projectPackage?.version === 'string'
35
+ ? { packageVersion: projectPackage.version }
36
+ : {}),
37
+ },
38
+ tsconfig: {
39
+ path: manifestPath(tsconfigPath, workspaceRoot),
40
+ rootNames: config.fileNames.map((fileName) => manifestPath(fileName, workspaceRoot)),
41
+ references: (config.projectReferences ?? []).map((reference) => manifestPath(reference.path, workspaceRoot)),
42
+ },
43
+ packages: collectPackages(workspaceRoot),
44
+ files: {
45
+ views: files.views.map((view) => manifestPath(view.file, workspaceRoot)),
46
+ routes: routes.map((route) => toManifestRoute(route, workspaceRoot)),
47
+ components: files.components.map((component) => toManifestComponent(component, workspaceRoot)),
48
+ globalComponents: checkResult.globalComponentTags ?? [],
49
+ viewContracts: files.views.map((view) => toManifestView(view, workspaceRoot)),
50
+ authorization: files.authorization.map((file) => toManifestPermission(file, workspaceRoot)),
51
+ },
52
+ diagnostics: checkResult.diagnostics.map((diagnostic) => toManifestDiagnostic(diagnostic, workspaceRoot)),
53
+ };
54
+ }
55
+ export function writeInspectOutput(manifest, outputPath) {
56
+ const workingDirectory = path.resolve(process.cwd());
57
+ const target = path.resolve(workingDirectory, outputPath);
58
+ const relativeTarget = path.relative(workingDirectory, target);
59
+ if (relativeTarget === '' ||
60
+ relativeTarget.startsWith(`..${path.sep}`) ||
61
+ path.isAbsolute(relativeTarget)) {
62
+ throw new Error(`Refusing to write inspect output outside the current directory: ${target}`);
63
+ }
64
+ const fileName = path.basename(target).toLowerCase();
65
+ if (fileName === '' || /(?:^|[._-])(?:env|secret|token|credential|password)/u.test(fileName)) {
66
+ throw new Error(`Refusing to write inspect output to a sensitive filename: ${target}`);
67
+ }
68
+ mkdirSync(path.dirname(target), { recursive: true });
69
+ writeFileSync(target, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
70
+ }
71
+ function collectProjectFiles(root) {
72
+ const views = [];
73
+ const components = [];
74
+ const authorization = [];
75
+ for (const fileName of walkFiles(root)) {
76
+ if (fileName.endsWith('.html')) {
77
+ views.push({ file: fileName, ...analyzeView(readFileSync(fileName, 'utf8'), fileName) });
78
+ }
79
+ if (!isTypeScriptFile(fileName))
80
+ continue;
81
+ const source = readFileSync(fileName, 'utf8');
82
+ const component = readComponentFile(fileName, source);
83
+ if (component !== undefined)
84
+ components.push(component);
85
+ const permission = readPermissionFile(fileName, source);
86
+ if (permission !== undefined)
87
+ authorization.push(permission);
88
+ }
89
+ views.sort((left, right) => left.file.localeCompare(right.file));
90
+ components.sort((left, right) => left.file.localeCompare(right.file));
91
+ authorization.sort((left, right) => left.file.localeCompare(right.file));
92
+ return { views, components, authorization };
93
+ }
94
+ function readComponentFile(fileName, source) {
95
+ const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true);
96
+ const definition = findDefinitionCall(sourceFile);
97
+ if (definition === undefined)
98
+ return undefined;
99
+ const viewRequest = readHtmlImport(sourceFile);
100
+ const view = viewRequest === undefined ? undefined : path.resolve(path.dirname(fileName), viewRequest);
101
+ return {
102
+ kind: definition.kind,
103
+ file: fileName,
104
+ ...(view === undefined ? {} : { view }),
105
+ exports: readExportNames(source),
106
+ props: readStaticArrayProperty(definition.options, 'props'),
107
+ emits: readStaticArrayProperty(definition.options, 'emits'),
108
+ exposed: readSetupExposedNames(definition.options, sourceFile),
109
+ registeredComponents: readStaticObjectKeys(definition.options, 'components'),
110
+ };
111
+ }
112
+ function findDefinitionCall(sourceFile) {
113
+ let result;
114
+ const visit = (node) => {
115
+ if (result === undefined &&
116
+ ts.isCallExpression(node) &&
117
+ ts.isIdentifier(node.expression) &&
118
+ (node.expression.text === 'definePage' || node.expression.text === 'defineComponent')) {
119
+ const options = node.arguments[0];
120
+ if (options !== undefined && ts.isObjectLiteralExpression(options)) {
121
+ result = {
122
+ kind: node.expression.text === 'definePage' ? 'page' : 'component',
123
+ options,
124
+ };
125
+ return;
126
+ }
127
+ }
128
+ ts.forEachChild(node, visit);
129
+ };
130
+ visit(sourceFile);
131
+ return result;
132
+ }
133
+ function readHtmlImport(sourceFile) {
134
+ for (const statement of sourceFile.statements) {
135
+ if (ts.isImportDeclaration(statement) &&
136
+ ts.isStringLiteral(statement.moduleSpecifier) &&
137
+ statement.moduleSpecifier.text.endsWith('.html')) {
138
+ return statement.moduleSpecifier.text;
139
+ }
140
+ }
141
+ return undefined;
142
+ }
143
+ function readStaticArrayProperty(options, name) {
144
+ const expression = readPropertyExpression(options, name);
145
+ const array = expression === undefined ? undefined : unwrapExpression(expression);
146
+ if (array === undefined || !ts.isArrayLiteralExpression(array))
147
+ return [];
148
+ return array.elements
149
+ .map((element) => (ts.isStringLiteral(element) ? element.text : undefined))
150
+ .filter((value) => value !== undefined)
151
+ .sort();
152
+ }
153
+ function readStaticObjectKeys(options, name) {
154
+ const expression = readPropertyExpression(options, name);
155
+ const object = expression === undefined ? undefined : unwrapExpression(expression);
156
+ if (object === undefined || !ts.isObjectLiteralExpression(object))
157
+ return [];
158
+ return object.properties
159
+ .map(readObjectElementName)
160
+ .filter((value) => value !== undefined)
161
+ .sort();
162
+ }
163
+ function readSetupExposedNames(options, sourceFile) {
164
+ const setup = findSetupFunction(options, sourceFile);
165
+ const body = setup !== undefined && 'body' in setup ? setup.body : undefined;
166
+ if (body === undefined)
167
+ return [];
168
+ const names = new Set();
169
+ if (!ts.isBlock(body)) {
170
+ const expression = unwrapExpression(body);
171
+ if (ts.isObjectLiteralExpression(expression))
172
+ collectObjectKeys(expression, names);
173
+ }
174
+ const visit = (node) => {
175
+ if (node !== body && ts.isFunctionLike(node))
176
+ return;
177
+ if (ts.isReturnStatement(node) && node.expression !== undefined) {
178
+ const expression = unwrapExpression(node.expression);
179
+ if (ts.isObjectLiteralExpression(expression))
180
+ collectObjectKeys(expression, names);
181
+ }
182
+ if (ts.isCallExpression(node) && isExposeCall(node.expression)) {
183
+ const argument = node.arguments[0];
184
+ if (argument !== undefined) {
185
+ const expression = unwrapExpression(argument);
186
+ if (ts.isObjectLiteralExpression(expression))
187
+ collectObjectKeys(expression, names);
188
+ }
189
+ }
190
+ ts.forEachChild(node, visit);
191
+ };
192
+ if (ts.isBlock(body))
193
+ visit(body);
194
+ return [...names].sort();
195
+ }
196
+ function findSetupFunction(options, sourceFile) {
197
+ for (const property of options.properties) {
198
+ if (readPropertyName(property.name) !== 'setup')
199
+ continue;
200
+ if (ts.isMethodDeclaration(property))
201
+ return property;
202
+ if (ts.isShorthandPropertyAssignment(property)) {
203
+ return findTopLevelFunction(sourceFile, property.name.text);
204
+ }
205
+ if (ts.isPropertyAssignment(property)) {
206
+ const initializer = unwrapExpression(property.initializer);
207
+ if (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)) {
208
+ return initializer;
209
+ }
210
+ if (ts.isIdentifier(initializer))
211
+ return findTopLevelFunction(sourceFile, initializer.text);
212
+ }
213
+ }
214
+ return undefined;
215
+ }
216
+ function findTopLevelFunction(sourceFile, name) {
217
+ for (const statement of sourceFile.statements) {
218
+ if (ts.isFunctionDeclaration(statement) && statement.name?.text === name)
219
+ return statement;
220
+ if (!ts.isVariableStatement(statement))
221
+ continue;
222
+ for (const declaration of statement.declarationList.declarations) {
223
+ if (!ts.isIdentifier(declaration.name) || declaration.name.text !== name)
224
+ continue;
225
+ const initializer = declaration.initializer === undefined
226
+ ? undefined
227
+ : unwrapExpression(declaration.initializer);
228
+ if (initializer !== undefined &&
229
+ (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer))) {
230
+ return initializer;
231
+ }
232
+ }
233
+ }
234
+ return undefined;
235
+ }
236
+ function isExposeCall(expression) {
237
+ return ((ts.isIdentifier(expression) && expression.text === 'expose') ||
238
+ (ts.isPropertyAccessExpression(expression) && expression.name.text === 'expose'));
239
+ }
240
+ function collectObjectKeys(object, names) {
241
+ for (const property of object.properties) {
242
+ const name = readObjectElementName(property);
243
+ if (name !== undefined)
244
+ names.add(name);
245
+ }
246
+ }
247
+ function readPropertyExpression(object, name) {
248
+ for (const property of object.properties) {
249
+ if (readPropertyName(property.name) !== name)
250
+ continue;
251
+ if (ts.isPropertyAssignment(property))
252
+ return property.initializer;
253
+ }
254
+ return undefined;
255
+ }
256
+ function readPropertyName(name) {
257
+ if (name === undefined)
258
+ return undefined;
259
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
260
+ return name.text;
261
+ }
262
+ return undefined;
263
+ }
264
+ function readObjectElementName(property) {
265
+ return ts.isSpreadAssignment(property) ? undefined : readPropertyName(property.name);
266
+ }
267
+ function unwrapExpression(expression) {
268
+ let current = expression;
269
+ while (ts.isParenthesizedExpression(current) ||
270
+ ts.isAsExpression(current) ||
271
+ ts.isSatisfiesExpression(current) ||
272
+ ts.isNonNullExpression(current)) {
273
+ current = current.expression;
274
+ }
275
+ return current;
276
+ }
277
+ function readPermissionFile(fileName, source) {
278
+ const exportedSymbols = readExportNames(source);
279
+ const kinds = [];
280
+ const authorizerOptions = readAuthorizerOptionNames(fileName, source);
281
+ if (authorizerOptions !== undefined ||
282
+ exportedSymbols.some((name) => /authorization|authorizer|permission/iu.test(name))) {
283
+ kinds.push('authorization');
284
+ }
285
+ if (authorizerOptions?.has('roles') === true ||
286
+ exportedSymbols.some((name) => /^roles?$/iu.test(name) || /^AuthorizationRole/u.test(name))) {
287
+ kinds.push('role');
288
+ }
289
+ if (authorizerOptions?.has('policies') === true ||
290
+ exportedSymbols.some((name) => /^polic(?:y|ies)$/iu.test(name) || /^AuthorizationPolicy/u.test(name))) {
291
+ kinds.push('policy');
292
+ }
293
+ if (kinds.length === 0)
294
+ return undefined;
295
+ return {
296
+ file: fileName,
297
+ kinds,
298
+ symbols: exportedSymbols.filter((name) => /authorization|authorizer|permission|roles?|polic(?:y|ies)/iu.test(name)),
299
+ };
300
+ }
301
+ function readAuthorizerOptionNames(fileName, source) {
302
+ const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true);
303
+ let options;
304
+ const visit = (node) => {
305
+ if (options === undefined &&
306
+ ts.isCallExpression(node) &&
307
+ ts.isIdentifier(node.expression) &&
308
+ node.expression.text === 'createAuthorizer') {
309
+ options = new Set();
310
+ const argument = node.arguments[0];
311
+ if (argument !== undefined && ts.isObjectLiteralExpression(argument)) {
312
+ for (const property of argument.properties) {
313
+ if ((ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property)) &&
314
+ ts.isIdentifier(property.name)) {
315
+ options.add(property.name.text);
316
+ }
317
+ }
318
+ }
319
+ }
320
+ ts.forEachChild(node, visit);
321
+ };
322
+ visit(sourceFile);
323
+ return options;
324
+ }
325
+ function readExportNames(source) {
326
+ const names = new Set();
327
+ if (/\bexport\s+default\b/u.test(source))
328
+ names.add('default');
329
+ for (const match of source.matchAll(/\bexport\s+(?:const|let|var|function|class|interface|type)\s+([\w$]+)/gu)) {
330
+ const name = match[1];
331
+ if (name !== undefined)
332
+ names.add(name);
333
+ }
334
+ for (const match of source.matchAll(/\bexport\s*\{([^}]*)\}/gu)) {
335
+ for (const entry of (match[1] ?? '').split(',')) {
336
+ const exported = /\bas\s+([\w$]+)/u.exec(entry)?.[1] ?? entry.trim();
337
+ if (exported !== '')
338
+ names.add(exported);
339
+ }
340
+ }
341
+ return [...names].sort();
342
+ }
343
+ function collectRoutes(root, projectRoot) {
344
+ if (!existsSync(root) || !statSync(root).isDirectory())
345
+ return [];
346
+ const routes = [];
347
+ walkRouteDirectory(root, root, [], undefined, routes);
348
+ const notFound = path.join(root, '_404.ts');
349
+ if (existsSync(notFound)) {
350
+ routes.push({
351
+ kind: 'not-found',
352
+ path: '*',
353
+ file: notFound,
354
+ layouts: [],
355
+ hasGuard: false,
356
+ hasLoader: false,
357
+ });
358
+ }
359
+ const error = path.join(root, '_error.ts');
360
+ if (existsSync(error)) {
361
+ routes.push({
362
+ kind: 'error',
363
+ path: 'error',
364
+ file: error,
365
+ layouts: [],
366
+ hasGuard: false,
367
+ hasLoader: false,
368
+ });
369
+ }
370
+ return routes
371
+ .filter((route) => route.file.startsWith(projectRoot))
372
+ .sort((left, right) => left.path.localeCompare(right.path) || left.file.localeCompare(right.file));
373
+ }
374
+ function walkRouteDirectory(directory, pagesRoot, parentLayouts, parentError, routes) {
375
+ const layout = path.join(directory, '_layout.ts');
376
+ const layouts = existsSync(layout) ? [...parentLayouts, layout] : parentLayouts;
377
+ const localError = path.join(directory, '_error.ts');
378
+ const error = existsSync(localError) ? localError : parentError;
379
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
380
+ const entryPath = path.join(directory, entry.name);
381
+ if (entry.isDirectory()) {
382
+ walkRouteDirectory(entryPath, pagesRoot, layouts, error, routes);
383
+ continue;
384
+ }
385
+ if (!entry.name.endsWith('.ts') || entry.name.endsWith('.d.ts') || entry.name.startsWith('_')) {
386
+ continue;
387
+ }
388
+ const source = readFileSync(entryPath, 'utf8');
389
+ routes.push({
390
+ kind: 'page',
391
+ path: routePathForFile(entryPath, pagesRoot),
392
+ file: entryPath,
393
+ layouts,
394
+ ...(error === undefined ? {} : { error }),
395
+ hasGuard: hasExportedRouteMember(source, 'guard'),
396
+ hasLoader: hasExportedRouteMember(source, 'loader'),
397
+ });
398
+ }
399
+ }
400
+ function routePathForFile(fileName, pagesRoot) {
401
+ const relative = path.relative(pagesRoot, fileName).replaceAll(path.sep, '/');
402
+ const segments = relative
403
+ .slice(0, -'.ts'.length)
404
+ .split('/')
405
+ .flatMap((segment) => {
406
+ if (segment === 'index')
407
+ return [];
408
+ if (segment.startsWith('(') && segment.endsWith(')'))
409
+ return [];
410
+ if (segment.startsWith('[...') && segment.endsWith(']'))
411
+ return [`*${segment.slice(4, -1)}`];
412
+ if (segment.startsWith('[') && segment.endsWith(']'))
413
+ return [`:${segment.slice(1, -1)}`];
414
+ return [segment];
415
+ });
416
+ return segments.length === 0 ? '/' : `/${segments.join('/')}`;
417
+ }
418
+ function hasExportedRouteMember(source, name) {
419
+ return new RegExp(`^\\s*export\\s+(?:(?:async\\s+)?function|const|let|var)\\s+${name}\\b`, 'mu').test(source);
420
+ }
421
+ function walkFiles(root) {
422
+ const files = [];
423
+ walk(root, files);
424
+ return files;
425
+ }
426
+ function walk(directory, files) {
427
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
428
+ if (entry.isDirectory()) {
429
+ if (!ignoredDirectories.has(entry.name) && entry.name !== '__tests__') {
430
+ walk(path.join(directory, entry.name), files);
431
+ }
432
+ continue;
433
+ }
434
+ const fileName = path.join(directory, entry.name);
435
+ if (!entry.name.endsWith('.test.ts') &&
436
+ !entry.name.endsWith('.spec.ts') &&
437
+ (entry.name.endsWith('.html') || isTypeScriptFile(fileName))) {
438
+ files.push(fileName);
439
+ }
440
+ }
441
+ }
442
+ function isTypeScriptFile(fileName) {
443
+ return (fileName.endsWith('.ts') || fileName.endsWith('.tsx')) && !fileName.endsWith('.d.ts');
444
+ }
445
+ function collectPackages(workspaceRoot) {
446
+ const roots = [];
447
+ for (const groupName of ['packages', 'plugins']) {
448
+ const group = path.join(workspaceRoot, groupName);
449
+ if (!existsSync(group) || !statSync(group).isDirectory())
450
+ continue;
451
+ for (const entry of readdirSync(group, { withFileTypes: true })) {
452
+ if (!entry.isDirectory())
453
+ continue;
454
+ const direct = path.join(group, entry.name);
455
+ if (existsSync(path.join(direct, 'package.json'))) {
456
+ roots.push(direct);
457
+ continue;
458
+ }
459
+ for (const nested of readdirSync(direct, { withFileTypes: true })) {
460
+ if (nested.isDirectory() && existsSync(path.join(direct, nested.name, 'package.json'))) {
461
+ roots.push(path.join(direct, nested.name));
462
+ }
463
+ }
464
+ }
465
+ }
466
+ return roots
467
+ .map((root) => {
468
+ const packageJson = readJsonFile(path.join(root, 'package.json'));
469
+ return {
470
+ name: typeof packageJson?.name === 'string'
471
+ ? packageJson.name
472
+ : manifestPath(root, workspaceRoot),
473
+ ...(typeof packageJson?.version === 'string' ? { version: packageJson.version } : {}),
474
+ path: manifestPath(root, workspaceRoot),
475
+ ...(typeof packageJson?.private === 'boolean' ? { private: packageJson.private } : {}),
476
+ };
477
+ })
478
+ .sort((left, right) => left.path.localeCompare(right.path));
479
+ }
480
+ function readTsConfig(tsconfigPath, projectRoot) {
481
+ const read = ts.readConfigFile(tsconfigPath, (fileName) => ts.sys.readFile(fileName));
482
+ if (read.error !== undefined)
483
+ throw new Error(formatTsDiagnostic(read.error));
484
+ return ts.parseJsonConfigFileContent(read.config, ts.sys, projectRoot, undefined, tsconfigPath);
485
+ }
486
+ function readJsonFile(fileName) {
487
+ try {
488
+ const value = JSON.parse(readFileSync(fileName, 'utf8'));
489
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
490
+ ? value
491
+ : undefined;
492
+ }
493
+ catch {
494
+ return undefined;
495
+ }
496
+ }
497
+ function findWorkspaceRoot(start) {
498
+ let current = path.resolve(start);
499
+ while (true) {
500
+ if (existsSync(path.join(current, 'pnpm-workspace.yaml')))
501
+ return current;
502
+ const parent = path.dirname(current);
503
+ if (parent === current)
504
+ return path.resolve(start);
505
+ current = parent;
506
+ }
507
+ }
508
+ function manifestPath(fileName, workspaceRoot) {
509
+ const relative = path.relative(workspaceRoot, fileName).replaceAll(path.sep, '/');
510
+ return relative === '' ? '.' : relative;
511
+ }
512
+ function toManifestRoute(route, workspaceRoot) {
513
+ return {
514
+ kind: route.kind,
515
+ path: route.path,
516
+ file: manifestPath(route.file, workspaceRoot),
517
+ layouts: route.layouts.map((fileName) => manifestPath(fileName, workspaceRoot)),
518
+ ...(route.error === undefined ? {} : { error: manifestPath(route.error, workspaceRoot) }),
519
+ hasGuard: route.hasGuard,
520
+ hasLoader: route.hasLoader,
521
+ };
522
+ }
523
+ function toManifestComponent(component, workspaceRoot) {
524
+ return {
525
+ kind: component.kind,
526
+ file: manifestPath(component.file, workspaceRoot),
527
+ ...(component.view === undefined ? {} : { view: manifestPath(component.view, workspaceRoot) }),
528
+ exports: component.exports,
529
+ props: component.props,
530
+ emits: component.emits,
531
+ exposed: component.exposed,
532
+ registeredComponents: component.registeredComponents,
533
+ };
534
+ }
535
+ function toManifestView(view, workspaceRoot) {
536
+ return {
537
+ file: manifestPath(view.file, workspaceRoot),
538
+ bindings: view.bindings,
539
+ events: view.events,
540
+ directives: view.directives,
541
+ components: view.components,
542
+ includes: view.includes
543
+ .map((request) => path.resolve(path.dirname(view.file), request))
544
+ .filter((fileName) => isPathInside(workspaceRoot, fileName))
545
+ .map((fileName) => manifestPath(fileName, workspaceRoot)),
546
+ refs: view.refs,
547
+ slots: view.slots,
548
+ };
549
+ }
550
+ function toManifestPermission(file, workspaceRoot) {
551
+ return {
552
+ file: manifestPath(file.file, workspaceRoot),
553
+ kinds: file.kinds,
554
+ symbols: file.symbols,
555
+ };
556
+ }
557
+ function toManifestDiagnostic(diagnostic, workspaceRoot) {
558
+ return {
559
+ code: diagnostic.code,
560
+ message: diagnostic.message,
561
+ severity: diagnostic.severity,
562
+ phase: diagnostic.phase,
563
+ ...(diagnostic.sourceId === undefined
564
+ ? {}
565
+ : { source: manifestPath(diagnostic.sourceId, workspaceRoot) }),
566
+ ...(diagnostic.start === undefined ? {} : { start: diagnostic.start }),
567
+ ...(diagnostic.end === undefined ? {} : { end: diagnostic.end }),
568
+ };
569
+ }
570
+ function formatTsDiagnostic(diagnostic) {
571
+ return ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
572
+ }
573
+ function isPathInside(root, fileName) {
574
+ const relative = path.relative(root, fileName);
575
+ return relative === '' || (!relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
576
+ }
package/dist/scaffold.js CHANGED
@@ -43,7 +43,7 @@ export function isScaffoldTemplate(value) {
43
43
  value === 'lib-di');
44
44
  }
45
45
  export function scaffoldSuccessText(result) {
46
- const command = isAppTemplate(result.template) ? 'npm run dev' : 'npm run build';
46
+ const command = isAppTemplate(result.template) ? 'pnpm run dev' : 'pnpm run build';
47
47
  const diLine = result.template === 'app-di' || result.template === 'enterprise' || result.template === 'lib-di'
48
48
  ? '\nDI integration included.\n'
49
49
  : '\n';
@@ -51,7 +51,7 @@ export function scaffoldSuccessText(result) {
51
51
  `Created ${result.name}`,
52
52
  '',
53
53
  ` cd ${result.name}`,
54
- ' npm install',
54
+ ' pnpm install',
55
55
  ` ${command}`,
56
56
  diLine,
57
57
  ].join('\n');
@@ -136,7 +136,7 @@ function libFiles(withDi) {
136
136
  { path: 'README.md', content: withDi ? libReadmeWithDi : libReadme },
137
137
  ];
138
138
  }
139
- function appPackageJson(withDi) {
139
+ function appPackageJson(_withDi) {
140
140
  return `{
141
141
  "name": "{{packageName}}",
142
142
  "version": "0.1.0",
@@ -149,8 +149,7 @@ function appPackageJson(withDi) {
149
149
  "typecheck": "tsc --noEmit"
150
150
  },
151
151
  "dependencies": {
152
- ${withDi ? ' "@vobs/di": "latest",\n' : ''} "@vobs/reactivity": "latest",
153
- "@vobs/runtime-dom": "latest"
152
+ "vobs": "latest"
154
153
  },
155
154
  "devDependencies": {
156
155
  "@vobs/vite-plugin": "latest",
@@ -192,7 +191,7 @@ import './styles/global.css';
192
191
 
193
192
  await app.mount('#app');
194
193
  `;
195
- const appModule = `import { createVobsApp } from '@vobs/runtime-dom';
194
+ const appModule = `import { createVobsApp } from 'vobs';
196
195
  import router from 'vobs:routes';
197
196
 
198
197
  const reportAppError = (error: unknown): void => {
@@ -210,8 +209,7 @@ const app = createVobsApp({
210
209
 
211
210
  export { app };
212
211
  `;
213
- const appModuleWithDi = `import { createInjectionScope } from '@vobs/di';
214
- import { createVobsApp } from '@vobs/runtime-dom';
212
+ const appModuleWithDi = `import { createInjectionScope, createVobsApp } from 'vobs';
215
213
  import router from 'vobs:routes';
216
214
  import { registerServices } from './app/di.js';
217
215
 
@@ -237,7 +235,7 @@ export { app };
237
235
  const appDiFiles = [
238
236
  {
239
237
  path: 'src/app/di.ts',
240
- content: `import type { InjectionScope } from '@vobs/di';
238
+ content: `import type { InjectionScope } from 'vobs';
241
239
  import { createToastService, ToastServiceKey } from '../services/toast.js';
242
240
 
243
241
  export function registerServices(scope: InjectionScope): void {
@@ -252,7 +250,7 @@ export function registerServices(scope: InjectionScope): void {
252
250
  },
253
251
  {
254
252
  path: 'src/services/toast.ts',
255
- content: `import { createInjectionKey, inject } from '@vobs/di';
253
+ content: `import { createInjectionKey, inject } from 'vobs';
256
254
 
257
255
  export interface ToastService {
258
256
  success(message: string): void;
@@ -274,8 +272,7 @@ export function useToast(): ToastService {
274
272
  `,
275
273
  },
276
274
  ];
277
- const indexPage = `import { signal } from '@vobs/reactivity';
278
- import { definePage } from '@vobs/runtime-dom';
275
+ const indexPage = `import { definePage, signal } from 'vobs';
279
276
  import template from './index.html';
280
277
 
281
278
  export default definePage({
@@ -291,8 +288,7 @@ export default definePage({
291
288
  },
292
289
  });
293
290
  `;
294
- const indexPageWithDi = `import { signal } from '@vobs/reactivity';
295
- import { definePage } from '@vobs/runtime-dom';
291
+ const indexPageWithDi = `import { definePage, signal } from 'vobs';
296
292
  import { useToast } from '../services/toast.js';
297
293
  import template from './index.html';
298
294