Skip to content

Commit 65b837d

Browse files
custom elements are now declared in settings.json
1 parent a3f7d4c commit 65b837d

5 files changed

Lines changed: 150 additions & 43 deletions

File tree

README.md

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,46 @@ Core validation and parsing classes:
174174

175175
- `CustomElement` - The central class of the project that handles custom element processing and validation. To add custom attributes for a specific tag, please add it here in the right place in the hierarchie.
176176

177+
## Add Custom Elements
178+
179+
- `To add a custom elements:`
180+
181+
- Search `customtags` or `Cntr + , then search customtags` then add an element with the following structure
182+
183+
`Valid example`
184+
185+
```Text
186+
"html-css-template-validator.customTags": [
187+
{
188+
"name": "input",
189+
"description": "demo",
190+
"attributes": [
191+
{"name": ".checked", "description": "demo"},
192+
{"name": "a-valid-attribut-for-input", "description": "demo2"}
193+
] //Make sure to add all Attributes to the Tag and do not add single Attribute to single Tag.
194+
// for each Tag is only one declaration valid do not create more than one
195+
},
196+
197+
```
198+
199+
`Invalid example`
200+
177201
```Text
178-
{
179-
name: 'input',
180-
description: 'Custom web component',
181-
attributes: [
182-
{ name: 'a-random-valid-tag-only-for-input', description: 'Custom attribute' }
183-
]
184-
}, ...
202+
"html-css-template-validator.customTags": [
203+
{
204+
"name": "input",
205+
"description": "demo",
206+
"attributes": [
207+
{"name": ".checked", "description": "demo"}
208+
]
209+
},
210+
{
211+
"name": "input",
212+
"description": "demo",
213+
"attributes": [
214+
{"name": "a-valid-attribut-for-input", "description": "demo2"}
215+
]
216+
},
185217
186218
```
187219

