diff --git a/.eslintrc.js b/.eslintrc.js index 4649e62cf..04d334e36 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,7 @@ module.exports = { ignorePatterns: ['vite.*.js', 'packages/**/*.js', 'src/**/*'], root: true, - plugins: ['html', 'import'], + plugins: ['html', 'import', 'eslint-plugin-local-rules'], overrides: [ { files: ['*.ts', '*.tsx'], @@ -25,6 +25,8 @@ module.exports = { '@typescript-eslint/ban-types': 'off', //TODO: Remove (maybe) 'lit/no-useless-template-literals': 'error', 'lit/prefer-nothing': 'error', + 'local-rules/uui-class-prefix': 'warn', + 'local-rules/prefer-static-styles-last': 'warn', }, parserOptions: { project: './tsconfig.json', diff --git a/eslint-local-rules.cjs b/eslint-local-rules.cjs new file mode 100644 index 000000000..274f26e3c --- /dev/null +++ b/eslint-local-rules.cjs @@ -0,0 +1,88 @@ +'use strict'; + +// eslint-disable-next-line no-undef +module.exports = { + /** @type {import('eslint').Rule.RuleModule} */ + 'uui-class-prefix': { + meta: { + type: 'problem', + docs: { + description: + 'Ensure that all class declarations are prefixed with "UUI"', + category: 'Best Practices', + recommended: true, + }, + schema: [], + }, + create: function (context) { + function checkClassName(node) { + if (node.id && node.id.name && !node.id.name.startsWith('UUI')) { + context.report({ + node: node.id, + message: 'Class declaration should be prefixed with "UUI"', + }); + } + } + + return { + ClassDeclaration: checkClassName, + }; + }, + }, + + /** @type {import('eslint').Rule.RuleModule}*/ + 'prefer-static-styles-last': { + meta: { + type: 'suggestion', + docs: { + description: + 'Enforce the "styles" property with the static modifier to be the last property of a class that ends with "Element".', + category: 'Best Practices', + recommended: true, + }, + fixable: 'code', + schema: [], + }, + create: function (context) { + return { + ClassDeclaration(node) { + const className = node.id.name; + if (className.endsWith('Element')) { + const staticStylesProperty = node.body.body.find(bodyNode => { + return ( + bodyNode.type === 'PropertyDefinition' && + bodyNode.key.name === 'styles' && + bodyNode.static + ); + }); + if (staticStylesProperty) { + const lastProperty = node.body.body[node.body.body.length - 1]; + if (lastProperty.key.name !== staticStylesProperty.key.name) { + context.report({ + node: staticStylesProperty, + message: + 'The "styles" property should be the last property of a class declaration.', + data: { + className: className, + }, + fix: function (fixer) { + const sourceCode = context.getSourceCode(); + const staticStylesPropertyText = + sourceCode.getText(staticStylesProperty); + return [ + fixer.replaceTextRange(staticStylesProperty.range, ''), + fixer.insertTextAfterRange( + lastProperty.range, + '\n \n ' + staticStylesPropertyText + ), + ]; + }, + }); + } + } + } + }, + }; + }, + }, +}; diff --git a/package-lock.json b/package-lock.json index eeff97b10..1fcba39ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "eslint-plugin-import": "2.27.5", "eslint-plugin-lit": "1.8.3", "eslint-plugin-lit-a11y": "4.1.0", + "eslint-plugin-local-rules": "^2.0.0", "eslint-plugin-storybook": "0.6.14", "eslint-plugin-wc": "1.5.0", "github-markdown-css": "5.2.0", @@ -16585,6 +16586,11 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/eslint-plugin-local-rules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-local-rules/-/eslint-plugin-local-rules-2.0.0.tgz", + "integrity": "sha512-sWueme0kUcP0JC1+6OBDQ9edBDVFJR92WJHSRbhiRExlenMEuUisdaVBPR+ItFBFXo2Pdw6FD2UfGZWkz8e93g==" + }, "node_modules/eslint-plugin-storybook": { "version": "0.6.14", "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.14.tgz", @@ -32871,7 +32877,10 @@ "version": "1.5.0-rc.0", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.5.0-rc.0" + "@umbraco-ui/uui-base": "1.5.0-rc.0", + "@umbraco-ui/uui-button": "1.5.0-rc.0", + "@umbraco-ui/uui-popover-container": "1.5.0-rc.0", + "@umbraco-ui/uui-symbol-more": "1.5.0-rc.0" } }, "packages/uui-tag": { diff --git a/package.json b/package.json index b7c0df583..3b902ad70 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "eslint-plugin-import": "2.27.5", "eslint-plugin-lit": "1.8.3", "eslint-plugin-lit-a11y": "4.1.0", + "eslint-plugin-local-rules": "^2.0.0", "eslint-plugin-storybook": "0.6.14", "eslint-plugin-wc": "1.5.0", "github-markdown-css": "5.2.0",