Skip to content

Commit 7ba357f

Browse files
authored
Merge pull request #1295 from valadas/eslint-rules-v0.24
Created eslint rules to handle deprecations in 0.24 breaking changes
2 parents d678e4e + a031bc5 commit 7ba357f

File tree

7 files changed

+161
-5
lines changed

7 files changed

+161
-5
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,37 @@ Stay tuned - coming soon.
3535

3636
## Usage (Individual Components)
3737
Usage of each component is documented within the component folder right here on GitHub, along with some code samples too.
38+
39+
## Usage (ESLint Plugin)
40+
41+
The `dnn-elements` package includes an ESLint plugin with custom rules, such as `noLabelSlotInCheckbox`. Here's how to configure it:
42+
43+
### For Older ESLint Configurations (e.g., `.eslintrc`)
44+
45+
Update your ESLint configuration file (e.g., `.eslintrc.json`):
46+
```json
47+
{
48+
"plugins": ["@dnncommunity/dnn-elements"],
49+
"rules": {
50+
"@dnncommunity/dnn-elements/no-label-slot-in-checkbox": "error"
51+
}
52+
}
53+
```
54+
55+
### For Newer ESLint Flat Configurations
56+
57+
Update your ESLint configuration file (e.g., `eslint.config.js`):
58+
```javascript
59+
import dnnElementsPlugin from "@dnncommunity/dnn-elements/src/eslint-plugin/dist";
60+
61+
export default [
62+
{
63+
plugins: {
64+
"@dnncommunity/dnn-elements": dnnElementsPlugin
65+
},
66+
rules: {
67+
"@dnncommunity/dnn-elements/no-label-slot-in-checkbox": "error"
68+
}
69+
}
70+
];
71+
```
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
preset: "ts-jest",
3+
testEnvironment: "node",
4+
testMatch: ["<rootDir>/src/eslint-plugin/__tests__/**/*.tests.ts"],
5+
}