package.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,39 @@
1414
],
1515
"main": "./out/extension.js",
1616
"contributes": {
17+
"configuration": {
18+
"title": "Html-Css Template Validator",
19+
"properties": {
20+
"html-css-template-validator.customTags": {
21+
"type": "array",
22+
"default": [],
23+
"scope": "resource",
24+
"description": "Custom tags + attributes (leer = keine Custom-Regeln).",
25+
"items": {
26+
"type": "object",
27+
"required": ["name", "attributes"],
28+
"additionalProperties": false,
29+
"properties": {
30+
"name": { "type": "string" },
31+
"description": { "type": "string", "default": "" },
32+
"attributes": {
33+
"type": "array",
34+
"default": [],
35+
"items": {
36+
"type": "object",
37+
"required": ["name"],
38+
"additionalProperties": false,
39+
"properties": {
40+
"name": { "type": "string" },
41+
"description": { "type": "string", "default": "" }
42+
}
43+
}
44+
}
45+
}
46+
}
47+
}
48+
}
49+
},
1750
"grammars": [
1851
{
1952
"injectTo": [

src/class/HtmlValidator.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,25 @@ import { IHtmlValidator } from "../interface/IHtmlValidator";
1010

1111
type Parse5 = typeof import("parse5", { with: { "resolution-mode": "import" } });
1212
const htmlLanguageService = htmlService.getDefaultHTMLDataProvider();
13-
const customHtmlElements = new CustomElement();
1413

1514
export class HtmlValidator implements IHtmlValidator {
1615

1716
private validTagNames: Set<string>;
1817
private ps: Parse5;
1918
private document: vscode.TextDocument;
19+
private customHtmlElements: CustomElement;
2020
diagnosticCollection: vscode.Diagnostic[];
2121

22-
constructor(ps: Parse5,document: vscode.TextDocument,diagnosticCollection: vscode.Diagnostic[]) {
22+
constructor(ps: Parse5,document: vscode.TextDocument,diagnosticCollection: vscode.Diagnostic[],userConfig: vscode.WorkspaceConfiguration) {
2323
this.ps = ps;
2424
this.document = document;
2525
this.diagnosticCollection = diagnosticCollection;
26+
27+
this.customHtmlElements = new CustomElement(userConfig);
28+
2629
this.validTagNames = new Set(htmlLanguageService.provideTags().map(t => t.name));
30+
for (const t of this.customHtmlElements.provideTags()) this.validTagNames.add(t.name);
31+
2732
}
2833

2934
validate(htmlTemplateArray: Array<{ htmlTemplate: HtmlTagTemplate }>): void {
@@ -76,7 +81,7 @@ export class HtmlValidator implements IHtmlValidator {
7681
);
7782

7883
const customValidAttributeNames = new Set(
79-
customHtmlElements.provideAttributes(tagName).map(t => t.name)
84+
this.customHtmlElements.provideAttributes(tagName).map(t => t.name)
8085
);
8186

8287
return validAttributeNames.has(attributeName) ||

src/class/customElement.ts

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,50 @@
11
import { IHTMLDataProvider, ITagData, IAttributeData, IValueData } from 'vscode-html-languageservice';
2+
import * as vscode from "vscode"
23

4+
type CustomTagSetting = {
5+
name: string;
6+
description?: string;
7+
attributes?: Array<{ name: string; description?: string }>;
8+
};
9+
10+
function normalizeCustomTags(raw: unknown): ITagData[] {
11+
if (!Array.isArray(raw)) return [];
12+
13+
return raw
14+
.filter((t): t is CustomTagSetting => !!t && typeof (t as any).name === "string")
15+
.map((t) => {
16+
const attributesRaw = Array.isArray(t.attributes) ? t.attributes : [];
17+
const attributes: IAttributeData[] = attributesRaw
18+
.filter((a) => a && typeof (a as any).name === "string")
19+
.map((a) => ({
20+
name: String(a.name),
21+
description: typeof a.description === "string" ? a.description : ""
22+
}));
23+
24+
return {
25+
name: String(t.name),
26+
description: typeof t.description === "string" ? t.description : "",
27+
attributes
28+
};
29+
});
30+
}
31+
32+
function mergeTagsByName(defaults: ITagData[], user: ITagData[]): ITagData[] {
33+
const map = new Map<string, ITagData>();
34+
for (const t of defaults) map.set(t.name, t);
35+
for (const t of user) map.set(t.name, t); // user überschreibt defaults bei gleichem Namen
36+
return [...map.values()];
37+
}
338
// Eigener Data Provider
439
class CustomElement implements IHTMLDataProvider {
5-
private customTags: ITagData[] = [
6-
{
7-
name: 'input',
8-
description: 'Custom web component',
9-
attributes: [
10-
{ name: '.checked', description: 'Custom attribute' },
11-
{ name: 'a-valid-attribut-for-input', description: 'Custom attribute' }
12-
]
13-
},
14-
{
15-
name: 'img',
16-
description: 'Custom web component',
17-
attributes: [
18-
{ name: 'a-valid-attribut-for-img', description: 'Custom attribute' }
19-
]
20-
}
21-
];
40+
41+
private customTags: ITagData[];
42+
43+
constructor(userConfig?: vscode.WorkspaceConfiguration) {
44+
const cfg = userConfig ?? vscode.workspace.getConfiguration("html-css-template-validator");
45+
const raw = cfg.get<unknown>("customTags", []);
46+
this.customTags = normalizeCustomTags(raw);
47+
}
2248

2349
getId(): string {
2450
return 'custom-html-provider';
@@ -33,12 +59,7 @@ class CustomElement implements IHTMLDataProvider {
3359
}
3460

3561
provideAttributes(tag: string): IAttributeData[] {
36-
const tagData = this.customTags.find(t => t.name === tag);
37-
const tagSpecificAttributes = tagData?.attributes || [];
38-
39-
return [
40-
...tagSpecificAttributes
41-
];
62+
return this.customTags.find((t) => t.name === tag)?.attributes ?? [];
4263
}
4364

4465
provideValues(tag: string, attribute: string): IValueData[] {

src/extension.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ function htmlValidator(
1414
ps: Parse5,
1515
htmlTemplateArray: Array<{ htmlTemplate: HtmlTagTemplate }>,
1616
document: vscode.TextDocument,
17-
diagnosticCollection: vscode.Diagnostic[]
17+
diagnosticCollection: vscode.Diagnostic[],
18+
userConfig: vscode.WorkspaceConfiguration
1819
):void
1920
{
2021
const validator = new HtmlValidator(
2122
ps,
2223
document,
2324
diagnosticCollection,
25+
userConfig
2426
).validate(htmlTemplateArray);
2527
}
2628

@@ -41,37 +43,51 @@ export async function activate(context: vscode.ExtensionContext) {
4143
const ps = await import("parse5");
4244

4345
const diagnostics = vscode.languages.createDiagnosticCollection("myExtension");
46+
47+
4448

4549
context.subscriptions.push(diagnostics);
4650

4751
vscode.window.showInformationMessage("Validator is now active");
52+
4853
const timers = new Map<string, NodeJS.Timeout>();
54+
const supported = new Set(["typescript","javascript","typescriptreact","javascriptreact"]);
4955
const schedule = (doc: vscode.TextDocument) => {
50-
if (timers.get(doc.uri.toString())) clearTimeout(timers.get(doc.uri.toString()));
56+
if (!supported.has(doc.languageId)) {
57+
errorCollection.delete(doc.uri);
58+
return;
59+
}
60+
const key = doc.uri.toString();
61+
if (timers.get(key)) clearTimeout(timers.get(key));
5162
timers.set
5263
(
53-
doc.uri.toString(), setTimeout
64+
key, setTimeout
5465
(() =>
5566
{
67+
timers.delete(key);
5668
const diagnosticCollection: vscode.Diagnostic[] = [];
57-
69+
const userConfig = vscode.workspace.getConfiguration("html-css-template-validator");
5870
const htmlTemplates = extractHtmlTemplateBlock(doc);
5971
if (!htmlTemplates){return}
60-
htmlValidator(ps,htmlTemplates,doc,diagnosticCollection);
72+
htmlValidator(ps,htmlTemplates,doc,diagnosticCollection,userConfig);
6173
const cssTemplates = extractCssTemplateBlock(doc)
6274
if(!cssTemplates){return}
6375
cssValidator(cssTemplates,doc,diagnosticCollection)
6476

6577
errorCollection.set(doc.uri,diagnosticCollection)
66-
}
67-
)
68-
)
69-
}
78+
},300))}
7079

7180
context.subscriptions.push(
7281
vscode.workspace.onDidChangeTextDocument((e) => schedule(e.document)),
7382
vscode.workspace.onDidOpenTextDocument((doc) => schedule(doc)),
74-
vscode.workspace.onDidCloseTextDocument((doc) => diagnostics.delete(doc.uri))
83+
vscode.workspace.onDidCloseTextDocument((doc) => diagnostics.delete(doc.uri)),
84+
vscode.workspace.onDidChangeConfiguration((e) => {
85+
if (e.affectsConfiguration("html-css-template-validator")) {
86+
for (const doc of vscode.workspace.textDocuments) {
87+
if (["typescript", "javascript", "typescriptreact", "javascriptreact"].includes(doc.languageId)) {
88+
schedule(doc);
89+
}}}
90+
})
7591
);
7692
};
7793
export function deactivate() {}

0 commit comments

Comments
 (0)