diff --git a/src/Umbraco.Web.UI.Client/.storybook/preview.js b/src/Umbraco.Web.UI.Client/.storybook/preview.js index 4ed958e32c..98c8ab65b5 100644 --- a/src/Umbraco.Web.UI.Client/.storybook/preview.js +++ b/src/Umbraco.Web.UI.Client/.storybook/preview.js @@ -140,10 +140,10 @@ class UmbStoryBookElement extends UmbLitElement { new UmbModalManagerContext(this); new UmbNotificationContext(this); - umbLocalizationRegistry.loadLanguage('en-us'); // register default language + umbLocalizationRegistry.loadLanguage('en'); // register default language this.consumeContext(UMB_APP_LANGUAGE_CONTEXT, (appLanguageContext) => { - appLanguageContext.setLanguage('en-us'); // set default language + appLanguageContext?.setLanguage('en'); // set default language }); } diff --git a/src/Umbraco.Web.UI.Client/devops/eslint/rules/bad-type-import.cjs b/src/Umbraco.Web.UI.Client/devops/eslint/rules/bad-type-import.cjs deleted file mode 100644 index eebdca6c75..0000000000 --- a/src/Umbraco.Web.UI.Client/devops/eslint/rules/bad-type-import.cjs +++ /dev/null @@ -1,31 +0,0 @@ - /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { - meta: { - type: 'problem', - docs: { - description: 'Ensures the use of the `import type` operator from the `src/core/models/index.ts` file.', - category: 'Best Practices', - recommended: true, - }, - fixable: 'code', - schema: [], - }, - create: function (context) { - return { - ImportDeclaration: function (node) { - if ( - node.source.parent.importKind !== 'type' && - (node.source.value.endsWith('/models') || node.source.value === 'router-slot/model') - ) { - const sourceCode = context.getSourceCode(); - const nodeSource = sourceCode.getText(node); - context.report({ - node, - message: 'Use `import type` instead of `import`.', - fix: (fixer) => fixer.replaceText(node, nodeSource.replace('import', 'import type')), - }); - } - }, - }; - }, -}; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/devops/eslint/rules/enforce-umb-prefix-on-element-name.cjs b/src/Umbraco.Web.UI.Client/devops/eslint/rules/enforce-umb-prefix-on-element-name.cjs deleted file mode 100644 index e290422e62..0000000000 --- a/src/Umbraco.Web.UI.Client/devops/eslint/rules/enforce-umb-prefix-on-element-name.cjs +++ /dev/null @@ -1,43 +0,0 @@ -const ALLOWED_PREFIXES = ['umb-', 'ufm-', 'test-', 'example-']; - -/** @type {import('eslint').Rule.RuleModule} */ -module.exports = { - meta: { - type: 'suggestion', - docs: { - description: 'Enforce Custom Element names to start with "umb-".', - category: 'Naming', - recommended: true, - }, - schema: [], - }, - create: function (context) { - return { - CallExpression(node) { - // check if the expression is @customElement decorator - const isCustomElementDecorator = - node.callee.type === 'Identifier' && - node.callee.name === 'customElement' && - node.arguments.length === 1 && - node.arguments[0].type === 'Literal' && - typeof node.arguments[0].value === 'string'; - - if (isCustomElementDecorator) { - const elementName = node.arguments[0].value; - - // check if the element name starts with an allowed prefix: - const isElementNameValid = ALLOWED_PREFIXES.some((prefix) => elementName.startsWith(prefix)); - - if (!isElementNameValid) { - context.report({ - node, - message: 'Custom Element name should start with "umb-" or "ufm-".', - // There is no fixer on purpose because it's not safe to automatically rename the element name. - // Renaming should be done manually with consideration of potential impacts. - }); - } - } - }, - }; - }, -}; diff --git a/src/Umbraco.Web.UI.Client/devops/eslint/rules/exported-string-constant-naming.cjs b/src/Umbraco.Web.UI.Client/devops/eslint/rules/exported-string-constant-naming.cjs deleted file mode 100644 index 8118a54ab1..0000000000 --- a/src/Umbraco.Web.UI.Client/devops/eslint/rules/exported-string-constant-naming.cjs +++ /dev/null @@ -1,54 +0,0 @@ -/** @type {import('eslint').Rule.RuleModule}*/ -module.exports = { - meta: { - type: 'problem', - docs: { - description: - 'Ensure all exported string constants should be in uppercase with words separated by underscores and prefixed with UMB_', - }, - schema: [ - { - type: 'object', - properties: { - excludedFileNames: { - type: 'array', - items: { - type: 'string', - }, - }, - }, - additionalProperties: false, - }, - ], - }, - create: function (context) { - const excludedFileNames = context.options[0]?.excludedFileNames || []; - return { - ExportNamedDeclaration(node) { - const fileName = context.filename; - - if (excludedFileNames.some((excludedFileName) => fileName.includes(excludedFileName))) { - // Skip the rule check for files in the excluded list - return; - } - - if (node.declaration && node.declaration.type === 'VariableDeclaration') { - const declaration = node.declaration.declarations[0]; - const { id, init } = declaration; - - if (id && id.type === 'Identifier' && init && init.type === 'Literal' && typeof init.value === 'string') { - const isValidName = /^[A-Z]+(_[A-Z]+)*$/.test(id.name); - - if (!isValidName || !id.name.startsWith('UMB_')) { - context.report({ - node: id, - message: - 'Exported string constant should be in uppercase with words separated by underscores and prefixed with UMB_', - }); - } - } - } - }, - }; - }, -}; diff --git a/src/Umbraco.Web.UI.Client/devops/eslint/rules/umb-class-prefix.cjs b/src/Umbraco.Web.UI.Client/devops/eslint/rules/umb-class-prefix.cjs deleted file mode 100644 index 7856aec460..0000000000 --- a/src/Umbraco.Web.UI.Client/devops/eslint/rules/umb-class-prefix.cjs +++ /dev/null @@ -1,28 +0,0 @@ -const ALLOWED_PREFIXES = ['Umb', 'Example']; - -/** @type {import('eslint').Rule.RuleModule} */ -module.exports = { - meta: { - type: 'problem', - docs: { - description: 'Ensure that all class declarations are prefixed with "Umb"', - category: 'Best Practices', - recommended: true, - }, - schema: [], - }, - create: function (context) { - function checkClassName(node) { - if (node.id && node.id.name && !ALLOWED_PREFIXES.some((prefix) => node.id.name.startsWith(prefix))) { - context.report({ - node: node.id, - message: `Class declaration should be prefixed with one of the following prefixes: ${ALLOWED_PREFIXES.join(', ')}`, - }); - } - } - - return { - ClassDeclaration: checkClassName, - }; - }, -}; diff --git a/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs b/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs index ed3c3364ff..94e82feafc 100644 --- a/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs +++ b/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs @@ -1,27 +1,19 @@ 'use strict'; -const badTypeImportRule = require('./devops/eslint/rules/bad-type-import.cjs'); const enforceElementSuffixOnElementClassNameRule = require('./devops/eslint/rules/enforce-element-suffix-on-element-class-name.cjs'); -const enforceUmbPrefixOnElementNameRule = require('./devops/eslint/rules/enforce-umb-prefix-on-element-name.cjs'); const enforceUmbracoExternalImportsRule = require('./devops/eslint/rules/enforce-umbraco-external-imports.cjs'); const ensureRelativeImportUseJsExtensionRule = require('./devops/eslint/rules/ensure-relative-import-use-js-extension.cjs'); -const exportedStringConstantNaming = require('./devops/eslint/rules/exported-string-constant-naming.cjs'); const noDirectApiImportRule = require('./devops/eslint/rules/no-direct-api-import.cjs'); const preferImportAliasesRule = require('./devops/eslint/rules/prefer-import-aliases.cjs'); const preferStaticStylesLastRule = require('./devops/eslint/rules/prefer-static-styles-last.cjs'); -const umbClassPrefixRule = require('./devops/eslint/rules/umb-class-prefix.cjs'); const noRelativeImportToImportMapModule = require('./devops/eslint/rules/no-relative-import-to-import-map-module.cjs'); module.exports = { - 'bad-type-import': badTypeImportRule, 'enforce-element-suffix-on-element-class-name': enforceElementSuffixOnElementClassNameRule, - 'enforce-umb-prefix-on-element-name': enforceUmbPrefixOnElementNameRule, 'enforce-umbraco-external-imports': enforceUmbracoExternalImportsRule, 'ensure-relative-import-use-js-extension': ensureRelativeImportUseJsExtensionRule, - 'exported-string-constant-naming': exportedStringConstantNaming, 'no-direct-api-import': noDirectApiImportRule, 'prefer-import-aliases': preferImportAliasesRule, 'prefer-static-styles-last': preferStaticStylesLastRule, - 'umb-class-prefix': umbClassPrefixRule, 'no-relative-import-to-import-map-module': noRelativeImportToImportMapModule, }; diff --git a/src/Umbraco.Web.UI.Client/eslint.config.js b/src/Umbraco.Web.UI.Client/eslint.config.js index 2bcbbb5b51..123cbed404 100644 --- a/src/Umbraco.Web.UI.Client/eslint.config.js +++ b/src/Umbraco.Web.UI.Client/eslint.config.js @@ -14,10 +14,12 @@ import jsdoc from 'eslint-plugin-jsdoc'; export default [ // Recommended config applied to all files js.configs.recommended, + importPlugin.flatConfigs.recommended, ...tseslint.configs.recommended, wcPlugin.configs['flat/recommended'], litPlugin.configs['flat/recommended'], // We use the non typescript version to allow types to be defined in the jsdoc comments. This will allow js docs as an alternative to typescript types. jsdoc.configs['flat/recommended'], + ...storybook.configs['flat/recommended'], localRules.configs.all, eslintPluginPrettierRecommended, @@ -39,22 +41,17 @@ export default [ // Global config { plugins: { - import: importPlugin, 'local-rules': localRules, }, rules: { semi: ['warn', 'always'], 'prettier/prettier': ['warn', { endOfLine: 'auto' }], - 'no-unused-vars': 'off', //Let '@typescript-eslint/no-unused-vars' catch the errors to allow unused function parameters (ex: in interfaces) 'no-var': 'error', - ...importPlugin.configs.recommended.rules, 'import/namespace': 'off', 'import/no-unresolved': 'off', 'import/order': ['warn', { groups: ['builtin', 'parent', 'sibling', 'index', 'external'] }], 'import/no-self-import': 'error', 'import/no-cycle': ['error', { maxDepth: 6, allowUnsafeDynamicCyclicDependency: true }], - 'import/no-named-as-default': 'off', // Does not work with eslint 9 - 'import/no-named-as-default-member': 'off', // Does not work with eslint 9 'local-rules/prefer-static-styles-last': 'warn', 'local-rules/enforce-umbraco-external-imports': [ 'error', @@ -62,12 +59,6 @@ export default [ exceptions: ['@umbraco-cms', '@open-wc/testing', '@storybook', 'msw', '.', 'vite', 'uuid', 'diff'], }, ], - 'local-rules/exported-string-constant-naming': [ - 'error', - { - excludedFileNames: ['umbraco-package'], - }, - ], 'jsdoc/check-tag-names': [ 'warn', { @@ -81,6 +72,7 @@ export default [ // Pattern-specific overrides { files: ['**/*.ts'], + ignores: ['.storybook', '**/*.stories.ts', '**/umbraco-package.ts', 'src/assets/lang/*.ts'], languageOptions: { parserOptions: { project: true, @@ -90,7 +82,10 @@ export default [ ...globals.browser, }, }, + ...importPlugin.flatConfigs.typescript, rules: { + 'no-unused-vars': 'off', //Let '@typescript-eslint/no-unused-vars' catch the errors to allow unused function parameters (ex: in interfaces) + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/no-unused-vars': 'error', @@ -98,6 +93,84 @@ export default [ '@typescript-eslint/consistent-type-imports': 'error', '@typescript-eslint/no-import-type-side-effects': 'warn', '@typescript-eslint/no-deprecated': 'warn', + '@typescript-eslint/naming-convention': [ + 'error', + // All private members should be camelCase with leading underscore + // This is to ensure that private members are not used outside the class, as they + // are not part of the public API. + // Example NOT OK: private myPrivateVariable + // Example OK: private _myPrivateVariable + { + selector: 'memberLike', + modifiers: ['private'], + format: ['camelCase'], + leadingUnderscore: 'require', + trailingUnderscore: 'forbid', + }, + // All public members and variables should be camelCase without leading underscore + // Example: myPublicVariable, myPublicMethod + { + selector: ['variableLike', 'memberLike'], + modifiers: ['public'], + filter: { + regex: '^_host$', + match: false, + }, + format: ['camelCase', 'UPPER_CASE', 'PascalCase'], + leadingUnderscore: 'allowDouble', + trailingUnderscore: 'forbid', + }, + // All #private members and variables should be camelCase without leading underscore + // Example: #myPublicVariable, #myPublicMethod + { + selector: ['variableLike', 'memberLike'], + modifiers: ['#private'], + format: ['camelCase', 'UPPER_CASE', 'PascalCase'], + leadingUnderscore: 'allowDouble', + trailingUnderscore: 'forbid', + }, + // All protected members and variables should be camelCase with optional leading underscore (if needed to be pseudo-private) + // Example: protected myPublicVariable, protected _myPublicMethod + { + selector: ['variableLike', 'memberLike'], + modifiers: ['protected'], + format: ['camelCase'], + leadingUnderscore: 'allow', + trailingUnderscore: 'forbid', + }, + // Allow quoted properties, as they are often used in JSON or when the property name is not a valid identifier + // This is to ensure that properties can be used in JSON or when the property name + // is not a valid identifier (e.g. contains spaces or special characters) + // Example: { "umb-some-component": UmbSomeComponent } + { + selector: ['objectLiteralProperty', 'typeProperty', 'enumMember'], + modifiers: ['requiresQuotes'], + format: null, + }, + // All (exported) types should be PascalCase with leading 'Umb' or 'Example' + // Example: UmbExampleType, ExampleTypeLike + { + selector: 'typeLike', + modifiers: ['exported'], + format: ['PascalCase'], + prefix: ['Umb', 'Ufm', 'Manifest', 'Meta', 'Example'] + }, + // All exported string constants should be UPPER_CASE with leading 'UMB_' + // Example: UMB_EXAMPLE_CONSTANT + { + selector: 'variable', + modifiers: ['exported', 'const'], + types: ['string', 'number', 'boolean'], + format: ['UPPER_CASE'], + prefix: ['UMB_'], + }, + // Allow destructured variables to be named as they are in the object + { + selector: "variable", + modifiers: ["destructured"], + format: null, + }, + ], }, }, { @@ -109,5 +182,4 @@ export default [ }, }, }, - ...storybook.configs['flat/recommended'], ]; diff --git a/src/Umbraco.Web.UI.Client/src/apps/installer/error/installer-error.element.ts b/src/Umbraco.Web.UI.Client/src/apps/installer/error/installer-error.element.ts index a7aad8e470..968c4fda70 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/installer/error/installer-error.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/installer/error/installer-error.element.ts @@ -8,7 +8,7 @@ import type { UmbProblemDetails } from '@umbraco-cms/backoffice/resources'; @customElement('umb-installer-error') export class UmbInstallerErrorElement extends UmbLitElement { @state() - _error?: UmbProblemDetails; + private _error?: UmbProblemDetails; private _installerContext?: UmbInstallerContext; diff --git a/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader-view.element.ts b/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader-view.element.ts index 1b1f78132d..21fc60d6db 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader-view.element.ts @@ -52,7 +52,7 @@ export class UmbUpgraderViewElement extends LitElement {
Simply click continue below to be guided through the rest of the upgrade.
-