diff --git a/src/Umbraco.Web.UI.Client/.eslintrc.json b/src/Umbraco.Web.UI.Client/.eslintrc.json index ca3b358d25..f8a724d5c1 100644 --- a/src/Umbraco.Web.UI.Client/.eslintrc.json +++ b/src/Umbraco.Web.UI.Client/.eslintrc.json @@ -48,6 +48,7 @@ "local-rules/prefer-import-aliases": "error", "local-rules/prefer-static-styles-last": "warn", "local-rules/umb-class-prefix": "error", + "local-rules/no-relative-import-to-import-map-module": "error", "local-rules/enforce-umbraco-external-imports": [ "error", { diff --git a/src/Umbraco.Web.UI.Client/devops/eslint/rules/no-relative-import-to-import-map-module.cjs b/src/Umbraco.Web.UI.Client/devops/eslint/rules/no-relative-import-to-import-map-module.cjs new file mode 100644 index 0000000000..104e9123bf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/devops/eslint/rules/no-relative-import-to-import-map-module.cjs @@ -0,0 +1,98 @@ +// TODO: figure out how to automatically generate this list of module paths +const modulePathIdentifiers = [ + '/core/action/', + '/core/audit-log/', + '/core/auth/', + '/core/collection/', + '/core/components/', + '/core/content-type/', + '/core/content/', + '/core/culture/', + '/core/debug/', + '/core/entity-action/', + '/core/entity-bulk-action/', + '/core/entity/', + '/core/event/', + '/core/extension-registry/', + '/core/icon-registry/', + '/core/id/', + '/core/lit-element/', + '/core/localization/', + '/core/menu/', + '/core/modal/', + '/core/models/', + '/core/notification/', + '/core/picker-input/', + '/core/property/', + '/core/property-editor/', + '/core/recycle-bin/', + '/core/repository/', + '/core/resources/', + '/core/router/', + '/core/section/', + '/core/server-file-system/', + '/core/settings/', + '/core/sorter/', + '/core/store/', + '/core/style/', + '/core/temporary-file/', + '/core/themes/', + '/core/tree/', + '/core/utils/', + '/core/validation/', + '/core/variant/', + '/core/workspace/', + '/class-api/', + '/context-api/', + '/controller-api/', + '/element-api/', + '/extension-api/', + '/formatting-api/', + '/localization-api/', + '/observable-api/', + '/backend-api/', + '/base64-js/', + '/diff/', + '/dompurify/', + '/lit/', + '/marked/', + '/monaco-editor/', + '/openid/', + '/router-slot/', + '/rxjs/', + '/tinymce/', + '/uui/', + '/uuid/', +]; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'Prevent relative import to a module that is in the import map.', + category: 'Best Practices', + recommended: true, + }, + schema: [], + messages: { + unexpectedValue: 'Relative import paths should include "{{value}}".', + }, + }, + create: function (context) { + return { + ImportDeclaration(node) { + const importPath = node.source.value; + + if (importPath.startsWith('./') || importPath.startsWith('../')) { + if (modulePathIdentifiers.some((moduleName) => importPath.includes(moduleName))) { + context.report({ + node, + message: 'Use the correct import map alias instead of a relative import path: ' + importPath, + }); + } + } + }, + }; + }, +}; diff --git a/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs b/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs index 6f720a1a7d..ed3c3364ff 100644 --- a/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs +++ b/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs @@ -10,6 +10,7 @@ const noDirectApiImportRule = require('./devops/eslint/rules/no-direct-api-impor 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, @@ -22,4 +23,5 @@ module.exports = { 'prefer-import-aliases': preferImportAliasesRule, 'prefer-static-styles-last': preferStaticStylesLastRule, 'umb-class-prefix': umbClassPrefixRule, + 'no-relative-import-to-import-map-module': noRelativeImportToImportMapModule, };