packages/stencil-library/package.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,25 @@
1515
"unpkg": "dist/dnn/dnn.js",
1616
"files": [
1717
"dist/",
18-
"loader/"
18+
"loader/",
19+
"src/eslint-plugin/"
1920
],
2021
"scripts": {
21-
"build": "npm run eslint && stencil build --docs",
22+
"build": "npm run build:eslint-plugin && npm run eslint && stencil build --docs",
2223
"postbuild": "license-checker-rseidelsohn --out licenses.json --json --direct --relativeLicensePath --relativeModulePath",
2324
"watch": "stencil build --dev --watch",
2425
"start": "stencil build --dev --watch --serve",
2526
"start.edge": "stencil build --dev --watch --serve --es5",
2627
"poststart": "license-checker-rseidelsohn --out licenses.json --json --direct --relativeLicensePath --relativeModulePath",
2728
"test": "stencil test --spec --e2e",
2829
"test.watch": "stencil test --spec --e2e --watchAll",
30+
"test.eslint": "jest --config jest.eslint.config.js",
2931
"generate": "stencil generate",
3032
"eslint": "eslint",
3133
"storybook": "storybook dev -p 6006",
3234
"build-storybook": "storybook build",
33-
"deploy-storybook": "gh-pages -d storybook-static --branch site"
35+
"deploy-storybook": "gh-pages -d storybook-static --branch site",
36+
"build:eslint-plugin": "tsc -p src/eslint-plugin/tsconfig.json"
3437
},
3538
"devDependencies": {
3639
"@chromatic-com/storybook": "^3.1.0",
@@ -48,6 +51,7 @@
4851
"@storybook/web-components": "^8.3.2",
4952
"@storybook/web-components-webpack5": "^8.3.2",
5053
"@timkendrick/monaco-editor": "^0.0.9",
54+
"@types/estree-jsx": "^1.0.5",
5155
"@types/jest": "^29.5.10",
5256
"@typescript-eslint/eslint-plugin": "^8.0.1",
5357
"@typescript-eslint/parser": "^8.0.1",
@@ -67,8 +71,8 @@
6771
"react-dom": "^18.2.0",
6872
"rollup-plugin-node-polyfills": "^0.2.1",
6973
"storybook": "^8.3.2",
70-
"typescript": "5.6.3",
71-
"typescript-debounce-decorator": "^0.0.18"
74+
"ts-jest": "^29.3.2",
75+
"typescript": "5.6.3"
7276
},
7377
"dependencies": {
7478
"jodit": "^4.2.27"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { RuleTester } from "eslint";
2+
import { noLabelSlotInCheckbox } from "../src/rules/no-label-slot-in-checkbox";
3+
const tsParser = require.resolve("@typescript-eslint/parser");
4+
5+
const ruleTester = new RuleTester({
6+
parser: tsParser,
7+
parserOptions: {
8+
ecmaVersion: 2022,
9+
ecmaFeatures: {
10+
jsx: true,
11+
},
12+
sourceType: "module",
13+
},
14+
});
15+
16+
ruleTester.run("no-label-slot-in-checkbox", noLabelSlotInCheckbox, {
17+
valid: [
18+
{ code: "<dnn-checkbox></dnn-checkbox>" },
19+
],
20+
invalid: [
21+
{
22+
code: "<dnn-checkbox onClick={e => console.log(e)}><label>Label Text</label></dnn-checkbox>",
23+
errors: [{ messageId: "noLabelSlotInCheckbox" }],
24+
output: "<label>\nLabel Text\n<dnn-checkbox onClick={e => console.log(e)}></dnn-checkbox>\n</label>",
25+
},
26+
],
27+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { noLabelSlotInCheckbox } from "./src/rules/no-label-slot-in-checkbox";
2+
3+
export const rules = {
4+
"no-label-slot-in-checkbox": noLabelSlotInCheckbox
5+
};
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import type { Rule } from 'eslint';
2+
import type { JSXOpeningElement } from "estree-jsx";
3+
4+
export const noLabelSlotInCheckbox: Rule.RuleModule = {
5+
meta: {
6+
type: "problem",
7+
docs: {
8+
description: "Disallow label slot in checkbox",
9+
recommended: true,
10+
url: "https://github.com/DNNCommunity/dnn-elements/releases/tag/v0.24.0",
11+
},
12+
messages: {
13+
noLabelSlotInCheckbox: "Label slot is not allowed in dnn-checkbox, wrap dnn-checkbox with a label instead."
14+
},
15+
fixable: "code",
16+
},
17+
create(context: Rule.RuleContext): Rule.RuleListener {
18+
return {
19+
JSXOpeningElement(node: Rule.Node) {
20+
if (node.type === "JSXOpeningElement") {
21+
const jsxNode = node as JSXOpeningElement;
22+
if (jsxNode.name.type === "JSXIdentifier" && jsxNode.name.name === "dnn-checkbox") {
23+
const parent = context.getAncestors().find(ancestor => ancestor.type === "JSXElement");
24+
if (parent) {
25+
const hasLabel = parent.children.some(child => {
26+
return child.type === "JSXElement" &&
27+
child.openingElement.name.type === "JSXIdentifier" &&
28+
child.openingElement.name.name === "label";
29+
});
30+
if (hasLabel) {
31+
context.report({
32+
node: node,
33+
messageId: "noLabelSlotInCheckbox",
34+
fix: (fixer) => {
35+
if (parent && parent.type === "JSXElement" && Array.isArray(parent.children)) {
36+
const label = parent.children.find(child =>
37+
child &&
38+
child.type === "JSXElement" &&
39+
child.openingElement &&
40+
child.openingElement.name &&
41+
child.openingElement.name.type === "JSXIdentifier" &&
42+
child.openingElement.name.name === "label");
43+
44+
if (label && label.type === "JSXElement") {
45+
const labelText = context.sourceCode.getText(label).replace(/<label>|<\/label>/g, '').trim();
46+
const checkboxText = context.sourceCode.getText(node);
47+
48+
// Combine the label text and checkbox into a label wrapper with multiline formatting
49+
const fixedText = `<label>\n${labelText}\n${checkboxText}</dnn-checkbox>\n</label>`;
50+
51+
return fixer.replaceText(parent, fixedText);
52+
}
53+
}
54+
55+
return null;
56+
}
57+
});
58+
}
59+
}
60+
}
61+
}
62+
}
63+
};
64+
}
65+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2022",
4+
"module": "CommonJS",
5+
"moduleResolution": "Node",
6+
"outDir": "./dist",
7+
"strict": true,
8+
"esModuleInterop": true,
9+
"declaration": true,
10+
"skipLibCheck": true,
11+
"types": ["jest", "node", "estree-jsx"],
12+
"forceConsistentCasingInFileNames": true,
13+
"sourceMap": true
14+
},
15+
"include": ["src", "jest.config.js"]
16+
}

0 commit comments

Comments
 (0)