@zenithbuild/language-server 0.2.7 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/RELEASE_NOTES.md +24 -0
- package/package.json +4 -3
- package/scripts/release.ts +24 -7
- package/src/diagnostics.ts +51 -8
- package/src/server.ts +19 -19
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.8] - 2026-01-26
|
|
9
|
+
|
|
10
|
+
### 📝 Other Changes
|
|
11
|
+
|
|
12
|
+
- **** ()
|
|
13
|
+
|
|
8
14
|
## [0.2.1] - 2026-01-16
|
|
9
15
|
|
|
10
16
|
### 🐛 Bug Fixes
|
package/RELEASE_NOTES.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# 🚀 @zenithbuild/language-server v0.2.8
|
|
2
|
+
|
|
3
|
+
## [0.2.8] - 2026-01-26
|
|
4
|
+
|
|
5
|
+
### 📝 Other Changes
|
|
6
|
+
|
|
7
|
+
- **** ()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## 📦 Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bun add @zenithbuild/language-server@0.2.8
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
*or with npm:*
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @zenithbuild/language-server@0.2.8
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
*Generated by Zenith Automated Release*
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenithbuild/language-server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Language Server for Zenith Framework",
|
|
5
5
|
"main": "./dist/server.js",
|
|
6
6
|
"types": "./dist/server.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "npx esbuild src/server.ts --bundle --outdir=dist --platform=node --format=cjs",
|
|
8
|
+
"build": "npx esbuild src/server.ts --bundle --outdir=dist --platform=node --format=cjs --external:@zenithbuild/compiler",
|
|
9
9
|
"dev": "npm run build -- --watch",
|
|
10
10
|
"release": "bun run scripts/release.ts",
|
|
11
11
|
"release:dry": "bun run scripts/release.ts --dry-run",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"release:major": "bun run scripts/release.ts --bump=major"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
+
"@zenithbuild/compiler": "^1.3.0",
|
|
17
18
|
"vscode-languageserver": "^9.0.1",
|
|
18
19
|
"vscode-languageserver-textdocument": "^1.0.11"
|
|
19
20
|
},
|
|
@@ -31,4 +32,4 @@
|
|
|
31
32
|
"type": "git",
|
|
32
33
|
"url": "https://github.com/zenithbuild/zenith-language-server.git"
|
|
33
34
|
}
|
|
34
|
-
}
|
|
35
|
+
}
|
package/scripts/release.ts
CHANGED
|
@@ -279,9 +279,13 @@ function generateChangelog(
|
|
|
279
279
|
changelog += `### ⚠️ BREAKING CHANGES\n\n`;
|
|
280
280
|
for (const commit of breakingChanges) {
|
|
281
281
|
const scope = commit.scope ? `**${commit.scope}**: ` : "";
|
|
282
|
-
changelog +=
|
|
282
|
+
changelog += `#### ${scope}${commit.subject} (${commit.hash.slice(0, 7)})\n`;
|
|
283
|
+
if (commit.body) {
|
|
284
|
+
changelog += `\n${commit.body.split('\n').map(line => `> ${line}`).join('\n')}\n`;
|
|
285
|
+
}
|
|
286
|
+
changelog += "\n";
|
|
283
287
|
}
|
|
284
|
-
changelog += "\n";
|
|
288
|
+
changelog += "---\n\n";
|
|
285
289
|
}
|
|
286
290
|
|
|
287
291
|
// Regular changes by type
|
|
@@ -294,7 +298,14 @@ function generateChangelog(
|
|
|
294
298
|
changelog += `### ${typeConfig.title}\n\n`;
|
|
295
299
|
for (const commit of typeCommits) {
|
|
296
300
|
const scope = commit.scope ? `**${commit.scope}**: ` : "";
|
|
297
|
-
changelog += `-
|
|
301
|
+
changelog += `- **${scope}${commit.subject}** (${commit.hash.slice(0, 7)})\n`;
|
|
302
|
+
|
|
303
|
+
// Include body if present
|
|
304
|
+
if (commit.body && commit.body.trim()) {
|
|
305
|
+
const cleanedBody = commit.body.trim();
|
|
306
|
+
// Indent body for better hierarchy
|
|
307
|
+
changelog += `${cleanedBody.split('\n').map(line => ` > ${line}`).join('\n')}\n`;
|
|
308
|
+
}
|
|
298
309
|
}
|
|
299
310
|
changelog += "\n";
|
|
300
311
|
}
|
|
@@ -303,7 +314,10 @@ function generateChangelog(
|
|
|
303
314
|
if (groupedCommits.other && groupedCommits.other.length > 0) {
|
|
304
315
|
changelog += `### 📝 Other Changes\n\n`;
|
|
305
316
|
for (const commit of groupedCommits.other) {
|
|
306
|
-
changelog += `-
|
|
317
|
+
changelog += `- **${commit.subject}** (${commit.hash.slice(0, 7)})\n`;
|
|
318
|
+
if (commit.body && commit.body.trim()) {
|
|
319
|
+
changelog += `${commit.body.trim().split('\n').map(line => ` > ${line}`).join('\n')}\n`;
|
|
320
|
+
}
|
|
307
321
|
}
|
|
308
322
|
changelog += "\n";
|
|
309
323
|
}
|
|
@@ -319,21 +333,24 @@ function generateReleaseNotes(
|
|
|
319
333
|
): string {
|
|
320
334
|
const changelog = generateChangelog(commits, newVersion, config);
|
|
321
335
|
|
|
322
|
-
return `# ${packageName} v${newVersion}
|
|
336
|
+
return `# 🚀 ${packageName} v${newVersion}
|
|
323
337
|
|
|
324
338
|
${changelog}
|
|
325
339
|
|
|
326
|
-
## Installation
|
|
340
|
+
## 📦 Installation
|
|
327
341
|
|
|
328
342
|
\`\`\`bash
|
|
329
343
|
bun add ${packageName}@${newVersion}
|
|
330
344
|
\`\`\`
|
|
331
345
|
|
|
332
|
-
or with npm
|
|
346
|
+
*or with npm:*
|
|
333
347
|
|
|
334
348
|
\`\`\`bash
|
|
335
349
|
npm install ${packageName}@${newVersion}
|
|
336
350
|
\`\`\`
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
*Generated by Zenith Automated Release*
|
|
337
354
|
`;
|
|
338
355
|
}
|
|
339
356
|
|
package/src/diagnostics.ts
CHANGED
|
@@ -19,23 +19,39 @@ export interface ValidationContext {
|
|
|
19
19
|
graph: ProjectGraph | null;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
import { compile } from '@zenithbuild/compiler';
|
|
23
|
+
|
|
22
24
|
/**
|
|
23
25
|
* Collect all diagnostics for a document
|
|
24
26
|
*/
|
|
25
|
-
export function collectDiagnostics(document: TextDocument, graph: ProjectGraph | null): Diagnostic[] {
|
|
27
|
+
export async function collectDiagnostics(document: TextDocument, graph: ProjectGraph | null): Promise<Diagnostic[]> {
|
|
26
28
|
const diagnostics: Diagnostic[] = [];
|
|
27
29
|
const text = document.getText();
|
|
30
|
+
const filePath = new URL(document.uri).pathname;
|
|
31
|
+
|
|
32
|
+
// 1. High-Fidelity Compiler Validation (Native)
|
|
33
|
+
try {
|
|
34
|
+
// Enable caching to make this fast for subsequent calls
|
|
35
|
+
process.env.ZENITH_CACHE = '1';
|
|
36
|
+
await compile(text, filePath);
|
|
37
|
+
} catch (error: any) {
|
|
38
|
+
if (error.name === 'InvariantError' || error.name === 'CompilerError') {
|
|
39
|
+
diagnostics.push({
|
|
40
|
+
severity: DiagnosticSeverity.Error,
|
|
41
|
+
range: {
|
|
42
|
+
start: { line: (error.line || 1) - 1, character: (error.column || 1) - 1 },
|
|
43
|
+
end: { line: (error.line || 1) - 1, character: (error.column || 1) + 20 } // Approximate
|
|
44
|
+
},
|
|
45
|
+
message: `[${error.code}] ${error.message}${error.hints ? '\n\nHints:\n' + error.hints.join('\n') : ''}`,
|
|
46
|
+
source: 'zenith-compiler'
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
28
50
|
|
|
29
|
-
//
|
|
51
|
+
// 2. Light-weight LSP rules (Component resolution, etc.)
|
|
30
52
|
collectComponentDiagnostics(document, text, graph, diagnostics);
|
|
31
|
-
|
|
32
|
-
// Validate directive usage
|
|
33
53
|
collectDirectiveDiagnostics(document, text, diagnostics);
|
|
34
|
-
|
|
35
|
-
// Validate imports
|
|
36
54
|
collectImportDiagnostics(document, text, diagnostics);
|
|
37
|
-
|
|
38
|
-
// Validate expressions
|
|
39
55
|
collectExpressionDiagnostics(document, text, diagnostics);
|
|
40
56
|
|
|
41
57
|
return diagnostics;
|
|
@@ -146,6 +162,20 @@ function collectDirectiveDiagnostics(
|
|
|
146
162
|
source: 'zenith'
|
|
147
163
|
});
|
|
148
164
|
}
|
|
165
|
+
|
|
166
|
+
// Check for forbidden inline functions in event handlers (Contract Violation)
|
|
167
|
+
const eventHandlerPattern = /\bon(?:[a-z]+)\s*=\s*["']([^"']*(?:=>|function)[^"']*)["']/gi;
|
|
168
|
+
while ((match = eventHandlerPattern.exec(text)) !== null) {
|
|
169
|
+
const startPos = document.positionAt(match.index);
|
|
170
|
+
const endPos = document.positionAt(match.index + match[0].length);
|
|
171
|
+
|
|
172
|
+
diagnostics.push({
|
|
173
|
+
severity: DiagnosticSeverity.Error,
|
|
174
|
+
range: { start: startPos, end: endPos },
|
|
175
|
+
message: `Forbidden inline function in event handler. Use a direct symbolic reference instead (e.g. "handleClick").`,
|
|
176
|
+
source: 'zenith'
|
|
177
|
+
});
|
|
178
|
+
}
|
|
149
179
|
}
|
|
150
180
|
|
|
151
181
|
/**
|
|
@@ -256,5 +286,18 @@ function collectExpressionDiagnostics(
|
|
|
256
286
|
source: 'zenith'
|
|
257
287
|
});
|
|
258
288
|
}
|
|
289
|
+
|
|
290
|
+
// Check for TypeScript syntax in runtime expressions (Contract Violation)
|
|
291
|
+
if (expression.includes(' as ') || (expression.includes('<') && expression.includes('>'))) {
|
|
292
|
+
const startPos = document.positionAt(offset);
|
|
293
|
+
const endPos = document.positionAt(offset + match[0].length);
|
|
294
|
+
|
|
295
|
+
diagnostics.push({
|
|
296
|
+
severity: DiagnosticSeverity.Error,
|
|
297
|
+
range: { start: startPos, end: endPos },
|
|
298
|
+
message: `TypeScript syntax (type casting or generics) detected in runtime expression. Runtime code must be pure JavaScript.`,
|
|
299
|
+
source: 'zenith'
|
|
300
|
+
});
|
|
301
|
+
}
|
|
259
302
|
}
|
|
260
303
|
}
|
package/src/server.ts
CHANGED
|
@@ -35,27 +35,27 @@ import {
|
|
|
35
35
|
ProjectGraph
|
|
36
36
|
} from './project';
|
|
37
37
|
|
|
38
|
-
import {
|
|
39
|
-
DIRECTIVES,
|
|
40
|
-
isDirective,
|
|
41
|
-
getDirective,
|
|
38
|
+
import {
|
|
39
|
+
DIRECTIVES,
|
|
40
|
+
isDirective,
|
|
41
|
+
getDirective,
|
|
42
42
|
getDirectiveNames,
|
|
43
43
|
canPlaceDirective,
|
|
44
|
-
parseForExpression
|
|
44
|
+
parseForExpression
|
|
45
45
|
} from './metadata/directive-metadata';
|
|
46
46
|
|
|
47
|
-
import {
|
|
48
|
-
parseZenithImports,
|
|
49
|
-
hasRouterImport,
|
|
50
|
-
resolveModule,
|
|
47
|
+
import {
|
|
48
|
+
parseZenithImports,
|
|
49
|
+
hasRouterImport,
|
|
50
|
+
resolveModule,
|
|
51
51
|
resolveExport,
|
|
52
52
|
getAllModules,
|
|
53
|
-
getModuleExports
|
|
53
|
+
getModuleExports
|
|
54
54
|
} from './imports';
|
|
55
55
|
|
|
56
|
-
import {
|
|
57
|
-
ROUTER_HOOKS,
|
|
58
|
-
ZENLINK_PROPS,
|
|
56
|
+
import {
|
|
57
|
+
ROUTER_HOOKS,
|
|
58
|
+
ZENLINK_PROPS,
|
|
59
59
|
ROUTE_FIELDS,
|
|
60
60
|
getRouterHook,
|
|
61
61
|
isRouterHook,
|
|
@@ -190,7 +190,7 @@ function extractLoopVariables(text: string): string[] {
|
|
|
190
190
|
const vars: string[] = [];
|
|
191
191
|
const loopPattern = /zen:for\s*=\s*["']([^"']+)["']/g;
|
|
192
192
|
let match;
|
|
193
|
-
|
|
193
|
+
|
|
194
194
|
while ((match = loopPattern.exec(text)) !== null) {
|
|
195
195
|
const parsed = parseForExpression(match[1]);
|
|
196
196
|
if (parsed) {
|
|
@@ -198,7 +198,7 @@ function extractLoopVariables(text: string): string[] {
|
|
|
198
198
|
if (parsed.indexVar) vars.push(parsed.indexVar);
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
-
|
|
201
|
+
|
|
202
202
|
return vars;
|
|
203
203
|
}
|
|
204
204
|
|
|
@@ -257,7 +257,7 @@ function getPositionContext(text: string, offset: number): {
|
|
|
257
257
|
// Get current word being typed
|
|
258
258
|
const wordMatch = before.match(/[a-zA-Z_$:@][a-zA-Z0-9_$:-]*$/);
|
|
259
259
|
const currentWord = wordMatch ? wordMatch[0] : '';
|
|
260
|
-
|
|
260
|
+
|
|
261
261
|
// Check for @ or : prefix for event/binding completion
|
|
262
262
|
const afterAt = before.endsWith('@') || currentWord.startsWith('@');
|
|
263
263
|
const afterColon = before.endsWith(':') || (currentWord.startsWith(':') && !currentWord.startsWith(':'));
|
|
@@ -529,7 +529,7 @@ connection.onCompletion((params: TextDocumentPositionParams): CompletionItem[] =
|
|
|
529
529
|
if (ctx.inTag && ctx.tagName && !ctx.inAttributeValue) {
|
|
530
530
|
// Directives (zen:if, zen:for, etc.)
|
|
531
531
|
const elementType = ctx.tagName === 'slot' ? 'slot' : (/^[A-Z]/.test(ctx.tagName) ? 'component' : 'element');
|
|
532
|
-
|
|
532
|
+
|
|
533
533
|
for (const directiveName of getDirectiveNames()) {
|
|
534
534
|
if (canPlaceDirective(directiveName, elementType as 'element' | 'component' | 'slot')) {
|
|
535
535
|
if (!ctx.currentWord || directiveName.toLowerCase().startsWith(ctx.currentWord.toLowerCase())) {
|
|
@@ -693,7 +693,7 @@ connection.onHover((params: TextDocumentPositionParams): Hover | null => {
|
|
|
693
693
|
} else {
|
|
694
694
|
notes = '- Compile-time directive\n- No runtime assumptions\n- Processed at build time';
|
|
695
695
|
}
|
|
696
|
-
|
|
696
|
+
|
|
697
697
|
return {
|
|
698
698
|
contents: {
|
|
699
699
|
kind: MarkupKind.Markdown,
|
|
@@ -822,7 +822,7 @@ documents.onDidOpen(event => {
|
|
|
822
822
|
|
|
823
823
|
async function validateDocument(document: TextDocument) {
|
|
824
824
|
const graph = getProjectGraph(document.uri);
|
|
825
|
-
const diagnostics = collectDiagnostics(document, graph);
|
|
825
|
+
const diagnostics = await collectDiagnostics(document, graph);
|
|
826
826
|
connection.sendDiagnostics({ uri: document.uri, diagnostics });
|
|
827
827
|
}
|
|
828
828
|
|