From e845882c5cbf1d2c3d2624cf751ba6b7c8642fb7 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Mon, 20 Jul 2026 12:02:40 +0200 Subject: [PATCH] refactor(editor): Move `htmlUtils` into new `@n8n/frontend-utils` package behind re-export shims (no-changelog) (#34355) Co-authored-by: Claude Opus 4.8 Co-authored-by: multica-agent --- .boundaries-baseline.json | 2 +- .../@n8n/composables/eslint.config.mjs | 4 -- .../frontend/@n8n/composables/package.json | 3 +- .../frontend/@n8n/frontend-utils/.gitignore | 24 ++++++++++ .../frontend/@n8n/frontend-utils/README.md | 25 ++++++++++ .../frontend/@n8n/frontend-utils/biome.jsonc | 4 ++ .../@n8n/frontend-utils/eslint.config.mjs | 15 ++++++ .../frontend/@n8n/frontend-utils/package.json | 46 +++++++++++++++++++ .../frontend-utils/src/__tests__/setup.ts | 4 ++ .../src/constants/sanitization.ts | 0 .../src/htmlUtils.test.ts | 0 .../src/htmlUtils.ts | 0 .../@n8n/frontend-utils/tsconfig.json | 10 ++++ .../@n8n/frontend-utils/tsdown.config.ts | 11 +++++ .../@n8n/frontend-utils/vite.config.ts | 4 ++ packages/frontend/editor-ui/package.json | 1 + .../src/app/constants/sanitization.ts | 6 +-- .../editor-ui/src/app/utils/htmlUtils.ts | 4 +- packages/frontend/editor-ui/tsconfig.json | 4 +- pnpm-lock.yaml | 46 +++++++++++++++++-- 20 files changed, 197 insertions(+), 16 deletions(-) create mode 100644 packages/frontend/@n8n/frontend-utils/.gitignore create mode 100644 packages/frontend/@n8n/frontend-utils/README.md create mode 100644 packages/frontend/@n8n/frontend-utils/biome.jsonc create mode 100644 packages/frontend/@n8n/frontend-utils/eslint.config.mjs create mode 100644 packages/frontend/@n8n/frontend-utils/package.json create mode 100644 packages/frontend/@n8n/frontend-utils/src/__tests__/setup.ts rename packages/frontend/@n8n/{composables => frontend-utils}/src/constants/sanitization.ts (100%) rename packages/frontend/@n8n/{composables => frontend-utils}/src/htmlUtils.test.ts (100%) rename packages/frontend/@n8n/{composables => frontend-utils}/src/htmlUtils.ts (100%) create mode 100644 packages/frontend/@n8n/frontend-utils/tsconfig.json create mode 100644 packages/frontend/@n8n/frontend-utils/tsdown.config.ts create mode 100644 packages/frontend/@n8n/frontend-utils/vite.config.ts diff --git a/.boundaries-baseline.json b/.boundaries-baseline.json index 0581f025444..d79831f1bc3 100644 --- a/.boundaries-baseline.json +++ b/.boundaries-baseline.json @@ -1,3 +1,3 @@ { - "issues": 168 + "issues": 169 } diff --git a/packages/frontend/@n8n/composables/eslint.config.mjs b/packages/frontend/@n8n/composables/eslint.config.mjs index b6a6bb9845f..aa8c5f9dd2a 100644 --- a/packages/frontend/@n8n/composables/eslint.config.mjs +++ b/packages/frontend/@n8n/composables/eslint.config.mjs @@ -4,11 +4,7 @@ import { frontendConfig } from '@n8n/eslint-config/frontend'; export default defineConfig(frontendConfig, { files: ['**/*.test.ts'], rules: { - // Vitest spies stored in untyped `let`s surface as `any`; align with the - // sibling FE app-libs (editor-ui) that treat these as warnings in tests. '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', // Component tests key DOM attributes (e.g. `data-test-id`) via string // literals; align with the sibling FE app-libs that treat this as a warning. '@typescript-eslint/naming-convention': 'warn', diff --git a/packages/frontend/@n8n/composables/package.json b/packages/frontend/@n8n/composables/package.json index 2c8cb76fe15..3ccbb780e6d 100644 --- a/packages/frontend/@n8n/composables/package.json +++ b/packages/frontend/@n8n/composables/package.json @@ -30,8 +30,7 @@ "dependencies": { "@vueuse/core": "catalog:frontend", "lodash": "catalog:", - "vue": "catalog:frontend", - "xss": "catalog:" + "vue": "catalog:frontend" }, "devDependencies": { "@n8n/eslint-config": "workspace:*", diff --git a/packages/frontend/@n8n/frontend-utils/.gitignore b/packages/frontend/@n8n/frontend-utils/.gitignore new file mode 100644 index 00000000000..a547bf36d8d --- /dev/null +++ b/packages/frontend/@n8n/frontend-utils/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/frontend/@n8n/frontend-utils/README.md b/packages/frontend/@n8n/frontend-utils/README.md new file mode 100644 index 00000000000..8f09dae7ec5 --- /dev/null +++ b/packages/frontend/@n8n/frontend-utils/README.md @@ -0,0 +1,25 @@ +# @n8n/frontend-utils + +A collection of framework-light utility functions shared across n8n's front-end +packages — HTML sanitization, DOM helpers, and other pure utilities that are not +Vue composables. + +## Table of Contents + +- [Features](#features) +- [Contributing](#contributing) +- [License](#license) + +## Features + +- **Pure utilities**: Stateless helpers with no component or store coupling. +- **Consistency**: A single home for cross-package front-end utilities. +- **Extensible**: A foundation for utilities as the front end grows. + +## Contributing + +For more details, please read our [CONTRIBUTING.md](CONTRIBUTING.md). + +## License + +For more details, please read our [LICENSE.md](LICENSE.md). diff --git a/packages/frontend/@n8n/frontend-utils/biome.jsonc b/packages/frontend/@n8n/frontend-utils/biome.jsonc new file mode 100644 index 00000000000..f882da95a58 --- /dev/null +++ b/packages/frontend/@n8n/frontend-utils/biome.jsonc @@ -0,0 +1,4 @@ +{ + "$schema": "../../../../node_modules/@biomejs/biome/configuration_schema.json", + "extends": ["../../../../biome.jsonc"] +} diff --git a/packages/frontend/@n8n/frontend-utils/eslint.config.mjs b/packages/frontend/@n8n/frontend-utils/eslint.config.mjs new file mode 100644 index 00000000000..f292cdd98ab --- /dev/null +++ b/packages/frontend/@n8n/frontend-utils/eslint.config.mjs @@ -0,0 +1,15 @@ +import { defineConfig } from 'eslint/config'; +import { frontendConfig } from '@n8n/eslint-config/frontend'; + +export default defineConfig(frontendConfig, { + files: ['**/*.test.ts'], + rules: { + // Test spies (e.g. `vi.spyOn`) surface as loosely-typed values; align with + // the sibling FE app-libs (composables, editor-ui) that treat these as + // warnings in test files rather than errors. + '@typescript-eslint/no-unsafe-assignment': 'warn', + '@typescript-eslint/no-unsafe-call': 'warn', + '@typescript-eslint/no-unsafe-member-access': 'warn', + '@typescript-eslint/naming-convention': 'warn', + }, +}); diff --git a/packages/frontend/@n8n/frontend-utils/package.json b/packages/frontend/@n8n/frontend-utils/package.json new file mode 100644 index 00000000000..e2b6e8640ab --- /dev/null +++ b/packages/frontend/@n8n/frontend-utils/package.json @@ -0,0 +1,46 @@ +{ + "name": "@n8n/frontend-utils", + "type": "module", + "version": "0.1.0", + "files": [ + "dist" + ], + "exports": { + "./*": { + "types": "./dist/*.d.mts", + "import": "./dist/*.mjs", + "require": "./dist/*.cjs" + } + }, + "scripts": { + "clean": "rimraf dist .turbo", + "dev": "tsdown --watch", + "build": "tsdown", + "build:unchecked": "pnpm run build", + "preview": "vite preview", + "typecheck": "vue-tsc --noEmit", + "test": "vitest run", + "test:dev": "vitest --silent=false", + "lint": "eslint src --quiet", + "lint:fix": "eslint src --fix", + "format": "biome format --write . && prettier --write . --ignore-path ../../../../.prettierignore", + "format:check": "biome ci . && prettier --check . --ignore-path ../../../../.prettierignore" + }, + "dependencies": { + "vue": "catalog:frontend", + "xss": "catalog:" + }, + "devDependencies": { + "@n8n/eslint-config": "workspace:*", + "@n8n/typescript-config": "workspace:*", + "@n8n/vitest-config": "workspace:*", + "@testing-library/jest-dom": "catalog:frontend", + "@testing-library/vue": "catalog:frontend", + "tsdown": "catalog:", + "typescript": "catalog:", + "vite": "catalog:", + "vitest": "catalog:", + "vue-tsc": "catalog:frontend" + }, + "license": "LicenseRef-n8n-sustainable-use" +} diff --git a/packages/frontend/@n8n/frontend-utils/src/__tests__/setup.ts b/packages/frontend/@n8n/frontend-utils/src/__tests__/setup.ts new file mode 100644 index 00000000000..639b36ad4af --- /dev/null +++ b/packages/frontend/@n8n/frontend-utils/src/__tests__/setup.ts @@ -0,0 +1,4 @@ +import '@testing-library/jest-dom/vitest'; +import { configure } from '@testing-library/vue'; + +configure({ testIdAttribute: 'data-test-id' }); diff --git a/packages/frontend/@n8n/composables/src/constants/sanitization.ts b/packages/frontend/@n8n/frontend-utils/src/constants/sanitization.ts similarity index 100% rename from packages/frontend/@n8n/composables/src/constants/sanitization.ts rename to packages/frontend/@n8n/frontend-utils/src/constants/sanitization.ts diff --git a/packages/frontend/@n8n/composables/src/htmlUtils.test.ts b/packages/frontend/@n8n/frontend-utils/src/htmlUtils.test.ts similarity index 100% rename from packages/frontend/@n8n/composables/src/htmlUtils.test.ts rename to packages/frontend/@n8n/frontend-utils/src/htmlUtils.test.ts diff --git a/packages/frontend/@n8n/composables/src/htmlUtils.ts b/packages/frontend/@n8n/frontend-utils/src/htmlUtils.ts similarity index 100% rename from packages/frontend/@n8n/composables/src/htmlUtils.ts rename to packages/frontend/@n8n/frontend-utils/src/htmlUtils.ts diff --git a/packages/frontend/@n8n/frontend-utils/tsconfig.json b/packages/frontend/@n8n/frontend-utils/tsconfig.json new file mode 100644 index 00000000000..e889321b568 --- /dev/null +++ b/packages/frontend/@n8n/frontend-utils/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@n8n/typescript-config/tsconfig.frontend.json", + "compilerOptions": { + "moduleResolution": "bundler", + "outDir": "dist", + "types": ["vite/client", "vitest/globals", "@testing-library/jest-dom"], + "isolatedModules": true + }, + "include": ["src/**/*.ts", "vite.config.ts", "tsdown.config.ts"] +} diff --git a/packages/frontend/@n8n/frontend-utils/tsdown.config.ts b/packages/frontend/@n8n/frontend-utils/tsdown.config.ts new file mode 100644 index 00000000000..2df2e3fef7d --- /dev/null +++ b/packages/frontend/@n8n/frontend-utils/tsdown.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsdown'; + +// eslint-disable-next-line import-x/no-default-export +export default defineConfig({ + entry: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts', '!src/__tests__/**/*'], + format: ['cjs', 'esm'], + clean: true, + dts: true, + sourcemap: true, + hash: false, +}); diff --git a/packages/frontend/@n8n/frontend-utils/vite.config.ts b/packages/frontend/@n8n/frontend-utils/vite.config.ts new file mode 100644 index 00000000000..784f3fb497b --- /dev/null +++ b/packages/frontend/@n8n/frontend-utils/vite.config.ts @@ -0,0 +1,4 @@ +import { defineConfig, mergeConfig } from 'vite'; +import { vitestConfig } from '@n8n/vitest-config/frontend'; + +export default mergeConfig(defineConfig({}), vitestConfig); diff --git a/packages/frontend/editor-ui/package.json b/packages/frontend/editor-ui/package.json index b9397cfbb33..764d082bc4d 100644 --- a/packages/frontend/editor-ui/package.json +++ b/packages/frontend/editor-ui/package.json @@ -49,6 +49,7 @@ "@n8n/constants": "workspace:*", "@n8n/design-system": "workspace:*", "@n8n/frontend-module-sdk": "workspace:*", + "@n8n/frontend-utils": "workspace:*", "@n8n/i18n": "workspace:*", "@n8n/permissions": "workspace:*", "@n8n/rest-api-client": "workspace:*", diff --git a/packages/frontend/editor-ui/src/app/constants/sanitization.ts b/packages/frontend/editor-ui/src/app/constants/sanitization.ts index 9978279bda3..8425cc4ebc6 100644 --- a/packages/frontend/editor-ui/src/app/constants/sanitization.ts +++ b/packages/frontend/editor-ui/src/app/constants/sanitization.ts @@ -1,6 +1,6 @@ /** - * @deprecated Import from `@n8n/composables/constants/sanitization` instead. These - * constants moved into `@n8n/composables` (folded in with their only consumer + * @deprecated Import from `@n8n/frontend-utils/constants/sanitization` instead. These + * constants moved into `@n8n/frontend-utils` (alongside their only consumer * `htmlUtils`) during the CAT-3686 frontend-modularization migration; this * re-export keeps `@/app/constants` consumers working until they are retired. * (N8N-36) @@ -8,4 +8,4 @@ export { ALLOWED_HTML_ATTRIBUTES, ALLOWED_HTML_TAGS, -} from '@n8n/composables/constants/sanitization'; +} from '@n8n/frontend-utils/constants/sanitization'; diff --git a/packages/frontend/editor-ui/src/app/utils/htmlUtils.ts b/packages/frontend/editor-ui/src/app/utils/htmlUtils.ts index a33d440c49f..7360627e47a 100644 --- a/packages/frontend/editor-ui/src/app/utils/htmlUtils.ts +++ b/packages/frontend/editor-ui/src/app/utils/htmlUtils.ts @@ -1,5 +1,5 @@ /** - * @deprecated Import from `@n8n/composables/htmlUtils` instead. This re-export + * @deprecated Import from `@n8n/frontend-utils/htmlUtils` instead. This re-export * shim keeps existing `@/app/utils/htmlUtils` call sites working during the * CAT-3686 frontend-modularization migration and will be removed once importers * are retired per-directory. (N8N-36) @@ -13,4 +13,4 @@ export { openSafeUrl, sanitizeHtml, sanitizeIfString, -} from '@n8n/composables/htmlUtils'; +} from '@n8n/frontend-utils/htmlUtils'; diff --git a/packages/frontend/editor-ui/tsconfig.json b/packages/frontend/editor-ui/tsconfig.json index b58e2519a40..33cb254d64c 100644 --- a/packages/frontend/editor-ui/tsconfig.json +++ b/packages/frontend/editor-ui/tsconfig.json @@ -8,7 +8,8 @@ "../@n8n/composables/src", "../@n8n/chat/src", "../@n8n/design-system/src", - "../@n8n/frontend-module-sdk/src" + "../@n8n/frontend-module-sdk/src", + "../@n8n/frontend-utils/src" ], "noEmit": true, "types": [ @@ -23,6 +24,7 @@ "@n8n/composables*": ["../@n8n/composables/src*"], "@n8n/constants*": ["../../@n8n/constants/src*"], "@n8n/frontend-module-sdk": ["../@n8n/frontend-module-sdk/src/index.ts"], + "@n8n/frontend-utils*": ["../@n8n/frontend-utils/src*"], "@n8n/chat*": ["../@n8n/chat/src*"], "@n8n/chat-hub*": ["../../@n8n/chat-hub/src*"], "@n8n/design-system*": ["../@n8n/design-system/src*"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 52dad3494ac..c4e236e530b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4839,9 +4839,6 @@ importers: vue: specifier: catalog:frontend version: 3.5.26(typescript@6.0.2) - xss: - specifier: 'catalog:' - version: 1.0.15 devDependencies: '@n8n/eslint-config': specifier: workspace:* @@ -5146,6 +5143,46 @@ importers: specifier: ^2.2.8 version: 2.2.8(patch_hash=e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f)(typescript@6.0.2) + packages/frontend/@n8n/frontend-utils: + dependencies: + vue: + specifier: catalog:frontend + version: 3.5.26(typescript@6.0.2) + xss: + specifier: 'catalog:' + version: 1.0.15 + devDependencies: + '@n8n/eslint-config': + specifier: workspace:* + version: link:../../../@n8n/eslint-config + '@n8n/typescript-config': + specifier: workspace:* + version: link:../../../@n8n/typescript-config + '@n8n/vitest-config': + specifier: workspace:* + version: link:../../../@n8n/vitest-config + '@testing-library/jest-dom': + specifier: catalog:frontend + version: 6.6.3 + '@testing-library/vue': + specifier: catalog:frontend + version: 8.1.0(@vue/compiler-sfc@3.5.26)(vue@3.5.26(typescript@6.0.2)) + tsdown: + specifier: 'catalog:' + version: 0.16.5(typescript@6.0.2)(vue-tsc@2.2.8(patch_hash=e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f)(typescript@6.0.2)) + typescript: + specifier: 'catalog:' + version: 6.0.2 + vite: + specifier: 'catalog:' + version: 8.0.2(@types/node@20.19.41)(esbuild@0.28.1)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3) + vitest: + specifier: 'catalog:' + version: 4.1.9(@opentelemetry/api@1.9.0)(@types/node@20.19.41)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(jsdom@23.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(vite@8.0.2(@types/node@20.19.41)(esbuild@0.28.1)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3)) + vue-tsc: + specifier: ^2.2.8 + version: 2.2.8(patch_hash=e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f)(typescript@6.0.2) + packages/frontend/@n8n/i18n: dependencies: n8n-workflow: @@ -5527,6 +5564,9 @@ importers: '@n8n/frontend-module-sdk': specifier: workspace:* version: link:../@n8n/frontend-module-sdk + '@n8n/frontend-utils': + specifier: workspace:* + version: link:../@n8n/frontend-utils '@n8n/i18n': specifier: workspace:* version: link:../@n8n/i18n