coherent-language-support 1.0.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.
Files changed (55) hide show
  1. package/.vscodeignore +8 -0
  2. package/CHANGELOG.md +14 -0
  3. package/LICENSE +21 -0
  4. package/README.md +108 -0
  5. package/coherent-language-support-1.0.0.vsix +0 -0
  6. package/dist/extension.js +18075 -0
  7. package/dist/extension.js.map +7 -0
  8. package/esbuild.config.mjs +24 -0
  9. package/icon.png +0 -0
  10. package/icon.svg +10 -0
  11. package/package.json +78 -0
  12. package/server/analysis/coherent-analyzer.d.ts +93 -0
  13. package/server/analysis/coherent-analyzer.d.ts.map +1 -0
  14. package/server/analysis/coherent-analyzer.js +288 -0
  15. package/server/analysis/coherent-analyzer.js.map +1 -0
  16. package/server/analysis/element-validator.d.ts +45 -0
  17. package/server/analysis/element-validator.d.ts.map +1 -0
  18. package/server/analysis/element-validator.js +84 -0
  19. package/server/analysis/element-validator.js.map +1 -0
  20. package/server/analysis/nesting-validator.d.ts +49 -0
  21. package/server/analysis/nesting-validator.d.ts.map +1 -0
  22. package/server/analysis/nesting-validator.js +68 -0
  23. package/server/analysis/nesting-validator.js.map +1 -0
  24. package/server/data/element-attributes.d.ts +92 -0
  25. package/server/data/element-attributes.d.ts.map +1 -0
  26. package/server/data/element-attributes.generated.json +7085 -0
  27. package/server/data/element-attributes.js +282 -0
  28. package/server/data/element-attributes.js.map +1 -0
  29. package/server/data/nesting-rules.d.ts +67 -0
  30. package/server/data/nesting-rules.d.ts.map +1 -0
  31. package/server/data/nesting-rules.js +240 -0
  32. package/server/data/nesting-rules.js.map +1 -0
  33. package/server/providers/code-actions.d.ts +15 -0
  34. package/server/providers/code-actions.d.ts.map +1 -0
  35. package/server/providers/code-actions.js +191 -0
  36. package/server/providers/code-actions.js.map +1 -0
  37. package/server/providers/completion.d.ts +15 -0
  38. package/server/providers/completion.d.ts.map +1 -0
  39. package/server/providers/completion.js +247 -0
  40. package/server/providers/completion.js.map +1 -0
  41. package/server/providers/diagnostics.d.ts +26 -0
  42. package/server/providers/diagnostics.d.ts.map +1 -0
  43. package/server/providers/diagnostics.js +143 -0
  44. package/server/providers/diagnostics.js.map +1 -0
  45. package/server/providers/hover.d.ts +15 -0
  46. package/server/providers/hover.d.ts.map +1 -0
  47. package/server/providers/hover.js +215 -0
  48. package/server/providers/hover.js.map +1 -0
  49. package/server/server.d.ts +17 -0
  50. package/server/server.d.ts.map +1 -0
  51. package/server/server.js +82 -0
  52. package/server/server.js.map +1 -0
  53. package/snippets/coherent.json +226 -0
  54. package/src/extension.ts +75 -0
  55. package/tsconfig.json +18 -0
