@html-validate/plugin-template 0.0.1 → 1.0.2

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,23 @@
1
+ # @html-validate/plugin-template changelog
2
+
3
+ ## 1.0.2 (2026-06-25)
4
+
5
+ ### Bug Fixes
6
+
7
+ - **deps:** update cloneman to v1.18.1 ([881c389](https://gitlab.com/html-validate/plugin-template/commit/881c3893e2cf5323c83e7658f1b4883a8c262be6))
8
+
9
+ ## 1.0.1 (2026-06-21)
10
+
11
+ ### Bug Fixes
12
+
13
+ - ignore eslint and prettier dependencies ([1f49d2a](https://gitlab.com/html-validate/plugin-template/commit/1f49d2a5964150df02342dbf22a0090bb2eb68e1))
14
+
15
+ ## 1.0.0 (2026-06-21)
16
+
17
+ ### Features
18
+
19
+ - initial version ([1ac7862](https://gitlab.com/html-validate/plugin-template/commit/1ac786208f5f0f91b7d0f0f8bee6d9ff52e23062))
20
+
21
+ ### Bug Fixes
22
+
23
+ - no need to rerun test-files ([e7a7391](https://gitlab.com/html-validate/plugin-template/commit/e7a73910ee1e2b077e0f076e6fa236e295c7c26a))
package/files/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018-2026 David Sveningsson <ext@sidvind.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,11 @@
1
+ # @html-validate/plugin-template
2
+
3
+ [Cloneman] template for [HTML-Validate][html-validate] plugins.
4
+
5
+ [cloneman]: https://github.com/Forsakringskassan/cloneman
6
+ [html-validate]: https://www.npmjs.com/package/html-validate
7
+
8
+ ## Usage
9
+
10
+ npx cloneman create awesome-plugin @html-validate/plugin-template
11
+ npx cloneman update
@@ -0,0 +1,20 @@
1
+ # EditorConfig: http://EditorConfig.org
2
+ #
3
+ # This file is managed by "@html-validate/plugin-template".
4
+ # Changes will be overwritten.
5
+
6
+ root = true
7
+
8
+ [*]
9
+ insert_final_newline = true
10
+ trim_trailing_whitespace = true
11
+ indent_style = tab
12
+ charset = utf-8
13
+
14
+ [*.json]
15
+ indent_style = space
16
+ indent_size = 2
17
+
18
+ [*.yml]
19
+ indent_style = space
20
+ indent_size = 2
@@ -0,0 +1,9 @@
1
+ # This file is managed by "@html-validate/plugin-template".
2
+ # Changes will be overwritten.
3
+
4
+ *.tgz
5
+ .eslintcache
6
+ dist/
7
+ coverage/
8
+ node_modules/
9
+ temp/
@@ -0,0 +1,117 @@
1
+ image: node:24
2
+
3
+ include:
4
+ - component: $CI_SERVER_FQDN/html-validate/gitlab-ci/release@master
5
+ inputs:
6
+ legacy: false
7
+ on-push: true
8
+
9
+ stages:
10
+ - prepare
11
+ - test
12
+ - build
13
+ - compatibility
14
+ - release
15
+
16
+ NPM:
17
+ stage: prepare
18
+ artifacts:
19
+ name: ${CI_PROJECT_PATH_SLUG}-${CI_PIPELINE_ID}-npm
20
+ paths:
21
+ - node_modules/
22
+ - packages/*/node_modules/
23
+ reports:
24
+ dependency_scanning: gl-dependency-scanning.json
25
+ script:
26
+ - node --version
27
+ - npm --version
28
+ - npm ci --no-fund --no-audit --no-update-notifier
29
+ - npm audit --json --production | npx --yes gitlab-npm-audit-parser -o gl-dependency-scanning.json || true
30
+ - test -z "${UPSTREAM_VERSION}" || npm install "html-validate@${UPSTREAM_VERSION}"
31
+
32
+ ESLint:
33
+ stage: test
34
+ script:
35
+ - npm exec eslint-config -- --check
36
+ - npm run eslint -- --max-warnings 0
37
+
38
+ Jest:
39
+ stage: test
40
+ coverage: /Branches\s+:\s(\d+.\d+%)/
41
+ artifacts:
42
+ name: ${CI_PROJECT_PATH_SLUG}-${CI_PIPELINE_ID}-coverage
43
+ paths:
44
+ - coverage
45
+ reports:
46
+ junit: temp/jest.xml
47
+ before_script:
48
+ - npm run --if-present build
49
+ script:
50
+ - npm test
51
+
52
+ Prettier:
53
+ stage: test
54
+ script:
55
+ - npm run prettier:check
56
+
57
+ attw:
58
+ stage: test
59
+ needs: []
60
+ script:
61
+ - npm ci
62
+ - npm run build
63
+ - npm run attw
64
+
65
+ Build:
66
+ stage: build
67
+ needs:
68
+ - NPM
69
+ dependencies:
70
+ - NPM
71
+ artifacts:
72
+ name: ${CI_PROJECT_PATH_SLUG}-${CI_PIPELINE_ID}-build
73
+ paths:
74
+ - dist
75
+ script:
76
+ - npm run --if-present build
77
+ - node dist/cjs/index.cjs
78
+ - node dist/esm/index.mjs
79
+ - npm pack
80
+ - npm exec npm-pkg-lint
81
+
82
+ .compat: &compat
83
+ stage: compatibility
84
+ dependencies:
85
+ - NPM
86
+ - Build
87
+ needs:
88
+ - NPM
89
+ - Build
90
+ script:
91
+ - for spec in test/*.spec.ts; do sed 's#../src#../dist/cjs/index.cjs#g' -i "${spec}"; done
92
+ - sed '/dist/s#jest.mock#//#' -i "test/htmlvalidate.spec.ts"
93
+ - npm test -- --no-coverage test
94
+
95
+ Node:
96
+ extends: .compat
97
+ parallel:
98
+ matrix:
99
+ - VERSION:
100
+ - 22
101
+ - 24
102
+ image: "node:${VERSION}"
103
+
104
+ HTML-validate:
105
+ extends: .compat
106
+ parallel:
107
+ matrix:
108
+ - VERSION:
109
+ - 8
110
+ - 9
111
+ - 10
112
+ - 11
113
+ before_script:
114
+ - test ${VERSION} -lt 10 && npm install jest@29 jest-environment-jsdom@29 jest-snapshot@29 jest-diff@29 jest-runtime@29 @types/jest@29
115
+ - npm install $(npx -y npm-min-peer html-validate --major ${VERSION} --with-name)
116
+ - npm ls html-validate
117
+ - npm ls jest
@@ -0,0 +1,9 @@
1
+ /* This file is managed by "@html-validate/plugin-template". Changes will be overwritten */
2
+
3
+ import Plugin from "./dist/esm/index.mjs";
4
+
5
+ export default {
6
+ plugins: [Plugin],
7
+ extends: ["html-validate:recommended", `${Plugin.name}:recommended`],
8
+ elements: ["html5"],
9
+ };
package/files/_npmrc ADDED
@@ -0,0 +1,3 @@
1
+ save-exact = true
2
+ provenance = true
3
+ strict-allow-scripts = true
@@ -0,0 +1,9 @@
1
+ # This file is managed by "@html-validate/plugin-template".
2
+ # Changes will be overwritten.
3
+
4
+ # generated by npm
5
+ package-lock.json
6
+
7
+ # compiled files
8
+ dist/
9
+ coverage/
@@ -0,0 +1,37 @@
1
+ /* This file is managed by "@html-validate/plugin-template". Changes will be overwritten */
2
+
3
+ import fs from "node:fs/promises";
4
+ import { analyzeMetafile, build as esbuild } from "esbuild";
5
+
6
+ const entrypoint = {
7
+ cjs: "src/entry-cjs.ts",
8
+ esm: "src/entry-esm.ts",
9
+ };
10
+
11
+ const extension = {
12
+ cjs: ".cjs",
13
+ esm: ".mjs",
14
+ };
15
+
16
+ await fs.rm("dist", { recursive: true, force: true });
17
+
18
+ for (const format of ["cjs", "esm"]) {
19
+ const result = await esbuild({
20
+ entryPoints: [{ in: entrypoint[format], out: "index" }],
21
+ outdir: `dist/${format}`,
22
+ bundle: true,
23
+ platform: "node",
24
+ format,
25
+ target: "node22",
26
+ logLevel: "info",
27
+ sourcemap: true,
28
+ metafile: true,
29
+ outExtension: {
30
+ ".js": extension[format],
31
+ },
32
+ external: ["@html-validate/plugin-utils", "html-validate"],
33
+ });
34
+ if (format === "esm") {
35
+ console.log(await analyzeMetafile(result.metafile));
36
+ }
37
+ }
@@ -0,0 +1,37 @@
1
+ /* This file is managed by @html-validate/eslint-config */
2
+ /* Changes may be overwritten */
3
+
4
+ import defaultConfig from "@html-validate/eslint-config";
5
+ import jestConfig from "@html-validate/eslint-config-jest";
6
+ import typescriptConfig from "@html-validate/eslint-config-typescript";
7
+ import typescriptTypeinfoConfig from "@html-validate/eslint-config-typescript-typeinfo";
8
+
9
+ export default [
10
+ ...defaultConfig({ type: "commonjs" }),
11
+
12
+ {
13
+ name: "@html-validate/eslint-config-typescript",
14
+ files: ["**/*.{ts,cts,mts}"],
15
+ ...typescriptConfig,
16
+ },
17
+
18
+ {
19
+ name: "@html-validate/eslint-config-typeinfo",
20
+ files: ["src/**/*.{ts,cts,mts}"],
21
+ ignores: ["src/**/*.spec.ts"],
22
+ languageOptions: {
23
+ parserOptions: {
24
+ tsconfigRootDir: import.meta.dirname,
25
+ projectService: true,
26
+ },
27
+ },
28
+ ...typescriptTypeinfoConfig,
29
+ },
30
+
31
+ {
32
+ name: "@html-validate/eslint-config-jest",
33
+ files: ["**/*.spec.[jt]s"],
34
+ ignores: ["cypress/**", "tests/e2e/**"],
35
+ ...jestConfig,
36
+ },
37
+ ];
@@ -0,0 +1,117 @@
1
+ {
2
+ "name": "${name}",
3
+ "version": "${version}",
4
+ "description": "${description}",
5
+ "keywords": [
6
+ "cloneman",
7
+ "html-validate"
8
+ ],
9
+ "homepage": "https://gitlab.com/html-validate/plugin-template",
10
+ "bugs": {
11
+ "url": "https://gitlab.com/html-validate/plugin-template/-/work_items/new?type=Issue"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://gitlab.com/html-validate/plugin-template.git"
16
+ },
17
+ "license": "MIT",
18
+ "author": "David Sveningsson <ext@sidvind.com>",
19
+ "exports": {
20
+ ".": {
21
+ "import": {
22
+ "types": "./dist/esm/index.d.mts",
23
+ "default": "./dist/esm/index.mjs"
24
+ },
25
+ "require": {
26
+ "types": "./dist/cjs/index.d.cts",
27
+ "default": "./dist/cjs/index.cjs"
28
+ }
29
+ }
30
+ },
31
+ "main": "dist/cjs/index.cjs",
32
+ "module": "dist/esm/index.mjs",
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "scripts": {
37
+ "prebuild": "tsc",
38
+ "build": "run-s build:*",
39
+ "postbuild": "cloneman build -o temp/cloneman",
40
+ "build:lib": "node build.mjs",
41
+ "build:cjs": "dts-bundle-generator --no-banner --project tsconfig.cjs.json -o dist/cjs/index.d.cts src/entry-cjs.ts",
42
+ "build:esm": "dts-bundle-generator --no-banner --project tsconfig.esm.json -o dist/esm/index.d.mts src/entry-esm.ts",
43
+ "eslint": "eslint --cache .",
44
+ "eslint:fix": "eslint --cache --fix .",
45
+ "prepack": "release-prepack package.json --bundle",
46
+ "postpack": "release-postpack package.json",
47
+ "prepare": "husky",
48
+ "prepublishOnly": "release-prepack package.json --bundle",
49
+ "prettier:check": "prettier --check .",
50
+ "prettier:write": "prettier --write .",
51
+ "postpublish": "release-postpack package.json",
52
+ "attw": "attw --no-emoji --pack .",
53
+ "test": "jest"
54
+ },
55
+ "commitlint": {
56
+ "extends": "@html-validate"
57
+ },
58
+ "prettier": "@html-validate/prettier-config",
59
+ "release": {
60
+ "extends": "@html-validate/semantic-release-cloneman-config"
61
+ },
62
+ "jest": {
63
+ "preset": "@html-validate/jest-config"
64
+ },
65
+ "dependencies": {
66
+ "@html-validate/plugin-utils": "^4.0.0"
67
+ },
68
+ "devDependencies": {
69
+ "@arethetypeswrong/cli": "0.18.4",
70
+ "@html-validate/commitlint-config": "4.0.3",
71
+ "@html-validate/eslint-config": "9.8.2",
72
+ "@html-validate/eslint-config-jest": "9.8.0",
73
+ "@html-validate/eslint-config-typescript": "9.8.0",
74
+ "@html-validate/eslint-config-typescript-typeinfo": "9.8.0",
75
+ "@html-validate/jest-config": "3.18.2",
76
+ "@html-validate/plugin-template": "1.0.2",
77
+ "@html-validate/prettier-config": "4.1.1",
78
+ "@html-validate/release-scripts": "7.3.1",
79
+ "@jest/globals": "30.4.1",
80
+ "@tsconfig/node22": "22.0.5",
81
+ "@tsconfig/strictest": "2.0.8",
82
+ "@types/estree": "1.0.9",
83
+ "@types/node": "22.20.0",
84
+ "@types/semver": "7.7.1",
85
+ "cloneman": "1.18.1",
86
+ "dts-bundle-generator": "9.5.1",
87
+ "esbuild": "0.28.1",
88
+ "html-validate": "11.5.3",
89
+ "husky": "9.1.7",
90
+ "jest": "30.4.2",
91
+ "jest-environment-jsdom": "30.4.1",
92
+ "npm-pkg-lint": "5.1.8",
93
+ "npm-run-all2": "9.0.2",
94
+ "semver": "7.8.5",
95
+ "ts-jest": "29.4.11",
96
+ "typescript": "6.0.3"
97
+ },
98
+ "peerDependencies": {
99
+ "html-validate": "^8.21.0 || ^9.0.0 || ^10.0.0 || ^11.0.0"
100
+ },
101
+ "engines": {
102
+ "node": "^22.16 || >= 24"
103
+ },
104
+ "allowScripts": {
105
+ "@html-validate/commitlint-config": true,
106
+ "esbuild": true,
107
+ "fsevents": false,
108
+ "unrs-resolver": true
109
+ },
110
+ "cloneman": {
111
+ "template": "@html-validate/plugin-template",
112
+ "version": "1.0.2"
113
+ },
114
+ "externalDependencies": [
115
+ "@html-validate/plugin-utils"
116
+ ]
117
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": ["gitlab>html-validate/renovate-config"]
3
+ }
@@ -0,0 +1,71 @@
1
+ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2
+
3
+ exports[`HTML elements <custom-component> invalid markup 1`] = `
4
+ [
5
+ {
6
+ "errorCount": 3,
7
+ "filePath": "test-files/elements/custom-component-invalid.html",
8
+ "messages": [
9
+ {
10
+ "column": 2,
11
+ "context": {
12
+ "attr": "value",
13
+ "tagName": "<custom-component>",
14
+ },
15
+ "line": 2,
16
+ "message": "<custom-component> is missing required "value" attribute",
17
+ "offset": 43,
18
+ "ruleId": "element-required-attributes",
19
+ "ruleUrl": "https://html-validate.org/rules/element-required-attributes.html",
20
+ "selector": "custom-component:nth-child(1)",
21
+ "severity": 2,
22
+ "size": 16,
23
+ },
24
+ {
25
+ "column": 26,
26
+ "context": {
27
+ "allowed": {
28
+ "enum": [
29
+ "yes",
30
+ "no",
31
+ ],
32
+ },
33
+ "attribute": "value",
34
+ "element": "custom-component",
35
+ "value": "invalid",
36
+ },
37
+ "line": 5,
38
+ "message": "Attribute "value" has invalid value "invalid"",
39
+ "offset": 139,
40
+ "ruleId": "attribute-allowed-values",
41
+ "ruleUrl": "https://html-validate.org/rules/attribute-allowed-values.html",
42
+ "selector": "custom-component:nth-child(2)",
43
+ "severity": 2,
44
+ "size": 7,
45
+ },
46
+ {
47
+ "column": 2,
48
+ "line": 8,
49
+ "message": "lorem ipsum",
50
+ "offset": 212,
51
+ "ruleId": "awesome-plugin/custom-rule",
52
+ "selector": "custom-component:nth-child(3)",
53
+ "severity": 2,
54
+ "size": 16,
55
+ },
56
+ ],
57
+ "source": "<!-- missing required value attribute -->
58
+ <custom-component></custom-component>
59
+
60
+ <!-- invalid value attribute -->
61
+ <custom-component value="invalid"></custom-component>
62
+
63
+ <!-- custom rule disallows value "no" -->
64
+ <custom-component value="no"></custom-component>
65
+ ",
66
+ "warningCount": 0,
67
+ },
68
+ ]
69
+ `;
70
+
71
+ exports[`HTML elements <custom-component> valid markup 1`] = `[]`;
@@ -0,0 +1,5 @@
1
+ import recommended from "./recommended";
2
+
3
+ export default {
4
+ recommended,
5
+ };
@@ -0,0 +1,13 @@
1
+ import { type ConfigData } from "html-validate";
2
+ import elements from "../elements";
3
+
4
+ export function createConfig(): ConfigData {
5
+ return {
6
+ elements: [elements],
7
+ rules: {
8
+ "awesome-plugin/custom-rule": "error",
9
+ },
10
+ };
11
+ }
12
+
13
+ export default createConfig();
@@ -0,0 +1,49 @@
1
+ import path from "node:path";
2
+ import { describe, expect, it } from "@jest/globals";
3
+ import { type Report, HtmlValidate } from "html-validate";
4
+ import metadata from "./elements";
5
+ import Plugin from "./index";
6
+
7
+ const rootDir = path.resolve(__dirname, "..");
8
+ const fileDirectory = path.join(rootDir, "test-files/elements");
9
+ const tagNames = Object.keys(metadata).filter((it) => it !== "*");
10
+
11
+ const htmlvalidate = new HtmlValidate({
12
+ root: true,
13
+ plugins: [Plugin],
14
+ extends: ["html-validate:recommended", `${Plugin.name}:recommended`],
15
+ elements: ["html5"],
16
+ rules: {
17
+ "no-unknown-attributes": "error",
18
+ "no-unknown-elements": "error",
19
+ },
20
+ });
21
+
22
+ function normalizePath(report: Report): void {
23
+ for (const result of report.results) {
24
+ result.filePath = path.relative(rootDir, result.filePath);
25
+ }
26
+ }
27
+
28
+ describe("HTML elements", () => {
29
+ for (const tagName of tagNames) {
30
+ const slug = tagName.replace(":", "_");
31
+ const filename = (variant: string): string => `${fileDirectory}/${slug}-${variant}.html`;
32
+
33
+ describe(`<${tagName}>`, () => {
34
+ it("valid markup", async () => {
35
+ expect.assertions(1);
36
+ const report = await htmlvalidate.validateFile(filename("valid"));
37
+ normalizePath(report);
38
+ expect(report.results).toMatchSnapshot();
39
+ });
40
+
41
+ it("invalid markup", async () => {
42
+ expect.assertions(1);
43
+ const report = await htmlvalidate.validateFile(filename("invalid"));
44
+ normalizePath(report);
45
+ expect(report.results).toMatchSnapshot();
46
+ });
47
+ });
48
+ }
49
+ });
@@ -0,0 +1,13 @@
1
+ import { defineMetadata } from "html-validate";
2
+
3
+ export default defineMetadata({
4
+ "custom-component": {
5
+ flow: true,
6
+ attributes: {
7
+ value: {
8
+ required: true,
9
+ enum: ["yes", "no"],
10
+ },
11
+ },
12
+ },
13
+ });
@@ -0,0 +1,5 @@
1
+ /* istanbul ignore file */
2
+
3
+ import plugin from "./index";
4
+
5
+ export = plugin;
@@ -0,0 +1,3 @@
1
+ /* istanbul ignore file */
2
+
3
+ export { default } from "./index";
@@ -0,0 +1,17 @@
1
+ import { type Plugin, compatibilityCheck } from "html-validate";
2
+ import { name, peerDependencies } from "../package.json";
3
+ import configs from "./configs";
4
+ import rules from "./rules";
5
+
6
+ const range = peerDependencies["html-validate"];
7
+
8
+ /* eslint-disable-next-line unicorn/no-top-level-side-effects -- intentional side-effect */
9
+ compatibilityCheck(name, range);
10
+
11
+ const plugin: Plugin = {
12
+ name,
13
+ configs,
14
+ rules,
15
+ };
16
+
17
+ export default plugin;
@@ -0,0 +1,61 @@
1
+ import { beforeAll, describe, expect, it } from "@jest/globals";
2
+ import { ConfigData, FileSystemConfigLoader, HtmlValidate } from "html-validate";
3
+ import "html-validate/jest";
4
+ import Plugin from "..";
5
+
6
+ const config: ConfigData = {
7
+ root: true,
8
+ plugins: [Plugin],
9
+ rules: { "awesome-plugin/custom-rule": "error" },
10
+ };
11
+ const loader = new FileSystemConfigLoader(config);
12
+
13
+ describe("awesome-plugin/custom-rule", () => {
14
+ let htmlvalidate: HtmlValidate;
15
+
16
+ beforeAll(() => {
17
+ htmlvalidate = new HtmlValidate(loader);
18
+ });
19
+
20
+ it("should not report error for other elements", async () => {
21
+ expect.assertions(1);
22
+ const markup = /* HTML */ ` <input value="no" /> `;
23
+ const report = await htmlvalidate.validateString(markup);
24
+ expect(report).toBeValid();
25
+ });
26
+
27
+ it("should not report error when <custom-component> omits value", async () => {
28
+ expect.assertions(1);
29
+ const markup = /* HTML */ ` <custom-component></custom-component> `;
30
+ const report = await htmlvalidate.validateString(markup);
31
+ expect(report).toBeValid();
32
+ });
33
+
34
+ it("should not report error when <custom-component> uses positive value", async () => {
35
+ expect.assertions(1);
36
+ const markup = /* HTML */ ` <custom-component value="yes"></custom-component> `;
37
+ const report = await htmlvalidate.validateString(markup);
38
+ expect(report).toBeValid();
39
+ });
40
+
41
+ it("should report error when <custom-component> uses negative value", async () => {
42
+ expect.assertions(2);
43
+ const markup = /* HTML */ ` <custom-component value="no"></custom-component> `;
44
+ const report = await htmlvalidate.validateString(markup);
45
+ expect(report).toBeInvalid();
46
+ expect(report).toMatchInlineCodeframe(`
47
+ "error: lorem ipsum (awesome-plugin/custom-rule)
48
+ > 1 | <custom-component value="no"></custom-component>
49
+ | ^^^^^^^^^^^^^^^^
50
+ Selector: custom-component"
51
+ `);
52
+ });
53
+
54
+ it("should contain documentation", async () => {
55
+ expect.assertions(1);
56
+ const docs = await htmlvalidate.getContextualDocumentation({
57
+ ruleId: "awesome-plugin/custom-rule",
58
+ });
59
+ expect(docs?.description).toMatchInlineSnapshot(`"Lorem ipsum dolor sit amet."`);
60
+ });
61
+ });
@@ -0,0 +1,24 @@
1
+ import { type RuleDocumentation, Rule } from "html-validate";
2
+
3
+ export class CustomRule extends Rule {
4
+ public override documentation(): RuleDocumentation {
5
+ return {
6
+ description: "Lorem ipsum dolor sit amet.",
7
+ };
8
+ }
9
+
10
+ public setup(): void {
11
+ this.on("dom:ready", (event) => {
12
+ const { document } = event;
13
+ for (const node of document.querySelectorAll("custom-component")) {
14
+ const attr = node.getAttribute("value");
15
+ if (attr?.value === "no") {
16
+ this.report({
17
+ node,
18
+ message: "lorem ipsum",
19
+ });
20
+ }
21
+ }
22
+ });
23
+ }
24
+ }
@@ -0,0 +1,7 @@
1
+ import { CustomRule } from "./custom-rule";
2
+
3
+ const rules = {
4
+ "awesome-plugin/custom-rule": CustomRule,
5
+ };
6
+
7
+ export default rules;
@@ -0,0 +1,40 @@
1
+ /* eslint-disable jest/no-conditional-expect, jest/no-conditional-in-test -- needed to handle backwards compatibility, we use expect.assertions(N) to ensure the right number of expectations have run */
2
+
3
+ import { expect, it } from "@jest/globals";
4
+ import { ConfigData, FileSystemConfigLoader, HtmlValidate, version } from "html-validate";
5
+ import "html-validate/jest";
6
+ import { gtr } from "semver";
7
+ import Plugin from "../src";
8
+
9
+ const config: ConfigData = {
10
+ root: true,
11
+ plugins: [Plugin],
12
+ extends: ["html-validate:recommended", `${Plugin.name}:recommended`],
13
+ elements: ["html5"],
14
+ };
15
+
16
+ const loader = new FileSystemConfigLoader(config);
17
+
18
+ it('should find errors in "smoketest.html"', async () => {
19
+ expect.assertions(2);
20
+ const htmlvalidate = new HtmlValidate(loader);
21
+ const report = await htmlvalidate.validateFile("test-files/smoketest.html");
22
+ expect(report.valid).toBeFalsy();
23
+ if (gtr(version, "10.12.0")) {
24
+ expect(report).toMatchInlineCodeframe(`
25
+ "error: lorem ipsum (awesome-plugin/custom-rule)
26
+ > 1 | <custom-component value="no"></custom-component>
27
+ | ^^^^^^^^^^^^^^^^
28
+ 2 |
29
+ Selector: custom-component"
30
+ `);
31
+ } else {
32
+ expect(report).toMatchInlineCodeframe(`
33
+ "error: lorem ipsum (awesome-plugin/custom-rule) at test-files/smoketest.html:1:2:
34
+ > 1 | <custom-component value="no"></custom-component>
35
+ | ^^^^^^^^^^^^^^^^
36
+ 2 |
37
+ Selector: custom-component"
38
+ `);
39
+ }
40
+ });
@@ -0,0 +1,8 @@
1
+ <!-- missing required value attribute -->
2
+ <custom-component></custom-component>
3
+
4
+ <!-- invalid value attribute -->
5
+ <custom-component value="invalid"></custom-component>
6
+
7
+ <!-- custom rule disallows value "no" -->
8
+ <custom-component value="no"></custom-component>
@@ -0,0 +1 @@
1
+ <custom-component value="yes"></custom-component>
@@ -0,0 +1 @@
1
+ <custom-component value="no"></custom-component>
@@ -0,0 +1,8 @@
1
+ /* This file is managed by "@html-validate/plugin-template". Changes will be overwritten */
2
+
3
+ {
4
+ "extends": ["./tsconfig.json"],
5
+ "compilerOptions": {
6
+ "rootDirs": ["./src", "./tests"]
7
+ }
8
+ }
@@ -0,0 +1,10 @@
1
+ /* This file is managed by "@html-validate/plugin-template". Changes will be overwritten */
2
+
3
+ {
4
+ "extends": ["./tsconfig.json"],
5
+ "compilerOptions": {
6
+ "rootDirs": ["./src", "./tests"],
7
+ "module": "esnext",
8
+ "moduleResolution": "bundler"
9
+ }
10
+ }
@@ -0,0 +1,16 @@
1
+ /* This file is managed by "@html-validate/plugin-template". Changes will be overwritten */
2
+
3
+ {
4
+ "extends": ["@tsconfig/node22", "@tsconfig/strictest"],
5
+ "compilerOptions": {
6
+ "rootDir": ".",
7
+ "declaration": true,
8
+ "noEmit": true,
9
+ "outDir": "dist",
10
+ "resolveJsonModule": true,
11
+ "sourceMap": true,
12
+ "types": ["node"]
13
+ },
14
+ "include": ["src/**/*.ts", "test/**/*.ts"],
15
+ "exclude": ["node_modules"]
16
+ }
package/index.js ADDED
@@ -0,0 +1,15 @@
1
+
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+
5
+ const packageJsonFile = await fs.readFile(path.join(import.meta.dirname, "package.json"), "utf8");
6
+ const packageJson = JSON.parse(packageJsonFile);
7
+
8
+ const options = {
9
+ ...packageJson.cloneman,
10
+ filesDir: path.join(import.meta.dirname, "files"),
11
+ hooksDir: path.join(import.meta.dirname, "hooks"),
12
+ }
13
+
14
+ export default options;
15
+
package/package.json CHANGED
@@ -1,10 +1,79 @@
1
1
  {
2
2
  "name": "@html-validate/plugin-template",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @html-validate/plugin-template",
3
+ "version": "1.0.2",
4
+ "description": "cloneman template for html-validate plugins",
5
5
  "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
6
+ "cloneman",
7
+ "html-validate"
8
+ ],
9
+ "homepage": "https://gitlab.com/html-validate/plugin-template",
10
+ "bugs": {
11
+ "url": "https://gitlab.com/html-validate/plugin-template/-/work_items/new?type=Issue"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://gitlab.com/html-validate/plugin-template.git"
16
+ },
17
+ "license": "MIT",
18
+ "author": "David Sveningsson <ext@sidvind.com>",
19
+ "type": "module",
20
+ "exports": {
21
+ ".": "./index.js"
22
+ },
23
+ "cloneman": {
24
+ "boilerplateFiles": [
25
+ ".editorconfig",
26
+ ".gitignore",
27
+ ".gitlab-ci.yml",
28
+ ".htmlvalidate.mjs",
29
+ ".npmrc",
30
+ ".prettierignore",
31
+ "CHANGELOG.md",
32
+ "LICENSE",
33
+ "README.md",
34
+ "build.mjs",
35
+ "eslint.config.mjs",
36
+ "renovate.json",
37
+ "src/__snapshots__/elements.spec.ts.snap",
38
+ "src/configs/index.ts",
39
+ "src/configs/recommended.ts",
40
+ "src/elements.spec.ts",
41
+ "src/elements.ts",
42
+ "src/entry-cjs.ts",
43
+ "src/entry-esm.ts",
44
+ "src/index.ts",
45
+ "src/rules/custom-rule.spec.ts",
46
+ "src/rules/custom-rule.ts",
47
+ "src/rules/index.ts",
48
+ "test-files/elements/custom-component-invalid.html",
49
+ "test-files/elements/custom-component-valid.html",
50
+ "test-files/smoketest.html",
51
+ "test/htmlvalidate.spec.ts",
52
+ "tsconfig.cjs.json",
53
+ "tsconfig.esm.json",
54
+ "tsconfig.json"
55
+ ],
56
+ "managedFiles": [
57
+ ".editorconfig",
58
+ ".gitignore",
59
+ ".gitlab-ci.yml",
60
+ ".htmlvalidate.mjs",
61
+ ".npmrc",
62
+ ".prettierignore",
63
+ "LICENSE",
64
+ "build.mjs",
65
+ "eslint.config.mjs",
66
+ "package.json",
67
+ "renovate.json",
68
+ "tsconfig.cjs.json",
69
+ "tsconfig.esm.json",
70
+ "tsconfig.json"
71
+ ],
72
+ "uninstallDependencies": [],
73
+ "ignoredDependencies": [
74
+ "html-validate",
75
+ "@html-validate/eslint-*",
76
+ "@html-validate/prettier-*"
77
+ ]
78
+ }
10
79
  }
package/README.md DELETED
@@ -1,45 +0,0 @@
1
- # @html-validate/plugin-template
2
-
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
4
-
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
6
-
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
8
-
9
- ## Purpose
10
-
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@html-validate/plugin-template`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
15
-
16
- ## What is OIDC Trusted Publishing?
17
-
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
19
-
20
- ## Setup Instructions
21
-
22
- To properly configure OIDC trusted publishing for this package:
23
-
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
28
-
29
- ## DO NOT USE THIS PACKAGE
30
-
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
36
-
37
- ## More Information
38
-
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
42
-
43
- ---
44
-
45
- **Maintained for OIDC setup purposes only**