@zenithbuild/language-server 0.6.0 → 0.6.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -39
- package/bin/zenith-language-server.js +2 -0
- package/dist/server.mjs +597 -0
- package/package.json +37 -26
- package/.github/workflows/release.yml +0 -261
- package/.releaserc.json +0 -73
- package/CHANGELOG.md +0 -59
- package/RELEASE_NOTES.md +0 -33
- package/RELEASE_NOTES_v0.6.0.md +0 -39
- package/scripts/release.ts +0 -571
- package/src/code-actions.ts +0 -219
- package/src/contracts.ts +0 -100
- package/src/diagnostics.ts +0 -603
- package/src/imports.ts +0 -207
- package/src/metadata/core-imports.ts +0 -163
- package/src/metadata/directive-metadata.ts +0 -109
- package/src/metadata/plugin-imports.ts +0 -116
- package/src/project.ts +0 -283
- package/src/router.ts +0 -180
- package/src/server.ts +0 -937
- package/src/settings.ts +0 -18
- package/src/types/zenith-compiler.d.ts +0 -3
- package/test/contracts.spec.ts +0 -37
- package/test/diagnostics.spec.ts +0 -120
- package/test/fixtures/content-plugin.zen +0 -77
- package/test/fixtures/core-only.zen +0 -59
- package/test/fixtures/no-plugins.zen +0 -115
- package/test/fixtures/router-enabled.zen +0 -76
- package/test/project-root.spec.ts +0 -44
- package/tsconfig.json +0 -25
- package/tsconfig.test.json +0 -25
package/src/imports.ts
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Import Resolution & Awareness
|
|
3
|
-
*
|
|
4
|
-
* Handles recognition and resolution of Zenith imports.
|
|
5
|
-
* - zenith/* imports are core modules (virtual, symbolic resolution)
|
|
6
|
-
* - zenith:* imports are plugin modules (soft diagnostics if missing)
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
CORE_MODULES,
|
|
11
|
-
getCoreModule,
|
|
12
|
-
getCoreExport,
|
|
13
|
-
isCoreModule,
|
|
14
|
-
type CoreModuleMetadata,
|
|
15
|
-
type ModuleExport
|
|
16
|
-
} from './metadata/core-imports';
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
PLUGIN_MODULES,
|
|
20
|
-
getPluginModule,
|
|
21
|
-
getPluginExport,
|
|
22
|
-
isPluginModule,
|
|
23
|
-
isKnownPluginModule,
|
|
24
|
-
type PluginModuleMetadata,
|
|
25
|
-
type PluginExport
|
|
26
|
-
} from './metadata/plugin-imports';
|
|
27
|
-
|
|
28
|
-
export interface ParsedImport {
|
|
29
|
-
module: string;
|
|
30
|
-
specifiers: string[];
|
|
31
|
-
isType: boolean;
|
|
32
|
-
line: number;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface ResolvedImport {
|
|
36
|
-
module: string;
|
|
37
|
-
kind: 'core' | 'plugin' | 'external';
|
|
38
|
-
metadata?: CoreModuleMetadata | PluginModuleMetadata;
|
|
39
|
-
isKnown: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Parse Zenith imports from script content
|
|
44
|
-
*/
|
|
45
|
-
export function parseZenithImports(script: string): ParsedImport[] {
|
|
46
|
-
const imports: ParsedImport[] = [];
|
|
47
|
-
const lines = script.split('\n');
|
|
48
|
-
|
|
49
|
-
for (let i = 0; i < lines.length; i++) {
|
|
50
|
-
const line = lines[i];
|
|
51
|
-
|
|
52
|
-
// Match: import { x, y } from 'module' or import type { x } from 'module'
|
|
53
|
-
const importMatch = line.match(/import\s+(type\s+)?(?:\{([^}]+)\}|(\*\s+as\s+\w+)|(\w+))\s+from\s+['"]([^'"]+)['"]/);
|
|
54
|
-
|
|
55
|
-
if (importMatch) {
|
|
56
|
-
const isType = !!importMatch[1];
|
|
57
|
-
const namedImports = importMatch[2];
|
|
58
|
-
const namespaceImport = importMatch[3];
|
|
59
|
-
const defaultImport = importMatch[4];
|
|
60
|
-
const moduleName = importMatch[5];
|
|
61
|
-
|
|
62
|
-
// Only track zenith imports
|
|
63
|
-
if (moduleName.startsWith('zenith') || moduleName.startsWith('zenith:')) {
|
|
64
|
-
const specifiers: string[] = [];
|
|
65
|
-
|
|
66
|
-
if (namedImports) {
|
|
67
|
-
// Parse named imports: { a, b as c, d }
|
|
68
|
-
const parts = namedImports.split(',');
|
|
69
|
-
for (const part of parts) {
|
|
70
|
-
const cleaned = part.trim().split(/\s+as\s+/)[0].trim();
|
|
71
|
-
if (cleaned) specifiers.push(cleaned);
|
|
72
|
-
}
|
|
73
|
-
} else if (namespaceImport) {
|
|
74
|
-
specifiers.push(namespaceImport.trim());
|
|
75
|
-
} else if (defaultImport) {
|
|
76
|
-
specifiers.push(defaultImport);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
imports.push({
|
|
80
|
-
module: moduleName,
|
|
81
|
-
specifiers,
|
|
82
|
-
isType,
|
|
83
|
-
line: i + 1
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Match: import 'module' (side-effect import)
|
|
89
|
-
const sideEffectMatch = line.match(/import\s+['"]([^'"]+)['"]/);
|
|
90
|
-
if (sideEffectMatch && !importMatch) {
|
|
91
|
-
const moduleName = sideEffectMatch[1];
|
|
92
|
-
if (moduleName.startsWith('zenith') || moduleName.startsWith('zenith:')) {
|
|
93
|
-
imports.push({
|
|
94
|
-
module: moduleName,
|
|
95
|
-
specifiers: [],
|
|
96
|
-
isType: false,
|
|
97
|
-
line: i + 1
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return imports;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Resolve a module name to its metadata
|
|
108
|
-
*/
|
|
109
|
-
export function resolveModule(moduleName: string): ResolvedImport {
|
|
110
|
-
if (isCoreModule(moduleName)) {
|
|
111
|
-
return {
|
|
112
|
-
module: moduleName,
|
|
113
|
-
kind: 'core',
|
|
114
|
-
metadata: getCoreModule(moduleName),
|
|
115
|
-
isKnown: true
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (isPluginModule(moduleName)) {
|
|
120
|
-
return {
|
|
121
|
-
module: moduleName,
|
|
122
|
-
kind: 'plugin',
|
|
123
|
-
metadata: getPluginModule(moduleName),
|
|
124
|
-
isKnown: isKnownPluginModule(moduleName)
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return {
|
|
129
|
-
module: moduleName,
|
|
130
|
-
kind: 'external',
|
|
131
|
-
isKnown: false
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Get export metadata for a specific import
|
|
137
|
-
*/
|
|
138
|
-
export function resolveExport(moduleName: string, exportName: string): ModuleExport | PluginExport | undefined {
|
|
139
|
-
if (isCoreModule(moduleName)) {
|
|
140
|
-
return getCoreExport(moduleName, exportName);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (isKnownPluginModule(moduleName)) {
|
|
144
|
-
return getPluginExport(moduleName, exportName);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return undefined;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Check if router is imported in the given imports
|
|
152
|
-
*/
|
|
153
|
-
export function hasRouterImport(imports: ParsedImport[]): boolean {
|
|
154
|
-
return imports.some(i => i.module === 'zenith/router');
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Check if a specific export is imported
|
|
159
|
-
*/
|
|
160
|
-
export function hasImport(imports: ParsedImport[], exportName: string): boolean {
|
|
161
|
-
return imports.some(i => i.specifiers.includes(exportName));
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Get all available modules for completion
|
|
166
|
-
*/
|
|
167
|
-
export function getAllModules(): Array<{ module: string; kind: 'core' | 'plugin'; description: string }> {
|
|
168
|
-
const modules: Array<{ module: string; kind: 'core' | 'plugin'; description: string }> = [];
|
|
169
|
-
|
|
170
|
-
for (const [name, meta] of Object.entries(CORE_MODULES)) {
|
|
171
|
-
modules.push({
|
|
172
|
-
module: name,
|
|
173
|
-
kind: 'core',
|
|
174
|
-
description: meta.description
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
for (const [name, meta] of Object.entries(PLUGIN_MODULES)) {
|
|
179
|
-
modules.push({
|
|
180
|
-
module: name,
|
|
181
|
-
kind: 'plugin',
|
|
182
|
-
description: meta.description
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return modules;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Get exports for completion from a module
|
|
191
|
-
*/
|
|
192
|
-
export function getModuleExports(moduleName: string): Array<ModuleExport | PluginExport> {
|
|
193
|
-
const coreModule = getCoreModule(moduleName);
|
|
194
|
-
if (coreModule) return coreModule.exports;
|
|
195
|
-
|
|
196
|
-
const pluginModule = getPluginModule(moduleName);
|
|
197
|
-
if (pluginModule) return pluginModule.exports;
|
|
198
|
-
|
|
199
|
-
return [];
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Re-export utilities
|
|
203
|
-
export { isPluginModule } from './metadata/plugin-imports';
|
|
204
|
-
|
|
205
|
-
// Re-export types
|
|
206
|
-
export type { CoreModuleMetadata, ModuleExport } from './metadata/core-imports';
|
|
207
|
-
export type { PluginModuleMetadata, PluginExport } from './metadata/plugin-imports';
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core Import Metadata
|
|
3
|
-
*
|
|
4
|
-
* Static metadata for Zenith core modules.
|
|
5
|
-
* These are virtual modules resolved symbolically (no FS probing).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export interface ModuleExport {
|
|
9
|
-
name: string;
|
|
10
|
-
kind: 'function' | 'component' | 'type' | 'variable';
|
|
11
|
-
description: string;
|
|
12
|
-
signature?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface CoreModuleMetadata {
|
|
16
|
-
module: string;
|
|
17
|
-
description: string;
|
|
18
|
-
exports: ModuleExport[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Core Zenith module exports
|
|
23
|
-
*/
|
|
24
|
-
export const CORE_MODULES: Record<string, CoreModuleMetadata> = {
|
|
25
|
-
'zenith': {
|
|
26
|
-
module: 'zenith',
|
|
27
|
-
description: 'Core Zenith runtime primitives and lifecycle hooks.',
|
|
28
|
-
exports: [
|
|
29
|
-
{
|
|
30
|
-
name: 'zenEffect',
|
|
31
|
-
kind: 'function',
|
|
32
|
-
description: 'Reactive effect that re-runs when dependencies change.',
|
|
33
|
-
signature: 'zenEffect(callback: () => void | (() => void)): void'
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: 'zenOnMount',
|
|
37
|
-
kind: 'function',
|
|
38
|
-
description: 'Called when component is mounted to the DOM.',
|
|
39
|
-
signature: 'zenOnMount(callback: () => void | (() => void)): void'
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
name: 'zenOnDestroy',
|
|
43
|
-
kind: 'function',
|
|
44
|
-
description: 'Called when component is removed from the DOM.',
|
|
45
|
-
signature: 'zenOnDestroy(callback: () => void): void'
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: 'zenOnUpdate',
|
|
49
|
-
kind: 'function',
|
|
50
|
-
description: 'Called after any state update causes a re-render.',
|
|
51
|
-
signature: 'zenOnUpdate(callback: () => void): void'
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: 'zenRef',
|
|
55
|
-
kind: 'function',
|
|
56
|
-
description: 'Create a reactive reference.',
|
|
57
|
-
signature: 'zenRef<T>(initial: T): { value: T }'
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
name: 'zenState',
|
|
61
|
-
kind: 'function',
|
|
62
|
-
description: 'Create reactive state.',
|
|
63
|
-
signature: 'zenState<T>(initial: T): [T, (value: T) => void]'
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'zenMemo',
|
|
67
|
-
kind: 'function',
|
|
68
|
-
description: 'Memoize a computed value.',
|
|
69
|
-
signature: 'zenMemo<T>(compute: () => T): T'
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
name: 'zenBatch',
|
|
73
|
-
kind: 'function',
|
|
74
|
-
description: 'Batch multiple state updates.',
|
|
75
|
-
signature: 'zenBatch(callback: () => void): void'
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
name: 'zenUntrack',
|
|
79
|
-
kind: 'function',
|
|
80
|
-
description: 'Run code without tracking dependencies.',
|
|
81
|
-
signature: 'zenUntrack<T>(callback: () => T): T'
|
|
82
|
-
}
|
|
83
|
-
]
|
|
84
|
-
},
|
|
85
|
-
'zenith/router': {
|
|
86
|
-
module: 'zenith/router',
|
|
87
|
-
description: 'File-based SPA router for Zenith framework.',
|
|
88
|
-
exports: [
|
|
89
|
-
{
|
|
90
|
-
name: 'ZenLink',
|
|
91
|
-
kind: 'component',
|
|
92
|
-
description: 'Declarative navigation component for routes.',
|
|
93
|
-
signature: '<ZenLink to="/path" preload?>{children}</ZenLink>'
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: 'useRoute',
|
|
97
|
-
kind: 'function',
|
|
98
|
-
description: 'Provides reactive access to the current route. Must be called at top-level script scope.',
|
|
99
|
-
signature: 'useRoute(): { path: string; params: Record<string, string>; query: Record<string, string> }'
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
name: 'useRouter',
|
|
103
|
-
kind: 'function',
|
|
104
|
-
description: 'Provides programmatic navigation methods.',
|
|
105
|
-
signature: 'useRouter(): { navigate: (to: string, options?: { replace?: boolean }) => void; back: () => void; forward: () => void }'
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
name: 'navigate',
|
|
109
|
-
kind: 'function',
|
|
110
|
-
description: 'Navigate to a route programmatically.',
|
|
111
|
-
signature: 'navigate(to: string, options?: { replace?: boolean }): void'
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
name: 'prefetch',
|
|
115
|
-
kind: 'function',
|
|
116
|
-
description: 'Prefetch a route for faster navigation.',
|
|
117
|
-
signature: 'prefetch(path: string): Promise<void>'
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
name: 'isActive',
|
|
121
|
-
kind: 'function',
|
|
122
|
-
description: 'Check if a route is currently active.',
|
|
123
|
-
signature: 'isActive(path: string, exact?: boolean): boolean'
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
name: 'getRoute',
|
|
127
|
-
kind: 'function',
|
|
128
|
-
description: 'Get the current route state.',
|
|
129
|
-
signature: 'getRoute(): { path: string; params: Record<string, string>; query: Record<string, string> }'
|
|
130
|
-
}
|
|
131
|
-
]
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Get a core module by name
|
|
137
|
-
*/
|
|
138
|
-
export function getCoreModule(moduleName: string): CoreModuleMetadata | undefined {
|
|
139
|
-
return CORE_MODULES[moduleName];
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Get all core module names
|
|
144
|
-
*/
|
|
145
|
-
export function getCoreModuleNames(): string[] {
|
|
146
|
-
return Object.keys(CORE_MODULES);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Get an export from a core module
|
|
151
|
-
*/
|
|
152
|
-
export function getCoreExport(moduleName: string, exportName: string): ModuleExport | undefined {
|
|
153
|
-
const module = CORE_MODULES[moduleName];
|
|
154
|
-
if (!module) return undefined;
|
|
155
|
-
return module.exports.find(e => e.name === exportName);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Check if a module is a core Zenith module
|
|
160
|
-
*/
|
|
161
|
-
export function isCoreModule(moduleName: string): boolean {
|
|
162
|
-
return moduleName in CORE_MODULES;
|
|
163
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Directive Metadata
|
|
3
|
-
*
|
|
4
|
-
* Compile-time directive definitions for Zenith.
|
|
5
|
-
* These are compiler directives, not runtime attributes.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export interface DirectiveMetadata {
|
|
9
|
-
name: string;
|
|
10
|
-
category: 'control-flow' | 'iteration' | 'reactive-effect' | 'conditional-visibility';
|
|
11
|
-
description: string;
|
|
12
|
-
syntax: string;
|
|
13
|
-
placement: ('element' | 'component')[];
|
|
14
|
-
example: string;
|
|
15
|
-
createsScope?: boolean;
|
|
16
|
-
scopeVariables?: string[];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* All Zenith compiler directives
|
|
21
|
-
*
|
|
22
|
-
* These are processed at compile-time and transformed into static DOM instructions.
|
|
23
|
-
* The LSP must describe these directives without assuming runtime behavior.
|
|
24
|
-
*/
|
|
25
|
-
export const DIRECTIVES: Record<string, DirectiveMetadata> = {
|
|
26
|
-
'zen:if': {
|
|
27
|
-
name: 'zen:if',
|
|
28
|
-
category: 'control-flow',
|
|
29
|
-
description: 'Compile-time conditional directive. Conditionally renders the element based on a boolean expression.',
|
|
30
|
-
syntax: 'zen:if="condition"',
|
|
31
|
-
placement: ['element', 'component'],
|
|
32
|
-
example: '<div zen:if="isVisible">Conditionally rendered</div>'
|
|
33
|
-
},
|
|
34
|
-
'zen:for': {
|
|
35
|
-
name: 'zen:for',
|
|
36
|
-
category: 'iteration',
|
|
37
|
-
description: 'Compile-time iteration directive. Repeats the element for each item in a collection.',
|
|
38
|
-
syntax: 'zen:for="item in items" or zen:for="item, index in items"',
|
|
39
|
-
placement: ['element', 'component'],
|
|
40
|
-
example: '<li zen:for="item in items">{item.name}</li>',
|
|
41
|
-
createsScope: true,
|
|
42
|
-
scopeVariables: ['item', 'index']
|
|
43
|
-
},
|
|
44
|
-
'zen:effect': {
|
|
45
|
-
name: 'zen:effect',
|
|
46
|
-
category: 'reactive-effect',
|
|
47
|
-
description: 'Compile-time reactive effect directive. Attaches a side effect to the element lifecycle.',
|
|
48
|
-
syntax: 'zen:effect="expression"',
|
|
49
|
-
placement: ['element', 'component'],
|
|
50
|
-
example: '<div zen:effect="console.log(\'rendered\')">Content</div>'
|
|
51
|
-
},
|
|
52
|
-
'zen:show': {
|
|
53
|
-
name: 'zen:show',
|
|
54
|
-
category: 'conditional-visibility',
|
|
55
|
-
description: 'Compile-time visibility directive. Toggles element visibility without removing from DOM.',
|
|
56
|
-
syntax: 'zen:show="condition"',
|
|
57
|
-
placement: ['element', 'component'],
|
|
58
|
-
example: '<div zen:show="isOpen">Toggle visibility</div>'
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Check if a string is a valid directive name
|
|
64
|
-
*/
|
|
65
|
-
export function isDirective(name: string): name is keyof typeof DIRECTIVES {
|
|
66
|
-
return name in DIRECTIVES;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Get directive metadata by name
|
|
71
|
-
*/
|
|
72
|
-
export function getDirective(name: string): DirectiveMetadata | undefined {
|
|
73
|
-
return DIRECTIVES[name];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Get all directive names
|
|
78
|
-
*/
|
|
79
|
-
export function getDirectiveNames(): string[] {
|
|
80
|
-
return Object.keys(DIRECTIVES);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Check if a directive can be placed on a specific element type
|
|
85
|
-
*/
|
|
86
|
-
export function canPlaceDirective(directiveName: string, elementType: 'element' | 'component' | 'slot'): boolean {
|
|
87
|
-
const directive = DIRECTIVES[directiveName];
|
|
88
|
-
if (!directive) return false;
|
|
89
|
-
|
|
90
|
-
// Directives cannot be placed on <slot>
|
|
91
|
-
if (elementType === 'slot') return false;
|
|
92
|
-
|
|
93
|
-
return directive.placement.includes(elementType as 'element' | 'component');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Parse a zen:for expression to extract variables
|
|
98
|
-
*/
|
|
99
|
-
export function parseForExpression(expression: string): { itemVar: string; indexVar?: string; source: string } | null {
|
|
100
|
-
// Match: "item in items" or "item, index in items"
|
|
101
|
-
const match = expression.match(/^\s*([a-zA-Z_$][\w$]*)(?:\s*,\s*([a-zA-Z_$][\w$]*))?\s+in\s+(.+)\s*$/);
|
|
102
|
-
if (!match) return null;
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
itemVar: match[1],
|
|
106
|
-
indexVar: match[2],
|
|
107
|
-
source: match[3].trim()
|
|
108
|
-
};
|
|
109
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin Import Metadata
|
|
3
|
-
*
|
|
4
|
-
* Static metadata for Zenith plugin modules.
|
|
5
|
-
* Plugin modules use the zenith:* namespace.
|
|
6
|
-
*
|
|
7
|
-
* Important: The LSP must NOT assume plugin presence.
|
|
8
|
-
* If a plugin is not installed, diagnostics should be soft warnings, not errors.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export interface PluginExport {
|
|
12
|
-
name: string;
|
|
13
|
-
kind: 'function' | 'type' | 'variable';
|
|
14
|
-
description: string;
|
|
15
|
-
signature?: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface PluginModuleMetadata {
|
|
19
|
-
module: string;
|
|
20
|
-
description: string;
|
|
21
|
-
exports: PluginExport[];
|
|
22
|
-
required: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Known Zenith plugin modules
|
|
27
|
-
*/
|
|
28
|
-
export const PLUGIN_MODULES: Record<string, PluginModuleMetadata> = {
|
|
29
|
-
'zenith:content': {
|
|
30
|
-
module: 'zenith:content',
|
|
31
|
-
description: 'Content collections plugin for Zenith. Provides type-safe content management for Markdown, MDX, and JSON files.',
|
|
32
|
-
exports: [
|
|
33
|
-
{
|
|
34
|
-
name: 'zenCollection',
|
|
35
|
-
kind: 'function',
|
|
36
|
-
description: 'Define a content collection with schema validation.',
|
|
37
|
-
signature: 'zenCollection<T>(options: { name: string; schema: T }): Collection<T>'
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
name: 'getCollection',
|
|
41
|
-
kind: 'function',
|
|
42
|
-
description: 'Get all entries from a content collection.',
|
|
43
|
-
signature: 'getCollection(name: string): Promise<CollectionEntry[]>'
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: 'getEntry',
|
|
47
|
-
kind: 'function',
|
|
48
|
-
description: 'Get a single entry from a content collection.',
|
|
49
|
-
signature: 'getEntry(collection: string, slug: string): Promise<CollectionEntry | undefined>'
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: 'useZenOrder',
|
|
53
|
-
kind: 'function',
|
|
54
|
-
description: 'Hook to sort collection entries by frontmatter order field.',
|
|
55
|
-
signature: 'useZenOrder(entries: CollectionEntry[]): CollectionEntry[]'
|
|
56
|
-
}
|
|
57
|
-
],
|
|
58
|
-
required: false
|
|
59
|
-
},
|
|
60
|
-
'zenith:image': {
|
|
61
|
-
module: 'zenith:image',
|
|
62
|
-
description: 'Image optimization plugin for Zenith.',
|
|
63
|
-
exports: [
|
|
64
|
-
{
|
|
65
|
-
name: 'Image',
|
|
66
|
-
kind: 'function',
|
|
67
|
-
description: 'Optimized image component with automatic format conversion and lazy loading.',
|
|
68
|
-
signature: 'Image({ src: string; alt: string; width?: number; height?: number })'
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'getImage',
|
|
72
|
-
kind: 'function',
|
|
73
|
-
description: 'Get optimized image metadata.',
|
|
74
|
-
signature: 'getImage(src: string, options?: ImageOptions): Promise<ImageMetadata>'
|
|
75
|
-
}
|
|
76
|
-
],
|
|
77
|
-
required: false
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Get a plugin module by name
|
|
83
|
-
*/
|
|
84
|
-
export function getPluginModule(moduleName: string): PluginModuleMetadata | undefined {
|
|
85
|
-
return PLUGIN_MODULES[moduleName];
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Get all plugin module names
|
|
90
|
-
*/
|
|
91
|
-
export function getPluginModuleNames(): string[] {
|
|
92
|
-
return Object.keys(PLUGIN_MODULES);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Get an export from a plugin module
|
|
97
|
-
*/
|
|
98
|
-
export function getPluginExport(moduleName: string, exportName: string): PluginExport | undefined {
|
|
99
|
-
const module = PLUGIN_MODULES[moduleName];
|
|
100
|
-
if (!module) return undefined;
|
|
101
|
-
return module.exports.find(e => e.name === exportName);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Check if a module is a plugin module (zenith:* namespace)
|
|
106
|
-
*/
|
|
107
|
-
export function isPluginModule(moduleName: string): boolean {
|
|
108
|
-
return moduleName.startsWith('zenith:');
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Check if a plugin module is known
|
|
113
|
-
*/
|
|
114
|
-
export function isKnownPluginModule(moduleName: string): boolean {
|
|
115
|
-
return moduleName in PLUGIN_MODULES;
|
|
116
|
-
}
|