@@ -0,0 +1,24 @@
1
+ import * as esbuild from 'esbuild';
2
+ import { cp, mkdir } from 'fs/promises';
3
+ import { dirname, join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+
8
+ // Bundle extension
9
+ await esbuild.build({
10
+ entryPoints: ['src/extension.ts'],
11
+ bundle: true,
12
+ outfile: 'dist/extension.js',
13
+ external: ['vscode'],
14
+ platform: 'node',
15
+ format: 'cjs', // VS Code requires CommonJS for extension main
16
+ sourcemap: true,
17
+ });
18
+
19
+ // Copy language server from sibling package
20
+ await mkdir(join(__dirname, 'server'), { recursive: true });
21
+ const serverSource = join(__dirname, '../language-server/dist');
22
+ await cp(serverSource, join(__dirname, 'server'), { recursive: true });
23
+
24
+ console.log('Build complete: extension bundled, server copied to server/');
package/icon.png ADDED
Binary file
package/icon.svg ADDED
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128" height="128">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#7c3aed"/>
5
+ <stop offset="100%" style="stop-color:#2563eb"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect width="128" height="128" rx="24" fill="url(#bg)"/>
9
+ <text x="64" y="88" font-family="Arial, sans-serif" font-size="72" font-weight="bold" fill="white" text-anchor="middle">C</text>
10
+ </svg>
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "coherent-language-support",
3
+ "displayName": "Coherent.js Language Support",
4
+ "description": "IntelliSense, validation, and snippets for Coherent.js",
5
+ "version": "1.0.0",
6
+ "publisher": "coherentjs",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/Tomdrouv1/coherent.js"
10
+ },
11
+ "engines": {
12
+ "vscode": "^1.85.0"
13
+ },
14
+ "categories": [
15
+ "Programming Languages",
16
+ "Linters",
17
+ "Snippets"
18
+ ],
19
+ "keywords": [
20
+ "coherent",
21
+ "ssr",
22
+ "server-side-rendering",
23
+ "html",
24
+ "components"
25
+ ],
26
+ "icon": "icon.png",
27
+ "activationEvents": [],
28
+ "main": "./dist/extension.js",
29
+ "contributes": {
30
+ "snippets": [
31
+ {
32
+ "language": "javascript",
33
+ "path": "./snippets/coherent.json"
34
+ },
35
+ {
36
+ "language": "typescript",
37
+ "path": "./snippets/coherent.json"
38
+ },
39
+ {
40
+ "language": "javascriptreact",
41
+ "path": "./snippets/coherent.json"
42
+ },
43
+ {
44
+ "language": "typescriptreact",
45
+ "path": "./snippets/coherent.json"
46
+ }
47
+ ],
48
+ "configuration": {
49
+ "title": "Coherent.js",
50
+ "properties": {
51
+ "coherent.trace.server": {
52
+ "type": "string",
53
+ "enum": [
54
+ "off",
55
+ "messages",
56
+ "verbose"
57
+ ],
58
+ "default": "off",
59
+ "description": "Traces communication between VS Code and the language server"
60
+ }
61
+ }
62
+ }
63
+ },
64
+ "dependencies": {
65
+ "vscode-languageclient": "^9.0.1"
66
+ },
67
+ "devDependencies": {
68
+ "@types/vscode": "^1.85.0",
69
+ "@vscode/vsce": "^3.3.2",
70
+ "esbuild": "^0.25.0",
71
+ "sharp": "0.34.5",
72
+ "typescript": "^5.9.3"
73
+ },
74
+ "scripts": {
75
+ "build": "node esbuild.config.mjs",
76
+ "package": "vsce package --no-dependencies"
77
+ }
78
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Coherent.js Object Analyzer
3
+ *
4
+ * Uses TypeScript AST to detect and analyze Coherent.js element objects
5
+ * in source code. Identifies tag names, attributes, children, and nesting.
6
+ */
7
+ import * as ts from 'typescript';
8
+ import { Range, Position } from 'vscode-languageserver';
9
+ /**
10
+ * Information about a detected Coherent.js element.
11
+ */
12
+ export interface CoherentElementInfo {
13
+ /** HTML tag name (e.g., 'div', 'input') */
14
+ tagName: string;
15
+ /** Source code range of the entire element object */
16
+ range: Range;
17
+ /** Source code range of just the tag name property */
18
+ tagNameRange: Range;
19
+ /** Attributes found on this element */
20
+ attributes: AttributeInfo[];
21
+ /** Parent element info (if nested) */
22
+ parent: CoherentElementInfo | null;
23
+ /** Children elements */
24
+ children: CoherentElementInfo[];
25
+ /** The TypeScript AST node */
26
+ node: ts.ObjectLiteralExpression;
27
+ }
28
+ /**
29
+ * Information about an attribute on a Coherent element.
30
+ */
31
+ export interface AttributeInfo {
32
+ /** Attribute name (e.g., 'className', 'onClick') */
33
+ name: string;
34
+ /** Attribute value as string (for display) */
35
+ value: string;
36
+ /** Source code range of the attribute */
37
+ range: Range;
38
+ /** Source code range of just the attribute name */
39
+ nameRange: Range;
40
+ /** The TypeScript AST node */
41
+ node: ts.PropertyAssignment | ts.ShorthandPropertyAssignment;
42
+ }
43
+ /**
44
+ * Context for position-based queries.
45
+ */
46
+ export interface PositionContext {
47
+ /** Type of context at the position */
48
+ type: 'tag-name' | 'attribute-name' | 'attribute-value' | 'children' | 'outside';
49
+ /** Current element (if inside one) */
50
+ element?: CoherentElementInfo;
51
+ /** Current attribute (if inside one) */
52
+ attribute?: AttributeInfo;
53
+ }
54
+ /**
55
+ * Check if a node is a Coherent.js element object.
56
+ *
57
+ * A Coherent element is an object with a single property whose name is an HTML tag,
58
+ * and whose value is an object containing attributes.
59
+ */
60
+ export declare function isCoherentElement(node: ts.Node, sourceFile: ts.SourceFile): node is ts.ObjectLiteralExpression;
61
+ /**
62
+ * Find all Coherent.js elements in a source file.
63
+ *
64
+ * @param sourceFile - TypeScript source file to analyze
65
+ * @returns Array of found Coherent element info
66
+ */
67
+ export declare function findCoherentElements(sourceFile: ts.SourceFile): CoherentElementInfo[];
68
+ /**
69
+ * Get the Coherent element at a specific position.
70
+ *
71
+ * @param sourceFile - TypeScript source file
72
+ * @param position - LSP position
73
+ * @param elements - Previously found elements (optional, will find if not provided)
74
+ * @returns Element info at position or undefined
75
+ */
76
+ export declare function getElementAtPosition(sourceFile: ts.SourceFile, position: Position, elements?: CoherentElementInfo[]): CoherentElementInfo | undefined;
77
+ /**
78
+ * Get the context at a specific position for completion/hover.
79
+ *
80
+ * @param sourceFile - TypeScript source file
81
+ * @param position - LSP position
82
+ * @returns Context information
83
+ */
84
+ export declare function getPositionContext(sourceFile: ts.SourceFile, position: Position): PositionContext;
85
+ /**
86
+ * Create a TypeScript source file from text content.
87
+ *
88
+ * @param content - Source file content
89
+ * @param fileName - File name for the source file
90
+ * @returns TypeScript SourceFile
91
+ */
92
+ export declare function createSourceFile(content: string, fileName: string): ts.SourceFile;
93
+ //# sourceMappingURL=coherent-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coherent-analyzer.d.ts","sourceRoot":"","sources":["../../src/analysis/coherent-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAEhB,qDAAqD;IACrD,KAAK,EAAE,KAAK,CAAC;IAEb,sDAAsD;IACtD,YAAY,EAAE,KAAK,CAAC;IAEpB,uCAAuC;IACvC,UAAU,EAAE,aAAa,EAAE,CAAC;IAE5B,sCAAsC;IACtC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAEnC,wBAAwB;IACxB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAEhC,8BAA8B;IAC9B,IAAI,EAAE,EAAE,CAAC,uBAAuB,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IAEb,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IAEd,yCAAyC;IACzC,KAAK,EAAE,KAAK,CAAC;IAEb,mDAAmD;IACnD,SAAS,EAAE,KAAK,CAAC;IAEjB,8BAA8B;IAC9B,IAAI,EAAE,EAAE,CAAC,kBAAkB,GAAG,EAAE,CAAC,2BAA2B,CAAC;CAC9D;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,IAAI,EAAE,UAAU,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,UAAU,GAAG,SAAS,CAAC;IAEjF,sCAAsC;IACtC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAE9B,wCAAwC;IACxC,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B;AA8CD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,uBAAuB,CA4B9G;AAmGD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,mBAAmB,EAAE,CAyBrF;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,EAAE,CAAC,UAAU,EACzB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,CAAC,EAAE,mBAAmB,EAAE,GAC/B,mBAAmB,GAAG,SAAS,CAgBjC;AAcD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,EAAE,CAAC,UAAU,EACzB,QAAQ,EAAE,QAAQ,GACjB,eAAe,CA+CjB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,EAAE,CAAC,UAAU,CAQjF"}
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Coherent.js Object Analyzer
3
+ *
4
+ * Uses TypeScript AST to detect and analyze Coherent.js element objects
5
+ * in source code. Identifies tag names, attributes, children, and nesting.
6
+ */
7
+ import * as ts from 'typescript';
8
+ import { HTML_ELEMENTS } from '../data/element-attributes.js';
9
+ /**
10
+ * Convert TypeScript position to LSP position.
11
+ */
12
+ function tsPositionToLsp(sourceFile, pos) {
13
+ const lineAndChar = sourceFile.getLineAndCharacterOfPosition(pos);
14
+ return {
15
+ line: lineAndChar.line,
16
+ character: lineAndChar.character,
17
+ };
18
+ }
19
+ /**
20
+ * Convert TypeScript range to LSP range.
21
+ */
22
+ function tsRangeToLsp(sourceFile, start, end) {
23
+ return {
24
+ start: tsPositionToLsp(sourceFile, start),
25
+ end: tsPositionToLsp(sourceFile, end),
26
+ };
27
+ }
28
+ /**
29
+ * Convert LSP position to TypeScript offset.
30
+ */
31
+ function lspPositionToTs(sourceFile, position) {
32
+ return sourceFile.getPositionOfLineAndCharacter(position.line, position.character);
33
+ }
34
+ /**
35
+ * Check if a position is within a range.
36
+ */
37
+ function isPositionInRange(position, range) {
38
+ if (position.line < range.start.line || position.line > range.end.line) {
39
+ return false;
40
+ }
41
+ if (position.line === range.start.line && position.character < range.start.character) {
42
+ return false;
43
+ }
44
+ if (position.line === range.end.line && position.character > range.end.character) {
45
+ return false;
46
+ }
47
+ return true;
48
+ }
49
+ /**
50
+ * Check if a node is a Coherent.js element object.
51
+ *
52
+ * A Coherent element is an object with a single property whose name is an HTML tag,
53
+ * and whose value is an object containing attributes.
54
+ */
55
+ export function isCoherentElement(node, sourceFile) {
56
+ if (!ts.isObjectLiteralExpression(node)) {
57
+ return false;
58
+ }
59
+ // Must have exactly one property
60
+ if (node.properties.length !== 1) {
61
+ return false;
62
+ }
63
+ const prop = node.properties[0];
64
+ if (!ts.isPropertyAssignment(prop)) {
65
+ return false;
66
+ }
67
+ // Property name must be an HTML element
68
+ const name = prop.name;
69
+ let tagName;
70
+ if (ts.isIdentifier(name)) {
71
+ tagName = name.text;
72
+ }
73
+ else if (ts.isStringLiteral(name)) {
74
+ tagName = name.text;
75
+ }
76
+ else {
77
+ return false;
78
+ }
79
+ return HTML_ELEMENTS.has(tagName);
80
+ }
81
+ /**
82
+ * Extract element information from a Coherent element node.
83
+ */
84
+ function extractElementInfo(node, sourceFile, parent = null) {
85
+ const prop = node.properties[0];
86
+ const name = prop.name;
87
+ let tagName;
88
+ if (ts.isIdentifier(name)) {
89
+ tagName = name.text;
90
+ }
91
+ else if (ts.isStringLiteral(name)) {
92
+ tagName = name.text;
93
+ }
94
+ else {
95
+ tagName = 'unknown';
96
+ }
97
+ const elementInfo = {
98
+ tagName,
99
+ range: tsRangeToLsp(sourceFile, node.getStart(sourceFile), node.getEnd()),
100
+ tagNameRange: tsRangeToLsp(sourceFile, name.getStart(sourceFile), name.getEnd()),
101
+ attributes: [],
102
+ parent,
103
+ children: [],
104
+ node,
105
+ };
106
+ // Extract attributes from the property value
107
+ if (ts.isObjectLiteralExpression(prop.initializer)) {
108
+ const attrs = prop.initializer;
109
+ for (const attrProp of attrs.properties) {
110
+ if (ts.isPropertyAssignment(attrProp)) {
111
+ const attrName = ts.isIdentifier(attrProp.name)
112
+ ? attrProp.name.text
113
+ : ts.isStringLiteral(attrProp.name)
114
+ ? attrProp.name.text
115
+ : null;
116
+ if (attrName) {
117
+ const attrInfo = {
118
+ name: attrName,
119
+ value: attrProp.initializer.getText(sourceFile),
120
+ range: tsRangeToLsp(sourceFile, attrProp.getStart(sourceFile), attrProp.getEnd()),
121
+ nameRange: tsRangeToLsp(sourceFile, attrProp.name.getStart(sourceFile), attrProp.name.getEnd()),
122
+ node: attrProp,
123
+ };
124
+ elementInfo.attributes.push(attrInfo);
125
+ // Check for children array and extract nested elements
126
+ if (attrName === 'children') {
127
+ extractChildElements(attrProp.initializer, sourceFile, elementInfo);
128
+ }
129
+ }
130
+ }
131
+ else if (ts.isShorthandPropertyAssignment(attrProp)) {
132
+ const attrName = attrProp.name.text;
133
+ const attrInfo = {
134
+ name: attrName,
135
+ value: attrName, // Shorthand uses same name as value
136
+ range: tsRangeToLsp(sourceFile, attrProp.getStart(sourceFile), attrProp.getEnd()),
137
+ nameRange: tsRangeToLsp(sourceFile, attrProp.name.getStart(sourceFile), attrProp.name.getEnd()),
138
+ node: attrProp,
139
+ };
140
+ elementInfo.attributes.push(attrInfo);
141
+ }
142
+ }
143
+ }
144
+ return elementInfo;
145
+ }
146
+ /**
147
+ * Extract child Coherent elements from a children property value.
148
+ */
149
+ function extractChildElements(node, sourceFile, parent) {
150
+ if (ts.isArrayLiteralExpression(node)) {
151
+ // Children array
152
+ for (const element of node.elements) {
153
+ if (isCoherentElement(element, sourceFile)) {
154
+ const childInfo = extractElementInfo(element, sourceFile, parent);
155
+ parent.children.push(childInfo);
156
+ }
157
+ }
158
+ }
159
+ else if (isCoherentElement(node, sourceFile)) {
160
+ // Single child element
161
+ const childInfo = extractElementInfo(node, sourceFile, parent);
162
+ parent.children.push(childInfo);
163
+ }
164
+ }
165
+ /**
166
+ * Find all Coherent.js elements in a source file.
167
+ *
168
+ * @param sourceFile - TypeScript source file to analyze
169
+ * @returns Array of found Coherent element info
170
+ */
171
+ export function findCoherentElements(sourceFile) {
172
+ const elements = [];
173
+ const processed = new WeakSet();
174
+ function visit(node, parent = null) {
175
+ if (isCoherentElement(node, sourceFile) && !processed.has(node)) {
176
+ processed.add(node);
177
+ const elementInfo = extractElementInfo(node, sourceFile, parent);
178
+ elements.push(elementInfo);
179
+ // Also collect nested elements that were added as children
180
+ function collectChildren(el) {
181
+ for (const child of el.children) {
182
+ elements.push(child);
183
+ collectChildren(child);
184
+ }
185
+ }
186
+ collectChildren(elementInfo);
187
+ }
188
+ else {
189
+ ts.forEachChild(node, (child) => visit(child, parent));
190
+ }
191
+ }
192
+ visit(sourceFile);
193
+ return elements;
194
+ }
195
+ /**
196
+ * Get the Coherent element at a specific position.
197
+ *
198
+ * @param sourceFile - TypeScript source file
199
+ * @param position - LSP position
200
+ * @param elements - Previously found elements (optional, will find if not provided)
201
+ * @returns Element info at position or undefined
202
+ */
203
+ export function getElementAtPosition(sourceFile, position, elements) {
204
+ const allElements = elements || findCoherentElements(sourceFile);
205
+ // Find the most specific (deepest) element containing the position
206
+ let result;
207
+ for (const element of allElements) {
208
+ if (isPositionInRange(position, element.range)) {
209
+ // Prefer more specific (smaller range) element
210
+ if (!result || isRangeContained(element.range, result.range)) {
211
+ result = element;
212
+ }
213
+ }
214
+ }
215
+ return result;
216
+ }
217
+ /**
218
+ * Check if range a is contained within range b.
219
+ */
220
+ function isRangeContained(a, b) {
221
+ if (a.start.line > b.start.line || (a.start.line === b.start.line && a.start.character >= b.start.character)) {
222
+ if (a.end.line < b.end.line || (a.end.line === b.end.line && a.end.character <= b.end.character)) {
223
+ return true;
224
+ }
225
+ }
226
+ return false;
227
+ }
228
+ /**
229
+ * Get the context at a specific position for completion/hover.
230
+ *
231
+ * @param sourceFile - TypeScript source file
232
+ * @param position - LSP position
233
+ * @returns Context information
234
+ */
235
+ export function getPositionContext(sourceFile, position) {
236
+ const elements = findCoherentElements(sourceFile);
237
+ const element = getElementAtPosition(sourceFile, position, elements);
238
+ if (!element) {
239
+ return { type: 'outside' };
240
+ }
241
+ // Check if on tag name
242
+ if (isPositionInRange(position, element.tagNameRange)) {
243
+ return { type: 'tag-name', element };
244
+ }
245
+ // Check if on an attribute
246
+ for (const attr of element.attributes) {
247
+ if (isPositionInRange(position, attr.nameRange)) {
248
+ return { type: 'attribute-name', element, attribute: attr };
249
+ }
250
+ if (isPositionInRange(position, attr.range) && !isPositionInRange(position, attr.nameRange)) {
251
+ return { type: 'attribute-value', element, attribute: attr };
252
+ }
253
+ }
254
+ // Check if in children context (after children: [ or just after children:)
255
+ const offset = lspPositionToTs(sourceFile, position);
256
+ const text = sourceFile.text;
257
+ // Look backward from position to see context
258
+ let i = offset - 1;
259
+ while (i >= 0 && (text[i] === ' ' || text[i] === '\n' || text[i] === '\r' || text[i] === '\t')) {
260
+ i--;
261
+ }
262
+ if (i >= 0) {
263
+ // Check if we're right after [ or { in a children context
264
+ if (text[i] === '[' || text[i] === '{' || text[i] === ',') {
265
+ // Check if this is within a children attribute
266
+ const childrenAttr = element.attributes.find(a => a.name === 'children');
267
+ if (childrenAttr && isPositionInRange(position, childrenAttr.range)) {
268
+ return { type: 'children', element };
269
+ }
270
+ }
271
+ }
272
+ // Default to attribute-name context if inside element but not on specific attribute
273
+ // This allows completion of new attributes
274
+ return { type: 'attribute-name', element };
275
+ }
276
+ /**
277
+ * Create a TypeScript source file from text content.
278
+ *
279
+ * @param content - Source file content
280
+ * @param fileName - File name for the source file
281
+ * @returns TypeScript SourceFile
282
+ */
283
+ export function createSourceFile(content, fileName) {
284
+ return ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true, // setParentNodes
285
+ ts.ScriptKind.TSX // Support both JS and TSX syntax
286
+ );
287
+ }
288
+ //# sourceMappingURL=coherent-analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coherent-analyzer.js","sourceRoot":"","sources":["../../src/analysis/coherent-analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Element Attribute Validator
3
+ *
4
+ * Validates attributes on Coherent.js elements against the allowed
5
+ * attributes for each HTML element type.
6
+ */
7
+ import { Range } from 'vscode-languageserver';
8
+ import { CoherentElementInfo } from './coherent-analyzer.js';
9
+ /**
10
+ * Validation error for an attribute.
11
+ */
12
+ export interface AttributeValidationError {
13
+ /** Error message */
14
+ message: string;
15
+ /** Source range of the error */
16
+ range: Range;
17
+ /** Error code for categorization */
18
+ code: 'invalid-attribute' | 'typo-attribute' | 'void-children';
19
+ /** Severity of the error */
20
+ severity: 'error' | 'warning';
21
+ /** Suggestion for fix (for typos) */
22
+ suggestion?: string;
23
+ /** Additional data for code actions */
24
+ data?: Record<string, unknown>;
25
+ }
26
+ /**
27
+ * Validate attributes on a Coherent element.
28
+ *
29
+ * Checks for:
30
+ * - Invalid attribute names for the element
31
+ * - Typos in attribute names (with suggestions)
32
+ * - Children on void elements
33
+ *
34
+ * @param element - The Coherent element to validate
35
+ * @returns Array of validation errors
36
+ */
37
+ export declare function validateAttributes(element: CoherentElementInfo): AttributeValidationError[];
38
+ /**
39
+ * Validate multiple elements at once.
40
+ *
41
+ * @param elements - Array of Coherent elements to validate
42
+ * @returns Array of all validation errors
43
+ */
44
+ export declare function validateAllAttributes(elements: CoherentElementInfo[]): AttributeValidationError[];
45
+ //# sourceMappingURL=element-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-validator.d.ts","sourceRoot":"","sources":["../../src/analysis/element-validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAiB,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAO5E;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAEhB,gCAAgC;IAChC,KAAK,EAAE,KAAK,CAAC;IAEb,oCAAoC;IACpC,IAAI,EAAE,mBAAmB,GAAG,gBAAgB,GAAG,eAAe,CAAC;IAE/D,4BAA4B;IAC5B,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAE9B,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,wBAAwB,EAAE,CAsD3F;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,wBAAwB,EAAE,CAQjG"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Element Attribute Validator
3
+ *
4
+ * Validates attributes on Coherent.js elements against the allowed
5
+ * attributes for each HTML element type.
6
+ */
7
+ import { isValidAttribute, isVoidElement, getSuggestions, } from '../data/element-attributes.js';
8
+ /**
9
+ * Validate attributes on a Coherent element.
10
+ *
11
+ * Checks for:
12
+ * - Invalid attribute names for the element
13
+ * - Typos in attribute names (with suggestions)
14
+ * - Children on void elements
15
+ *
16
+ * @param element - The Coherent element to validate
17
+ * @returns Array of validation errors
18
+ */
19
+ export function validateAttributes(element) {
20
+ const errors = [];
21
+ const tagName = element.tagName;
22
+ for (const attr of element.attributes) {
23
+ // Skip Coherent-specific properties that we handle separately
24
+ if (attr.name === 'children' || attr.name === 'text' || attr.name === 'html' || attr.name === 'key') {
25
+ // Check for children on void elements
26
+ if (attr.name === 'children' && isVoidElement(tagName)) {
27
+ errors.push({
28
+ message: `<${tagName}> is a void element and cannot have children`,
29
+ range: attr.range,
30
+ code: 'void-children',
31
+ severity: 'error',
32
+ data: { tagName, attributeName: attr.name },
33
+ });
34
+ }
35
+ continue;
36
+ }
37
+ // Check if attribute is valid for this element
38
+ if (!isValidAttribute(tagName, attr.name)) {
39
+ // Check for typo suggestions
40
+ const suggestions = getSuggestions(tagName, attr.name);
41
+ if (suggestions.length > 0) {
42
+ errors.push({
43
+ message: `Unknown attribute '${attr.name}' on <${tagName}>. Did you mean '${suggestions[0]}'?`,
44
+ range: attr.nameRange,
45
+ code: 'typo-attribute',
46
+ severity: 'error',
47
+ suggestion: suggestions[0],
48
+ data: {
49
+ tagName,
50
+ attributeName: attr.name,
51
+ suggestions,
52
+ },
53
+ });
54
+ }
55
+ else {
56
+ errors.push({
57
+ message: `Attribute '${attr.name}' is not valid for <${tagName}>`,
58
+ range: attr.nameRange,
59
+ code: 'invalid-attribute',
60
+ severity: 'error',
61
+ data: {
62
+ tagName,
63
+ attributeName: attr.name,
64
+ },
65
+ });
66
+ }
67
+ }
68
+ }
69
+ return errors;
70
+ }
71
+ /**
72
+ * Validate multiple elements at once.
73
+ *
74
+ * @param elements - Array of Coherent elements to validate
75
+ * @returns Array of all validation errors
76
+ */
77
+ export function validateAllAttributes(elements) {
78
+ const errors = [];
79
+ for (const element of elements) {
80
+ errors.push(...validateAttributes(element));
81
+ }
82
+ return errors;
83
+ }
84
+ //# sourceMappingURL=element-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-validator.js","sourceRoot":"","sources":["../../src/analysis/element-validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,+BAA+B,CAAC;AAyBvC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA4B;IAC7D,MAAM,MAAM,GAA+B,EAAE,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAEhC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QACtC,8DAA8D;QAC9D,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACpG,sCAAsC;YACtC,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;gBACvD,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,IAAI,OAAO,8CAA8C;oBAClE,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,IAAI,EAAE,eAAe;oBACrB,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,CAAC,IAAI,EAAE;iBAC5C,CAAC,CAAC;YACL,CAAC;YACD,SAAS;QACX,CAAC;QAED,+CAA+C;QAC/C,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,6BAA6B;YAC7B,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAEvD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,sBAAsB,IAAI,CAAC,IAAI,SAAS,OAAO,oBAAoB,WAAW,CAAC,CAAC,CAAC,IAAI;oBAC9F,KAAK,EAAE,IAAI,CAAC,SAAS;oBACrB,IAAI,EAAE,gBAAgB;oBACtB,QAAQ,EAAE,OAAO;oBACjB,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;oBAC1B,IAAI,EAAE;wBACJ,OAAO;wBACP,aAAa,EAAE,IAAI,CAAC,IAAI;wBACxB,WAAW;qBACZ;iBACF,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,cAAc,IAAI,CAAC,IAAI,uBAAuB,OAAO,GAAG;oBACjE,KAAK,EAAE,IAAI,CAAC,SAAS;oBACrB,IAAI,EAAE,mBAAmB;oBACzB,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE;wBACJ,OAAO;wBACP,aAAa,EAAE,IAAI,CAAC,IAAI;qBACzB;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAA+B;IACnE,MAAM,MAAM,GAA+B,EAAE,CAAC;IAE9C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}