diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 0c203c742..000000000 --- a/.eslintignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules -**/*.mock.* -**/code-pushup.config.ts -**/mocks/fixtures/** -**/__snapshots__/** diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 252cd16a7..000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "root": true, - "ignorePatterns": ["**/*", "*.config*"], - "plugins": ["@nx"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": { - "@nx/enforce-module-boundaries": [ - "error", - { - "enforceBuildableLibDependency": true, - "allow": ["tools"], - "depConstraints": [ - { - "sourceTag": "scope:shared", - "onlyDependOnLibsWithTags": ["scope:shared"] - }, - { - "sourceTag": "scope:core", - "onlyDependOnLibsWithTags": ["scope:core", "scope:shared"] - }, - { - "sourceTag": "scope:plugin", - "onlyDependOnLibsWithTags": ["scope:shared"] - }, - { - "sourceTag": "scope:tooling", - "onlyDependOnLibsWithTags": ["scope:tooling", "scope:shared"] - }, - { - "sourceTag": "type:e2e", - "onlyDependOnLibsWithTags": [ - "type:app", - "type:feature", - "type:util", - "type:testing" - ] - }, - { - "sourceTag": "type:app", - "onlyDependOnLibsWithTags": [ - "type:feature", - "type:util", - "type:testing" - ] - }, - { - "sourceTag": "type:feature", - "onlyDependOnLibsWithTags": [ - "type:feature", - "type:util", - "type:testing" - ] - }, - { - "sourceTag": "type:util", - "onlyDependOnLibsWithTags": ["type:util", "type:testing"] - }, - { - "sourceTag": "type:testing", - "onlyDependOnLibsWithTags": ["type:util", "type:testing"] - } - ] - } - ], - "@typescript-eslint/no-unused-vars": [ - "error", - { - "argsIgnorePattern": "^_" - } - ], - "@typescript-eslint/no-import-type-side-effects": "warn" - } - }, - { - "files": ["*.ts", "*.tsx"], - "extends": [ - "plugin:@nx/typescript", - "@code-pushup/eslint-config/typescript", - "@code-pushup/eslint-config/node", - "@code-pushup/eslint-config/vitest" - ], - "settings": { - "import/resolver": { - "typescript": { - "project": "tsconfig.base.json" - } - } - }, - "rules": { - "vitest/consistent-test-filename": [ - "warn", - { - "pattern": ".*\\.(unit|integration|e2e)\\.test\\.[tj]sx?$" - } - ], - "@typescript-eslint/no-extra-semi": "error", - "no-extra-semi": "off" - } - }, - { - "files": ["*.js", "*.jsx"], - "extends": ["plugin:@nx/javascript", "@code-pushup"], - "rules": { - "@typescript-eslint/no-extra-semi": "error", - "no-extra-semi": "off" - } - }, - { - "files": "*.json", - "parser": "jsonc-eslint-parser", - "rules": {} - }, - { - "files": ["**/perf/**/*.ts"], - "rules": { - "no-magic-numbers": "off", - "sonarjs/no-duplicate-string": "off" - } - } - ] -} diff --git a/.nvmrc b/.nvmrc index 18c284172..2fdffdecf 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.11.0 \ No newline at end of file +22.10.0 \ No newline at end of file diff --git a/.tool-versions b/.tool-versions index 33f368048..c9526937f 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -nodejs 20.11.0 \ No newline at end of file +nodejs 22.10.0 \ No newline at end of file diff --git a/commitlint.config.mjs b/commitlint.config.js similarity index 100% rename from commitlint.config.mjs rename to commitlint.config.js diff --git a/docs/e2e.md b/docs/e2e.md index a239dd047..6bf043d7f 100644 --- a/docs/e2e.md +++ b/docs/e2e.md @@ -144,14 +144,14 @@ export default defineConfig({ // to avoid port conflicts ever E2E targets has a unique port const uniquePort = 6000 + Math.round(Math.random() * 1000); -const e2eDir = join('tmp', 'e2e'); +const e2eDir = path.join('tmp', 'e2e'); export async function setup() { // start local verdaccio registry const { registry } = await startLocalRegistry({ localRegistryTarget: '@code-pushup/cli-source:start-verdaccio', // to avoid file system conflicts ever E2E targets has a unique storage folder - storage: join(join(e2eDir, `registry-${uniquePort}`), 'storage'), + storage: path.join(path.join(e2eDir, `registry-${uniquePort}`), 'storage'), port: uniquePort, }); diff --git a/e2e/ci-e2e/.eslintrc.json b/e2e/ci-e2e/.eslintrc.json deleted file mode 100644 index eae7325ea..000000000 --- a/e2e/ci-e2e/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["e2e/ci-e2e/tsconfig.*?.json"] - } - } - ] -} diff --git a/e2e/ci-e2e/eslint.config.js b/e2e/ci-e2e/eslint.config.js new file mode 100644 index 000000000..2656b27cb --- /dev/null +++ b/e2e/ci-e2e/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, +}); diff --git a/e2e/ci-e2e/mocks/setup.ts b/e2e/ci-e2e/mocks/setup.ts index 1e40bae25..70d47a192 100644 --- a/e2e/ci-e2e/mocks/setup.ts +++ b/e2e/ci-e2e/mocks/setup.ts @@ -1,5 +1,5 @@ import { cp } from 'node:fs/promises'; -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { simpleGit } from 'simple-git'; import { nxTargetProject } from '@code-pushup/test-nx-utils'; @@ -14,12 +14,12 @@ import { export type TestRepo = Awaited>; export async function setupTestRepo(folder: string) { - const fixturesDir = join( - fileURLToPath(dirname(import.meta.url)), + const fixturesDir = path.join( + fileURLToPath(path.dirname(import.meta.url)), 'fixtures', folder, ); - const baseDir = join( + const baseDir = path.join( process.cwd(), E2E_ENVIRONMENTS_DIR, nxTargetProject(), diff --git a/e2e/ci-e2e/tests/basic.e2e.test.ts b/e2e/ci-e2e/tests/basic.e2e.test.ts index 728b6817f..f5f00d176 100644 --- a/e2e/ci-e2e/tests/basic.e2e.test.ts +++ b/e2e/ci-e2e/tests/basic.e2e.test.ts @@ -1,5 +1,5 @@ import { readFile, rename } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import type { SimpleGit } from 'simple-git'; import { afterEach } from 'vitest'; import { @@ -47,14 +47,14 @@ describe('CI - standalone mode', () => { mode: 'standalone', files: { report: { - json: join(repo.baseDir, '.code-pushup/report.json'), - md: join(repo.baseDir, '.code-pushup/report.md'), + json: path.join(repo.baseDir, '.code-pushup/report.json'), + md: path.join(repo.baseDir, '.code-pushup/report.md'), }, }, } satisfies RunResult); const jsonPromise = readFile( - join(repo.baseDir, '.code-pushup/report.json'), + path.join(repo.baseDir, '.code-pushup/report.json'), 'utf8', ); await expect(jsonPromise).resolves.toBeTruthy(); @@ -84,8 +84,8 @@ describe('CI - standalone mode', () => { await git.checkoutLocalBranch('feature-1'); await rename( - join(repo.baseDir, 'index.js'), - join(repo.baseDir, 'index.ts'), + path.join(repo.baseDir, 'index.js'), + path.join(repo.baseDir, 'index.ts'), ); await git.add('index.ts'); @@ -104,25 +104,27 @@ describe('CI - standalone mode', () => { newIssues: [], files: { report: { - json: join(repo.baseDir, '.code-pushup/report.json'), - md: join(repo.baseDir, '.code-pushup/report.md'), + json: path.join(repo.baseDir, '.code-pushup/report.json'), + md: path.join(repo.baseDir, '.code-pushup/report.md'), }, diff: { - json: join(repo.baseDir, '.code-pushup/report-diff.json'), - md: join(repo.baseDir, '.code-pushup/report-diff.md'), + json: path.join(repo.baseDir, '.code-pushup/report-diff.json'), + md: path.join(repo.baseDir, '.code-pushup/report-diff.md'), }, }, } satisfies RunResult); const mdPromise = readFile( - join(repo.baseDir, '.code-pushup/report-diff.md'), + path.join(repo.baseDir, '.code-pushup/report-diff.md'), 'utf8', ); await expect(mdPromise).resolves.toBeTruthy(); const md = await mdPromise; await expect( md.replace(/[\da-f]{40}/g, '``'), - ).toMatchFileSnapshot(join(TEST_SNAPSHOTS_DIR, 'basic-report-diff.md')); + ).toMatchFileSnapshot( + path.join(TEST_SNAPSHOTS_DIR, 'basic-report-diff.md'), + ); }); }); }); diff --git a/e2e/ci-e2e/tests/npm-workspaces.e2e.test.ts b/e2e/ci-e2e/tests/npm-workspaces.e2e.test.ts index 519f3fb7a..af2a7ddd9 100644 --- a/e2e/ci-e2e/tests/npm-workspaces.e2e.test.ts +++ b/e2e/ci-e2e/tests/npm-workspaces.e2e.test.ts @@ -1,5 +1,5 @@ import { readFile, rename } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import type { SimpleGit } from 'simple-git'; import { afterEach } from 'vitest'; import { @@ -53,11 +53,14 @@ describe('CI - monorepo mode (npm workspaces)', () => { name: '@example/cli', files: { report: { - json: join( + json: path.join( repo.baseDir, 'packages/cli/.code-pushup/report.json', ), - md: join(repo.baseDir, 'packages/cli/.code-pushup/report.md'), + md: path.join( + repo.baseDir, + 'packages/cli/.code-pushup/report.md', + ), }, }, }, @@ -66,7 +69,7 @@ describe('CI - monorepo mode (npm workspaces)', () => { await expect( readJsonFile( - join(repo.baseDir, 'packages/cli/.code-pushup/report.json'), + path.join(repo.baseDir, 'packages/cli/.code-pushup/report.json'), ), ).resolves.toEqual( expect.objectContaining({ @@ -92,16 +95,16 @@ describe('CI - monorepo mode (npm workspaces)', () => { await git.checkoutLocalBranch('feature-1'); await rename( - join(repo.baseDir, 'packages/cli/src/bin.js'), - join(repo.baseDir, 'packages/cli/src/bin.ts'), + path.join(repo.baseDir, 'packages/cli/src/bin.js'), + path.join(repo.baseDir, 'packages/cli/src/bin.ts'), ); await rename( - join(repo.baseDir, 'packages/core/src/index.js'), - join(repo.baseDir, 'packages/core/src/index.ts'), + path.join(repo.baseDir, 'packages/core/src/index.js'), + path.join(repo.baseDir, 'packages/core/src/index.ts'), ); await rename( - join(repo.baseDir, 'packages/core/code-pushup.config.js'), - join(repo.baseDir, 'packages/core/code-pushup.config.ts'), + path.join(repo.baseDir, 'packages/core/code-pushup.config.js'), + path.join(repo.baseDir, 'packages/core/code-pushup.config.ts'), ); await git.add('.'); @@ -117,24 +120,27 @@ describe('CI - monorepo mode (npm workspaces)', () => { await expect(runInCI(refs, MOCK_API, options, git)).resolves.toEqual({ mode: 'monorepo', commentId: MOCK_COMMENT.id, - diffPath: join(repo.baseDir, '.code-pushup/merged-report-diff.md'), + diffPath: path.join(repo.baseDir, '.code-pushup/merged-report-diff.md'), projects: expect.arrayContaining([ { name: '@example/core', files: { report: { - json: join( + json: path.join( repo.baseDir, 'packages/core/.code-pushup/report.json', ), - md: join(repo.baseDir, 'packages/core/.code-pushup/report.md'), + md: path.join( + repo.baseDir, + 'packages/core/.code-pushup/report.md', + ), }, diff: { - json: join( + json: path.join( repo.baseDir, 'packages/core/.code-pushup/report-diff.json', ), - md: join( + md: path.join( repo.baseDir, 'packages/core/.code-pushup/report-diff.md', ), @@ -146,7 +152,7 @@ describe('CI - monorepo mode (npm workspaces)', () => { } satisfies RunResult); const mdPromise = readFile( - join(repo.baseDir, '.code-pushup/merged-report-diff.md'), + path.join(repo.baseDir, '.code-pushup/merged-report-diff.md'), 'utf8', ); await expect(mdPromise).resolves.toBeTruthy(); @@ -154,7 +160,7 @@ describe('CI - monorepo mode (npm workspaces)', () => { await expect( md.replace(/[\da-f]{40}/g, '``'), ).toMatchFileSnapshot( - join(TEST_SNAPSHOTS_DIR, 'npm-workspaces-report-diff.md'), + path.join(TEST_SNAPSHOTS_DIR, 'npm-workspaces-report-diff.md'), ); }); }); diff --git a/e2e/ci-e2e/tests/nx-monorepo.e2e.test.ts b/e2e/ci-e2e/tests/nx-monorepo.e2e.test.ts index 0654d4d4f..3338980e8 100644 --- a/e2e/ci-e2e/tests/nx-monorepo.e2e.test.ts +++ b/e2e/ci-e2e/tests/nx-monorepo.e2e.test.ts @@ -1,5 +1,5 @@ import { readFile, rename, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import type { SimpleGit } from 'simple-git'; import { afterEach } from 'vitest'; import { @@ -54,8 +54,11 @@ describe('CI - monorepo mode (Nx)', () => { name: 'api', files: { report: { - json: join(repo.baseDir, 'apps/api/.code-pushup/report.json'), - md: join(repo.baseDir, 'apps/api/.code-pushup/report.md'), + json: path.join( + repo.baseDir, + 'apps/api/.code-pushup/report.json', + ), + md: path.join(repo.baseDir, 'apps/api/.code-pushup/report.md'), }, }, }, @@ -63,7 +66,9 @@ describe('CI - monorepo mode (Nx)', () => { } satisfies RunResult); await expect( - readJsonFile(join(repo.baseDir, 'apps/api/.code-pushup/report.json')), + readJsonFile( + path.join(repo.baseDir, 'apps/api/.code-pushup/report.json'), + ), ).resolves.toEqual( expect.objectContaining({ plugins: [ @@ -79,7 +84,9 @@ describe('CI - monorepo mode (Nx)', () => { }), ); await expect( - readJsonFile(join(repo.baseDir, 'libs/ui/.code-pushup/report.json')), + readJsonFile( + path.join(repo.baseDir, 'libs/ui/.code-pushup/report.json'), + ), ).resolves.toEqual( expect.objectContaining({ plugins: [ @@ -104,21 +111,24 @@ describe('CI - monorepo mode (Nx)', () => { await git.checkoutLocalBranch('feature-1'); await rename( - join(repo.baseDir, 'apps/api/src/index.js'), - join(repo.baseDir, 'apps/api/src/index.ts'), + path.join(repo.baseDir, 'apps/api/src/index.js'), + path.join(repo.baseDir, 'apps/api/src/index.ts'), ); await rename( - join(repo.baseDir, 'apps/web/src/index.ts'), - join(repo.baseDir, 'apps/web/src/index.js'), + path.join(repo.baseDir, 'apps/web/src/index.ts'), + path.join(repo.baseDir, 'apps/web/src/index.js'), ); await rename( - join(repo.baseDir, 'libs/ui/code-pushup.config.js'), - join(repo.baseDir, 'libs/ui/code-pushup.config.ts'), + path.join(repo.baseDir, 'libs/ui/code-pushup.config.js'), + path.join(repo.baseDir, 'libs/ui/code-pushup.config.ts'), ); await writeFile( - join(repo.baseDir, 'libs/ui/project.json'), + path.join(repo.baseDir, 'libs/ui/project.json'), ( - await readFile(join(repo.baseDir, 'libs/ui/project.json'), 'utf8') + await readFile( + path.join(repo.baseDir, 'libs/ui/project.json'), + 'utf8', + ) ).replace('code-pushup.config.js', 'code-pushup.config.ts'), ); @@ -135,21 +145,27 @@ describe('CI - monorepo mode (Nx)', () => { await expect(runInCI(refs, MOCK_API, options, git)).resolves.toEqual({ mode: 'monorepo', commentId: MOCK_COMMENT.id, - diffPath: join(repo.baseDir, '.code-pushup/merged-report-diff.md'), + diffPath: path.join(repo.baseDir, '.code-pushup/merged-report-diff.md'), projects: expect.arrayContaining([ { name: 'web', files: { report: { - json: join(repo.baseDir, 'apps/web/.code-pushup/report.json'), - md: join(repo.baseDir, 'apps/web/.code-pushup/report.md'), + json: path.join( + repo.baseDir, + 'apps/web/.code-pushup/report.json', + ), + md: path.join(repo.baseDir, 'apps/web/.code-pushup/report.md'), }, diff: { - json: join( + json: path.join( repo.baseDir, 'apps/web/.code-pushup/report-diff.json', ), - md: join(repo.baseDir, 'apps/web/.code-pushup/report-diff.md'), + md: path.join( + repo.baseDir, + 'apps/web/.code-pushup/report-diff.md', + ), }, }, newIssues: [ @@ -166,7 +182,7 @@ describe('CI - monorepo mode (Nx)', () => { } satisfies RunResult); const mdPromise = readFile( - join(repo.baseDir, '.code-pushup/merged-report-diff.md'), + path.join(repo.baseDir, '.code-pushup/merged-report-diff.md'), 'utf8', ); await expect(mdPromise).resolves.toBeTruthy(); @@ -174,7 +190,7 @@ describe('CI - monorepo mode (Nx)', () => { await expect( md.replace(/[\da-f]{40}/g, '``'), ).toMatchFileSnapshot( - join(TEST_SNAPSHOTS_DIR, 'nx-monorepo-report-diff.md'), + path.join(TEST_SNAPSHOTS_DIR, 'nx-monorepo-report-diff.md'), ); }); }); diff --git a/e2e/cli-e2e/.eslintrc.json b/e2e/cli-e2e/.eslintrc.json deleted file mode 100644 index 2b6e1900a..000000000 --- a/e2e/cli-e2e/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["e2e/cli-e2e/tsconfig.*?.json"] - } - } - ] -} diff --git a/e2e/cli-e2e/eslint.config.js b/e2e/cli-e2e/eslint.config.js new file mode 100644 index 000000000..2656b27cb --- /dev/null +++ b/e2e/cli-e2e/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, +}); diff --git a/e2e/cli-e2e/mocks/fixtures/dummy-setup/dummy.plugin.ts b/e2e/cli-e2e/mocks/fixtures/dummy-setup/dummy.plugin.ts index 8ff5ebd18..702967e60 100644 --- a/e2e/cli-e2e/mocks/fixtures/dummy-setup/dummy.plugin.ts +++ b/e2e/cli-e2e/mocks/fixtures/dummy-setup/dummy.plugin.ts @@ -1,5 +1,5 @@ import { readFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import type { PluginConfig } from '@code-pushup/models'; export const dummyPluginSlug = 'dummy-plugin'; @@ -32,7 +32,7 @@ export function create(): PluginConfig { description: 'A dummy plugin to test the cli.', runner: async () => { const itemCount = JSON.parse( - await readFile(join('src', 'items.json'), 'utf-8'), + await readFile(path.join('src', 'items.json'), 'utf-8'), ).length; return [ { diff --git a/e2e/cli-e2e/mocks/fixtures/existing-reports/dummy.plugin.ts b/e2e/cli-e2e/mocks/fixtures/existing-reports/dummy.plugin.ts index 8ff5ebd18..702967e60 100644 --- a/e2e/cli-e2e/mocks/fixtures/existing-reports/dummy.plugin.ts +++ b/e2e/cli-e2e/mocks/fixtures/existing-reports/dummy.plugin.ts @@ -1,5 +1,5 @@ import { readFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import type { PluginConfig } from '@code-pushup/models'; export const dummyPluginSlug = 'dummy-plugin'; @@ -32,7 +32,7 @@ export function create(): PluginConfig { description: 'A dummy plugin to test the cli.', runner: async () => { const itemCount = JSON.parse( - await readFile(join('src', 'items.json'), 'utf-8'), + await readFile(path.join('src', 'items.json'), 'utf-8'), ).length; return [ { diff --git a/e2e/cli-e2e/tests/collect.e2e.test.ts b/e2e/cli-e2e/tests/collect.e2e.test.ts index 707ae96bc..4a0f9d9fb 100644 --- a/e2e/cli-e2e/tests/collect.e2e.test.ts +++ b/e2e/cli-e2e/tests/collect.e2e.test.ts @@ -1,5 +1,5 @@ import { cp } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { afterEach, beforeAll, describe, expect, it } from 'vitest'; import { nxTargetProject } from '@code-pushup/test-nx-utils'; import { teardownTestFolder } from '@code-pushup/test-setup'; @@ -9,21 +9,21 @@ import { executeProcess, readTextFile } from '@code-pushup/utils'; describe('CLI collect', () => { const dummyPluginTitle = 'Dummy Plugin'; const dummyAuditTitle = 'Dummy Audit'; - const fixtureDummyDir = join( + const fixtureDummyDir = path.join( 'e2e', nxTargetProject(), 'mocks', 'fixtures', 'dummy-setup', ); - const testFileDir = join( + const testFileDir = path.join( E2E_ENVIRONMENTS_DIR, nxTargetProject(), TEST_OUTPUT_DIR, 'collect', ); - const dummyDir = join(testFileDir, 'dummy-setup'); - const dummyOutputDir = join(dummyDir, '.code-pushup'); + const dummyDir = path.join(testFileDir, 'dummy-setup'); + const dummyOutputDir = path.join(dummyDir, '.code-pushup'); beforeAll(async () => { await cp(fixtureDummyDir, dummyDir, { recursive: true }); @@ -52,7 +52,7 @@ describe('CLI collect', () => { expect(code).toBe(0); expect(stderr).toBe(''); - const md = await readTextFile(join(dummyOutputDir, 'report.md')); + const md = await readTextFile(path.join(dummyOutputDir, 'report.md')); expect(md).toContain('# Code PushUp Report'); expect(md).toContain(dummyPluginTitle); diff --git a/e2e/cli-e2e/tests/compare.e2e.test.ts b/e2e/cli-e2e/tests/compare.e2e.test.ts index 73d08b6b2..0d1140ebc 100644 --- a/e2e/cli-e2e/tests/compare.e2e.test.ts +++ b/e2e/cli-e2e/tests/compare.e2e.test.ts @@ -1,5 +1,5 @@ import { cp } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { beforeAll } from 'vitest'; import type { ReportsDiff } from '@code-pushup/models'; import { nxTargetProject } from '@code-pushup/test-nx-utils'; @@ -8,7 +8,7 @@ import { E2E_ENVIRONMENTS_DIR, TEST_OUTPUT_DIR } from '@code-pushup/test-utils'; import { executeProcess, readJsonFile, readTextFile } from '@code-pushup/utils'; describe('CLI compare', () => { - const fixtureDummyDir = join( + const fixtureDummyDir = path.join( 'e2e', nxTargetProject(), 'mocks', @@ -16,14 +16,14 @@ describe('CLI compare', () => { 'existing-reports', ); - const testFileDir = join( + const testFileDir = path.join( E2E_ENVIRONMENTS_DIR, nxTargetProject(), TEST_OUTPUT_DIR, 'compare', ); - const existingDir = join(testFileDir, 'existing-reports'); - const existingOutputDir = join(existingDir, '.code-pushup'); + const existingDir = path.join(testFileDir, 'existing-reports'); + const existingOutputDir = path.join(existingDir, '.code-pushup'); beforeAll(async () => { await cp(fixtureDummyDir, existingDir, { recursive: true }); @@ -39,14 +39,14 @@ describe('CLI compare', () => { args: [ '@code-pushup/cli', 'compare', - `--before=${join('.code-pushup', 'source-report.json')}`, - `--after=${join('.code-pushup', 'target-report.json')}`, + `--before=${path.join('.code-pushup', 'source-report.json')}`, + `--after=${path.join('.code-pushup', 'target-report.json')}`, ], cwd: existingDir, }); const reportsDiff = await readJsonFile( - join(existingOutputDir, 'report-diff.json'), + path.join(existingOutputDir, 'report-diff.json'), ); expect(reportsDiff).toMatchSnapshot({ commits: expect.any(Object), @@ -56,7 +56,7 @@ describe('CLI compare', () => { }); const reportsDiffMd = await readTextFile( - join(existingOutputDir, 'report-diff.md'), + path.join(existingOutputDir, 'report-diff.md'), ); // commits are variable, replace SHAs with placeholders const sanitizedMd = reportsDiffMd.replace(/[\da-f]{40}/g, '``'); diff --git a/e2e/cli-e2e/tests/help.e2e.test.ts b/e2e/cli-e2e/tests/help.e2e.test.ts index ec2390c98..63533b51c 100644 --- a/e2e/cli-e2e/tests/help.e2e.test.ts +++ b/e2e/cli-e2e/tests/help.e2e.test.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { nxTargetProject } from '@code-pushup/test-nx-utils'; import { E2E_ENVIRONMENTS_DIR, @@ -7,7 +7,7 @@ import { import { executeProcess } from '@code-pushup/utils'; describe('CLI help', () => { - const envRoot = join(E2E_ENVIRONMENTS_DIR, nxTargetProject()); + const envRoot = path.join(E2E_ENVIRONMENTS_DIR, nxTargetProject()); it('should print help with help command', async () => { const { code, stdout, stderr } = await executeProcess({ diff --git a/e2e/cli-e2e/tests/print-config.e2e.test.ts b/e2e/cli-e2e/tests/print-config.e2e.test.ts index 7f8a06ae1..e29230721 100644 --- a/e2e/cli-e2e/tests/print-config.e2e.test.ts +++ b/e2e/cli-e2e/tests/print-config.e2e.test.ts @@ -1,5 +1,5 @@ import { cp } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { beforeAll, expect } from 'vitest'; import { nxTargetProject } from '@code-pushup/test-nx-utils'; import { teardownTestFolder } from '@code-pushup/test-setup'; @@ -8,7 +8,7 @@ import { executeProcess } from '@code-pushup/utils'; describe('CLI print-config', () => { const extensions = ['js', 'mjs', 'ts'] as const; - const fixtureDummyDir = join( + const fixtureDummyDir = path.join( 'e2e', nxTargetProject(), 'mocks', @@ -16,15 +16,15 @@ describe('CLI print-config', () => { 'dummy-setup', ); - const testFileDir = join( + const testFileDir = path.join( E2E_ENVIRONMENTS_DIR, nxTargetProject(), TEST_OUTPUT_DIR, 'print-config', ); - const testFileDummySetup = join(testFileDir, 'dummy-setup'); + const testFileDummySetup = path.join(testFileDir, 'dummy-setup'); const configFilePath = (ext: (typeof extensions)[number]) => - join(process.cwd(), testFileDummySetup, `code-pushup.config.${ext}`); + path.join(process.cwd(), testFileDummySetup, `code-pushup.config.${ext}`); beforeAll(async () => { await cp(fixtureDummyDir, testFileDummySetup, { recursive: true }); diff --git a/e2e/create-cli-e2e/.eslintrc.json b/e2e/create-cli-e2e/.eslintrc.json deleted file mode 100644 index 05b10f8e7..000000000 --- a/e2e/create-cli-e2e/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["e2e/create-cli-e2e/tsconfig.*?.json"] - } - } - ] -} diff --git a/e2e/create-cli-e2e/eslint.config.js b/e2e/create-cli-e2e/eslint.config.js new file mode 100644 index 000000000..2656b27cb --- /dev/null +++ b/e2e/create-cli-e2e/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, +}); diff --git a/e2e/create-cli-e2e/tests/init.e2e.test.ts b/e2e/create-cli-e2e/tests/init.e2e.test.ts index 0521c44b9..c926e0c7b 100644 --- a/e2e/create-cli-e2e/tests/init.e2e.test.ts +++ b/e2e/create-cli-e2e/tests/init.e2e.test.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { afterEach, expect } from 'vitest'; import { nxTargetProject } from '@code-pushup/test-nx-utils'; import { teardownTestFolder } from '@code-pushup/test-setup'; @@ -14,15 +14,15 @@ const fakeCacheFolderName = () => `fake-cache-${new Date().toISOString().replace(/[:.]/g, '-')}`; describe('create-cli-init', () => { - const workspaceRoot = join(E2E_ENVIRONMENTS_DIR, nxTargetProject()); - const testFileDir = join(workspaceRoot, TEST_OUTPUT_DIR, 'init'); + const workspaceRoot = path.join(E2E_ENVIRONMENTS_DIR, nxTargetProject()); + const testFileDir = path.join(workspaceRoot, TEST_OUTPUT_DIR, 'init'); afterEach(async () => { await teardownTestFolder(testFileDir); }); it('should execute package correctly over npm exec', async () => { - const cwd = join(testFileDir, 'npm-exec'); + const cwd = path.join(testFileDir, 'npm-exec'); await createNpmWorkspace(cwd); const { code, stdout } = await executeProcess({ command: 'npm', @@ -42,7 +42,7 @@ describe('create-cli-init', () => { ); await expect( - readJsonFile(join(cwd, 'package.json')), + readJsonFile(path.join(cwd, 'package.json')), ).resolves.toStrictEqual( expect.objectContaining({ devDependencies: { @@ -54,14 +54,14 @@ describe('create-cli-init', () => { }), ); await expect( - readTextFile(join(cwd, 'code-pushup.config.ts')), + readTextFile(path.join(cwd, 'code-pushup.config.ts')), ).resolves.toContain( "import type { CoreConfig } from '@code-pushup/models';", ); }); it('should execute package correctly over npm init', async () => { - const cwd = join(testFileDir, 'npm-init-setup'); + const cwd = path.join(testFileDir, 'npm-init-setup'); await createNpmWorkspace(cwd); const { code, stdout } = await executeProcess({ @@ -82,7 +82,7 @@ describe('create-cli-init', () => { ); await expect( - readJsonFile(join(cwd, 'package.json')), + readJsonFile(path.join(cwd, 'package.json')), ).resolves.toStrictEqual( expect.objectContaining({ devDependencies: { @@ -94,14 +94,14 @@ describe('create-cli-init', () => { }), ); await expect( - readTextFile(join(cwd, 'code-pushup.config.ts')), + readTextFile(path.join(cwd, 'code-pushup.config.ts')), ).resolves.toContain( "import type { CoreConfig } from '@code-pushup/models';", ); }); it('should produce an executable setup when running npm exec', async () => { - const cwd = join(testFileDir, 'npm-executable'); + const cwd = path.join(testFileDir, 'npm-executable'); await createNpmWorkspace(cwd); await executeProcess({ diff --git a/e2e/nx-plugin-e2e/.eslintrc.json b/e2e/nx-plugin-e2e/.eslintrc.json deleted file mode 100644 index 1519d85ae..000000000 --- a/e2e/nx-plugin-e2e/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["e2e/nx-plugin-e2e/tsconfig.*?.json"] - } - } - ] -} diff --git a/e2e/nx-plugin-e2e/eslint.config.js b/e2e/nx-plugin-e2e/eslint.config.js new file mode 100644 index 000000000..2656b27cb --- /dev/null +++ b/e2e/nx-plugin-e2e/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, +}); diff --git a/e2e/nx-plugin-e2e/tests/executor-cli.e2e.test.ts b/e2e/nx-plugin-e2e/tests/executor-cli.e2e.test.ts index c3cb1ec80..369c0852f 100644 --- a/e2e/nx-plugin-e2e/tests/executor-cli.e2e.test.ts +++ b/e2e/nx-plugin-e2e/tests/executor-cli.e2e.test.ts @@ -1,5 +1,5 @@ import { type Tree, updateProjectConfiguration } from '@nx/devkit'; -import { join } from 'node:path'; +import path from 'node:path'; import { readProjectConfiguration } from 'nx/src/generators/utils/project-configuration'; import { afterEach, expect } from 'vitest'; import { generateCodePushupConfig } from '@code-pushup/nx-plugin'; @@ -27,7 +27,7 @@ async function addTargetToWorkspace( ...projectCfg, targets: { ...projectCfg.targets, - ['code-pushup']: { + 'code-pushup': { executor: '@code-pushup/nx-plugin:cli', }, }, @@ -47,7 +47,7 @@ async function addTargetToWorkspace( describe('executor command', () => { let tree: Tree; const project = 'my-lib'; - const testFileDir = join( + const testFileDir = path.join( E2E_ENVIRONMENTS_DIR, nxTargetProject(), TEST_OUTPUT_DIR, @@ -63,7 +63,7 @@ describe('executor command', () => { }); it('should execute no specific command by default', async () => { - const cwd = join(testFileDir, 'execute-default-command'); + const cwd = path.join(testFileDir, 'execute-default-command'); await addTargetToWorkspace(tree, { cwd, project }); const { stdout, code } = await executeProcess({ command: 'npx', @@ -77,7 +77,7 @@ describe('executor command', () => { }); it('should execute print-config executor', async () => { - const cwd = join(testFileDir, 'execute-print-config-command'); + const cwd = path.join(testFileDir, 'execute-print-config-command'); await addTargetToWorkspace(tree, { cwd, project }); const { stdout, code } = await executeProcess({ @@ -91,12 +91,12 @@ describe('executor command', () => { expect(cleanStdout).toContain('nx run my-lib:code-pushup print-config'); await expect(() => - readJsonFile(join(cwd, '.code-pushup', project, 'report.json')), + readJsonFile(path.join(cwd, '.code-pushup', project, 'report.json')), ).rejects.toThrow(''); }); it('should execute collect executor and add report to sub folder named by project', async () => { - const cwd = join(testFileDir, 'execute-collect-command'); + const cwd = path.join(testFileDir, 'execute-collect-command'); await addTargetToWorkspace(tree, { cwd, project }); const { stdout, code } = await executeProcess({ @@ -110,7 +110,7 @@ describe('executor command', () => { expect(cleanStdout).toContain('nx run my-lib:code-pushup collect'); const report = await readJsonFile( - join(cwd, '.code-pushup', project, 'report.json'), + path.join(cwd, '.code-pushup', project, 'report.json'), ); expect(report).toStrictEqual( expect.objectContaining({ diff --git a/e2e/nx-plugin-e2e/tests/generator-configuration.e2e.test.ts b/e2e/nx-plugin-e2e/tests/generator-configuration.e2e.test.ts index 4ad65594f..6237a9212 100644 --- a/e2e/nx-plugin-e2e/tests/generator-configuration.e2e.test.ts +++ b/e2e/nx-plugin-e2e/tests/generator-configuration.e2e.test.ts @@ -1,6 +1,6 @@ import type { Tree } from '@nx/devkit'; import { readFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { afterEach, expect } from 'vitest'; import { generateCodePushupConfig } from '@code-pushup/nx-plugin'; import { @@ -19,8 +19,8 @@ import { executeProcess } from '@code-pushup/utils'; describe('nx-plugin g configuration', () => { let tree: Tree; const project = 'my-lib'; - const projectRoot = join('libs', project); - const testFileDir = join( + const projectRoot = path.join('libs', project); + const testFileDir = path.join( E2E_ENVIRONMENTS_DIR, nxTargetProject(), TEST_OUTPUT_DIR, @@ -36,7 +36,7 @@ describe('nx-plugin g configuration', () => { }); it('should generate code-pushup.config.ts file and add target to project.json', async () => { - const cwd = join(testFileDir, 'configure'); + const cwd = path.join(testFileDir, 'configure'); await materializeTree(tree, cwd); const { code, stdout, stderr } = await executeProcess({ @@ -67,7 +67,7 @@ describe('nx-plugin g configuration', () => { expect(cleanedStdout).toMatch(/^UPDATE.*project.json/m); const projectJson = await readFile( - join(cwd, 'libs', project, 'project.json'), + path.join(cwd, 'libs', project, 'project.json'), 'utf8', ); @@ -81,12 +81,15 @@ describe('nx-plugin g configuration', () => { }), ); await expect( - readFile(join(cwd, 'libs', project, 'code-pushup.config.ts'), 'utf8'), + readFile( + path.join(cwd, 'libs', project, 'code-pushup.config.ts'), + 'utf8', + ), ).resolves.not.toThrow(); }); it('should NOT create a code-pushup.config.ts file if one already exists', async () => { - const cwd = join(testFileDir, 'configure-config-existing'); + const cwd = path.join(testFileDir, 'configure-config-existing'); generateCodePushupConfig(tree, projectRoot); await materializeTree(tree, cwd); @@ -111,7 +114,7 @@ describe('nx-plugin g configuration', () => { expect(cleanedStdout).toMatch(/^UPDATE.*project.json/m); const projectJson = await readFile( - join(cwd, 'libs', project, 'project.json'), + path.join(cwd, 'libs', project, 'project.json'), 'utf8', ); expect(JSON.parse(projectJson)).toStrictEqual( @@ -126,7 +129,7 @@ describe('nx-plugin g configuration', () => { }); it('should NOT create a code-pushup.config.ts file if skipConfig is given', async () => { - const cwd = join(testFileDir, 'configure-skip-config'); + const cwd = path.join(testFileDir, 'configure-skip-config'); await materializeTree(tree, cwd); const { code, stdout } = await executeProcess({ @@ -152,7 +155,7 @@ describe('nx-plugin g configuration', () => { expect(cleanedStdout).toMatch(/^UPDATE.*project.json/m); const projectJson = await readFile( - join(cwd, 'libs', project, 'project.json'), + path.join(cwd, 'libs', project, 'project.json'), 'utf8', ); expect(JSON.parse(projectJson)).toStrictEqual( @@ -166,12 +169,15 @@ describe('nx-plugin g configuration', () => { ); await expect( - readFile(join(cwd, 'libs', project, 'code-pushup.config.ts'), 'utf8'), + readFile( + path.join(cwd, 'libs', project, 'code-pushup.config.ts'), + 'utf8', + ), ).rejects.toThrow('no such file or directory'); }); it('should NOT add target to project.json if skipTarget is given', async () => { - const cwd = join(testFileDir, 'configure-skip-target'); + const cwd = path.join(testFileDir, 'configure-skip-target'); await materializeTree(tree, cwd); const { code, stdout } = await executeProcess({ @@ -196,7 +202,7 @@ describe('nx-plugin g configuration', () => { expect(cleanedStdout).not.toMatch(/^UPDATE.*project.json/m); const projectJson = await readFile( - join(cwd, 'libs', project, 'project.json'), + path.join(cwd, 'libs', project, 'project.json'), 'utf8', ); expect(JSON.parse(projectJson)).toStrictEqual( @@ -210,12 +216,15 @@ describe('nx-plugin g configuration', () => { ); await expect( - readFile(join(cwd, 'libs', project, 'code-pushup.config.ts'), 'utf8'), + readFile( + path.join(cwd, 'libs', project, 'code-pushup.config.ts'), + 'utf8', + ), ).resolves.toStrictEqual(expect.any(String)); }); it('should inform about dry run', async () => { - const cwd = join(testFileDir, 'configure'); + const cwd = path.join(testFileDir, 'configure'); await materializeTree(tree, cwd); const { stderr } = await executeProcess({ diff --git a/e2e/nx-plugin-e2e/tests/generator-init.e2e.test.ts b/e2e/nx-plugin-e2e/tests/generator-init.e2e.test.ts index 4070fd4d5..c3ea4c895 100644 --- a/e2e/nx-plugin-e2e/tests/generator-init.e2e.test.ts +++ b/e2e/nx-plugin-e2e/tests/generator-init.e2e.test.ts @@ -1,6 +1,6 @@ import type { Tree } from '@nx/devkit'; import { readFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { afterEach, expect } from 'vitest'; import { generateWorkspaceAndProject, @@ -18,7 +18,7 @@ import { executeProcess } from '@code-pushup/utils'; describe('nx-plugin g init', () => { let tree: Tree; const project = 'my-lib'; - const testFileDir = join( + const testFileDir = path.join( E2E_ENVIRONMENTS_DIR, nxTargetProject(), TEST_OUTPUT_DIR, @@ -34,7 +34,7 @@ describe('nx-plugin g init', () => { }); it('should inform about dry run when used on init generator', async () => { - const cwd = join(testFileDir, 'dry-run'); + const cwd = path.join(testFileDir, 'dry-run'); await materializeTree(tree, cwd); const { stderr } = await executeProcess({ @@ -50,7 +50,7 @@ describe('nx-plugin g init', () => { }); it('should update packages.json and configure nx.json', async () => { - const cwd = join(testFileDir, 'nx-update'); + const cwd = path.join(testFileDir, 'nx-update'); await materializeTree(tree, cwd); const { code, stdout } = await executeProcess({ @@ -73,8 +73,8 @@ describe('nx-plugin g init', () => { expect(cleanedStdout).toMatch(/^UPDATE package.json/m); expect(cleanedStdout).toMatch(/^UPDATE nx.json/m); - const packageJson = await readFile(join(cwd, 'package.json'), 'utf8'); - const nxJson = await readFile(join(cwd, 'nx.json'), 'utf8'); + const packageJson = await readFile(path.join(cwd, 'package.json'), 'utf8'); + const nxJson = await readFile(path.join(cwd, 'nx.json'), 'utf8'); expect(JSON.parse(packageJson)).toStrictEqual( expect.objectContaining({ @@ -99,7 +99,7 @@ describe('nx-plugin g init', () => { }); it('should skip packages.json update if --skipPackageJson is given', async () => { - const cwd = join(testFileDir, 'skip-packages'); + const cwd = path.join(testFileDir, 'skip-packages'); await materializeTree(tree, cwd); const { code, stdout } = await executeProcess({ @@ -123,8 +123,8 @@ describe('nx-plugin g init', () => { expect(cleanedStdout).not.toMatch(/^UPDATE package.json/m); expect(cleanedStdout).toMatch(/^UPDATE nx.json/m); - const packageJson = await readFile(join(cwd, 'package.json'), 'utf8'); - const nxJson = await readFile(join(cwd, 'nx.json'), 'utf8'); + const packageJson = await readFile(path.join(cwd, 'package.json'), 'utf8'); + const nxJson = await readFile(path.join(cwd, 'nx.json'), 'utf8'); expect(JSON.parse(packageJson)).toStrictEqual( expect.objectContaining({ diff --git a/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts b/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts index b3f7adca9..9ded9aab0 100644 --- a/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts +++ b/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts @@ -1,5 +1,5 @@ import type { Tree } from '@nx/devkit'; -import { join } from 'node:path'; +import path from 'node:path'; import { readProjectConfiguration } from 'nx/src/generators/utils/project-configuration'; import { afterEach, expect } from 'vitest'; import { generateCodePushupConfig } from '@code-pushup/nx-plugin'; @@ -22,8 +22,8 @@ import { INLINE_PLUGIN } from './inline-plugin.js'; describe('nx-plugin', () => { let tree: Tree; const project = 'my-lib'; - const projectRoot = join('libs', project); - const testFileDir = join( + const projectRoot = path.join('libs', project); + const testFileDir = path.join( E2E_ENVIRONMENTS_DIR, nxTargetProject(), TEST_OUTPUT_DIR, @@ -39,7 +39,7 @@ describe('nx-plugin', () => { }); it('should add configuration target dynamically', async () => { - const cwd = join(testFileDir, 'add-configuration-dynamically'); + const cwd = path.join(testFileDir, 'add-configuration-dynamically'); registerPluginInWorkspace(tree, '@code-pushup/nx-plugin'); await materializeTree(tree, cwd); @@ -47,7 +47,7 @@ describe('nx-plugin', () => { expect(code).toBe(0); expect(projectJson.targets).toEqual({ - ['code-pushup--configuration']: { + 'code-pushup--configuration': { configurations: {}, executor: 'nx:run-commands', options: { @@ -61,7 +61,7 @@ describe('nx-plugin', () => { }); it('should execute dynamic configuration target', async () => { - const cwd = join(testFileDir, 'execute-dynamic-configuration'); + const cwd = path.join(testFileDir, 'execute-dynamic-configuration'); registerPluginInWorkspace(tree, { plugin: '@code-pushup/nx-plugin', }); @@ -80,12 +80,12 @@ describe('nx-plugin', () => { `Successfully ran target code-pushup--configuration for project ${project}`, ); await expect( - readTextFile(join(cwd, projectRoot, 'code-pushup.config.ts')), + readTextFile(path.join(cwd, projectRoot, 'code-pushup.config.ts')), ).resolves.toMatchSnapshot(); }); it('should consider plugin option targetName in configuration target', async () => { - const cwd = join(testFileDir, 'configuration-option-target-name'); + const cwd = path.join(testFileDir, 'configuration-option-target-name'); registerPluginInWorkspace(tree, { plugin: '@code-pushup/nx-plugin', options: { @@ -99,12 +99,12 @@ describe('nx-plugin', () => { expect(code).toBe(0); expect(projectJson.targets).toStrictEqual({ - ['cp--configuration']: expect.any(Object), + 'cp--configuration': expect.any(Object), }); }); it('should consider plugin option bin in configuration target', async () => { - const cwd = join(testFileDir, 'configuration-option-bin'); + const cwd = path.join(testFileDir, 'configuration-option-bin'); registerPluginInWorkspace(tree, { plugin: '@code-pushup/nx-plugin', options: { @@ -118,7 +118,7 @@ describe('nx-plugin', () => { expect(code).toBe(0); expect(projectJson.targets).toStrictEqual({ - ['code-pushup--configuration']: expect.objectContaining({ + 'code-pushup--configuration': expect.objectContaining({ options: { command: `nx g XYZ:configuration --skipTarget --targetName="code-pushup" --project="${project}"`, }, @@ -127,7 +127,7 @@ describe('nx-plugin', () => { }); it('should NOT add config targets dynamically if the project is configured', async () => { - const cwd = join(testFileDir, 'configuration-already-configured'); + const cwd = path.join(testFileDir, 'configuration-already-configured'); registerPluginInWorkspace(tree, '@code-pushup/nx-plugin'); const { root } = readProjectConfiguration(tree, project); generateCodePushupConfig(tree, root); @@ -139,14 +139,14 @@ describe('nx-plugin', () => { expect(projectJson.targets).toStrictEqual( expect.not.objectContaining({ - ['code-pushup--configuration']: expect.any(Object), + 'code-pushup--configuration': expect.any(Object), }), ); expect(projectJson.targets).toMatchSnapshot(); }); it('should add executor target dynamically if the project is configured', async () => { - const cwd = join(testFileDir, 'add-executor-dynamically'); + const cwd = path.join(testFileDir, 'add-executor-dynamically'); registerPluginInWorkspace(tree, '@code-pushup/nx-plugin'); const { root } = readProjectConfiguration(tree, project); generateCodePushupConfig(tree, root); @@ -156,7 +156,7 @@ describe('nx-plugin', () => { expect(code).toBe(0); expect(projectJson.targets).toStrictEqual({ - ['code-pushup']: { + 'code-pushup': { configurations: {}, executor: `@code-pushup/nx-plugin:cli`, options: {}, @@ -168,7 +168,7 @@ describe('nx-plugin', () => { }); it('should execute dynamic executor target', async () => { - const cwd = join(testFileDir, 'execute-dynamic-executor'); + const cwd = path.join(testFileDir, 'execute-dynamic-executor'); registerPluginInWorkspace(tree, { plugin: '@code-pushup/nx-plugin', }); @@ -206,7 +206,7 @@ describe('nx-plugin', () => { }); it('should consider plugin option bin in executor target', async () => { - const cwd = join(testFileDir, 'configuration-option-bin'); + const cwd = path.join(testFileDir, 'configuration-option-bin'); registerPluginInWorkspace(tree, { plugin: '@code-pushup/nx-plugin', options: { @@ -222,14 +222,14 @@ describe('nx-plugin', () => { expect(code).toBe(0); expect(projectJson.targets).toStrictEqual({ - ['code-pushup']: expect.objectContaining({ + 'code-pushup': expect.objectContaining({ executor: 'XYZ:cli', }), }); }); it('should consider plugin option projectPrefix in executor target', async () => { - const cwd = join(testFileDir, 'configuration-option-bin'); + const cwd = path.join(testFileDir, 'configuration-option-bin'); registerPluginInWorkspace(tree, { plugin: '@code-pushup/nx-plugin', options: { @@ -245,7 +245,7 @@ describe('nx-plugin', () => { expect(code).toBe(0); expect(projectJson.targets).toStrictEqual({ - ['code-pushup']: expect.objectContaining({ + 'code-pushup': expect.objectContaining({ executor: `@code-pushup/nx-plugin:cli`, options: { projectPrefix: 'cli', @@ -255,7 +255,7 @@ describe('nx-plugin', () => { }); it('should NOT add targets dynamically if plugin is not registered', async () => { - const cwd = join(testFileDir, 'plugin-not-registered'); + const cwd = path.join(testFileDir, 'plugin-not-registered'); await materializeTree(tree, cwd); const { code, projectJson } = await nxShowProjectJson(cwd, project); diff --git a/e2e/plugin-coverage-e2e/.eslintrc.json b/e2e/plugin-coverage-e2e/.eslintrc.json deleted file mode 100644 index 664ebc398..000000000 --- a/e2e/plugin-coverage-e2e/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["e2e/plugin-coverage-e2e/tsconfig.*?.json"] - } - } - ] -} diff --git a/e2e/plugin-coverage-e2e/eslint.config.js b/e2e/plugin-coverage-e2e/eslint.config.js new file mode 100644 index 000000000..2656b27cb --- /dev/null +++ b/e2e/plugin-coverage-e2e/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, +}); diff --git a/e2e/plugin-coverage-e2e/mocks/fixtures/existing-report/code-pushup.config.ts b/e2e/plugin-coverage-e2e/mocks/fixtures/existing-report/code-pushup.config.ts index d955bbba0..41c688012 100644 --- a/e2e/plugin-coverage-e2e/mocks/fixtures/existing-report/code-pushup.config.ts +++ b/e2e/plugin-coverage-e2e/mocks/fixtures/existing-report/code-pushup.config.ts @@ -1,11 +1,11 @@ -import { join } from 'node:path'; +import path from 'node:path'; import coveragePlugin from '@code-pushup/coverage-plugin'; import type { CoreConfig } from '@code-pushup/models'; export default { plugins: [ await coveragePlugin({ - reports: [join('coverage', 'lcov.info')], + reports: [path.join('coverage', 'lcov.info')], }), ], categories: [ diff --git a/e2e/plugin-coverage-e2e/tests/collect.e2e.test.ts b/e2e/plugin-coverage-e2e/tests/collect.e2e.test.ts index 9dd257adf..09bc75e96 100644 --- a/e2e/plugin-coverage-e2e/tests/collect.e2e.test.ts +++ b/e2e/plugin-coverage-e2e/tests/collect.e2e.test.ts @@ -1,5 +1,5 @@ import { cp } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { afterAll, afterEach, beforeAll } from 'vitest'; import { type Report, reportSchema } from '@code-pushup/models'; import { nxTargetProject } from '@code-pushup/test-nx-utils'; @@ -12,23 +12,26 @@ import { import { executeProcess, readJsonFile } from '@code-pushup/utils'; describe('PLUGIN collect report with coverage-plugin NPM package', () => { - const envRoot = join(E2E_ENVIRONMENTS_DIR, nxTargetProject()); - const testFileDir = join(envRoot, TEST_OUTPUT_DIR, 'collect'); + const envRoot = path.join(E2E_ENVIRONMENTS_DIR, nxTargetProject()); + const testFileDir = path.join(envRoot, TEST_OUTPUT_DIR, 'collect'); - const basicDir = join(testFileDir, 'basic-setup'); - const existingDir = join(testFileDir, 'existing-report'); + const basicDir = path.join(testFileDir, 'basic-setup'); + const existingDir = path.join(testFileDir, 'existing-report'); + + const fixtureDir = path.join('e2e', nxTargetProject(), 'mocks', 'fixtures'); - const fixtureDir = join('e2e', nxTargetProject(), 'mocks', 'fixtures'); beforeAll(async () => { await cp(fixtureDir, testFileDir, { recursive: true }); }); + afterAll(async () => { await teardownTestFolder(basicDir); await teardownTestFolder(existingDir); }); + afterEach(async () => { - await teardownTestFolder(join(basicDir, '.code-pushup')); - await teardownTestFolder(join(existingDir, '.code-pushup')); + await teardownTestFolder(path.join(basicDir, '.code-pushup')); + await teardownTestFolder(path.join(existingDir, '.code-pushup')); }); it('should run Code coverage plugin which collects passed results and creates report.json', async () => { @@ -41,7 +44,7 @@ describe('PLUGIN collect report with coverage-plugin NPM package', () => { expect(code).toBe(0); const report = await readJsonFile( - join(basicDir, '.code-pushup', 'report.json'), + path.join(basicDir, '.code-pushup', 'report.json'), ); expect(() => reportSchema.parse(report)).not.toThrow(); @@ -58,7 +61,7 @@ describe('PLUGIN collect report with coverage-plugin NPM package', () => { expect(code).toBe(0); const report = await readJsonFile( - join(existingDir, '.code-pushup', 'report.json'), + path.join(existingDir, '.code-pushup', 'report.json'), ); expect(() => reportSchema.parse(report)).not.toThrow(); diff --git a/e2e/plugin-eslint-e2e/.eslintrc.json b/e2e/plugin-eslint-e2e/.eslintrc.json deleted file mode 100644 index 33b3a7745..000000000 --- a/e2e/plugin-eslint-e2e/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["e2e/plugin-eslint-e2e/tsconfig.*?.json"] - } - } - ] -} diff --git a/e2e/plugin-eslint-e2e/eslint.config.js b/e2e/plugin-eslint-e2e/eslint.config.js new file mode 100644 index 000000000..2656b27cb --- /dev/null +++ b/e2e/plugin-eslint-e2e/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, +}); diff --git a/e2e/plugin-eslint-e2e/mocks/fixtures/flat-config/eslint.config.cjs b/e2e/plugin-eslint-e2e/mocks/fixtures/flat-config/eslint.config.cjs index 4bb99249e..cef1161f4 100644 --- a/e2e/plugin-eslint-e2e/mocks/fixtures/flat-config/eslint.config.cjs +++ b/e2e/plugin-eslint-e2e/mocks/fixtures/flat-config/eslint.config.cjs @@ -1,4 +1,4 @@ -/** @type {import('eslint').Linter.FlatConfig[]} */ +/** @type {import('eslint').Linter.Config[]} */ module.exports = [ { ignores: ['code-pushup.config.ts'], diff --git a/e2e/plugin-eslint-e2e/tests/collect.e2e.test.ts b/e2e/plugin-eslint-e2e/tests/collect.e2e.test.ts index 8160cf91e..7df4feb45 100644 --- a/e2e/plugin-eslint-e2e/tests/collect.e2e.test.ts +++ b/e2e/plugin-eslint-e2e/tests/collect.e2e.test.ts @@ -1,5 +1,5 @@ import { cp } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; import { type Report, reportSchema } from '@code-pushup/models'; import { nxTargetProject } from '@code-pushup/test-nx-utils'; @@ -12,19 +12,24 @@ import { import { executeProcess, readJsonFile } from '@code-pushup/utils'; describe('PLUGIN collect report with eslint-plugin NPM package', () => { - const fixturesDir = join('e2e', 'plugin-eslint-e2e', 'mocks', 'fixtures'); - const fixturesFlatConfigDir = join(fixturesDir, 'flat-config'); - const fixturesLegacyConfigDir = join(fixturesDir, 'legacy-config'); + const fixturesDir = path.join( + 'e2e', + 'plugin-eslint-e2e', + 'mocks', + 'fixtures', + ); + const fixturesFlatConfigDir = path.join(fixturesDir, 'flat-config'); + const fixturesLegacyConfigDir = path.join(fixturesDir, 'legacy-config'); - const envRoot = join( + const envRoot = path.join( E2E_ENVIRONMENTS_DIR, nxTargetProject(), TEST_OUTPUT_DIR, ); - const flatConfigDir = join(envRoot, 'flat-config'); - const legacyConfigDir = join(envRoot, 'legacy-config'); - const flatConfigOutputDir = join(flatConfigDir, '.code-pushup'); - const legacyConfigOutputDir = join(legacyConfigDir, '.code-pushup'); + const flatConfigDir = path.join(envRoot, 'flat-config'); + const legacyConfigDir = path.join(envRoot, 'legacy-config'); + const flatConfigOutputDir = path.join(flatConfigDir, '.code-pushup'); + const legacyConfigOutputDir = path.join(legacyConfigDir, '.code-pushup'); beforeAll(async () => { await cp(fixturesFlatConfigDir, flatConfigDir, { recursive: true }); @@ -51,7 +56,9 @@ describe('PLUGIN collect report with eslint-plugin NPM package', () => { expect(code).toBe(0); expect(stderr).toBe(''); - const report = await readJsonFile(join(flatConfigOutputDir, 'report.json')); + const report = await readJsonFile( + path.join(flatConfigOutputDir, 'report.json'), + ); expect(() => reportSchema.parse(report)).not.toThrow(); expect(omitVariableReportData(report as Report)).toMatchSnapshot(); @@ -69,7 +76,7 @@ describe('PLUGIN collect report with eslint-plugin NPM package', () => { expect(stderr).toBe(''); const report = await readJsonFile( - join(legacyConfigOutputDir, 'report.json'), + path.join(legacyConfigOutputDir, 'report.json'), ); expect(() => reportSchema.parse(report)).not.toThrow(); diff --git a/e2e/plugin-lighthouse-e2e/.eslintrc.json b/e2e/plugin-lighthouse-e2e/.eslintrc.json deleted file mode 100644 index 024d3c547..000000000 --- a/e2e/plugin-lighthouse-e2e/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["e2e/plugin-lighthouse-e2e/tsconfig.*?.json"] - } - } - ] -} diff --git a/e2e/plugin-lighthouse-e2e/eslint.config.js b/e2e/plugin-lighthouse-e2e/eslint.config.js new file mode 100644 index 000000000..2656b27cb --- /dev/null +++ b/e2e/plugin-lighthouse-e2e/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, +}); diff --git a/e2e/plugin-lighthouse-e2e/tests/collect.e2e.test.ts b/e2e/plugin-lighthouse-e2e/tests/collect.e2e.test.ts index 22f9ecb73..0614fcf22 100644 --- a/e2e/plugin-lighthouse-e2e/tests/collect.e2e.test.ts +++ b/e2e/plugin-lighthouse-e2e/tests/collect.e2e.test.ts @@ -1,5 +1,5 @@ import { cp } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { afterAll, beforeAll, expect } from 'vitest'; import { type Report, reportSchema } from '@code-pushup/models'; import { nxTargetProject } from '@code-pushup/test-nx-utils'; @@ -13,15 +13,16 @@ import { import { executeProcess, readJsonFile } from '@code-pushup/utils'; describe('PLUGIN collect report with lighthouse-plugin NPM package', () => { - const testFileDir = join( + const testFileDir = path.join( E2E_ENVIRONMENTS_DIR, nxTargetProject(), TEST_OUTPUT_DIR, 'collect', ); - const defaultSetupDir = join(testFileDir, 'default-setup'); + const defaultSetupDir = path.join(testFileDir, 'default-setup'); + + const fixturesDir = path.join('e2e', nxTargetProject(), 'mocks/fixtures'); - const fixturesDir = join('e2e', nxTargetProject(), 'mocks/fixtures'); beforeAll(async () => { await cp(fixturesDir, testFileDir, { recursive: true }); }); @@ -43,7 +44,7 @@ describe('PLUGIN collect report with lighthouse-plugin NPM package', () => { expect(cleanStdout).toContain('● Largest Contentful Paint'); const report = await readJsonFile( - join(defaultSetupDir, '.code-pushup', 'report.json'), + path.join(defaultSetupDir, '.code-pushup', 'report.json'), ); expect(() => reportSchema.parse(report)).not.toThrow(); expect( diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..3734f3e67 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,115 @@ +import nxEslintPlugin from '@nx/eslint-plugin'; +import jsoncParser from 'jsonc-eslint-parser'; +import tseslint from 'typescript-eslint'; +import node from '@code-pushup/eslint-config/node.js'; +import typescript from '@code-pushup/eslint-config/typescript.js'; +import vitest from '@code-pushup/eslint-config/vitest.js'; + +export default tseslint.config( + ...typescript, + ...node, + ...vitest, + { + settings: { + 'import/resolver': { typescript: { project: 'tsconfig.base.json' } }, + }, + }, + { plugins: { '@nx': nxEslintPlugin } }, + { + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], + rules: { + '@nx/enforce-module-boundaries': [ + 'error', + { + enforceBuildableLibDependency: true, + allow: [ + String.raw`^.*/eslint(\.base)?\.config\.[cm]?js$`, + String.raw`^.*/code-pushup\.(config|preset)(\.m?[jt]s)?$`, + '^[./]+/tools/.*$', + ], + depConstraints: [ + { + sourceTag: 'scope:shared', + onlyDependOnLibsWithTags: ['scope:shared'], + }, + { + sourceTag: 'scope:core', + onlyDependOnLibsWithTags: ['scope:core', 'scope:shared'], + }, + { + sourceTag: 'scope:plugin', + onlyDependOnLibsWithTags: ['scope:shared'], + }, + { + sourceTag: 'scope:tooling', + onlyDependOnLibsWithTags: ['scope:tooling', 'scope:shared'], + }, + { + sourceTag: 'type:e2e', + onlyDependOnLibsWithTags: [ + 'type:app', + 'type:feature', + 'type:util', + 'type:testing', + ], + }, + { + sourceTag: 'type:app', + onlyDependOnLibsWithTags: [ + 'type:feature', + 'type:util', + 'type:testing', + ], + }, + { + sourceTag: 'type:feature', + onlyDependOnLibsWithTags: [ + 'type:feature', + 'type:util', + 'type:testing', + ], + }, + { + sourceTag: 'type:util', + onlyDependOnLibsWithTags: ['type:util', 'type:testing'], + }, + { + sourceTag: 'type:testing', + onlyDependOnLibsWithTags: ['type:util', 'type:testing'], + }, + ], + }, + ], + }, + }, + { + files: ['**/*.test.ts', '**/*.spec.ts'], + rules: { + 'vitest/consistent-test-filename': [ + 'warn', + { pattern: String.raw`.*\.(unit|integration|e2e)\.test\.[tj]sx?$` }, + ], + }, + }, + { + files: ['**/*.json'], + languageOptions: { parser: jsoncParser }, + }, + { + files: ['**/perf/**/*.ts'], + rules: { + '@typescript-eslint/no-magic-numbers': 'off', + 'sonarjs/no-duplicate-string': 'off', + }, + }, + { + ignores: [ + '**/*.mock.*', + '**/code-pushup.config.ts', + '**/mocks/fixtures/**', + '**/__snapshots__/**', + '**/dist', + '**/*.md', + ], + }, +); diff --git a/examples/plugins/.eslintrc.json b/examples/plugins/.eslintrc.json deleted file mode 100644 index 7122e043e..000000000 --- a/examples/plugins/.eslintrc.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["examples/plugins/tsconfig.*?.json"] - } - }, - { - "files": ["*.test.ts"], - "parserOptions": { - "project": ["examples/plugins/tsconfig.*?.json"] - }, - "rules": { - "no-magic-numbers": "off", - "max-lines": "off" - } - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": ["error"] - } - } - ] -} diff --git a/examples/plugins/code-pushup.config.ts b/examples/plugins/code-pushup.config.ts index 340964443..4d3d19abe 100644 --- a/examples/plugins/code-pushup.config.ts +++ b/examples/plugins/code-pushup.config.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { LIGHTHOUSE_OUTPUT_FILE_DEFAULT, fileSizePlugin, @@ -38,7 +38,7 @@ const config = { }), await lighthousePlugin({ url: 'https://staging.code-pushup.dev/login', - outputPath: join('.code-pushup', LIGHTHOUSE_OUTPUT_FILE_DEFAULT), + outputPath: path.join('.code-pushup', LIGHTHOUSE_OUTPUT_FILE_DEFAULT), headless: false, verbose: true, }), diff --git a/examples/plugins/eslint.config.js b/examples/plugins/eslint.config.js new file mode 100644 index 000000000..2656b27cb --- /dev/null +++ b/examples/plugins/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, +}); diff --git a/examples/plugins/src/file-size/src/file-size.plugin.ts b/examples/plugins/src/file-size/src/file-size.plugin.ts index b17b144fe..38a8bd449 100644 --- a/examples/plugins/src/file-size/src/file-size.plugin.ts +++ b/examples/plugins/src/file-size/src/file-size.plugin.ts @@ -1,5 +1,5 @@ import { stat } from 'node:fs/promises'; -import { basename } from 'node:path'; +import path from 'node:path'; import type { AuditOutput, AuditOutputs, @@ -59,7 +59,7 @@ export const recommendedRefs: CategoryRef[] = Object.values(auditsMap).map( * }, * plugins: [ * await fileSizePlugin({ - * directory: join(process.cwd(), './dist/packages/utils'), + * directory: path.join(process.cwd(), './dist/packages/utils'), * pattern: /\.js$/, * budget: 4200 * }) @@ -136,7 +136,7 @@ export function fileSizeIssues(options: { pattern, fileTransform: async (file: string) => { // get size of file - // const filePath = join(directory, file); + // const filePath = path.join(directory, file); const stats = await stat(file); return assertFileSize(file, stats.size, budget); @@ -145,14 +145,14 @@ export function fileSizeIssues(options: { } export function infoMessage(filePath: string, size: number) { - return `File ${basename(filePath)} is OK. (size: ${formatBytes(size)})`; + return `File ${path.basename(filePath)} is OK. (size: ${formatBytes(size)})`; } export function errorMessage(filePath: string, size: number, budget: number) { const sizeDifference = formatBytes(size - budget); const byteSize = formatBytes(size); const byteBudget = formatBytes(budget); - return `File ${basename( + return `File ${path.basename( filePath, )} has ${byteSize}, this is ${sizeDifference} too big. (budget: ${byteBudget})`; } diff --git a/examples/plugins/src/file-size/src/file-size.plugin.unit.test.ts b/examples/plugins/src/file-size/src/file-size.plugin.unit.test.ts index 5ce737439..7ed289eed 100644 --- a/examples/plugins/src/file-size/src/file-size.plugin.unit.test.ts +++ b/examples/plugins/src/file-size/src/file-size.plugin.unit.test.ts @@ -1,6 +1,6 @@ import { vol } from 'memfs'; import { unlink } from 'node:fs/promises'; -import { basename, join } from 'node:path'; +import path from 'node:path'; import { beforeEach, describe, expect, it } from 'vitest'; import { formatBytes } from '@code-pushup/utils'; import { @@ -29,11 +29,11 @@ const testJs = ` `; describe('infoMessage', () => { - it.each([['index.js'], [join('src', 'index.js')]])( + it.each([['index.js'], [path.join('src', 'index.js')]])( 'should return info message', file => { expect(infoMessage(file, 12)).toBe( - `File ${basename(file)} is OK. (size: ${formatBytes(12)})`, + `File ${path.basename(file)} is OK. (size: ${formatBytes(12)})`, ); }, ); @@ -198,7 +198,7 @@ describe('runnerFunction', () => { }, outputDir, ); - await unlink(join(outputDir, 'm.js')); + await unlink(path.join(outputDir, 'm.js')); await expect(runnerFunction(baseOptions)).resolves.toEqual([ filesizeAuditOutputBase, diff --git a/examples/plugins/src/lighthouse/README.md b/examples/plugins/src/lighthouse/README.md index a950a019e..5febe372c 100644 --- a/examples/plugins/src/lighthouse/README.md +++ b/examples/plugins/src/lighthouse/README.md @@ -30,7 +30,7 @@ You can configure the plugin with the following options: Pass in the path on the directory to crawl (relative to `process.cwd()`), as well as patterns and a budget. ```js - import { join } from 'node:path'; + import path from 'node:path'; import { LIGHTHOUSE_OUTPUT_FILE_DEFAULT } from './lighthouse-plugin.constants'; import lighthousePlugin from './lighthouse.plugin'; @@ -40,7 +40,7 @@ You can configure the plugin with the following options: // ... lighthousePlugin({ url: 'https://example.com', - outputPath: join('.code-pushup', LIGHTHOUSE_OUTPUT_FILE_DEFAULT), + outputPath: path.join('.code-pushup', LIGHTHOUSE_OUTPUT_FILE_DEFAULT), }), ], }; diff --git a/examples/plugins/src/lighthouse/src/constants.ts b/examples/plugins/src/lighthouse/src/constants.ts index c6b791b16..330aecca1 100644 --- a/examples/plugins/src/lighthouse/src/constants.ts +++ b/examples/plugins/src/lighthouse/src/constants.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-magic-numbers */ import type { Audit, CategoryRef, Group } from '@code-pushup/models'; export const LIGHTHOUSE_OUTPUT_FILE_DEFAULT = 'lighthouse-report.json'; @@ -77,6 +76,7 @@ export const categoryCorePerfGroup: Group = { slug: LIGHTHOUSE_PERFORMANCE_CORE_GROUP_SLUG, title: 'performance-core', refs: [ + /* eslint-disable @typescript-eslint/no-magic-numbers */ // web vitals { slug: fcpSlug, @@ -111,6 +111,7 @@ export const categoryCorePerfGroup: Group = { slug: 'user-timings', weight: 0, }, + /* eslint-enable @typescript-eslint/no-magic-numbers */ ], }; @@ -119,6 +120,7 @@ export const categoryCorePerfGroup2: Group = { title: 'performance-core-2', refs: [ // web vitals + /* eslint-disable @typescript-eslint/no-magic-numbers */ { slug: 'first-contentful-paint', weight: 10, @@ -139,5 +141,6 @@ export const categoryCorePerfGroup2: Group = { slug: 'speed-index', weight: 10, }, + /* eslint-enable @typescript-eslint/no-magic-numbers */ ], }; diff --git a/examples/plugins/src/lighthouse/src/lighthouse.plugin.integration.test.ts b/examples/plugins/src/lighthouse/src/lighthouse.plugin.integration.test.ts index c158fd33a..f233a2c83 100644 --- a/examples/plugins/src/lighthouse/src/lighthouse.plugin.integration.test.ts +++ b/examples/plugins/src/lighthouse/src/lighthouse.plugin.integration.test.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { describe, expect, it } from 'vitest'; import { auditSchema, @@ -89,7 +89,7 @@ describe('lighthouse-create-export-config', () => { it('should use onlyAudits', async () => { const pluginConfig = await create({ url: 'http://localhost:8080', - outputPath: `${join('tmp', 'lighthouse-report.json')}`, + outputPath: `${path.join('tmp', 'lighthouse-report.json')}`, onlyAudits: 'largest-contentful-paint', }); expect(pluginConfig.runner.args).toEqual( diff --git a/examples/plugins/src/lighthouse/src/lighthouse.plugin.ts b/examples/plugins/src/lighthouse/src/lighthouse.plugin.ts index 6fb88e400..d8ae99d3e 100644 --- a/examples/plugins/src/lighthouse/src/lighthouse.plugin.ts +++ b/examples/plugins/src/lighthouse/src/lighthouse.plugin.ts @@ -1,5 +1,5 @@ import type Result from 'lighthouse/types/lhr/lhr'; -import { dirname } from 'node:path'; +import path from 'node:path'; import type { AuditOutput, AuditOutputs, @@ -60,7 +60,7 @@ export async function create(options: PluginOptions) { // ensure output dir if (outputPath !== undefined) { - await ensureDirectoryExists(dirname(outputPath)); + await ensureDirectoryExists(path.dirname(outputPath)); } return { @@ -121,7 +121,7 @@ function lhrToAuditOutputs(lhr: Result): AuditOutputs { slug, score: score ?? 0, // score can be null value: Number.parseInt(value.toString(), 10), - displayValue: displayValue, + displayValue, }; const issues = lhrDetailsToIssueDetails(details); diff --git a/examples/plugins/src/lighthouse/src/utils.ts b/examples/plugins/src/lighthouse/src/utils.ts index 636c3349d..5b5ae6f08 100644 --- a/examples/plugins/src/lighthouse/src/utils.ts +++ b/examples/plugins/src/lighthouse/src/utils.ts @@ -33,7 +33,7 @@ export function getLighthouseCliArguments( // handle chrome flags // eslint-disable-next-line functional/no-let - let chromeFlags: Array = []; + let chromeFlags: string[] = []; if (headless) { chromeFlags = [...chromeFlags, `--headless=${headless}`]; } @@ -43,7 +43,7 @@ export function getLighthouseCliArguments( if (chromeFlags.length > 0) { argsObj = { ...argsObj, - ['chrome-flags']: chromeFlags.join(' '), + 'chrome-flags': chromeFlags.join(' '), }; } diff --git a/nx.json b/nx.json index 26e28e12a..4bb5b7b02 100644 --- a/nx.json +++ b/nx.json @@ -7,14 +7,9 @@ "cache": true }, "lint": { - "inputs": [ - "default", - "{workspaceRoot}/.eslintrc.json", - "{workspaceRoot}/.eslintignore" - ], + "inputs": ["default", "{workspaceRoot}/eslint.config.?(c)js"], "options": { - "maxWarnings": 0, - "reportUnusedDisableDirectives": "warn" + "maxWarnings": 0 }, "cache": true }, @@ -42,7 +37,7 @@ "default": ["{projectRoot}/**/*", "sharedGlobals"], "production": [ "default", - "!{projectRoot}/.eslintrc.json", + "!{projectRoot}/eslint.config.?(c)js", "!{projectRoot}/**/?(*.)test.[jt]s?(x)?(.snap)", "!{projectRoot}/tsconfig.test.json", "!{projectRoot}/src/test-setup.[jt]s", diff --git a/package-lock.json b/package-lock.json index 32e4ba6af..0459e1180 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "build-md": "^0.4.2", "bundle-require": "^4.0.1", "esbuild": "^0.19.12", - "eslint": "~8.56.0", + "eslint": "^9.16.0", "glob": "^10.4.5", "lighthouse": "^12.0.0", "lighthouse-logger": "2.0.1", @@ -36,7 +36,7 @@ }, "devDependencies": { "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/eslint-config": "^0.2.1", + "@code-pushup/eslint-config": "^0.10.7", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", @@ -62,8 +62,6 @@ "@types/node": "18.19.21", "@types/react": "18.3.1", "@types/react-dom": "18.3.0", - "@typescript-eslint/eslint-plugin": "6.13.2", - "@typescript-eslint/parser": "6.13.2", "@vitejs/plugin-react": "4.2.1", "@vitest/coverage-v8": "1.3.1", "@vitest/ui": "1.3.1", @@ -74,16 +72,17 @@ "commitlint-plugin-tense": "^1.0.3", "dotenv": "^16.4.5", "eslint-import-resolver-typescript": "^3.6.1", - "eslint-plugin-deprecation": "^2.0.0", - "eslint-plugin-functional": "^6.0.0", - "eslint-plugin-import": "2.27.5", - "eslint-plugin-n": "^16.3.1", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-sonarjs": "^0.22.0", - "eslint-plugin-unicorn": "^48.0.1", - "eslint-plugin-vitest": "^0.3.8", + "eslint-plugin-functional": "^7.1.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jest-formatting": "^3.1.0", + "eslint-plugin-n": "^17.15.0", + "eslint-plugin-promise": "^7.2.1", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-sonarjs": "^1.0.4", + "eslint-plugin-unicorn": "^56.0.1", + "eslint-plugin-vitest": "^0.5.4", + "globals": "^15.13.0", "husky": "^8.0.0", "inquirer": "^9.3.7", "jsdom": "~24.0.0", @@ -99,11 +98,15 @@ "tsx": "^4.19.0", "type-fest": "^4.26.1", "typescript": "5.5.4", + "typescript-eslint": "^8.18.0", "verdaccio": "^5.32.2", "vite": "^5.4.8", "vitest": "1.3.1", "zod2md": "^0.1.3" }, + "engines": { + "node": ">=22.10" + }, "optionalDependencies": { "@esbuild/darwin-arm64": "^0.19.12", "@nx/nx-darwin-arm64": "19.8.13", @@ -1173,6 +1176,15 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-classes/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", @@ -2192,6 +2204,15 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/types": { "version": "7.26.3", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", @@ -2227,65 +2248,58 @@ } }, "node_modules/@code-pushup/eslint-config": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@code-pushup/eslint-config/-/eslint-config-0.2.1.tgz", - "integrity": "sha512-32GXt3bg2FA6/tvTNpgQBrDNAW6ZKauDH4lvXQj8EDr56ghBaZbZHZ8aSzWw9Ke+RNPGO28spHGYP/GKLZppnw==", + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@code-pushup/eslint-config/-/eslint-config-0.10.7.tgz", + "integrity": "sha512-KlzMFRyi5ifZaNPyijM03PCC1kSkzcrF04L+2zAGg3KvEHLULg2aCkXo4qJeJVTH9DaMbRLW3/qY00eMYmb9tw==", "dev": true, "peerDependencies": { - "@angular-eslint/eslint-plugin": "^17.0.0", - "@angular-eslint/eslint-plugin-template": "^17.0.0", - "@angular-eslint/template-parser": "^17.0.0", + "@eslint/js": "^9.0.0", "@graphql-eslint/eslint-plugin": "^3.0.0", - "@ngrx/eslint-plugin": "^17.0.0", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "eslint": "^8.0.0", + "@ngrx/eslint-plugin": "^18.0.0", + "angular-eslint": "^18.0.0", + "eslint": "^9.0.0", "eslint-import-resolver-typescript": "^3.0.0", - "eslint-plugin-cypress": "^2.0.0", - "eslint-plugin-deprecation": "^2.0.0", - "eslint-plugin-functional": "^6.0.0", - "eslint-plugin-import": "^2.25.0", - "eslint-plugin-jest": "^27.0.0", - "eslint-plugin-n": "^16.0.0", - "eslint-plugin-promise": "^6.0.0", - "eslint-plugin-rxjs": "^5.0.0", - "eslint-plugin-sonarjs": ">=0.22.0", - "eslint-plugin-storybook": "^0.6.0", - "eslint-plugin-unicorn": ">=48.0.0", - "eslint-plugin-vitest": "^0.3.0" + "eslint-plugin-cypress": ">=3.3.0", + "eslint-plugin-functional": "^7.0.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jest": "^28.8.0", + "eslint-plugin-jest-formatting": "^3.0.0", + "eslint-plugin-n": ">=17.0.0", + "eslint-plugin-promise": ">=6.4.0", + "eslint-plugin-rxjs-x": ">=0.2.4", + "eslint-plugin-sonarjs": "^1.0.4", + "eslint-plugin-storybook": ">=0.10.0", + "eslint-plugin-unicorn": ">=50.0.0", + "eslint-plugin-vitest": ">=0.5.0", + "globals": ">=14.0.0", + "typescript-eslint": "^8.0.0" }, "peerDependenciesMeta": { - "@angular-eslint/eslint-plugin": { - "optional": true - }, - "@angular-eslint/eslint-plugin-template": { - "optional": true - }, - "@angular-eslint/template-parser": { - "optional": true - }, "@graphql-eslint/eslint-plugin": { "optional": true }, "@ngrx/eslint-plugin": { "optional": true }, + "angular-eslint": { + "optional": true + }, "eslint-import-resolver-typescript": { "optional": true }, "eslint-plugin-cypress": { "optional": true }, - "eslint-plugin-deprecation": { + "eslint-plugin-jest": { "optional": true }, - "eslint-plugin-jest": { + "eslint-plugin-jest-formatting": { "optional": true }, "eslint-plugin-n": { "optional": true }, - "eslint-plugin-rxjs": { + "eslint-plugin-rxjs-x": { "optional": true }, "eslint-plugin-storybook": { @@ -3083,15 +3097,18 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } @@ -3108,8 +3125,6 @@ "version": "0.19.0", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", - "dev": true, - "peer": true, "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", @@ -3123,8 +3138,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3134,8 +3147,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3147,129 +3158,22 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz", "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==", - "dev": true, - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz", + "integrity": "sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/object-schema": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", - "dev": true, - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -3278,8 +3182,6 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", - "dev": true, - "peer": true, "dependencies": { "levn": "^0.4.1" }, @@ -3343,8 +3245,6 @@ "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "peer": true, "engines": { "node": ">=18.18.0" } @@ -3353,8 +3253,6 @@ "version": "0.16.6", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "peer": true, "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" @@ -3367,8 +3265,6 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "peer": true, "engines": { "node": ">=18.18" }, @@ -3377,40 +3273,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -3423,18 +3285,10 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead" - }, "node_modules/@humanwhocodes/retry": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", - "dev": true, - "peer": true, "engines": { "node": ">=18.18" }, @@ -4914,93 +4768,28 @@ } } }, - "node_modules/@nx/eslint-plugin/node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz", + "integrity": "sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==", "dev": true, - "peer": true, "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/visitor-keys": "8.15.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/@eslint/js": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", - "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", - "dev": true, - "peer": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@nx/eslint-plugin/node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, - "peer": true - }, - "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/types": { "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz", - "integrity": "sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.15.0.tgz", + "integrity": "sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==", "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -5009,59 +4798,19 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/type-utils": { + "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz", - "integrity": "sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz", + "integrity": "sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.15.0", - "@typescript-eslint/utils": "8.15.0", + "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/visitor-keys": "8.15.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.15.0.tgz", - "integrity": "sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz", - "integrity": "sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { @@ -5121,23 +4870,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@nx/eslint-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/@nx/eslint-plugin/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -5153,13 +4885,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nx/eslint-plugin/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "peer": true - }, "node_modules/@nx/eslint-plugin/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5194,369 +4919,132 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/@nx/eslint-plugin/node_modules/eslint": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", - "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", + "node_modules/@nx/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.15.0", - "@eslint/plugin-kit": "^0.2.3", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.5", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "url": "https://opencollective.com/eslint" } }, - "node_modules/@nx/eslint-plugin/node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "node_modules/@nx/eslint-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=8" } }, - "node_modules/@nx/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "node_modules/@nx/eslint-plugin/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@nx/eslint-plugin/node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@nx/eslint-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "peer": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@nx/eslint-plugin/node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@nx/eslint/node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": "*" + "node": ">=14.17" } }, - "node_modules/@nx/eslint-plugin/node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "node_modules/@nx/jest": { + "version": "19.8.13", + "resolved": "https://registry.npmjs.org/@nx/jest/-/jest-19.8.13.tgz", + "integrity": "sha512-LV3Ranbm79O+Hxs4KyHzmX70TELBwip7LA70ZSAMCA4h03PYuCixq3Q5CNXPJaFTDiPewx+wIl2Ntzu04E8niA==", "dev": true, - "peer": true, "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "@jest/reporters": "^29.4.1", + "@jest/test-result": "^29.4.1", + "@nrwl/jest": "19.8.13", + "@nx/devkit": "19.8.13", + "@nx/js": "19.8.13", + "@phenomnomnominal/tsquery": "~5.0.1", + "chalk": "^4.1.0", + "identity-obj-proxy": "3.0.0", + "jest-config": "^29.4.1", + "jest-resolve": "^29.4.1", + "jest-util": "^29.4.1", + "minimatch": "9.0.3", + "resolve.exports": "1.1.0", + "semver": "^7.5.3", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" } }, - "node_modules/@nx/eslint-plugin/node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "node_modules/@nx/jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "peer": true, "dependencies": { - "flat-cache": "^4.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nx/eslint-plugin/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/@nx/jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "peer": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@nx/eslint-plugin/node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "node_modules/@nx/jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "peer": true, "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" + "color-name": "~1.1.4" }, "engines": { - "node": ">=16" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/globals": { - "version": "15.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", - "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "peer": true - }, - "node_modules/@nx/eslint-plugin/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "peer": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "peer": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@nx/eslint-plugin/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@nx/eslint/node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@nx/jest": { - "version": "19.8.13", - "resolved": "https://registry.npmjs.org/@nx/jest/-/jest-19.8.13.tgz", - "integrity": "sha512-LV3Ranbm79O+Hxs4KyHzmX70TELBwip7LA70ZSAMCA4h03PYuCixq3Q5CNXPJaFTDiPewx+wIl2Ntzu04E8niA==", - "dev": true, - "dependencies": { - "@jest/reporters": "^29.4.1", - "@jest/test-result": "^29.4.1", - "@nrwl/jest": "19.8.13", - "@nx/devkit": "19.8.13", - "@nx/js": "19.8.13", - "@phenomnomnominal/tsquery": "~5.0.1", - "chalk": "^4.1.0", - "identity-obj-proxy": "3.0.0", - "jest-config": "^29.4.1", - "jest-resolve": "^29.4.1", - "jest-util": "^29.4.1", - "minimatch": "9.0.3", - "resolve.exports": "1.1.0", - "semver": "^7.5.3", - "tslib": "^2.3.0", - "yargs-parser": "21.1.1" - } - }, - "node_modules/@nx/jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@nx/jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@nx/jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "node": ">=7.0.0" } }, "node_modules/@nx/jest/node_modules/color-name": { @@ -6538,6 +6026,12 @@ "win32" ] }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, "node_modules/@sentry/core": { "version": "6.19.7", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.19.7.tgz", @@ -7266,9 +6760,9 @@ } }, "node_modules/@swc/types": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", - "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz", + "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", "devOptional": true, "peer": true, "dependencies": { @@ -7623,6 +7117,15 @@ "node": ">=6.9.0" } }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -7767,6 +7270,17 @@ "@types/json-schema": "*" } }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -7824,8 +7338,7 @@ "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -7938,132 +7451,41 @@ "@types/node": "*" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz", - "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/type-utils": "6.13.2", - "@typescript-eslint/utils": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz", - "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.13.2", - "@typescript-eslint/utils": "6.13.2", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz", - "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/typescript-estree": "6.13.2", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz", - "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.0.tgz", + "integrity": "sha512-hgUZ3kTEpVzKaK3uNibExUYm6SKKOmTU2BOxBSvOYwtJEPdVQ70kZJpPjstlnhCHcuc2WGfSbpKlb/69ttyN5Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/typescript-estree": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "debug": "^4.3.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz", - "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.0.tgz", + "integrity": "sha512-PNGcHop0jkK2WVYGotk/hxj+UFLhXtGPiGtiaWgVBVP1jhMoMCHlTyJA+hEj4rszoSdLTK3fN4oOatrL0Cp+Xw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2" + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -8071,303 +7493,91 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", - "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.0.tgz", + "integrity": "sha512-er224jRepVAVLnMF2Q7MZJCq5CsdH2oqjP4dT7K6ij09Kyd+R21r7UVJrF0buMVdZS5QRhDzpvzAxHxabQadow==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/utils": "8.18.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz", - "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz", - "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.0.tgz", + "integrity": "sha512-p6GLdY383i7h5b0Qrfbix3Vc3+J2k6QWw6UMUeY5JGfm3C5LbZ4QIZzJNoNOfgyRe0uuYKjvVOsO/jD4SJO+xg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "node_modules/@typescript-eslint/types": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.0.tgz", + "integrity": "sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==", "dev": true, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.0.tgz", + "integrity": "sha512-rqQgFRu6yPkauz+ms3nQpohwejS8bvgbPyIDq13cgEDbkXt4LH4OkDMT0/fN1RUtzG8e8AKJyDBoocuQh8qNeg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", @@ -8383,26 +7593,33 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz", - "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.0.tgz", + "integrity": "sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "8.18.0", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, "node_modules/@verdaccio/auth": { "version": "8.0.0-next-8.1", @@ -9224,73 +8441,73 @@ "dev": true }, "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "peer": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true, "peer": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true, "peer": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true, "peer": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "peer": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true, "peer": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "peer": true, "dependencies": { @@ -9298,9 +8515,9 @@ } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "peer": true, "dependencies": { @@ -9308,79 +8525,79 @@ } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true, "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -9467,16 +8684,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "peer": true, - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -9555,6 +8762,37 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -9759,6 +8997,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array.prototype.flat": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", @@ -10776,9 +10034,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -10795,10 +10053,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -10888,15 +10146,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/builtins": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", - "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", - "dev": true, - "dependencies": { - "semver": "^7.0.0" - } - }, "node_modules/bundle-require": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-4.2.1.tgz", @@ -10994,16 +10243,44 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.2.tgz", + "integrity": "sha512-0lk0PHFe/uz0vl527fG9CgdE9WdafjDbCXvBbs+LUv000TVt2Jjhqbs4Jwm8gz070w8xXyEAxrPOMullsxXeGg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "get-intrinsic": "^1.2.5" }, "engines": { "node": ">= 0.4" @@ -11033,9 +10310,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001655", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", - "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", + "version": "1.0.30001688", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz", + "integrity": "sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA==", "dev": true, "funding": [ { @@ -12766,9 +12043,9 @@ } }, "node_modules/deepmerge-ts": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", - "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.3.tgz", + "integrity": "sha512-qCSH6I0INPxd9Y1VtAiLpnYvz5O//6rCfJXKk0z66Up9/VOSr+1yS8XSKA5IWRxjocFGlzPyaZYe+jxq7OOLtQ==", "dev": true, "engines": { "node": ">=16.0.0" @@ -12969,17 +12246,6 @@ "node": ">=8" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-accessibility-api": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", @@ -13087,6 +12353,20 @@ "url": "https://dotenvx.com" } }, + "node_modules/dunder-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", + "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -13167,9 +12447,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", + "version": "1.5.73", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.73.tgz", + "integrity": "sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg==", "dev": true }, "node_modules/emittery": { @@ -13293,9 +12573,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.23.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz", + "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", @@ -13313,7 +12593,7 @@ "function.prototype.name": "^1.1.6", "get-intrinsic": "^1.2.4", "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", + "globalthis": "^1.0.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", @@ -13329,10 +12609,10 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", + "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.9", @@ -13353,13 +12633,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -13374,9 +12651,9 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", - "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", + "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", "dev": true, "dependencies": { "call-bind": "^1.0.7", @@ -13386,12 +12663,13 @@ "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", + "globalthis": "^1.0.4", + "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", "has-symbols": "^1.0.3", "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", + "iterator.prototype": "^1.1.3", "safe-array-concat": "^1.1.2" }, "engines": { @@ -13864,57 +13142,61 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.16.0.tgz", + "integrity": "sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.16.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.5", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-compat-utils": { @@ -13932,6 +13214,20 @@ "eslint": ">=6.0.0" } }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "optional": true, + "peer": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -13971,196 +13267,46 @@ "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.9.0.tgz", - "integrity": "sha512-McVbYmwA3NEKwRQY5g4aWMdcZE5xZxV8i8l7CqJSrameuGSQJtSWaL/LxTEzSKKaCcOhlpDR8XEfYXWPrdo/ZQ==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-deprecation": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-2.0.0.tgz", - "integrity": "sha512-OAm9Ohzbj11/ZFyICyR5N6LbOIvQMp7ZU2zI7Ej0jIc8kiGUERXPNMfw2QqqHD1ZHtjMub3yPZILovYEYucgoQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^6.0.0", - "tslib": "^2.3.1", - "tsutils": "^3.21.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "typescript": "^4.2.4 || ^5.0.0" - } - }, - "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } } }, - "node_modules/eslint-plugin-deprecation/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "debug": "^3.2.7" }, "engines": { - "node": ">=10" + "node": ">=4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/eslint-plugin-deprecation/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "ms": "^2.1.1" } }, "node_modules/eslint-plugin-es-x": { @@ -14185,30 +13331,34 @@ } }, "node_modules/eslint-plugin-functional": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-functional/-/eslint-plugin-functional-6.6.3.tgz", - "integrity": "sha512-sVbbvNvwX3HVkXAykKyoNLv57r4DPF7f1sy+/8j4YtzLYVQPGljMUWv3T6Kd4lwnnjmcKuj0EkIbS+knL6P5jw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-functional/-/eslint-plugin-functional-7.1.0.tgz", + "integrity": "sha512-eu7lVAF9dDTw2xzlsLDvJRXx9t4g/S/pmCSdGx2oFmibmkz2LMoPDu7B+UA9CV/RzvNr4wWd4apc71nMAazdKQ==", "dev": true, "funding": [ { "type": "ko-fi", "url": "https://ko-fi.com/rebeccastevens" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/eslint-plugin-functional" } ], "dependencies": { - "@typescript-eslint/utils": "^7.3.1", - "deepmerge-ts": "^5.1.0", - "escape-string-regexp": "^4.0.0", - "is-immutable-type": "^4.0.0", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "@typescript-eslint/utils": "^8.10.0", + "deepmerge-ts": "^7.1.3", + "escape-string-regexp": "^5.0.0", + "is-immutable-type": "^5.0.0", + "ts-api-utils": "^1.3.0", + "ts-declaration-location": "^1.0.4" }, "engines": { - "node": ">=16.10.0" + "node": ">=v18.18.0" }, "peerDependencies": { - "eslint": "^8.0.0 || ^9.0.0", - "typescript": ">=4.3.5" + "eslint": "^9.0.0", + "typescript": ">=4.7.4" }, "peerDependenciesMeta": { "typescript": { @@ -14216,33 +13366,72 @@ } } }, + "node_modules/eslint-plugin-functional/node_modules/@typescript-eslint/utils": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.0.tgz", + "integrity": "sha512-p6GLdY383i7h5b0Qrfbix3Vc3+J2k6QWw6UMUeY5JGfm3C5LbZ4QIZzJNoNOfgyRe0uuYKjvVOsO/jD4SJO+xg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/eslint-plugin-functional/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "node_modules/eslint-plugin-import/node_modules/brace-expansion": { @@ -14330,90 +13519,68 @@ "strip-bom": "^3.0.0" } }, - "node_modules/eslint-plugin-n": { - "version": "16.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", - "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "node_modules/eslint-plugin-jest-formatting": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest-formatting/-/eslint-plugin-jest-formatting-3.1.0.tgz", + "integrity": "sha512-XyysraZ1JSgGbLSDxjj5HzKKh0glgWf+7CkqxbTqb7zEhW7X2WHo5SBQ8cGhnszKN+2Lj3/oevBlHNbHezoc/A==", "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "builtins": "^5.0.1", - "eslint-plugin-es-x": "^7.5.0", - "get-tsconfig": "^4.7.0", - "globals": "^13.24.0", - "ignore": "^5.2.4", - "is-builtin-module": "^3.2.1", - "is-core-module": "^2.12.1", - "minimatch": "^3.1.2", - "resolve": "^1.22.2", - "semver": "^7.5.3" - }, "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-n/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "eslint": ">=0.8.0" } }, - "node_modules/eslint-plugin-n/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/eslint-plugin-n": { + "version": "17.15.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.15.0.tgz", + "integrity": "sha512-xF3zJkOfLlFOm5TvmqmsnA9/fO+/z2pYs0dkuKXKN/ymS6UB1yEcaoIkqxLKQ9Dw/WmLX/Tdh6/5ZS5azVixFQ==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "@eslint-community/eslint-utils": "^4.4.1", + "enhanced-resolve": "^5.17.1", + "eslint-plugin-es-x": "^7.8.0", + "get-tsconfig": "^4.8.1", + "globals": "^15.11.0", + "ignore": "^5.3.2", + "minimatch": "^9.0.5", + "semver": "^7.6.3" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": ">=8.23.0" } }, "node_modules/eslint-plugin-n/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-n/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/eslint-plugin-promise": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", - "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.2.1.tgz", + "integrity": "sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA==", "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -14423,9 +13590,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.35.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.2.tgz", - "integrity": "sha512-Rbj2R9zwP2GYNcIak4xoAMV57hrBh3hTaR0k7hVjwCQgryE/pw5px4b13EYjduOI0hfXyZhwBxaGpOTbWSGzKQ==", + "version": "7.37.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", + "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", "dev": true, "dependencies": { "array-includes": "^3.1.8", @@ -14433,7 +13600,7 @@ "array.prototype.flatmap": "^1.3.2", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.19", + "es-iterator-helpers": "^1.1.0", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", @@ -14455,15 +13622,15 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", + "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", "dev": true, "engines": { "node": ">=10" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { @@ -14514,108 +13681,236 @@ "resolve": "bin/resolve" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-sonarjs": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-1.0.4.tgz", + "integrity": "sha512-jF0eGCUsq/HzMub4ExAyD8x1oEgjOyB9XVytYGyWgSFvdiJQJp6IuP7RmtauCf06o6N/kZErh+zW4b10y1WZ+Q==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "eslint": "^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-unicorn": { + "version": "56.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-56.0.1.tgz", + "integrity": "sha512-FwVV0Uwf8XPfVnKSGpMg7NtlZh0G0gBarCaFcMUOoqPxXryxdYxTRRv4kH6B9TFCVIrjRXG+emcxIk2ayZilog==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "@eslint-community/eslint-utils": "^4.4.0", + "ci-info": "^4.0.0", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.38.1", + "esquery": "^1.6.0", + "globals": "^15.9.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.1", + "jsesc": "^3.0.2", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.10.0", + "semver": "^7.6.3", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=18.18" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=8.56.0" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/ci-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-plugin-vitest": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.5.4.tgz", + "integrity": "sha512-um+odCkccAHU53WdKAw39MY61+1x990uXjSPguUCq3VcEHdqJrOb8OTMrbYlY6f9jAKx7x98kLVlIe3RJeJqoQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^7.7.1" + }, + "engines": { + "node": "^18.0.0 || >= 20.0.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "vitest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint-plugin-sonarjs": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.22.0.tgz", - "integrity": "sha512-LJz+TCosMOBLkbAsNk6Q1lCgmK6qNO5RCqtOAle1DCnqqnmxoSTPHakZ1R7Gcnjhw5n7VDcAwuqefmpd4XXPLQ==", + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, "engines": { - "node": ">=14" + "node": "^18.18.0 || >=20.0.0" }, - "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/eslint-plugin-unicorn": { - "version": "48.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-48.0.1.tgz", - "integrity": "sha512-FW+4r20myG/DqFcCSzoumaddKBicIPeFnTrifon2mWIzlfyvzwyqZjqVP7m4Cqr/ZYisS2aiLghkUWaPg6vtCw==", + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", "@eslint-community/eslint-utils": "^4.4.0", - "ci-info": "^3.8.0", - "clean-regexp": "^1.0.0", - "esquery": "^1.5.0", - "indent-string": "^4.0.0", - "is-builtin-module": "^3.2.1", - "jsesc": "^3.0.2", - "lodash": "^4.17.21", - "pluralize": "^8.0.0", - "read-pkg-up": "^7.0.1", - "regexp-tree": "^0.1.27", - "regjsparser": "^0.10.0", - "semver": "^7.5.4", - "strip-indent": "^3.0.0" + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" }, "engines": { - "node": ">=16" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": ">=8.44.0" + "eslint": "^8.56.0" } }, - "node_modules/eslint-plugin-unicorn/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=6" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint-plugin-vitest": { - "version": "0.3.26", - "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.26.tgz", - "integrity": "sha512-oxe5JSPgRjco8caVLTh7Ti8PxpwJdhSV0hTQAmkFcNcmy/9DnqLB/oNVRA11RmVRP//2+jIIT6JuBEcpW3obYg==", + "node_modules/eslint-plugin-vitest/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "^7.1.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "^18.0.0 || >= 20.0.0" - }, - "peerDependencies": { - "eslint": ">=8.0.0", - "vitest": "*" + "node": ">=16 || 14 >=14.17" }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "vitest": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -14632,38 +13927,33 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/eslint/node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/eslint/node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/eslint/node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + }, "node_modules/eslint/node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -14738,6 +14028,33 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/eslint/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -14754,14 +14071,11 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dependencies": { - "type-fest": "^0.20.2" - }, + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -14838,17 +14152,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -14860,21 +14163,11 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -15412,6 +14705,7 @@ "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -15462,14 +14756,14 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/file-loader": { @@ -15716,71 +15010,15 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/flat-cache/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flat-cache/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=16" } }, "node_modules/flatted": { @@ -15900,7 +15138,8 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -15990,16 +15229,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", + "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", "dev": true, "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -16047,9 +15291,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.0.tgz", - "integrity": "sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", "dev": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -16213,12 +15457,15 @@ } }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "15.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.13.0.tgz", + "integrity": "sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==", "dev": true, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globalthis": { @@ -16237,13 +15484,33 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -16282,7 +15549,8 @@ "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true }, "node_modules/graphql": { "version": "16.9.0", @@ -16371,15 +15639,6 @@ "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", "dev": true }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -16423,9 +15682,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "engines": { "node": ">= 0.4" @@ -16899,6 +16158,7 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -17233,25 +16493,28 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -17327,12 +16590,13 @@ } }, "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -17370,12 +16634,15 @@ } }, "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz", + "integrity": "sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -17437,14 +16704,14 @@ } }, "node_modules/is-immutable-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-immutable-type/-/is-immutable-type-4.0.0.tgz", - "integrity": "sha512-gyFBCXv+NikTs8/PGZhgjbMmFZQ5jvHGZIsVu6+/9Bk4K7imlWBIDN7hTr9fNioGzFg71I4YM3z8f0aKXarTAw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-immutable-type/-/is-immutable-type-5.0.0.tgz", + "integrity": "sha512-mcvHasqbRBWJznuPqqHRKiJgYAz60sZ0mvO3bN70JbkuK7ksfmgc489aKZYxMEjIbRvyOseaTjaRZLRF/xFeRA==", "dev": true, "dependencies": { - "@typescript-eslint/type-utils": "^7.2.0", + "@typescript-eslint/type-utils": "^8.0.0", "ts-api-utils": "^1.3.0", - "ts-declaration-location": "^1.0.0" + "ts-declaration-location": "^1.0.4" }, "peerDependencies": { "eslint": "*", @@ -17493,12 +16760,13 @@ } }, "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.0.tgz", + "integrity": "sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -17515,14 +16783,6 @@ "node": ">=8" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -17554,13 +16814,15 @@ "dev": true }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -17609,12 +16871,13 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.0.tgz", + "integrity": "sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -17624,12 +16887,14 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -17879,16 +17144,20 @@ } }, "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.4.tgz", + "integrity": "sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==", "dev": true, "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "reflect.getprototypeof": "^1.0.8", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/jackspeak": { @@ -19736,7 +19005,7 @@ "version": "1.21.6", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", - "dev": true, + "devOptional": true, "bin": { "jiti": "bin/jiti.js" } @@ -20950,6 +20219,15 @@ "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==" }, + "node_modules/math-intrinsics": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.0.0.tgz", + "integrity": "sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -21802,9 +21080,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "dev": true, "engines": { "node": ">= 0.4" @@ -21872,6 +21150,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/object.values": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", @@ -22395,6 +21687,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -23006,6 +22299,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -23334,18 +22628,19 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz", + "integrity": "sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", + "dunder-proto": "^1.0.0", + "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" + "gopd": "^1.2.0", + "which-builtin-type": "^1.2.0" }, "engines": { "node": ">= 0.4" @@ -23397,15 +22692,15 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -23604,6 +22899,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -23772,6 +23068,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -23844,14 +23141,14 @@ ] }, "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -25064,9 +24361,9 @@ } }, "node_modules/terser": { - "version": "5.31.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", - "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", "dev": true, "peer": true, "dependencies": { @@ -25083,17 +24380,17 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", + "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", "dev": true, "peer": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -25142,6 +24439,26 @@ "node": ">= 10.13.0" } }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/terser-webpack-plugin/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -25263,11 +24580,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, "node_modules/thingies": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", @@ -25523,12 +24835,22 @@ } }, "node_modules/ts-declaration-location": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/ts-declaration-location/-/ts-declaration-location-1.0.4.tgz", - "integrity": "sha512-r4JoxYhKULbZuH81Pjrp9OEG5St7XWk7zXwGkLKhmVcjiBVHTJXV5wK6dEa9JKW5QGSTW6b1lOjxAKp8R1SQhg==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ts-declaration-location/-/ts-declaration-location-1.0.5.tgz", + "integrity": "sha512-WqmlO9IoeYwCqJ2E9kHMcY9GZhhfLYItC3VnHDlPOrg6nNdUWS4wn4hhDZUPt60m1EvtjPIZyprTjpI992Bgzw==", "dev": true, + "funding": [ + { + "type": "ko-fi", + "url": "https://ko-fi.com/rebeccastevens" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/ts-declaration-location" + } + ], "dependencies": { - "minimatch": "^10.0.0" + "minimatch": "^10.0.1" }, "peerDependencies": { "typescript": ">=4.0.0" @@ -25612,27 +24934,6 @@ "node": ">=0.6.x" } }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/tsx": { "version": "4.19.0", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.0.tgz", @@ -25904,6 +25205,80 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.18.0.tgz", + "integrity": "sha512-Xq2rRjn6tzVpAyHr3+nmSg1/9k9aIHnJ2iZeOH7cfGOWqTkXTm3kwpQglEuLGdNrYvPF+2gtAs+/KF5rjVo+WQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.18.0", + "@typescript-eslint/parser": "8.18.0", + "@typescript-eslint/utils": "8.18.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.0.tgz", + "integrity": "sha512-NR2yS7qUqCL7AIxdJUQf2MKKNDVNaig/dEB0GBLU7D+ZdHgK1NoH/3wsgO3OnPVipn51tG3MAwaODEGil70WEw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/type-utils": "8.18.0", + "@typescript-eslint/utils": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.0.tgz", + "integrity": "sha512-p6GLdY383i7h5b0Qrfbix3Vc3+J2k6QWw6UMUeY5JGfm3C5LbZ4QIZzJNoNOfgyRe0uuYKjvVOsO/jD4SJO+xg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, "node_modules/ufo": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", @@ -26061,9 +25436,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -26080,8 +25455,8 @@ } ], "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -27160,19 +26535,19 @@ } }, "node_modules/webpack": { - "version": "5.94.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", - "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "version": "5.97.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", + "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", "dev": true, "peer": true, "dependencies": { - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", @@ -27216,6 +26591,13 @@ "node": ">=10.13.0" } }, + "node_modules/webpack/node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "peer": true + }, "node_modules/webpack/node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -27301,39 +26683,43 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.0.tgz", + "integrity": "sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==", "dev": true, "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.0", + "is-number-object": "^1.1.0", + "is-string": "^1.1.0", + "is-symbol": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-builtin-type": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", - "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, "dependencies": { + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -27367,9 +26753,9 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz", + "integrity": "sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.7", diff --git a/package.json b/package.json index 3b89d4117..2782021cf 100644 --- a/package.json +++ b/package.json @@ -11,14 +11,15 @@ "type": "git", "url": "git+https://github.com/code-pushup/cli.git" }, + "type": "module", "scripts": { "prepare": "husky install", "commit": "git-cz", "knip": "knip" }, "private": true, - "engine": { - "node": ">=18.20" + "engines": { + "node": ">=22.10" }, "dependencies": { "@code-pushup/portal-client": "^0.9.0", @@ -30,7 +31,7 @@ "build-md": "^0.4.2", "bundle-require": "^4.0.1", "esbuild": "^0.19.12", - "eslint": "~8.56.0", + "eslint": "^9.16.0", "glob": "^10.4.5", "lighthouse": "^12.0.0", "lighthouse-logger": "2.0.1", @@ -48,7 +49,7 @@ }, "devDependencies": { "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/eslint-config": "^0.2.1", + "@code-pushup/eslint-config": "^0.10.7", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", @@ -74,8 +75,6 @@ "@types/node": "18.19.21", "@types/react": "18.3.1", "@types/react-dom": "18.3.0", - "@typescript-eslint/eslint-plugin": "6.13.2", - "@typescript-eslint/parser": "6.13.2", "@vitejs/plugin-react": "4.2.1", "@vitest/coverage-v8": "1.3.1", "@vitest/ui": "1.3.1", @@ -86,16 +85,17 @@ "commitlint-plugin-tense": "^1.0.3", "dotenv": "^16.4.5", "eslint-import-resolver-typescript": "^3.6.1", - "eslint-plugin-deprecation": "^2.0.0", - "eslint-plugin-functional": "^6.0.0", - "eslint-plugin-import": "2.27.5", - "eslint-plugin-n": "^16.3.1", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-sonarjs": "^0.22.0", - "eslint-plugin-unicorn": "^48.0.1", - "eslint-plugin-vitest": "^0.3.8", + "eslint-plugin-functional": "^7.1.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jest-formatting": "^3.1.0", + "eslint-plugin-n": "^17.15.0", + "eslint-plugin-promise": "^7.2.1", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-sonarjs": "^1.0.4", + "eslint-plugin-unicorn": "^56.0.1", + "eslint-plugin-vitest": "^0.5.4", + "globals": "^15.13.0", "husky": "^8.0.0", "inquirer": "^9.3.7", "jsdom": "~24.0.0", @@ -111,6 +111,7 @@ "tsx": "^4.19.0", "type-fest": "^4.26.1", "typescript": "5.5.4", + "typescript-eslint": "^8.18.0", "verdaccio": "^5.32.2", "vite": "^5.4.8", "vitest": "1.3.1", diff --git a/packages/ci/.eslintrc.json b/packages/ci/.eslintrc.json deleted file mode 100644 index e4fcd6c29..000000000 --- a/packages/ci/.eslintrc.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "parserOptions": { - "project": ["packages/ci/tsconfig.*?.json"] - }, - "rules": { - "vitest/max-nested-describe": ["warn", { "max": 3 }] - } - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": [ - "error", - { - "ignoredDependencies": ["type-fest"] // only internal types - } - ] - } - } - ] -} diff --git a/packages/ci/eslint.config.js b/packages/ci/eslint.config.js new file mode 100644 index 000000000..13888c2a8 --- /dev/null +++ b/packages/ci/eslint.config.js @@ -0,0 +1,31 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': [ + 'error', + { ignoredDependencies: ['type-fest'] }, // only for internal typings + ], + }, + }, + { + files: ['**/*.test.ts'], + rules: { + 'vitest/max-nested-describe': ['warn', { max: 3 }], + 'n/no-unsupported-features/node-builtins': 'off', + }, + }, +); diff --git a/packages/ci/src/lib/cli/commands/merge-diffs.ts b/packages/ci/src/lib/cli/commands/merge-diffs.ts index 90c2025b8..1c94954d6 100644 --- a/packages/ci/src/lib/cli/commands/merge-diffs.ts +++ b/packages/ci/src/lib/cli/commands/merge-diffs.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { DEFAULT_PERSIST_FILENAME, DEFAULT_PERSIST_OUTPUT_DIR, @@ -10,7 +10,7 @@ export async function runMergeDiffs( files: string[], { bin, config, directory, silent }: CommandContext, ): Promise { - const outputDir = join(directory, DEFAULT_PERSIST_OUTPUT_DIR); + const outputDir = path.join(directory, DEFAULT_PERSIST_OUTPUT_DIR); const filename = `merged-${DEFAULT_PERSIST_FILENAME}`; const { stdout } = await executeProcess({ @@ -28,5 +28,5 @@ export async function runMergeDiffs( console.info(stdout); } - return join(outputDir, `${filename}-diff.md`); + return path.join(outputDir, `${filename}-diff.md`); } diff --git a/packages/ci/src/lib/cli/persist.ts b/packages/ci/src/lib/cli/persist.ts index f4cebf9ef..b7b8ce224 100644 --- a/packages/ci/src/lib/cli/persist.ts +++ b/packages/ci/src/lib/cli/persist.ts @@ -1,4 +1,4 @@ -import { isAbsolute, join } from 'node:path'; +import path from 'node:path'; import { z } from 'zod'; import { type CoreConfig, @@ -21,13 +21,15 @@ export function persistedFilesFromConfig( } = {}, } = config; - const dir = isAbsolute(outputDir) ? outputDir : join(directory, outputDir); + const dir = path.isAbsolute(outputDir) + ? outputDir + : path.join(directory, outputDir); const name = isDiff ? `${filename}-diff` : filename; return objectFromEntries( DEFAULT_PERSIST_FORMAT.map(format => [ format, - join(dir, `${name}.${format}`), + path.join(dir, `${name}.${format}`), ]), ); } diff --git a/packages/ci/src/lib/cli/persist.unit.test.ts b/packages/ci/src/lib/cli/persist.unit.test.ts index 3470fc714..222824134 100644 --- a/packages/ci/src/lib/cli/persist.unit.test.ts +++ b/packages/ci/src/lib/cli/persist.unit.test.ts @@ -1,12 +1,12 @@ -import { join } from 'node:path'; +import path from 'node:path'; import type { CoreConfig } from '@code-pushup/models'; import { parsePersistConfig, persistedFilesFromConfig } from './persist.js'; describe('persistedFilesFromConfig', () => { it('should return default report paths when no config is set', () => { expect(persistedFilesFromConfig({}, { directory: process.cwd() })).toEqual({ - json: join(process.cwd(), '.code-pushup', 'report.json'), - md: join(process.cwd(), '.code-pushup', 'report.md'), + json: path.join(process.cwd(), '.code-pushup', 'report.json'), + md: path.join(process.cwd(), '.code-pushup', 'report.md'), }); }); @@ -17,8 +17,8 @@ describe('persistedFilesFromConfig', () => { { directory: process.cwd(), isDiff: true }, ), ).toEqual({ - json: join(process.cwd(), '.code-pushup', 'report-diff.json'), - md: join(process.cwd(), '.code-pushup', 'report-diff.md'), + json: path.join(process.cwd(), '.code-pushup', 'report-diff.json'), + md: path.join(process.cwd(), '.code-pushup', 'report-diff.md'), }); }); @@ -29,8 +29,8 @@ describe('persistedFilesFromConfig', () => { { directory: process.cwd(), isDiff: true }, ), ).toEqual({ - json: join(process.cwd(), '.code-pushup', 'merged-report-diff.json'), - md: join(process.cwd(), '.code-pushup', 'merged-report-diff.md'), + json: path.join(process.cwd(), '.code-pushup', 'merged-report-diff.json'), + md: path.join(process.cwd(), '.code-pushup', 'merged-report-diff.md'), }); }); @@ -41,8 +41,8 @@ describe('persistedFilesFromConfig', () => { { directory: process.cwd() }, ), ).toEqual({ - json: join(process.cwd(), 'tmp', 'report.json'), - md: join(process.cwd(), 'tmp', 'report.md'), + json: path.join(process.cwd(), 'tmp', 'report.json'), + md: path.join(process.cwd(), 'tmp', 'report.md'), }); }); @@ -50,23 +50,23 @@ describe('persistedFilesFromConfig', () => { expect( persistedFilesFromConfig( { persist: { outputDir: 'tmp' } }, - { directory: join(process.cwd(), 'backend') }, + { directory: path.join(process.cwd(), 'backend') }, ), ).toEqual({ - json: join(process.cwd(), 'backend', 'tmp', 'report.json'), - md: join(process.cwd(), 'backend', 'tmp', 'report.md'), + json: path.join(process.cwd(), 'backend', 'tmp', 'report.json'), + md: path.join(process.cwd(), 'backend', 'tmp', 'report.md'), }); }); it('should ignore working directory when absolute outputDir in config', () => { expect( persistedFilesFromConfig( - { persist: { outputDir: join(process.cwd(), 'tmp') } }, - { directory: join(process.cwd(), 'backend') }, + { persist: { outputDir: path.join(process.cwd(), 'tmp') } }, + { directory: path.join(process.cwd(), 'backend') }, ), ).toEqual({ - json: join(process.cwd(), 'tmp', 'report.json'), - md: join(process.cwd(), 'tmp', 'report.md'), + json: path.join(process.cwd(), 'tmp', 'report.json'), + md: path.join(process.cwd(), 'tmp', 'report.md'), }); }); }); diff --git a/packages/ci/src/lib/comment.unit.test.ts b/packages/ci/src/lib/comment.unit.test.ts index 385e387c0..97a0c4c16 100644 --- a/packages/ci/src/lib/comment.unit.test.ts +++ b/packages/ci/src/lib/comment.unit.test.ts @@ -1,6 +1,6 @@ import { vol } from 'memfs'; import { writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { MEMFS_VOLUME } from '@code-pushup/test-utils'; import { commentOnPR } from './comment.js'; import type { Comment, Logger, ProviderAPIClient } from './models.js'; @@ -8,7 +8,7 @@ import type { Comment, Logger, ProviderAPIClient } from './models.js'; describe('commentOnPR', () => { const diffText = '# Code PushUp\n\nNo changes to report.\n'; const diffFile = 'report-diff.md'; - const diffPath = join(MEMFS_VOLUME, diffFile); + const diffPath = path.join(MEMFS_VOLUME, diffFile); const comment: Comment = { id: 42, diff --git a/packages/ci/src/lib/git.integration.test.ts b/packages/ci/src/lib/git.integration.test.ts index 1db1e40db..414991fba 100644 --- a/packages/ci/src/lib/git.integration.test.ts +++ b/packages/ci/src/lib/git.integration.test.ts @@ -1,11 +1,11 @@ import { mkdir, rename, rm, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { type SimpleGit, simpleGit } from 'simple-git'; import { initGitRepo } from '@code-pushup/test-utils'; import { type ChangedFiles, listChangedFiles } from './git.js'; describe('git diff', () => { - const baseDir = join('tmp', 'ci', 'git-utils-test'); + const baseDir = path.join('tmp', 'ci', 'git-utils-test'); let git: SimpleGit; @@ -13,13 +13,13 @@ describe('git diff', () => { await rm(baseDir, { recursive: true, force: true }); git = await initGitRepo(simpleGit, { baseDir }); - await writeFile(join(baseDir, 'LICENSE'), 'MIT License\n\n...'); + await writeFile(path.join(baseDir, 'LICENSE'), 'MIT License\n\n...'); await writeFile( - join(baseDir, 'index.js'), + path.join(baseDir, 'index.js'), 'export const sum = values => values.reduce((acc, val) => acc + val, 0)\n', ); await writeFile( - join(baseDir, 'package.json'), + path.join(baseDir, 'package.json'), JSON.stringify( { name: 'sum', type: 'module', main: 'index.js' }, null, @@ -30,11 +30,14 @@ describe('git diff', () => { await git.commit('Initial commit'); await git.checkoutLocalBranch('testing'); - await mkdir(join(baseDir, 'src')); - await mkdir(join(baseDir, 'test')); - await rename(join(baseDir, 'index.js'), join(baseDir, 'src/index.js')); + await mkdir(path.join(baseDir, 'src')); + await mkdir(path.join(baseDir, 'test')); + await rename( + path.join(baseDir, 'index.js'), + path.join(baseDir, 'src/index.js'), + ); await writeFile( - join(baseDir, 'test/index.test.js'), + path.join(baseDir, 'test/index.test.js'), [ "import assert from 'node:assert'", "import test from 'node:test'", @@ -48,7 +51,7 @@ describe('git diff', () => { .join(''), ); await writeFile( - join(baseDir, 'package.json'), + path.join(baseDir, 'package.json'), JSON.stringify( { name: 'sum', diff --git a/packages/ci/src/lib/monorepo/handlers/npm.ts b/packages/ci/src/lib/monorepo/handlers/npm.ts index d6d0161e7..859bd17db 100644 --- a/packages/ci/src/lib/monorepo/handlers/npm.ts +++ b/packages/ci/src/lib/monorepo/handlers/npm.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { fileExists } from '@code-pushup/utils'; import { hasCodePushUpDependency, @@ -13,7 +13,7 @@ export const npmHandler: MonorepoToolHandler = { async isConfigured(options) { return ( - (await fileExists(join(options.cwd, 'package-lock.json'))) && + (await fileExists(path.join(options.cwd, 'package-lock.json'))) && (await hasWorkspacesEnabled(options.cwd)) ); }, diff --git a/packages/ci/src/lib/monorepo/handlers/npm.unit.test.ts b/packages/ci/src/lib/monorepo/handlers/npm.unit.test.ts index d22e3eae9..45b36c1cb 100644 --- a/packages/ci/src/lib/monorepo/handlers/npm.unit.test.ts +++ b/packages/ci/src/lib/monorepo/handlers/npm.unit.test.ts @@ -1,5 +1,5 @@ import { vol } from 'memfs'; -import { join } from 'node:path'; +import path from 'node:path'; import type { PackageJson } from 'type-fest'; import { MEMFS_VOLUME } from '@code-pushup/test-utils'; import type { @@ -89,12 +89,12 @@ describe('npmHandler', () => { await expect(npmHandler.listProjects(options)).resolves.toEqual([ { name: 'backend', - directory: join(MEMFS_VOLUME, 'apps', 'backend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backend'), bin: 'npm run code-pushup --', }, { name: 'shared', - directory: join(MEMFS_VOLUME, 'libs', 'shared'), + directory: path.join(MEMFS_VOLUME, 'libs', 'shared'), bin: 'npm run code-pushup --', }, ] satisfies ProjectConfig[]); @@ -127,12 +127,12 @@ describe('npmHandler', () => { await expect(npmHandler.listProjects(options)).resolves.toEqual([ { name: 'backend', - directory: join(MEMFS_VOLUME, 'apps', 'backend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backend'), bin: 'npm exec code-pushup --', }, { name: 'shared', - directory: join(MEMFS_VOLUME, 'libs', 'shared'), + directory: path.join(MEMFS_VOLUME, 'libs', 'shared'), bin: 'npm exec code-pushup --', }, ] satisfies ProjectConfig[]); @@ -163,17 +163,17 @@ describe('npmHandler', () => { await expect(npmHandler.listProjects(options)).resolves.toEqual([ { name: 'backend', - directory: join(MEMFS_VOLUME, 'apps', 'backend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backend'), bin: 'npm exec code-pushup --', }, { name: 'frontend', - directory: join(MEMFS_VOLUME, 'apps', 'frontend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'frontend'), bin: 'npm exec code-pushup --', }, { name: 'shared', - directory: join(MEMFS_VOLUME, 'libs', 'shared'), + directory: path.join(MEMFS_VOLUME, 'libs', 'shared'), bin: 'npm exec code-pushup --', }, ] satisfies ProjectConfig[]); @@ -185,12 +185,12 @@ describe('npmHandler', () => { all: [ { name: 'api', - directory: join(MEMFS_VOLUME, 'api'), + directory: path.join(MEMFS_VOLUME, 'api'), bin: 'npm run code-pushup --', }, { name: 'ui', - directory: join(MEMFS_VOLUME, 'ui'), + directory: path.join(MEMFS_VOLUME, 'ui'), bin: 'npm run code-pushup --', }, ], diff --git a/packages/ci/src/lib/monorepo/handlers/nx.ts b/packages/ci/src/lib/monorepo/handlers/nx.ts index bb09c8d8e..c84ab2d84 100644 --- a/packages/ci/src/lib/monorepo/handlers/nx.ts +++ b/packages/ci/src/lib/monorepo/handlers/nx.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { executeProcess, fileExists, @@ -12,7 +12,7 @@ export const nxHandler: MonorepoToolHandler = { async isConfigured(options) { return ( - (await fileExists(join(options.cwd, 'nx.json'))) && + (await fileExists(path.join(options.cwd, 'nx.json'))) && ( await executeProcess({ command: 'npx', diff --git a/packages/ci/src/lib/monorepo/handlers/pnpm.ts b/packages/ci/src/lib/monorepo/handlers/pnpm.ts index 0947c9467..45885e823 100644 --- a/packages/ci/src/lib/monorepo/handlers/pnpm.ts +++ b/packages/ci/src/lib/monorepo/handlers/pnpm.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import * as YAML from 'yaml'; import { fileExists, readTextFile } from '@code-pushup/utils'; import { @@ -16,13 +16,13 @@ export const pnpmHandler: MonorepoToolHandler = { async isConfigured(options) { return ( - (await fileExists(join(options.cwd, WORKSPACE_FILE))) && - (await fileExists(join(options.cwd, 'package.json'))) + (await fileExists(path.join(options.cwd, WORKSPACE_FILE))) && + (await fileExists(path.join(options.cwd, 'package.json'))) ); }, async listProjects(options) { - const yaml = await readTextFile(join(options.cwd, WORKSPACE_FILE)); + const yaml = await readTextFile(path.join(options.cwd, WORKSPACE_FILE)); const workspace = YAML.parse(yaml) as { packages?: string[] }; const packages = await listPackages(options.cwd, workspace.packages); const rootPackageJson = await readRootPackageJson(options.cwd); diff --git a/packages/ci/src/lib/monorepo/handlers/pnpm.unit.test.ts b/packages/ci/src/lib/monorepo/handlers/pnpm.unit.test.ts index 124ce6acf..75c42538f 100644 --- a/packages/ci/src/lib/monorepo/handlers/pnpm.unit.test.ts +++ b/packages/ci/src/lib/monorepo/handlers/pnpm.unit.test.ts @@ -1,5 +1,5 @@ import { vol } from 'memfs'; -import { join } from 'node:path'; +import path from 'node:path'; import type { PackageJson } from 'type-fest'; import { MEMFS_VOLUME } from '@code-pushup/test-utils'; import type { @@ -83,12 +83,12 @@ describe('pnpmHandler', () => { await expect(pnpmHandler.listProjects(options)).resolves.toEqual([ { name: 'backend', - directory: join(MEMFS_VOLUME, 'apps', 'backend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backend'), bin: 'pnpm run code-pushup', }, { name: 'shared', - directory: join(MEMFS_VOLUME, 'libs', 'shared'), + directory: path.join(MEMFS_VOLUME, 'libs', 'shared'), bin: 'pnpm run code-pushup', }, ] satisfies ProjectConfig[]); @@ -118,12 +118,12 @@ describe('pnpmHandler', () => { await expect(pnpmHandler.listProjects(options)).resolves.toEqual([ { name: 'backend', - directory: join(MEMFS_VOLUME, 'apps', 'backend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backend'), bin: 'pnpm exec code-pushup', }, { name: 'shared', - directory: join(MEMFS_VOLUME, 'libs', 'shared'), + directory: path.join(MEMFS_VOLUME, 'libs', 'shared'), bin: 'pnpm exec code-pushup', }, ] satisfies ProjectConfig[]); @@ -154,17 +154,17 @@ describe('pnpmHandler', () => { await expect(pnpmHandler.listProjects(options)).resolves.toEqual([ { name: 'backend', - directory: join(MEMFS_VOLUME, 'apps', 'backend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backend'), bin: 'pnpm exec code-pushup', }, { name: 'frontend', - directory: join(MEMFS_VOLUME, 'apps', 'frontend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'frontend'), bin: 'pnpm exec code-pushup', }, { name: 'shared', - directory: join(MEMFS_VOLUME, 'libs', 'shared'), + directory: path.join(MEMFS_VOLUME, 'libs', 'shared'), bin: 'pnpm exec code-pushup', }, ] satisfies ProjectConfig[]); @@ -176,17 +176,17 @@ describe('pnpmHandler', () => { all: [ { name: 'backend', - directory: join(MEMFS_VOLUME, 'apps', 'backend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backend'), bin: 'pnpm run code-pushup', }, { name: 'frontend', - directory: join(MEMFS_VOLUME, 'apps', 'frontend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'frontend'), bin: 'pnpm run code-pushup', }, { name: 'shared', - directory: join(MEMFS_VOLUME, 'libs', 'shared'), + directory: path.join(MEMFS_VOLUME, 'libs', 'shared'), bin: 'pnpm run code-pushup', }, ], diff --git a/packages/ci/src/lib/monorepo/handlers/turbo.ts b/packages/ci/src/lib/monorepo/handlers/turbo.ts index c3c56c8a3..49e2e32f5 100644 --- a/packages/ci/src/lib/monorepo/handlers/turbo.ts +++ b/packages/ci/src/lib/monorepo/handlers/turbo.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { fileExists, readJsonFile } from '@code-pushup/utils'; import type { MonorepoToolHandler } from '../tools.js'; import { npmHandler } from './npm.js'; @@ -15,7 +15,7 @@ export const turboHandler: MonorepoToolHandler = { tool: 'turbo', async isConfigured(options) { - const configPath = join(options.cwd, 'turbo.json'); + const configPath = path.join(options.cwd, 'turbo.json'); return ( (await fileExists(configPath)) && options.task in (await readJsonFile(configPath)).tasks diff --git a/packages/ci/src/lib/monorepo/handlers/turbo.unit.test.ts b/packages/ci/src/lib/monorepo/handlers/turbo.unit.test.ts index cc0546a12..5c36993dc 100644 --- a/packages/ci/src/lib/monorepo/handlers/turbo.unit.test.ts +++ b/packages/ci/src/lib/monorepo/handlers/turbo.unit.test.ts @@ -1,5 +1,5 @@ import { vol } from 'memfs'; -import { join } from 'node:path'; +import path from 'node:path'; import type { PackageJson } from 'type-fest'; import { MEMFS_VOLUME } from '@code-pushup/test-utils'; import type { @@ -136,12 +136,12 @@ describe('turboHandler', () => { await expect(turboHandler.listProjects(options)).resolves.toEqual([ { name: '@example/cli', - directory: join(MEMFS_VOLUME, 'packages', 'cli'), + directory: path.join(MEMFS_VOLUME, 'packages', 'cli'), bin: 'npx turbo run code-pushup --', }, { name: '@example/core', - directory: join(MEMFS_VOLUME, 'packages', 'core'), + directory: path.join(MEMFS_VOLUME, 'packages', 'core'), bin: 'npx turbo run code-pushup --', }, ] satisfies ProjectConfig[]); @@ -169,17 +169,17 @@ describe('turboHandler', () => { all: [ { name: 'api', - directory: join(MEMFS_VOLUME, 'api'), + directory: path.join(MEMFS_VOLUME, 'api'), bin: 'npx turbo run code-pushup --', }, { name: 'cms', - directory: join(MEMFS_VOLUME, 'cms'), + directory: path.join(MEMFS_VOLUME, 'cms'), bin: 'npx turbo run code-pushup --', }, { name: 'web', - directory: join(MEMFS_VOLUME, 'web'), + directory: path.join(MEMFS_VOLUME, 'web'), bin: 'npx turbo run code-pushup --', }, ], diff --git a/packages/ci/src/lib/monorepo/handlers/yarn.ts b/packages/ci/src/lib/monorepo/handlers/yarn.ts index 8351afebb..8ba2dcf03 100644 --- a/packages/ci/src/lib/monorepo/handlers/yarn.ts +++ b/packages/ci/src/lib/monorepo/handlers/yarn.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { executeProcess, fileExists } from '@code-pushup/utils'; import { hasCodePushUpDependency, @@ -13,7 +13,7 @@ export const yarnHandler: MonorepoToolHandler = { async isConfigured(options) { return ( - (await fileExists(join(options.cwd, 'yarn.lock'))) && + (await fileExists(path.join(options.cwd, 'yarn.lock'))) && (await hasWorkspacesEnabled(options.cwd)) ); }, diff --git a/packages/ci/src/lib/monorepo/handlers/yarn.unit.test.ts b/packages/ci/src/lib/monorepo/handlers/yarn.unit.test.ts index 895ad343d..1963b7917 100644 --- a/packages/ci/src/lib/monorepo/handlers/yarn.unit.test.ts +++ b/packages/ci/src/lib/monorepo/handlers/yarn.unit.test.ts @@ -1,5 +1,5 @@ import { vol } from 'memfs'; -import { join } from 'node:path'; +import path from 'node:path'; import type { PackageJson } from 'type-fest'; import { MEMFS_VOLUME } from '@code-pushup/test-utils'; import * as utils from '@code-pushup/utils'; @@ -91,12 +91,12 @@ describe('yarnHandler', () => { await expect(yarnHandler.listProjects(options)).resolves.toEqual([ { name: 'backend', - directory: join(MEMFS_VOLUME, 'apps', 'backend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backend'), bin: 'yarn run code-pushup', }, { name: 'shared', - directory: join(MEMFS_VOLUME, 'libs', 'shared'), + directory: path.join(MEMFS_VOLUME, 'libs', 'shared'), bin: 'yarn run code-pushup', }, ] satisfies ProjectConfig[]); @@ -129,12 +129,12 @@ describe('yarnHandler', () => { await expect(yarnHandler.listProjects(options)).resolves.toEqual([ { name: 'backend', - directory: join(MEMFS_VOLUME, 'apps', 'backend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backend'), bin: 'yarn exec code-pushup', }, { name: 'shared', - directory: join(MEMFS_VOLUME, 'libs', 'shared'), + directory: path.join(MEMFS_VOLUME, 'libs', 'shared'), bin: 'yarn exec code-pushup', }, ] satisfies ProjectConfig[]); @@ -165,17 +165,17 @@ describe('yarnHandler', () => { await expect(yarnHandler.listProjects(options)).resolves.toEqual([ { name: 'backend', - directory: join(MEMFS_VOLUME, 'apps', 'backend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backend'), bin: 'yarn exec code-pushup', }, { name: 'frontend', - directory: join(MEMFS_VOLUME, 'apps', 'frontend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'frontend'), bin: 'yarn exec code-pushup', }, { name: 'shared', - directory: join(MEMFS_VOLUME, 'libs', 'shared'), + directory: path.join(MEMFS_VOLUME, 'libs', 'shared'), bin: 'yarn exec code-pushup', }, ] satisfies ProjectConfig[]); @@ -187,17 +187,17 @@ describe('yarnHandler', () => { all: [ { name: 'api', - directory: join(MEMFS_VOLUME, 'api'), + directory: path.join(MEMFS_VOLUME, 'api'), bin: 'yarn run code-pushup', }, { name: 'cms', - directory: join(MEMFS_VOLUME, 'cms'), + directory: path.join(MEMFS_VOLUME, 'cms'), bin: 'yarn run code-pushup', }, { name: 'web', - directory: join(MEMFS_VOLUME, 'web'), + directory: path.join(MEMFS_VOLUME, 'web'), bin: 'yarn run code-pushup', }, ], diff --git a/packages/ci/src/lib/monorepo/list-projects.ts b/packages/ci/src/lib/monorepo/list-projects.ts index 437c738ec..1eec0c37c 100644 --- a/packages/ci/src/lib/monorepo/list-projects.ts +++ b/packages/ci/src/lib/monorepo/list-projects.ts @@ -1,5 +1,5 @@ import { glob } from 'glob'; -import { join } from 'node:path'; +import path from 'node:path'; import type { Logger, Settings } from '../models.js'; import { detectMonorepoTool } from './detect-tool.js'; import { getToolHandler } from './handlers/index.js'; @@ -117,7 +117,7 @@ async function listProjectsByGlobs(args: { const { patterns, cwd, bin, logger } = args; const directories = await glob( - patterns.map(path => path.replace(/\/$/, '/')), + patterns.map(pattern => pattern.replace(/\/$/, '/')), { cwd }, ); @@ -131,7 +131,7 @@ async function listProjectsByGlobs(args: { return directories.toSorted().map(directory => ({ name: directory, bin, - directory: join(cwd, directory), + directory: path.join(cwd, directory), })); } diff --git a/packages/ci/src/lib/monorepo/list-projects.unit.test.ts b/packages/ci/src/lib/monorepo/list-projects.unit.test.ts index e68aec614..63f6a5b70 100644 --- a/packages/ci/src/lib/monorepo/list-projects.unit.test.ts +++ b/packages/ci/src/lib/monorepo/list-projects.unit.test.ts @@ -1,5 +1,5 @@ import { vol } from 'memfs'; -import { join } from 'node:path'; +import path from 'node:path'; import type { PackageJson } from 'type-fest'; import { MEMFS_VOLUME } from '@code-pushup/test-utils'; import * as utils from '@code-pushup/utils'; @@ -119,22 +119,22 @@ describe('listMonorepoProjects', () => { projects: [ { name: 'api', - directory: join(MEMFS_VOLUME, 'backend', 'api'), + directory: path.join(MEMFS_VOLUME, 'backend', 'api'), bin: 'npx turbo run code-pushup --', }, { name: 'auth', - directory: join(MEMFS_VOLUME, 'backend', 'auth'), + directory: path.join(MEMFS_VOLUME, 'backend', 'auth'), bin: 'npx turbo run code-pushup --', }, { name: 'cms', - directory: join(MEMFS_VOLUME, 'frontend', 'cms'), + directory: path.join(MEMFS_VOLUME, 'frontend', 'cms'), bin: 'npx turbo run code-pushup --', }, { name: 'web', - directory: join(MEMFS_VOLUME, 'frontend', 'web'), + directory: path.join(MEMFS_VOLUME, 'frontend', 'web'), bin: 'npx turbo run code-pushup --', }, ], @@ -171,17 +171,17 @@ describe('listMonorepoProjects', () => { projects: [ { name: 'backend', - directory: join(MEMFS_VOLUME, 'apps', 'backend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backend'), bin: 'pnpm run code-pushup', }, { name: 'frontend', - directory: join(MEMFS_VOLUME, 'apps', 'frontend'), + directory: path.join(MEMFS_VOLUME, 'apps', 'frontend'), bin: 'pnpm run code-pushup', }, { name: '@repo/utils', - directory: join(MEMFS_VOLUME, 'libs', 'utils'), + directory: path.join(MEMFS_VOLUME, 'libs', 'utils'), bin: 'pnpm run code-pushup', }, ], @@ -217,12 +217,12 @@ describe('listMonorepoProjects', () => { projects: [ { name: 'cli', - directory: join(MEMFS_VOLUME, 'packages', 'cli'), + directory: path.join(MEMFS_VOLUME, 'packages', 'cli'), bin: 'yarn exec code-pushup', }, { name: 'core', - directory: join(MEMFS_VOLUME, 'packages', 'core'), + directory: path.join(MEMFS_VOLUME, 'packages', 'core'), bin: 'yarn exec code-pushup', }, ], @@ -254,12 +254,12 @@ describe('listMonorepoProjects', () => { projects: [ { name: 'backend', - directory: join(MEMFS_VOLUME, 'packages', 'backend'), + directory: path.join(MEMFS_VOLUME, 'packages', 'backend'), bin: 'npm exec code-pushup --', }, { name: 'frontend', - directory: join(MEMFS_VOLUME, 'packages', 'frontend'), + directory: path.join(MEMFS_VOLUME, 'packages', 'frontend'), bin: 'npm exec code-pushup --', }, ], @@ -296,19 +296,19 @@ describe('listMonorepoProjects', () => { tool: null, projects: [ { - name: join('backend', 'api'), + name: path.join('backend', 'api'), bin: 'npx --no-install code-pushup', - directory: join(MEMFS_VOLUME, 'backend', 'api'), + directory: path.join(MEMFS_VOLUME, 'backend', 'api'), }, { - name: join('backend', 'auth'), + name: path.join('backend', 'auth'), bin: 'npx --no-install code-pushup', - directory: join(MEMFS_VOLUME, 'backend', 'auth'), + directory: path.join(MEMFS_VOLUME, 'backend', 'auth'), }, { name: 'frontend', bin: 'npx --no-install code-pushup', - directory: join(MEMFS_VOLUME, 'frontend'), + directory: path.join(MEMFS_VOLUME, 'frontend'), }, ], } satisfies MonorepoProjects); @@ -340,22 +340,22 @@ describe('listMonorepoProjects', () => { { name: 'my-app', bin: 'npx --no-install code-pushup', - directory: join(MEMFS_VOLUME), + directory: path.join(MEMFS_VOLUME), }, { name: 'migrate', bin: 'npx --no-install code-pushup', - directory: join(MEMFS_VOLUME, 'scripts', 'db', 'migrate'), + directory: path.join(MEMFS_VOLUME, 'scripts', 'db', 'migrate'), }, { name: 'seed', bin: 'npx --no-install code-pushup', - directory: join(MEMFS_VOLUME, 'scripts', 'db', 'seed'), + directory: path.join(MEMFS_VOLUME, 'scripts', 'db', 'seed'), }, { name: 'generate-token', bin: 'npx --no-install code-pushup', - directory: join(MEMFS_VOLUME, 'scripts', 'generate-token'), + directory: path.join(MEMFS_VOLUME, 'scripts', 'generate-token'), }, ], } satisfies MonorepoProjects); @@ -396,22 +396,22 @@ describe('listMonorepoProjects', () => { projects: [ { name: 'backoffice', - directory: join(MEMFS_VOLUME, 'apps', 'backoffice'), + directory: path.join(MEMFS_VOLUME, 'apps', 'backoffice'), bin: 'pnpm exec code-pushup', }, { name: 'frontoffice', - directory: join(MEMFS_VOLUME, 'apps', 'frontoffice'), + directory: path.join(MEMFS_VOLUME, 'apps', 'frontoffice'), bin: 'pnpm exec code-pushup', }, { name: '@repo/models', - directory: join(MEMFS_VOLUME, 'packages', 'models'), + directory: path.join(MEMFS_VOLUME, 'packages', 'models'), bin: 'pnpm exec code-pushup', }, { name: '@repo/ui', - directory: join(MEMFS_VOLUME, 'packages', 'ui'), + directory: path.join(MEMFS_VOLUME, 'packages', 'ui'), bin: 'pnpm exec code-pushup', }, ], diff --git a/packages/ci/src/lib/monorepo/packages.ts b/packages/ci/src/lib/monorepo/packages.ts index 514c53e93..43b43a778 100644 --- a/packages/ci/src/lib/monorepo/packages.ts +++ b/packages/ci/src/lib/monorepo/packages.ts @@ -1,5 +1,5 @@ import { glob } from 'glob'; -import { basename, dirname, join } from 'node:path'; +import path from 'node:path'; import type { PackageJson } from 'type-fest'; import { readJsonFile } from '@code-pushup/utils'; @@ -20,9 +20,9 @@ export async function listPackages( return Promise.all( files.toSorted().map(async (file): Promise => { - const packageJson = await readJsonFile(join(cwd, file)); - const directory = join(cwd, dirname(file)); - const name = packageJson.name || basename(directory); + const packageJson = await readJsonFile(path.join(cwd, file)); + const directory = path.join(cwd, path.dirname(file)); + const name = packageJson.name || path.basename(directory); return { name, directory, packageJson }; }), ); @@ -56,7 +56,7 @@ export async function hasWorkspacesEnabled(cwd: string): Promise { } export async function readRootPackageJson(cwd: string): Promise { - return await readJsonFile(join(cwd, 'package.json')); + return await readJsonFile(path.join(cwd, 'package.json')); } export function hasDependency(packageJson: PackageJson, name: string): boolean { diff --git a/packages/ci/src/lib/monorepo/packages.unit.test.ts b/packages/ci/src/lib/monorepo/packages.unit.test.ts index 0e42538d3..2677c6b0d 100644 --- a/packages/ci/src/lib/monorepo/packages.unit.test.ts +++ b/packages/ci/src/lib/monorepo/packages.unit.test.ts @@ -1,5 +1,5 @@ import { vol } from 'memfs'; -import { basename, join } from 'node:path'; +import path from 'node:path'; import type { PackageJson } from 'type-fest'; import { MEMFS_VOLUME } from '@code-pushup/test-utils'; import { @@ -30,22 +30,22 @@ describe('listPackages', () => { await expect(listPackages(MEMFS_VOLUME)).resolves.toEqual([ { name: 'e2e', - directory: join(MEMFS_VOLUME, 'e2e'), + directory: path.join(MEMFS_VOLUME, 'e2e'), packageJson: { name: 'e2e' }, }, { name: 'example-monorepo', - directory: join(MEMFS_VOLUME), + directory: path.join(MEMFS_VOLUME), packageJson: { name: 'example-monorepo' }, }, { name: '@example/cli', - directory: join(MEMFS_VOLUME, 'packages', 'cli'), + directory: path.join(MEMFS_VOLUME, 'packages', 'cli'), packageJson: { name: '@example/cli' }, }, { name: '@example/core', - directory: join(MEMFS_VOLUME, 'packages', 'core'), + directory: path.join(MEMFS_VOLUME, 'packages', 'core'), packageJson: { name: '@example/core' }, }, ]); @@ -105,7 +105,7 @@ describe('listPackages', () => { await expect(listPackages(MEMFS_VOLUME)).resolves.toEqual([ expect.objectContaining({ name: 'e2e' }), - expect.objectContaining({ name: basename(MEMFS_VOLUME) }), + expect.objectContaining({ name: path.basename(MEMFS_VOLUME) }), expect.objectContaining({ name: '@example/cli' }), expect.objectContaining({ name: '@example/core' }), expect.objectContaining({ name: 'utils' }), @@ -132,12 +132,12 @@ describe('listWorkspaces', () => { workspaces: [ { name: 'api', - directory: join(MEMFS_VOLUME, 'api'), + directory: path.join(MEMFS_VOLUME, 'api'), packageJson: { name: 'api' }, }, { name: 'ui', - directory: join(MEMFS_VOLUME, 'ui'), + directory: path.join(MEMFS_VOLUME, 'ui'), packageJson: { name: 'ui' }, }, ], @@ -166,12 +166,12 @@ describe('listWorkspaces', () => { workspaces: [ { name: 'cli', - directory: join(MEMFS_VOLUME, 'packages', 'cli'), + directory: path.join(MEMFS_VOLUME, 'packages', 'cli'), packageJson: { name: 'cli' }, }, { name: 'core', - directory: join(MEMFS_VOLUME, 'packages', 'core'), + directory: path.join(MEMFS_VOLUME, 'packages', 'core'), packageJson: { name: 'core' }, }, ], @@ -202,12 +202,12 @@ describe('listWorkspaces', () => { workspaces: [ { name: 'desktop', - directory: join(MEMFS_VOLUME, 'apps', 'desktop'), + directory: path.join(MEMFS_VOLUME, 'apps', 'desktop'), packageJson: { name: 'desktop' }, }, { name: 'mobile', - directory: join(MEMFS_VOLUME, 'apps', 'mobile'), + directory: path.join(MEMFS_VOLUME, 'apps', 'mobile'), packageJson: { name: 'mobile' }, }, ], diff --git a/packages/ci/src/lib/run-monorepo.ts b/packages/ci/src/lib/run-monorepo.ts index eb3a7b37c..6b339dc75 100644 --- a/packages/ci/src/lib/run-monorepo.ts +++ b/packages/ci/src/lib/run-monorepo.ts @@ -1,5 +1,5 @@ import { copyFile, readFile } from 'node:fs/promises'; -import { basename, join } from 'node:path'; +import path from 'node:path'; import { type CoreConfig, DEFAULT_PERSIST_OUTPUT_DIR, @@ -61,10 +61,10 @@ export async function runInMonorepoMode( createCommandContext(settings, projects[0]), ); logger.debug(`Merged ${diffJsonPaths.length} diffs into ${tmpDiffPath}`); - const diffPath = join( + const diffPath = path.join( directory, DEFAULT_PERSIST_OUTPUT_DIR, - basename(tmpDiffPath), + path.basename(tmpDiffPath), ); if (tmpDiffPath !== diffPath) { await copyFile(tmpDiffPath, diffPath); diff --git a/packages/ci/src/lib/run-utils.ts b/packages/ci/src/lib/run-utils.ts index 196df1d2c..c7f65f4dd 100644 --- a/packages/ci/src/lib/run-utils.ts +++ b/packages/ci/src/lib/run-utils.ts @@ -1,5 +1,5 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import type { SimpleGit } from 'simple-git'; import type { CoreConfig, Report, ReportsDiff } from '@code-pushup/models'; import { stringifyError } from '@code-pushup/utils'; @@ -127,9 +127,9 @@ export async function compareReports( const ctx = createCommandContext(settings, project); - const reportsDir = join(settings.directory, '.code-pushup'); - const currPath = join(reportsDir, 'curr-report.json'); - const prevPath = join(reportsDir, 'prev-report.json'); + const reportsDir = path.join(settings.directory, '.code-pushup'); + const currPath = path.join(reportsDir, 'curr-report.json'); + const prevPath = path.join(reportsDir, 'prev-report.json'); await mkdir(reportsDir, { recursive: true }); await writeFile(currPath, currReport); await writeFile(prevPath, prevReport); diff --git a/packages/ci/src/lib/run.integration.test.ts b/packages/ci/src/lib/run.integration.test.ts index f7579d490..bd362834d 100644 --- a/packages/ci/src/lib/run.integration.test.ts +++ b/packages/ci/src/lib/run.integration.test.ts @@ -6,7 +6,7 @@ import { rename, writeFile, } from 'node:fs/promises'; -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { type SimpleGit, simpleGit } from 'simple-git'; import type { MockInstance } from 'vitest'; @@ -26,37 +26,37 @@ import type { MonorepoTool } from './monorepo/index.js'; import { runInCI } from './run.js'; describe('runInCI', () => { - const fixturesDir = join( - fileURLToPath(dirname(import.meta.url)), + const fixturesDir = path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', '..', 'mocks', 'fixtures', ); - const reportsDir = join(fixturesDir, 'outputs'); - const workDir = join(process.cwd(), 'tmp', 'ci', 'run-test'); + const reportsDir = path.join(fixturesDir, 'outputs'); + const workDir = path.join(process.cwd(), 'tmp', 'ci', 'run-test'); const fixturePaths = { reports: { before: { - json: join(reportsDir, 'report-before.json'), - md: join(reportsDir, 'report-before.md'), + json: path.join(reportsDir, 'report-before.json'), + md: path.join(reportsDir, 'report-before.md'), }, after: { - json: join(reportsDir, 'report-after.json'), - md: join(reportsDir, 'report-after.md'), + json: path.join(reportsDir, 'report-after.json'), + md: path.join(reportsDir, 'report-after.md'), }, }, diffs: { project: { - json: join(reportsDir, 'diff-project.json'), - md: join(reportsDir, 'diff-project.md'), + json: path.join(reportsDir, 'diff-project.json'), + md: path.join(reportsDir, 'diff-project.md'), }, merged: { - md: join(reportsDir, 'diff-merged.md'), + md: path.join(reportsDir, 'diff-merged.md'), }, }, - config: join(reportsDir, 'config.json'), + config: path.join(reportsDir, 'config.json'), }; const logger: Logger = { @@ -98,16 +98,16 @@ describe('runInCI', () => { }: utils.ProcessConfig): Promise { const nxMatch = command.match(/nx run (\w+):code-pushup/); const outputDir = nxMatch - ? join(workDir, `packages/${nxMatch[1]}/.code-pushup`) - : join(cwd as string, '.code-pushup'); + ? path.join(workDir, `packages/${nxMatch[1]}/.code-pushup`) + : path.join(cwd as string, '.code-pushup'); await mkdir(outputDir, { recursive: true }); let stdout = ''; switch (args![0]) { case 'compare': const diffs = fixturePaths.diffs.project; - await copyFile(diffs.json, join(outputDir, 'report-diff.json')); - await copyFile(diffs.md, join(outputDir, 'report-diff.md')); + await copyFile(diffs.json, path.join(outputDir, 'report-diff.json')); + await copyFile(diffs.md, path.join(outputDir, 'report-diff.md')); break; case 'print-config': @@ -124,7 +124,7 @@ describe('runInCI', () => { case 'merge-diffs': await copyFile( fixturePaths.diffs.merged.md, - join( + path.join( nxMatch ? workDir : (cwd as string), '.code-pushup/merged-report-diff.md', ), @@ -138,17 +138,23 @@ describe('runInCI', () => { if (/workspaces|concurrency|parallel/.test(command)) { // eslint-disable-next-line functional/no-loop-statements for (const project of ['cli', 'core', 'utils']) { - const projectOutputDir = join( + const projectOutputDir = path.join( workDir, `packages/${project}/.code-pushup`, ); await mkdir(projectOutputDir, { recursive: true }); - await copyFile(reports.json, join(projectOutputDir, 'report.json')); - await copyFile(reports.json, join(projectOutputDir, 'report.md')); + await copyFile( + reports.json, + path.join(projectOutputDir, 'report.json'), + ); + await copyFile( + reports.json, + path.join(projectOutputDir, 'report.md'), + ); } } else { - await copyFile(reports.json, join(outputDir, 'report.json')); - await copyFile(reports.md, join(outputDir, 'report.md')); + await copyFile(reports.json, path.join(outputDir, 'report.json')); + await copyFile(reports.md, path.join(outputDir, 'report.md')); } break; } @@ -181,7 +187,10 @@ describe('runInCI', () => { git = await initGitRepo(simpleGit, { baseDir: workDir }); await simulateGitFetch(git); - await writeFile(join(workDir, 'index.js'), 'console.log("Hello, world!")'); + await writeFile( + path.join(workDir, 'index.js'), + 'console.log("Hello, world!")', + ); await git.add('index.js'); await git.commit('Initial commit'); }); @@ -194,7 +203,7 @@ describe('runInCI', () => { }); describe('standalone mode', () => { - const outputDir = join(workDir, '.code-pushup'); + const outputDir = path.join(workDir, '.code-pushup'); describe('push event', () => { beforeEach(async () => { @@ -213,8 +222,8 @@ describe('runInCI', () => { mode: 'standalone', files: { report: { - json: join(outputDir, 'report.json'), - md: join(outputDir, 'report.md'), + json: path.join(outputDir, 'report.json'), + md: path.join(outputDir, 'report.md'), }, }, } satisfies RunResult); @@ -245,7 +254,10 @@ describe('runInCI', () => { beforeEach(async () => { await git.checkoutLocalBranch('feature-1'); - await rename(join(workDir, 'index.js'), join(workDir, 'index.ts')); + await rename( + path.join(workDir, 'index.js'), + path.join(workDir, 'index.ts'), + ); await git.add('index.ts'); await git.commit('Convert JS file to TS'); @@ -272,12 +284,12 @@ describe('runInCI', () => { newIssues: [], files: { report: { - json: join(outputDir, 'report.json'), - md: join(outputDir, 'report.md'), + json: path.join(outputDir, 'report.json'), + md: path.join(outputDir, 'report.md'), }, diff: { - json: join(outputDir, 'report-diff.json'), - md: join(outputDir, 'report-diff.md'), + json: path.join(outputDir, 'report-diff.json'), + md: path.join(outputDir, 'report-diff.md'), }, }, } satisfies RunResult); @@ -313,8 +325,8 @@ describe('runInCI', () => { command: options.bin, args: [ 'compare', - `--before=${join(outputDir, 'prev-report.json')}`, - `--after=${join(outputDir, 'curr-report.json')}`, + `--before=${path.join(outputDir, 'prev-report.json')}`, + `--after=${path.join(outputDir, 'curr-report.json')}`, '--persist.format=json', '--persist.format=md', ], @@ -334,7 +346,7 @@ describe('runInCI', () => { updateComment: vi.fn().mockResolvedValue(mockComment), listComments: vi.fn().mockResolvedValue([mockComment]), downloadReportArtifact: vi.fn().mockImplementation(async () => { - const downloadPath = join(workDir, 'downloaded-report.json'); + const downloadPath = path.join(workDir, 'downloaded-report.json'); await copyFile(fixturePaths.reports.before.json, downloadPath); return downloadPath; }), @@ -346,12 +358,12 @@ describe('runInCI', () => { newIssues: [], files: { report: { - json: join(outputDir, 'report.json'), - md: join(outputDir, 'report.md'), + json: path.join(outputDir, 'report.json'), + md: path.join(outputDir, 'report.md'), }, diff: { - json: join(outputDir, 'report-diff.json'), - md: join(outputDir, 'report-diff.md'), + json: path.join(outputDir, 'report-diff.json'), + md: path.join(outputDir, 'report-diff.md'), }, }, } satisfies RunResult); @@ -379,8 +391,8 @@ describe('runInCI', () => { command: options.bin, args: [ 'compare', - `--before=${join(outputDir, 'prev-report.json')}`, - `--after=${join(outputDir, 'curr-report.json')}`, + `--before=${path.join(outputDir, 'prev-report.json')}`, + `--after=${path.join(outputDir, 'curr-report.json')}`, '--persist.format=json', '--persist.format=md', ], @@ -449,7 +461,7 @@ describe('runInCI', () => { }, ])('monorepo mode - $name', ({ tool, run, runMany, setup }) => { beforeEach(async () => { - const monorepoDir = join(fixturesDir, 'monorepos', tool); + const monorepoDir = path.join(fixturesDir, 'monorepos', tool); await cp(monorepoDir, workDir, { recursive: true }); await git.add('.'); await git.commit(`Create packages in ${tool} monorepo`); @@ -476,8 +488,11 @@ describe('runInCI', () => { name: 'cli', files: { report: { - json: join(workDir, 'packages/cli/.code-pushup/report.json'), - md: join(workDir, 'packages/cli/.code-pushup/report.md'), + json: path.join( + workDir, + 'packages/cli/.code-pushup/report.json', + ), + md: path.join(workDir, 'packages/cli/.code-pushup/report.md'), }, }, }, @@ -485,8 +500,14 @@ describe('runInCI', () => { name: 'core', files: { report: { - json: join(workDir, 'packages/core/.code-pushup/report.json'), - md: join(workDir, 'packages/core/.code-pushup/report.md'), + json: path.join( + workDir, + 'packages/core/.code-pushup/report.json', + ), + md: path.join( + workDir, + 'packages/core/.code-pushup/report.md', + ), }, }, }, @@ -494,11 +515,14 @@ describe('runInCI', () => { name: 'utils', files: { report: { - json: join( + json: path.join( workDir, 'packages/utils/.code-pushup/report.json', ), - md: join(workDir, 'packages/utils/.code-pushup/report.md'), + md: path.join( + workDir, + 'packages/utils/.code-pushup/report.md', + ), }, }, }, @@ -535,7 +559,7 @@ describe('runInCI', () => { beforeEach(async () => { await git.checkoutLocalBranch('feature-1'); - await writeFile(join(workDir, 'README.md'), '# Hello, world\n'); + await writeFile(path.join(workDir, 'README.md'), '# Hello, world\n'); await git.add('README.md'); await git.commit('Create README'); @@ -558,8 +582,13 @@ describe('runInCI', () => { // simulates a project which has no cached report return null; } - const downloadPath = join(workDir, 'tmp', project, 'report.json'); - await mkdir(dirname(downloadPath), { recursive: true }); + const downloadPath = path.join( + workDir, + 'tmp', + project, + 'report.json', + ); + await mkdir(path.dirname(downloadPath), { recursive: true }); await copyFile(fixturePaths.reports.before.json, downloadPath); return downloadPath; }), @@ -570,21 +599,27 @@ describe('runInCI', () => { ).resolves.toEqual({ mode: 'monorepo', commentId: mockComment.id, - diffPath: join(workDir, '.code-pushup/merged-report-diff.md'), + diffPath: path.join(workDir, '.code-pushup/merged-report-diff.md'), projects: [ { name: 'cli', files: { report: { - json: join(workDir, 'packages/cli/.code-pushup/report.json'), - md: join(workDir, 'packages/cli/.code-pushup/report.md'), + json: path.join( + workDir, + 'packages/cli/.code-pushup/report.json', + ), + md: path.join(workDir, 'packages/cli/.code-pushup/report.md'), }, diff: { - json: join( + json: path.join( workDir, 'packages/cli/.code-pushup/report-diff.json', ), - md: join(workDir, 'packages/cli/.code-pushup/report-diff.md'), + md: path.join( + workDir, + 'packages/cli/.code-pushup/report-diff.md', + ), }, }, newIssues: [], @@ -593,15 +628,21 @@ describe('runInCI', () => { name: 'core', files: { report: { - json: join(workDir, 'packages/core/.code-pushup/report.json'), - md: join(workDir, 'packages/core/.code-pushup/report.md'), + json: path.join( + workDir, + 'packages/core/.code-pushup/report.json', + ), + md: path.join( + workDir, + 'packages/core/.code-pushup/report.md', + ), }, diff: { - json: join( + json: path.join( workDir, 'packages/core/.code-pushup/report-diff.json', ), - md: join( + md: path.join( workDir, 'packages/core/.code-pushup/report-diff.md', ), @@ -613,18 +654,21 @@ describe('runInCI', () => { name: 'utils', files: { report: { - json: join( + json: path.join( workDir, 'packages/utils/.code-pushup/report.json', ), - md: join(workDir, 'packages/utils/.code-pushup/report.md'), + md: path.join( + workDir, + 'packages/utils/.code-pushup/report.md', + ), }, diff: { - json: join( + json: path.join( workDir, 'packages/utils/.code-pushup/report-diff.json', ), - md: join( + md: path.join( workDir, 'packages/utils/.code-pushup/report-diff.md', ), @@ -636,7 +680,10 @@ describe('runInCI', () => { } satisfies RunResult); await expect( - readFile(join(workDir, '.code-pushup/merged-report-diff.md'), 'utf8'), + readFile( + path.join(workDir, '.code-pushup/merged-report-diff.md'), + 'utf8', + ), ).resolves.toBe(diffMdString); expect(api.listComments).toHaveBeenCalledWith(); @@ -682,9 +729,9 @@ describe('runInCI', () => { command: run, args: [ 'merge-diffs', - `--files=${join(workDir, 'packages/cli/.code-pushup/report-diff.json')}`, - `--files=${join(workDir, 'packages/core/.code-pushup/report-diff.json')}`, - `--files=${join(workDir, 'packages/utils/.code-pushup/report-diff.json')}`, + `--files=${path.join(workDir, 'packages/cli/.code-pushup/report-diff.json')}`, + `--files=${path.join(workDir, 'packages/core/.code-pushup/report-diff.json')}`, + `--files=${path.join(workDir, 'packages/utils/.code-pushup/report-diff.json')}`, expect.stringMatching(/^--persist.outputDir=.*\.code-pushup$/), '--persist.filename=merged-report', ], @@ -715,7 +762,7 @@ describe('runInCI', () => { ], ])('monorepo mode - custom: %s', (_, monorepoOptions) => { beforeEach(async () => { - const monorepoDir = join(fixturesDir, 'monorepos', 'custom'); + const monorepoDir = path.join(fixturesDir, 'monorepos', 'custom'); await cp(monorepoDir, workDir, { recursive: true }); await git.add('.'); await git.commit('Create projects in monorepo'); @@ -741,8 +788,11 @@ describe('runInCI', () => { name: expect.stringContaining('api'), files: { report: { - json: join(workDir, 'backend/api/.code-pushup/report.json'), - md: join(workDir, 'backend/api/.code-pushup/report.md'), + json: path.join( + workDir, + 'backend/api/.code-pushup/report.json', + ), + md: path.join(workDir, 'backend/api/.code-pushup/report.md'), }, }, }, @@ -750,8 +800,11 @@ describe('runInCI', () => { name: expect.stringContaining('auth'), files: { report: { - json: join(workDir, 'backend/auth/.code-pushup/report.json'), - md: join(workDir, 'backend/auth/.code-pushup/report.md'), + json: path.join( + workDir, + 'backend/auth/.code-pushup/report.json', + ), + md: path.join(workDir, 'backend/auth/.code-pushup/report.md'), }, }, }, @@ -759,8 +812,8 @@ describe('runInCI', () => { name: 'frontend', files: { report: { - json: join(workDir, 'frontend/.code-pushup/report.json'), - md: join(workDir, 'frontend/.code-pushup/report.md'), + json: path.join(workDir, 'frontend/.code-pushup/report.json'), + md: path.join(workDir, 'frontend/.code-pushup/report.md'), }, }, }, @@ -797,7 +850,7 @@ describe('runInCI', () => { beforeEach(async () => { await git.checkoutLocalBranch('feature-1'); - await writeFile(join(workDir, 'README.md'), '# Hello, world\n'); + await writeFile(path.join(workDir, 'README.md'), '# Hello, world\n'); await git.add('README.md'); await git.commit('Create README'); @@ -816,8 +869,13 @@ describe('runInCI', () => { updateComment: vi.fn(), listComments: vi.fn().mockResolvedValue([]), downloadReportArtifact: vi.fn().mockImplementation(async project => { - const downloadPath = join(workDir, 'tmp', project, 'report.json'); - await mkdir(dirname(downloadPath), { recursive: true }); + const downloadPath = path.join( + workDir, + 'tmp', + project, + 'report.json', + ); + await mkdir(path.dirname(downloadPath), { recursive: true }); await copyFile(fixturePaths.reports.before.json, downloadPath); return downloadPath; }), @@ -828,21 +886,27 @@ describe('runInCI', () => { ).resolves.toEqual({ mode: 'monorepo', commentId: mockComment.id, - diffPath: join(workDir, '.code-pushup/merged-report-diff.md'), + diffPath: path.join(workDir, '.code-pushup/merged-report-diff.md'), projects: [ { name: expect.stringContaining('api'), files: { report: { - json: join(workDir, 'backend/api/.code-pushup/report.json'), - md: join(workDir, 'backend/api/.code-pushup/report.md'), + json: path.join( + workDir, + 'backend/api/.code-pushup/report.json', + ), + md: path.join(workDir, 'backend/api/.code-pushup/report.md'), }, diff: { - json: join( + json: path.join( workDir, 'backend/api/.code-pushup/report-diff.json', ), - md: join(workDir, 'backend/api/.code-pushup/report-diff.md'), + md: path.join( + workDir, + 'backend/api/.code-pushup/report-diff.md', + ), }, }, newIssues: [], @@ -851,15 +915,21 @@ describe('runInCI', () => { name: expect.stringContaining('auth'), files: { report: { - json: join(workDir, 'backend/auth/.code-pushup/report.json'), - md: join(workDir, 'backend/auth/.code-pushup/report.md'), + json: path.join( + workDir, + 'backend/auth/.code-pushup/report.json', + ), + md: path.join(workDir, 'backend/auth/.code-pushup/report.md'), }, diff: { - json: join( + json: path.join( workDir, 'backend/auth/.code-pushup/report-diff.json', ), - md: join(workDir, 'backend/auth/.code-pushup/report-diff.md'), + md: path.join( + workDir, + 'backend/auth/.code-pushup/report-diff.md', + ), }, }, newIssues: [], @@ -868,12 +938,18 @@ describe('runInCI', () => { name: 'frontend', files: { report: { - json: join(workDir, 'frontend/.code-pushup/report.json'), - md: join(workDir, 'frontend/.code-pushup/report.md'), + json: path.join(workDir, 'frontend/.code-pushup/report.json'), + md: path.join(workDir, 'frontend/.code-pushup/report.md'), }, diff: { - json: join(workDir, 'frontend/.code-pushup/report-diff.json'), - md: join(workDir, 'frontend/.code-pushup/report-diff.md'), + json: path.join( + workDir, + 'frontend/.code-pushup/report-diff.json', + ), + md: path.join( + workDir, + 'frontend/.code-pushup/report-diff.md', + ), }, }, newIssues: [], @@ -882,7 +958,10 @@ describe('runInCI', () => { } satisfies RunResult); await expect( - readFile(join(workDir, '.code-pushup/merged-report-diff.md'), 'utf8'), + readFile( + path.join(workDir, '.code-pushup/merged-report-diff.md'), + 'utf8', + ), ).resolves.toBe(diffMdString); expect(api.listComments).toHaveBeenCalledWith(); @@ -927,9 +1006,9 @@ describe('runInCI', () => { command: options.bin, args: [ 'merge-diffs', - `--files=${join(workDir, 'backend/api/.code-pushup/report-diff.json')}`, - `--files=${join(workDir, 'backend/auth/.code-pushup/report-diff.json')}`, - `--files=${join(workDir, 'frontend/.code-pushup/report-diff.json')}`, + `--files=${path.join(workDir, 'backend/api/.code-pushup/report-diff.json')}`, + `--files=${path.join(workDir, 'backend/auth/.code-pushup/report-diff.json')}`, + `--files=${path.join(workDir, 'frontend/.code-pushup/report-diff.json')}`, expect.stringMatching(/^--persist.outputDir=.*\.code-pushup$/), '--persist.filename=merged-report', ], diff --git a/packages/cli/.eslintrc.json b/packages/cli/.eslintrc.json deleted file mode 100644 index 622b17e34..000000000 --- a/packages/cli/.eslintrc.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["packages/cli/tsconfig.*?.json"] - }, - "rules": {} - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": ["error"] - } - } - ] -} diff --git a/packages/cli/docs/custom-plugins.md b/packages/cli/docs/custom-plugins.md index 2736b758f..93c1bb725 100644 --- a/packages/cli/docs/custom-plugins.md +++ b/packages/cli/docs/custom-plugins.md @@ -328,7 +328,7 @@ The basic implementation of a `RunnerConfig` for the above command looks like th ```typescript // lighthouse.plugin.ts // ... -import { join } from 'path'; +import path from 'node:path'; import { AuditOutputs } from '@code-pushup/models'; import { objectToCliArgs } from '@code-pushup/utils'; @@ -336,7 +336,7 @@ function runnerConfig(options: Options): RunnerConfig { const { url } = options; // hardcoded to run only the LCP audit const audits = [lcpAuditMeta.slug]; - const outputFile = join(process.cwd(), '.code-pushup', 'lighthouse-report.json'); + const outputFile = path.join(process.cwd(), '.code-pushup', 'lighthouse-report.json'); return { // npx lighthouse https://example.com --output=json --outputFile=lighthouse-report.json --onlyAudits=largest-contentful-paint command: 'npx', diff --git a/packages/cli/eslint.config.js b/packages/cli/eslint.config.js new file mode 100644 index 000000000..40165321a --- /dev/null +++ b/packages/cli/eslint.config.js @@ -0,0 +1,21 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': 'error', + }, + }, +); diff --git a/packages/cli/src/lib/history/history.options.ts b/packages/cli/src/lib/history/history.options.ts index c4504309d..29dc7f4b3 100644 --- a/packages/cli/src/lib/history/history.options.ts +++ b/packages/cli/src/lib/history/history.options.ts @@ -30,7 +30,7 @@ export function yargsHistoryOptionsDefinition(): Record< // https://git-scm.com/docs/git-log#Documentation/git-log.txt---max-countltnumbergt describe: 'Number of steps in history', type: 'number', - // eslint-disable-next-line no-magic-numbers + // eslint-disable-next-line @typescript-eslint/no-magic-numbers default: 5, }, from: { diff --git a/packages/cli/src/lib/implementation/core-config.middleware.integration.test.ts b/packages/cli/src/lib/implementation/core-config.middleware.integration.test.ts index b0d29dd27..8ea4bb283 100644 --- a/packages/cli/src/lib/implementation/core-config.middleware.integration.test.ts +++ b/packages/cli/src/lib/implementation/core-config.middleware.integration.test.ts @@ -1,10 +1,10 @@ -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect } from 'vitest'; import { coreConfigMiddleware } from './core-config.middleware.js'; -const configDirPath = join( - fileURLToPath(dirname(import.meta.url)), +const configDirPath = path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', '..', '..', @@ -30,7 +30,7 @@ describe('coreConfigMiddleware', () => { 'should load a valid .%s config', async extension => { const config = await coreConfigMiddleware({ - config: join(configDirPath, `code-pushup.config.${extension}`), + config: path.join(configDirPath, `code-pushup.config.${extension}`), ...CLI_DEFAULTS, }); expect(config.config).toContain(`code-pushup.config.${extension}`); @@ -47,8 +47,11 @@ describe('coreConfigMiddleware', () => { it('should load config which relies on provided --tsconfig', async () => { await expect( coreConfigMiddleware({ - config: join(configDirPath, 'code-pushup.needs-tsconfig.config.ts'), - tsconfig: join(configDirPath, 'tsconfig.json'), + config: path.join( + configDirPath, + 'code-pushup.needs-tsconfig.config.ts', + ), + tsconfig: path.join(configDirPath, 'tsconfig.json'), ...CLI_DEFAULTS, }), ).resolves.toBeTruthy(); @@ -57,7 +60,10 @@ describe('coreConfigMiddleware', () => { it('should throw if --tsconfig is missing but needed to resolve import', async () => { await expect( coreConfigMiddleware({ - config: join(configDirPath, 'code-pushup.needs-tsconfig.config.ts'), + config: path.join( + configDirPath, + 'code-pushup.needs-tsconfig.config.ts', + ), ...CLI_DEFAULTS, }), ).rejects.toThrow("Cannot find package '@example/custom-plugin'"); diff --git a/packages/cli/src/lib/implementation/core-config.model.ts b/packages/cli/src/lib/implementation/core-config.model.ts index 7293c7420..612c071cc 100644 --- a/packages/cli/src/lib/implementation/core-config.model.ts +++ b/packages/cli/src/lib/implementation/core-config.model.ts @@ -1,6 +1,5 @@ import type { CoreConfig, Format, UploadConfig } from '@code-pushup/models'; -/* eslint-disable @typescript-eslint/naming-convention */ export type PersistConfigCliOptions = { 'persist.outputDir'?: string; 'persist.filename'?: string; @@ -13,7 +12,6 @@ export type UploadConfigCliOptions = { 'upload.apiKey'?: string; 'upload.server'?: string; }; -/* eslint-enable @typescript-eslint/naming-convention */ export type ConfigCliOptions = { config?: string; diff --git a/packages/cli/src/lib/implementation/global.utils.ts b/packages/cli/src/lib/implementation/global.utils.ts index 54b1e9212..83d18beac 100644 --- a/packages/cli/src/lib/implementation/global.utils.ts +++ b/packages/cli/src/lib/implementation/global.utils.ts @@ -33,7 +33,6 @@ export function logErrorBeforeThrow any>( // eslint-disable-next-line @typescript-eslint/no-explicit-any return (async (...args: any[]) => { try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument return await fn(...args); } catch (error) { if (error instanceof OptionValidationError) { diff --git a/packages/cli/src/lib/print-config/print-config-command.ts b/packages/cli/src/lib/print-config/print-config-command.ts index 7325729fd..278d7d5ac 100644 --- a/packages/cli/src/lib/print-config/print-config-command.ts +++ b/packages/cli/src/lib/print-config/print-config-command.ts @@ -8,7 +8,6 @@ export function yargsConfigCommandObject() { command, describe: 'Print config', handler: yargsArgs => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars const { _, $0, ...args } = yargsArgs; // it is important to filter out kebab case keys // because yargs duplicates options in camel case and kebab case diff --git a/packages/core/.eslintrc.json b/packages/core/.eslintrc.json deleted file mode 100644 index 09622f940..000000000 --- a/packages/core/.eslintrc.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["packages/core/tsconfig.*?.json"] - }, - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": "error" - } - } - ] -} diff --git a/packages/core/eslint.config.js b/packages/core/eslint.config.js new file mode 100644 index 000000000..40165321a --- /dev/null +++ b/packages/core/eslint.config.js @@ -0,0 +1,21 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': 'error', + }, + }, +); diff --git a/packages/core/src/lib/compare.ts b/packages/core/src/lib/compare.ts index 227ccbf7f..c56ff63d8 100644 --- a/packages/core/src/lib/compare.ts +++ b/packages/core/src/lib/compare.ts @@ -1,6 +1,6 @@ import { writeFile } from 'node:fs/promises'; import { createRequire } from 'node:module'; -import { join } from 'node:path'; +import path from 'node:path'; import { type Format, type PersistConfig, @@ -58,7 +58,7 @@ export async function compareReportFiles( return Promise.all( format.map(async fmt => { - const outputPath = join(outputDir, `${filename}-diff.${fmt}`); + const outputPath = path.join(outputDir, `${filename}-diff.${fmt}`); const content = reportsDiffToFileContent(diff, fmt); await ensureDirectoryExists(outputDir); await writeFile(outputPath, content); diff --git a/packages/core/src/lib/compare.unit.test.ts b/packages/core/src/lib/compare.unit.test.ts index 78b5a6779..d1ea58ddd 100644 --- a/packages/core/src/lib/compare.unit.test.ts +++ b/packages/core/src/lib/compare.unit.test.ts @@ -1,6 +1,6 @@ import { vol } from 'memfs'; import { readFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { getPortalComparisonLink } from '@code-pushup/portal-client'; import { type Commit, @@ -37,15 +37,15 @@ describe('compareReportFiles', () => { it('should create valid report-diff.json from report.json files', async () => { await compareReportFiles( { - before: join(MEMFS_VOLUME, 'source-report.json'), - after: join(MEMFS_VOLUME, 'target-report.json'), + before: path.join(MEMFS_VOLUME, 'source-report.json'), + after: path.join(MEMFS_VOLUME, 'target-report.json'), }, { outputDir: MEMFS_VOLUME, filename: 'report', format: ['json'] }, undefined, ); const reportsDiffPromise = readJsonFile( - join(MEMFS_VOLUME, 'report-diff.json'), + path.join(MEMFS_VOLUME, 'report-diff.json'), ); await expect(reportsDiffPromise).resolves.toBeTruthy(); @@ -56,26 +56,26 @@ describe('compareReportFiles', () => { it('should create all diff files specified by persist.format', async () => { await compareReportFiles( { - before: join(MEMFS_VOLUME, 'source-report.json'), - after: join(MEMFS_VOLUME, 'target-report.json'), + before: path.join(MEMFS_VOLUME, 'source-report.json'), + after: path.join(MEMFS_VOLUME, 'target-report.json'), }, { outputDir: MEMFS_VOLUME, filename: 'report', format: ['json', 'md'] }, undefined, ); await expect( - fileExists(join(MEMFS_VOLUME, 'report-diff.json')), + fileExists(path.join(MEMFS_VOLUME, 'report-diff.json')), ).resolves.toBeTruthy(); await expect( - fileExists(join(MEMFS_VOLUME, 'report-diff.md')), + fileExists(path.join(MEMFS_VOLUME, 'report-diff.md')), ).resolves.toBeTruthy(); }); it('should include portal link (fetched using upload config) in Markdown file', async () => { await compareReportFiles( { - before: join(MEMFS_VOLUME, 'source-report.json'), - after: join(MEMFS_VOLUME, 'target-report.json'), + before: path.join(MEMFS_VOLUME, 'source-report.json'), + after: path.join(MEMFS_VOLUME, 'target-report.json'), }, { outputDir: MEMFS_VOLUME, filename: 'report', format: ['json', 'md'] }, { @@ -87,7 +87,7 @@ describe('compareReportFiles', () => { ); await expect( - readFile(join(MEMFS_VOLUME, 'report-diff.md'), 'utf8'), + readFile(path.join(MEMFS_VOLUME, 'report-diff.md'), 'utf8'), ).resolves.toContain( `[🕵️ See full comparison in Code PushUp portal 🔍](https://code-pushup.example.com/portal/dunder-mifflin/website/comparison/${commitShas.before}/${commitShas.after})`, ); @@ -109,15 +109,15 @@ describe('compareReportFiles', () => { it('should not include portal link in Markdown if upload config is missing', async () => { await compareReportFiles( { - before: join(MEMFS_VOLUME, 'source-report.json'), - after: join(MEMFS_VOLUME, 'target-report.json'), + before: path.join(MEMFS_VOLUME, 'source-report.json'), + after: path.join(MEMFS_VOLUME, 'target-report.json'), }, { outputDir: MEMFS_VOLUME, filename: 'report', format: ['json', 'md'] }, undefined, ); await expect( - readFile(join(MEMFS_VOLUME, 'report-diff.md'), 'utf8'), + readFile(path.join(MEMFS_VOLUME, 'report-diff.md'), 'utf8'), ).resolves.not.toContain( '[🕵️ See full comparison in Code PushUp portal 🔍]', ); @@ -138,8 +138,8 @@ describe('compareReportFiles', () => { ); await compareReportFiles( { - before: join(MEMFS_VOLUME, 'source-report.json'), - after: join(MEMFS_VOLUME, 'target-report.json'), + before: path.join(MEMFS_VOLUME, 'source-report.json'), + after: path.join(MEMFS_VOLUME, 'target-report.json'), }, { outputDir: MEMFS_VOLUME, filename: 'report', format: ['json', 'md'] }, { @@ -151,7 +151,7 @@ describe('compareReportFiles', () => { ); await expect( - readFile(join(MEMFS_VOLUME, 'report-diff.md'), 'utf8'), + readFile(path.join(MEMFS_VOLUME, 'report-diff.md'), 'utf8'), ).resolves.not.toContain( '[🕵️ See full comparison in Code PushUp portal 🔍]', ); @@ -162,8 +162,8 @@ describe('compareReportFiles', () => { it('should include portal link in JSON file', async () => { await compareReportFiles( { - before: join(MEMFS_VOLUME, 'source-report.json'), - after: join(MEMFS_VOLUME, 'target-report.json'), + before: path.join(MEMFS_VOLUME, 'source-report.json'), + after: path.join(MEMFS_VOLUME, 'target-report.json'), }, { outputDir: MEMFS_VOLUME, filename: 'report', format: ['json', 'md'] }, { @@ -175,7 +175,7 @@ describe('compareReportFiles', () => { ); await expect( - readJsonFile(join(MEMFS_VOLUME, 'report-diff.json')), + readJsonFile(path.join(MEMFS_VOLUME, 'report-diff.json')), ).resolves.toEqual( expect.objectContaining({ portalUrl: `https://code-pushup.example.com/portal/dunder-mifflin/website/comparison/${commitShas.before}/${commitShas.after}`, @@ -186,8 +186,8 @@ describe('compareReportFiles', () => { it('should include label in JSON file', async () => { await compareReportFiles( { - before: join(MEMFS_VOLUME, 'source-report.json'), - after: join(MEMFS_VOLUME, 'target-report.json'), + before: path.join(MEMFS_VOLUME, 'source-report.json'), + after: path.join(MEMFS_VOLUME, 'target-report.json'), }, { outputDir: MEMFS_VOLUME, filename: 'report', format: ['json', 'md'] }, undefined, @@ -195,7 +195,7 @@ describe('compareReportFiles', () => { ); await expect( - readJsonFile(join(MEMFS_VOLUME, 'report-diff.json')), + readJsonFile(path.join(MEMFS_VOLUME, 'report-diff.json')), ).resolves.toEqual( expect.objectContaining({ label: 'backoffice', diff --git a/packages/core/src/lib/history.unit.test.ts b/packages/core/src/lib/history.unit.test.ts index dec3a3d37..787ef5a51 100644 --- a/packages/core/src/lib/history.unit.test.ts +++ b/packages/core/src/lib/history.unit.test.ts @@ -31,6 +31,7 @@ describe('history', () => { }, plugins: [MINIMAL_PLUGIN_CONFIG_MOCK], }; + it('should check out all passed commits and reset to initial branch or tag', async () => { await history(historyBaseOptions, ['abc', 'def']); diff --git a/packages/core/src/lib/implementation/persist.ts b/packages/core/src/lib/implementation/persist.ts index 4a1a5ebdd..0d4c062c2 100644 --- a/packages/core/src/lib/implementation/persist.ts +++ b/packages/core/src/lib/implementation/persist.ts @@ -1,5 +1,5 @@ import { mkdir, stat, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import type { PersistConfig, Report } from '@code-pushup/models'; import { type MultipleFileResults, @@ -58,7 +58,7 @@ export async function persistReport( return Promise.allSettled( results.map(result => persistResult( - join(outputDir, `${filename}.${result.format}`), + path.join(outputDir, `${filename}.${result.format}`), result.content, ), ), diff --git a/packages/core/src/lib/implementation/persist.unit.test.ts b/packages/core/src/lib/implementation/persist.unit.test.ts index fc4cb70c5..d512a355a 100644 --- a/packages/core/src/lib/implementation/persist.unit.test.ts +++ b/packages/core/src/lib/implementation/persist.unit.test.ts @@ -1,6 +1,6 @@ import { vol } from 'memfs'; import { readFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { beforeEach, describe, expect, it } from 'vitest'; import type { Report } from '@code-pushup/models'; import { @@ -26,7 +26,7 @@ describe('persistReport', () => { }); const jsonReport: Report = JSON.parse( - await readFile(join(MEMFS_VOLUME, 'report.json'), 'utf8'), + await readFile(path.join(MEMFS_VOLUME, 'report.json'), 'utf8'), ); expect(jsonReport).toEqual( expect.objectContaining({ @@ -36,7 +36,7 @@ describe('persistReport', () => { ); await expect(() => - readFile(join(MEMFS_VOLUME, 'report.md')), + readFile(path.join(MEMFS_VOLUME, 'report.md')), ).rejects.toThrow('no such file or directory'); }); @@ -48,11 +48,14 @@ describe('persistReport', () => { format: ['md'], }); - const mdReport = await readFile(join(MEMFS_VOLUME, 'report.md'), 'utf8'); + const mdReport = await readFile( + path.join(MEMFS_VOLUME, 'report.md'), + 'utf8', + ); expect(mdReport).toContain('Code PushUp Report'); await expect(() => - readFile(join(MEMFS_VOLUME, 'report.json'), 'utf8'), + readFile(path.join(MEMFS_VOLUME, 'report.json'), 'utf8'), ).rejects.toThrow('no such file or directory'); }); @@ -64,14 +67,17 @@ describe('persistReport', () => { filename: 'report', }); - const mdReport = await readFile(join(MEMFS_VOLUME, 'report.md'), 'utf8'); + const mdReport = await readFile( + path.join(MEMFS_VOLUME, 'report.md'), + 'utf8', + ); expect(mdReport).toContain('Code PushUp Report'); expect(mdReport).toMatch( /\|\s*🏷 Category\s*\|\s*⭐ Score\s*\|\s*🛡 Audits\s*\|/, ); const jsonReport: Report = JSON.parse( - await readFile(join(MEMFS_VOLUME, 'report.json'), 'utf8'), + await readFile(path.join(MEMFS_VOLUME, 'report.json'), 'utf8'), ); expect(jsonReport).toEqual( expect.objectContaining({ diff --git a/packages/core/src/lib/implementation/read-rc-file.integration.test.ts b/packages/core/src/lib/implementation/read-rc-file.integration.test.ts index b6a86bd1a..78cf90400 100644 --- a/packages/core/src/lib/implementation/read-rc-file.integration.test.ts +++ b/packages/core/src/lib/implementation/read-rc-file.integration.test.ts @@ -1,11 +1,11 @@ -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect } from 'vitest'; import { ConfigValidationError, readRcByPath } from './read-rc-file.js'; describe('readRcByPath', () => { - const configDirPath = join( - fileURLToPath(dirname(import.meta.url)), + const configDirPath = path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', '..', '..', @@ -21,7 +21,7 @@ describe('readRcByPath', () => { it('should load the configuration', async () => { await expect( - readRcByPath(join(configDirPath, 'code-pushup.config.js')), + readRcByPath(path.join(configDirPath, 'code-pushup.config.js')), ).resolves.toEqual( expect.objectContaining({ upload: expect.objectContaining({ @@ -40,8 +40,8 @@ describe('readRcByPath', () => { it('should load the configuration using provided tsconfig', async () => { await expect( readRcByPath( - join(configDirPath, 'code-pushup.needs-tsconfig.config.ts'), - join(configDirPath, 'tsconfig.json'), + path.join(configDirPath, 'code-pushup.needs-tsconfig.config.ts'), + path.join(configDirPath, 'tsconfig.json'), ), ).resolves.toEqual({ plugins: [ @@ -62,19 +62,19 @@ describe('readRcByPath', () => { it('should throw if the file does not exist', async () => { await expect( - readRcByPath(join('non-existent', 'config.file.js')), + readRcByPath(path.join('non-existent', 'config.file.js')), ).rejects.toThrow(/Provided path .* is not valid./); }); it('should throw if the configuration is empty', async () => { await expect( - readRcByPath(join(configDirPath, 'code-pushup.empty.config.js')), + readRcByPath(path.join(configDirPath, 'code-pushup.empty.config.js')), ).rejects.toThrow(expect.any(ConfigValidationError)); }); it('should throw if the configuration is invalid', async () => { await expect( - readRcByPath(join(configDirPath, 'code-pushup.invalid.config.ts')), + readRcByPath(path.join(configDirPath, 'code-pushup.invalid.config.ts')), ).rejects.toThrow(/refs are duplicates/); }); }); diff --git a/packages/core/src/lib/implementation/read-rc-file.ts b/packages/core/src/lib/implementation/read-rc-file.ts index bb310f5db..3b1572c4c 100644 --- a/packages/core/src/lib/implementation/read-rc-file.ts +++ b/packages/core/src/lib/implementation/read-rc-file.ts @@ -1,5 +1,5 @@ import { bold } from 'ansis'; -import path, { join } from 'node:path'; +import path from 'node:path'; import { fromError, isZodErrorLike } from 'zod-validation-error'; import { CONFIG_FILE_NAME, @@ -75,7 +75,7 @@ export async function autoloadRc(tsconfig?: string): Promise { } return readRcByPath( - join(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`), + path.join(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`), tsconfig, ); } diff --git a/packages/core/src/lib/implementation/runner.ts b/packages/core/src/lib/implementation/runner.ts index 686b759a5..1cde8cae0 100644 --- a/packages/core/src/lib/implementation/runner.ts +++ b/packages/core/src/lib/implementation/runner.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import type { OnProgress, RunnerConfig, @@ -26,7 +26,7 @@ export async function executeRunnerConfig( }); // read process output from file system and parse it - const outputs = await readJsonFile(join(process.cwd(), outputFile)); + const outputs = await readJsonFile(path.join(process.cwd(), outputFile)); // transform unknownAuditOutputs to auditOutputs const audits = outputTransform ? await outputTransform(outputs) : outputs; diff --git a/packages/core/src/lib/merge-diffs.ts b/packages/core/src/lib/merge-diffs.ts index 3a49344c2..6470c059c 100644 --- a/packages/core/src/lib/merge-diffs.ts +++ b/packages/core/src/lib/merge-diffs.ts @@ -1,5 +1,5 @@ import { writeFile } from 'node:fs/promises'; -import { basename, dirname, join } from 'node:path'; +import path from 'node:path'; import { type PersistConfig, reportsDiffSchema } from '@code-pushup/models'; import { ensureDirectoryExists, @@ -42,13 +42,13 @@ export async function mergeDiffs( const labeledDiffs = diffs.map(diff => ({ ...diff, - label: diff.label || basename(dirname(diff.file)), // fallback is parent folder name + label: diff.label || path.basename(path.dirname(diff.file)), // fallback is parent folder name })); const markdown = generateMdReportsDiffForMonorepo(labeledDiffs); const { outputDir, filename } = persistConfig; - const outputPath = join(outputDir, `${filename}-diff.md`); + const outputPath = path.join(outputDir, `${filename}-diff.md`); await ensureDirectoryExists(outputDir); await writeFile(outputPath, markdown); diff --git a/packages/core/src/lib/merge-diffs.unit.test.ts b/packages/core/src/lib/merge-diffs.unit.test.ts index b4db7ad64..483f720c7 100644 --- a/packages/core/src/lib/merge-diffs.unit.test.ts +++ b/packages/core/src/lib/merge-diffs.unit.test.ts @@ -1,6 +1,6 @@ import { vol } from 'memfs'; import { readFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import type { PersistConfig } from '@code-pushup/models'; import { MEMFS_VOLUME, @@ -34,7 +34,7 @@ describe('mergeDiffs', () => { it('should create Markdown file', async () => { const outputPath = await mergeDiffs(files, persistConfig); - expect(outputPath).toBe(join(MEMFS_VOLUME, 'report-diff.md')); + expect(outputPath).toBe(path.join(MEMFS_VOLUME, 'report-diff.md')); await expect(fileExists(outputPath)).resolves.toBe(true); await expect(readFile(outputPath, 'utf8')).resolves.toContain( '# Code PushUp', @@ -62,7 +62,7 @@ describe('mergeDiffs', () => { [...files, 'missing-report-diff.json', 'invalid-report-diff.json'], persistConfig, ), - ).resolves.toBe(join(MEMFS_VOLUME, 'report-diff.md')); + ).resolves.toBe(path.join(MEMFS_VOLUME, 'report-diff.md')); expect(getLogMessages(ui().logger)).toEqual([ expect.stringContaining( diff --git a/packages/core/src/lib/normalize.unit.test.ts b/packages/core/src/lib/normalize.unit.test.ts index 74a194e45..05333203d 100644 --- a/packages/core/src/lib/normalize.unit.test.ts +++ b/packages/core/src/lib/normalize.unit.test.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { describe, expect } from 'vitest'; import type { AuditOutput, Issue } from '@code-pushup/models'; import { normalizeAuditOutputs, normalizeIssue } from './normalize.js'; @@ -56,10 +56,10 @@ describe('normalizeAuditOutputs', () => { }); it('should normalize audit details with issues that have a source specified', async () => { - const path = '/Users/user/Projects/myProject/utils/index.js'; + const file = '/Users/user/Projects/myProject/utils/index.js'; const issues = [ { source: undefined }, - { source: { file: path } }, + { source: { file } }, { source: undefined }, ] as Issue[]; await expect( @@ -84,11 +84,11 @@ describe('normalizeIssue', () => { message: 'file too big', severity: 'error', } as Issue; - expect(normalizeIssue(issue, join('User', 'code-pushup'))).toBe(issue); + expect(normalizeIssue(issue, path.join('User', 'code-pushup'))).toBe(issue); }); it('should normalize filepath in issue if source file is given', () => { - const path = '/myProject/utils/index.js'; + const file = '/myProject/utils/index.js'; const gitRoot = '/myProject'; expect( normalizeIssue( @@ -96,7 +96,7 @@ describe('normalizeIssue', () => { message: 'file too big', severity: 'error', source: { - file: path, + file, }, }, gitRoot, diff --git a/packages/create-cli/.eslintrc.json b/packages/create-cli/.eslintrc.json deleted file mode 100644 index f3f79d6fe..000000000 --- a/packages/create-cli/.eslintrc.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["packages/create-cli/tsconfig.*?.json"] - }, - "rules": {} - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": [ - "error", - { - "ignoredDependencies": ["@code-pushup/nx-plugin"] // nx-plugin is run via CLI - } - ] - } - } - ] -} diff --git a/packages/create-cli/eslint.config.js b/packages/create-cli/eslint.config.js new file mode 100644 index 000000000..22fda2e40 --- /dev/null +++ b/packages/create-cli/eslint.config.js @@ -0,0 +1,24 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': [ + 'error', + { ignoredDependencies: ['@code-pushup/nx-plugin'] }, // nx-plugin is run via CLI + ], + }, + }, +); diff --git a/packages/models/.eslintrc.json b/packages/models/.eslintrc.json deleted file mode 100644 index acec8532f..000000000 --- a/packages/models/.eslintrc.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": [ - "!**/*", - "*.config*", - "**/tools/**/*.ts", - "*.generated.ts" - ], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["packages/models/tsconfig.*?.json"] - } - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": ["error"] - } - } - ] -} diff --git a/packages/models/eslint.config.js b/packages/models/eslint.config.js new file mode 100644 index 000000000..40165321a --- /dev/null +++ b/packages/models/eslint.config.js @@ -0,0 +1,21 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': 'error', + }, + }, +); diff --git a/packages/nx-plugin/.env.lint.local b/packages/nx-plugin/.env.lint.local new file mode 100644 index 000000000..ae6151bc4 --- /dev/null +++ b/packages/nx-plugin/.env.lint.local @@ -0,0 +1 @@ +NODE_OPTIONS=--experimental-require-module \ No newline at end of file diff --git a/packages/nx-plugin/.eslintrc.json b/packages/nx-plugin/.eslintrc.json deleted file mode 100644 index 4c948d1bd..000000000 --- a/packages/nx-plugin/.eslintrc.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["packages/nx-plugin/tsconfig.*?.json"] - }, - "rules": { - // Nx plugins don't yet support ESM: https://github.com/nrwl/nx/issues/15682 - "unicorn/prefer-module": "off", - // used instead of verbatimModuleSyntax tsconfig flag (requires ESM) - "@typescript-eslint/consistent-type-imports": [ - "warn", - { - "fixStyle": "inline-type-imports", - "disallowTypeAnnotations": false - } - ], - "@typescript-eslint/consistent-type-exports": [ - "warn", - { "fixMixedExportsWithInlineTypeSpecifier": true } - ] - } - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": "error" - } - }, - { - "files": ["./package.json", "./generators.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/nx-plugin-checks": "error" - } - } - ] -} diff --git a/packages/nx-plugin/eslint.config.cjs b/packages/nx-plugin/eslint.config.cjs new file mode 100644 index 000000000..e732748e6 --- /dev/null +++ b/packages/nx-plugin/eslint.config.cjs @@ -0,0 +1,51 @@ +const tseslint = require('typescript-eslint'); +const baseConfig = require('../../eslint.config.js').default; + +module.exports = tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: __dirname, + }, + }, + }, + { + files: ['**/*.ts'], + rules: { + // Nx plugins don't yet support ESM: https://github.com/nrwl/nx/issues/15682 + 'unicorn/prefer-module': 'off', + // used instead of verbatimModuleSyntax tsconfig flag (requires ESM) + '@typescript-eslint/consistent-type-imports': [ + 'warn', + { + fixStyle: 'inline-type-imports', + disallowTypeAnnotations: false, + }, + ], + '@typescript-eslint/consistent-type-exports': [ + 'warn', + { fixMixedExportsWithInlineTypeSpecifier: true }, + ], + // `import path from 'node:path'` incompatible with CJS runtime, prefer `import * as path from 'node:path'` + 'unicorn/import-style': [ + 'warn', + { styles: { 'node:path': { namespace: true } } }, + ], + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': 'error', + }, + }, + { + files: ['**/package.json', '**/generators.json'], + rules: { + '@nx/nx-plugin-checks': 'error', + }, + }, +); diff --git a/packages/nx-plugin/src/executors/cli/executor.integration.test.ts b/packages/nx-plugin/src/executors/cli/executor.integration.test.ts index 947e7c1fa..a68c9d78f 100644 --- a/packages/nx-plugin/src/executors/cli/executor.integration.test.ts +++ b/packages/nx-plugin/src/executors/cli/executor.integration.test.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line n/no-sync import { execSync } from 'node:child_process'; import { afterEach, expect, vi } from 'vitest'; import { executorContext } from '@code-pushup/test-nx-utils'; @@ -9,7 +8,6 @@ vi.mock('node:child_process', async () => { const actual = await vi.importActual('node:child_process'); return { ...actual, - // eslint-disable-next-line n/no-sync execSync: vi.fn(), }; }); @@ -19,9 +17,11 @@ describe('runAutorunExecutor', () => { utils, 'parseAutorunExecutorOptions', ); + afterEach(() => { parseAutorunExecutorOptionsSpy.mockReset(); }); + it('should normalize context, parse CLI options and execute command', async () => { const output = await runAutorunExecutor( { verbose: true }, diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index 39ac9575c..76d88e21b 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -1,5 +1,4 @@ import { type ExecutorContext, logger } from '@nx/devkit'; -// eslint-disable-next-line n/no-sync import { execSync } from 'node:child_process'; import { createCliCommand } from '../internal/cli.js'; import { normalizeContext } from '../internal/context.js'; diff --git a/packages/nx-plugin/src/executors/cli/executor.unit.test.ts b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts index 14827fb57..e538a470c 100644 --- a/packages/nx-plugin/src/executors/cli/executor.unit.test.ts +++ b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts @@ -1,5 +1,4 @@ import { logger } from '@nx/devkit'; -// eslint-disable-next-line n/no-sync import { execSync } from 'node:child_process'; import { afterEach, beforeEach, expect, vi } from 'vitest'; import { executorContext } from '@code-pushup/test-nx-utils'; @@ -10,7 +9,6 @@ vi.mock('node:child_process', async () => { return { ...actual, - // eslint-disable-next-line n/no-sync execSync: vi.fn((command: string) => { if (command.includes('THROW_ERROR')) { throw new Error(command); @@ -27,6 +25,7 @@ describe('runAutorunExecutor', () => { beforeEach(() => { envSpy.mockReturnValue({}); }); + afterEach(() => { loggerWarnSpy.mockReset(); loggerInfoSpy.mockReset(); diff --git a/packages/nx-plugin/src/executors/cli/utils.unit.test.ts b/packages/nx-plugin/src/executors/cli/utils.unit.test.ts index 67b3a0b7b..a37ac6732 100644 --- a/packages/nx-plugin/src/executors/cli/utils.unit.test.ts +++ b/packages/nx-plugin/src/executors/cli/utils.unit.test.ts @@ -46,6 +46,7 @@ describe('parseAutorunExecutorOnlyOptions', () => { describe('parseAutorunExecutorOptions', () => { let processEnvSpy: MockInstance<[], NodeJS.ProcessEnv>; + beforeAll(() => { processEnvSpy = vi.spyOn(process, 'env', 'get').mockReturnValue({}); }); diff --git a/packages/nx-plugin/src/executors/internal/cli.ts b/packages/nx-plugin/src/executors/internal/cli.ts index 82e3153d5..b733eec84 100644 --- a/packages/nx-plugin/src/executors/internal/cli.ts +++ b/packages/nx-plugin/src/executors/internal/cli.ts @@ -12,11 +12,10 @@ export function createCliCommand(options?: { type ArgumentValue = number | string | boolean | string[]; export type CliArgsObject> = T extends never - ? // eslint-disable-next-line @typescript-eslint/naming-convention - Record | { _: string } + ? Record | { _: string } : T; + // @TODO import from @code-pushup/utils => get rid of poppins for cjs support -// eslint-disable-next-line sonarjs/cognitive-complexity export function objectToCliArgs< T extends object = Record, >(params?: CliArgsObject): string[] { @@ -24,11 +23,9 @@ export function objectToCliArgs< return []; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return Object.entries(params).flatMap(([key, value]) => { // process/file/script if (key === '_') { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return (Array.isArray(value) ? value : [`${value}`]).filter( v => v != null, ); diff --git a/packages/nx-plugin/src/executors/internal/config.ts b/packages/nx-plugin/src/executors/internal/config.ts index 4f3829963..0eb13f8a8 100644 --- a/packages/nx-plugin/src/executors/internal/config.ts +++ b/packages/nx-plugin/src/executors/internal/config.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import * as path from 'node:path'; import type { PersistConfig, UploadConfig } from '@code-pushup/models'; import { parseEnv } from './env.js'; import type { @@ -18,7 +18,7 @@ export function globalConfig( return { verbose: !!verbose, progress: !!progress, - config: config ?? join(projectRoot, 'code-pushup.config.ts'), + config: config ?? path.join(projectRoot, 'code-pushup.config.ts'), }; } @@ -31,7 +31,7 @@ export function persistConfig( const { name: projectName = '' } = projectConfig ?? {}; const { format, - outputDir = join(workspaceRoot, '.code-pushup', projectName), // always in /.code-pushup/, + outputDir = path.join(workspaceRoot, '.code-pushup', projectName), // always in /.code-pushup/, filename, } = options; diff --git a/packages/nx-plugin/src/executors/internal/config.unit.test.ts b/packages/nx-plugin/src/executors/internal/config.unit.test.ts index b1229e8f0..c38554459 100644 --- a/packages/nx-plugin/src/executors/internal/config.unit.test.ts +++ b/packages/nx-plugin/src/executors/internal/config.unit.test.ts @@ -253,9 +253,11 @@ describe('uploadConfig', () => { }; let processEnvSpy: MockInstance<[], NodeJS.ProcessEnv>; + beforeAll(() => { processEnvSpy = vi.spyOn(process, 'env', 'get').mockReturnValue({}); }); + afterAll(() => { processEnvSpy.mockRestore(); }); diff --git a/packages/nx-plugin/src/executors/internal/context.unit.test.ts b/packages/nx-plugin/src/executors/internal/context.unit.test.ts index a1995da5a..9be17d2a9 100644 --- a/packages/nx-plugin/src/executors/internal/context.unit.test.ts +++ b/packages/nx-plugin/src/executors/internal/context.unit.test.ts @@ -9,7 +9,7 @@ describe('normalizeContext', () => { cwd: 'string', projectsConfigurations: { projects: { - ['my-app']: { + 'my-app': { root: './my-app', }, }, diff --git a/packages/nx-plugin/src/executors/internal/env.ts b/packages/nx-plugin/src/executors/internal/env.ts index bdc2c3e0f..16806a09e 100644 --- a/packages/nx-plugin/src/executors/internal/env.ts +++ b/packages/nx-plugin/src/executors/internal/env.ts @@ -16,7 +16,6 @@ type UploadEnvVars = z.infer; export function parseEnv(env: unknown = {}): Partial { const upload: UploadEnvVars = envSchema.parse(env); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return Object.fromEntries( Object.entries(upload).map(([envKey, value]) => { switch (envKey) { diff --git a/packages/nx-plugin/src/generators/configuration/code-pushup-config.integration.test.ts b/packages/nx-plugin/src/generators/configuration/code-pushup-config.integration.test.ts index 5a9734e79..38238ff50 100644 --- a/packages/nx-plugin/src/generators/configuration/code-pushup-config.integration.test.ts +++ b/packages/nx-plugin/src/generators/configuration/code-pushup-config.integration.test.ts @@ -1,14 +1,14 @@ import * as devKit from '@nx/devkit'; import { formatFiles } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; -import { join } from 'node:path'; +import * as path from 'node:path'; import { describe, expect, it } from 'vitest'; import { generateCodePushupConfig } from './code-pushup-config.js'; describe('generateCodePushupConfig options', () => { let tree: devKit.Tree; const project = 'test-app'; - const projectRoot = join('libs', project); + const projectRoot = path.join('libs', project); beforeEach(() => { tree = createTreeWithEmptyWorkspace(); @@ -37,9 +37,9 @@ describe('generateCodePushupConfig options', () => { }); await formatFiles(tree); - // eslint-disable-next-line @typescript-eslint/no-floating-promises + expect( - tree.read(join(projectRoot, 'code-pushup.config.ts'))?.toString(), + tree.read(path.join(projectRoot, 'code-pushup.config.ts'))?.toString(), ).toMatchFileSnapshot('__snapshots__/root-code-pushup.config.ts'); }); }); diff --git a/packages/nx-plugin/src/generators/configuration/code-pushup-config.ts b/packages/nx-plugin/src/generators/configuration/code-pushup-config.ts index 5ffac4578..6854797fb 100644 --- a/packages/nx-plugin/src/generators/configuration/code-pushup-config.ts +++ b/packages/nx-plugin/src/generators/configuration/code-pushup-config.ts @@ -1,5 +1,5 @@ import { type Tree, generateFiles, logger } from '@nx/devkit'; -import { join } from 'node:path'; +import * as path from 'node:path'; import type { PersistConfig, UploadConfig } from '@code-pushup/models'; import type { ItemOrArray } from '@code-pushup/utils'; import type { ExecutableCode } from './types.js'; @@ -29,7 +29,7 @@ export function generateCodePushupConfig( ) { const supportedFormats = ['ts', 'mjs', 'js']; const firstExistingFormat = supportedFormats.find(ext => - tree.exists(join(root, `code-pushup.config.${ext}`)), + tree.exists(path.join(root, `code-pushup.config.${ext}`)), ); if (firstExistingFormat) { logger.warn( @@ -52,7 +52,7 @@ export function generateCodePushupConfig( ...(categories ?? []).flatMap(({ fileImports }) => fileImports), ]; - generateFiles(tree, join(__dirname, 'files'), root, { + generateFiles(tree, path.join(__dirname, 'files'), root, { ...options, fileImports: formatArrayToLinesOfJsString(configFileImports), persist: formatObjectToFormattedJsString(persist), diff --git a/packages/nx-plugin/src/generators/configuration/code-pushup-config.unit.test.ts b/packages/nx-plugin/src/generators/configuration/code-pushup-config.unit.test.ts index 574b01623..4cd74d681 100644 --- a/packages/nx-plugin/src/generators/configuration/code-pushup-config.unit.test.ts +++ b/packages/nx-plugin/src/generators/configuration/code-pushup-config.unit.test.ts @@ -1,6 +1,6 @@ import * as devKit from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; -import { join } from 'node:path'; +import * as path from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; import { removeColorCodes } from '@code-pushup/test-utils'; import { @@ -74,7 +74,7 @@ describe('generateCodePushupConfig options', () => { }); it('should skip creation if config already exists', () => { - tree.write(join(testProjectName, 'code-pushup.config.js'), ''); + tree.write(path.join(testProjectName, 'code-pushup.config.js'), ''); generateCodePushupConfig(tree, testProjectName); expect(generateFilesSpy).toHaveBeenCalledTimes(0); expect(loggerWarnSpy).toHaveBeenCalledTimes(1); @@ -90,7 +90,7 @@ describe('generateCodePushupConfig options', () => { expect(generateFilesSpy).toHaveBeenCalledWith( expect.anything(), expect.stringContaining( - join('nx-plugin', 'src', 'generators', 'configuration', 'files'), + path.join('nx-plugin', 'src', 'generators', 'configuration', 'files'), ), expect.any(String), expect.any(Object), diff --git a/packages/nx-plugin/src/generators/configuration/generator.integration.test.ts b/packages/nx-plugin/src/generators/configuration/generator.integration.test.ts index 032632e04..c98d60064 100644 --- a/packages/nx-plugin/src/generators/configuration/generator.integration.test.ts +++ b/packages/nx-plugin/src/generators/configuration/generator.integration.test.ts @@ -5,7 +5,7 @@ import { readProjectConfiguration, } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; -import { join } from 'node:path'; +import * as path from 'node:path'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { DEFAULT_TARGET_NAME, PACKAGE_NAME } from '../../internal/constants.js'; import { addTargetToProject, configurationGenerator } from './generator.js'; @@ -20,6 +20,7 @@ describe('addTargetToProject', () => { root: 'test-app', }); }); + afterEach(() => { //reset tree tree.delete(testProjectName); @@ -115,7 +116,7 @@ describe('configurationGenerator', () => { readProjectConfiguration(tree, testProjectName); expect( - tree.read(join('libs', testProjectName, 'code-pushup.config.ts')), + tree.read(path.join('libs', testProjectName, 'code-pushup.config.ts')), ).toBeNull(); expect(loggerInfoSpy).toHaveBeenCalledWith('Skip config file creation'); }); diff --git a/packages/nx-plugin/src/generators/configuration/utils.ts b/packages/nx-plugin/src/generators/configuration/utils.ts index 01de15e82..36464358a 100644 --- a/packages/nx-plugin/src/generators/configuration/utils.ts +++ b/packages/nx-plugin/src/generators/configuration/utils.ts @@ -32,7 +32,7 @@ export function formatObjectToFormattedJsString( | { [key: string]: unknown; } - | Array, + | unknown[], ): string | undefined { if (!jsonObj) { return; diff --git a/packages/nx-plugin/src/internal/versions.ts b/packages/nx-plugin/src/internal/versions.ts index fa4003b33..884dad9a7 100644 --- a/packages/nx-plugin/src/internal/versions.ts +++ b/packages/nx-plugin/src/internal/versions.ts @@ -1,21 +1,21 @@ import { readJsonFile } from '@nx/devkit'; -import { join } from 'node:path'; +import * as path from 'node:path'; import type { PackageJson } from 'nx/src/utils/package-json'; -const workspaceRoot = join(__dirname, '../../'); -const projectsFolder = join(__dirname, '../../../'); +const workspaceRoot = path.join(__dirname, '../../'); +const projectsFolder = path.join(__dirname, '../../../'); export const cpNxPluginVersion = loadPackageJson(workspaceRoot).version; export const cpModelVersion = loadPackageJson( - join(projectsFolder, 'cli'), + path.join(projectsFolder, 'cli'), ).version; export const cpUtilsVersion = loadPackageJson( - join(projectsFolder, 'utils'), + path.join(projectsFolder, 'utils'), ).version; export const cpCliVersion = loadPackageJson( - join(projectsFolder, 'models'), + path.join(projectsFolder, 'models'), ).version; function loadPackageJson(folderPath: string): PackageJson { - return readJsonFile(join(folderPath, 'package.json')); + return readJsonFile(path.join(folderPath, 'package.json')); } diff --git a/packages/nx-plugin/src/plugin/plugin.ts b/packages/nx-plugin/src/plugin/plugin.ts index 8d43a570d..9129f1bd7 100644 --- a/packages/nx-plugin/src/plugin/plugin.ts +++ b/packages/nx-plugin/src/plugin/plugin.ts @@ -1,5 +1,4 @@ import type { - // eslint-disable-next-line import/no-deprecated CreateNodes, CreateNodesContext, CreateNodesResult, @@ -10,7 +9,6 @@ import type { CreateNodesOptions } from './types.js'; import { normalizedCreateNodesContext } from './utils.js'; // name has to be "createNodes" to get picked up by Nx -// eslint-disable-next-line import/no-deprecated,deprecation/deprecation export const createNodes: CreateNodes = [ `**/${PROJECT_JSON_FILE_NAME}`, async ( diff --git a/packages/nx-plugin/src/plugin/utils.ts b/packages/nx-plugin/src/plugin/utils.ts index 1e2ce4863..e7a819f8d 100644 --- a/packages/nx-plugin/src/plugin/utils.ts +++ b/packages/nx-plugin/src/plugin/utils.ts @@ -1,6 +1,6 @@ import type { CreateNodesContext } from '@nx/devkit'; import { readFile } from 'node:fs/promises'; -import { dirname } from 'node:path'; +import * as path from 'node:path'; import { CP_TARGET_NAME } from './constants.js'; import type { CreateNodesOptions, @@ -13,7 +13,7 @@ export async function normalizedCreateNodesContext( projectConfigurationFile: string, createOptions: CreateNodesOptions = {}, ): Promise { - const projectRoot = dirname(projectConfigurationFile); + const projectRoot = path.dirname(projectConfigurationFile); try { const projectJson = JSON.parse( diff --git a/packages/plugin-coverage/.eslintrc.json b/packages/plugin-coverage/.eslintrc.json deleted file mode 100644 index c5d86e86a..000000000 --- a/packages/plugin-coverage/.eslintrc.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["packages/plugin-coverage/tsconfig.*?.json"] - } - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": ["error"] - } - } - ] -} diff --git a/packages/plugin-coverage/eslint.config.js b/packages/plugin-coverage/eslint.config.js new file mode 100644 index 000000000..40165321a --- /dev/null +++ b/packages/plugin-coverage/eslint.config.js @@ -0,0 +1,21 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': 'error', + }, + }, +); diff --git a/packages/plugin-coverage/src/lib/coverage-plugin.ts b/packages/plugin-coverage/src/lib/coverage-plugin.ts index 0dc005f58..72e037d0b 100644 --- a/packages/plugin-coverage/src/lib/coverage-plugin.ts +++ b/packages/plugin-coverage/src/lib/coverage-plugin.ts @@ -1,5 +1,5 @@ import { createRequire } from 'node:module'; -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import type { Audit, Group, PluginConfig } from '@code-pushup/models'; import { capitalize } from '@code-pushup/utils'; @@ -55,8 +55,8 @@ export async function coveragePlugin( })), }; - const runnerScriptPath = join( - fileURLToPath(dirname(import.meta.url)), + const runnerScriptPath = path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', 'bin.js', ); diff --git a/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts b/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts index 59be14780..cfa60083d 100644 --- a/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts +++ b/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { describe, expect, it } from 'vitest'; import type { RunnerConfig } from '@code-pushup/models'; import { coveragePlugin } from './coverage-plugin.js'; @@ -11,7 +11,7 @@ vi.mock('./runner/index.ts', () => ({ })); describe('coveragePlugin', () => { - const LCOV_PATH = join( + const LCOV_PATH = path.join( 'packages', 'plugin-coverage', 'mocks', diff --git a/packages/plugin-coverage/src/lib/nx/coverage-paths.ts b/packages/plugin-coverage/src/lib/nx/coverage-paths.ts index d8f24f285..977b10ea5 100644 --- a/packages/plugin-coverage/src/lib/nx/coverage-paths.ts +++ b/packages/plugin-coverage/src/lib/nx/coverage-paths.ts @@ -7,7 +7,7 @@ import type { import type { JestExecutorOptions } from '@nx/jest/src/executors/jest/schema'; import type { VitestExecutorOptions } from '@nx/vite/executors'; import { bold } from 'ansis'; -import { isAbsolute, join } from 'node:path'; +import path from 'node:path'; import { importModule, ui } from '@code-pushup/utils'; import type { CoverageResult } from '../config.js'; @@ -117,7 +117,7 @@ export async function getCoveragePathForVitest( } = await import('@nx/vite'); const config = normalizeViteConfigFilePathWithTree( // HACK: only tree.exists is called, so injecting existSync from node:fs instead - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, n/no-sync + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions { exists: (await import('node:fs')).existsSync } as Tree, project.root, options.configFile, @@ -149,12 +149,12 @@ export async function getCoveragePathForVitest( ); } - if (isAbsolute(reportsDirectory)) { - return join(reportsDirectory, 'lcov.info'); + if (path.isAbsolute(reportsDirectory)) { + return path.join(reportsDirectory, 'lcov.info'); } return { pathToProject: project.root, - resultsPath: join(project.root, reportsDirectory, 'lcov.info'), + resultsPath: path.join(project.root, reportsDirectory, 'lcov.info'), }; } @@ -185,8 +185,8 @@ export async function getCoveragePathForJest( ); } - if (isAbsolute(coverageDirectory)) { - return join(coverageDirectory, 'lcov.info'); + if (path.isAbsolute(coverageDirectory)) { + return path.join(coverageDirectory, 'lcov.info'); } - return join(project.root, coverageDirectory, 'lcov.info'); + return path.join(project.root, coverageDirectory, 'lcov.info'); } diff --git a/packages/plugin-coverage/src/lib/nx/coverage-paths.unit.test.ts b/packages/plugin-coverage/src/lib/nx/coverage-paths.unit.test.ts index 10605d55e..8d57dba34 100644 --- a/packages/plugin-coverage/src/lib/nx/coverage-paths.unit.test.ts +++ b/packages/plugin-coverage/src/lib/nx/coverage-paths.unit.test.ts @@ -1,7 +1,7 @@ import type { JestExecutorOptions } from '@nx/jest/src/executors/jest/schema'; import type { VitestExecutorOptions } from '@nx/vite/executors'; import { vol } from 'memfs'; -import { join } from 'node:path'; +import path from 'node:path'; import { describe, expect, it } from 'vitest'; import { MEMFS_VOLUME } from '@code-pushup/test-utils'; import type { CoverageResult } from '../config.js'; @@ -19,7 +19,7 @@ vi.mock('bundle-require', () => ({ test: { coverage: { reporter: ['lcov'], - reportsDirectory: join('coverage', 'cli'), + reportsDirectory: path.join('coverage', 'cli'), }, }, }; @@ -39,7 +39,7 @@ vi.mock('bundle-require', () => ({ const JEST_VALID: JestCoverageConfig = { coverageReporters: ['lcov'], - coverageDirectory: join('coverage', 'core'), + coverageDirectory: path.join('coverage', 'core'), }; const JEST_NO_DIR: JestCoverageConfig = { @@ -99,7 +99,7 @@ describe('getCoveragePathForTarget', () => { getCoveragePathsForTarget( { name: 'cli', - root: join('packages', 'cli'), + root: path.join('packages', 'cli'), targets: { test: { executor: '@nx/vite:test', @@ -112,8 +112,8 @@ describe('getCoveragePathForTarget', () => { 'test', ), ).resolves.toEqual({ - pathToProject: join('packages', 'cli'), - resultsPath: join('packages', 'cli', 'coverage', 'cli', 'lcov.info'), + pathToProject: path.join('packages', 'cli'), + resultsPath: path.join('packages', 'cli', 'coverage', 'cli', 'lcov.info'), }); }); @@ -122,7 +122,7 @@ describe('getCoveragePathForTarget', () => { getCoveragePathsForTarget( { name: 'core', - root: join('packages', 'core'), + root: path.join('packages', 'core'), targets: { test: { executor: '@nx/jest:jest', @@ -134,7 +134,9 @@ describe('getCoveragePathForTarget', () => { }, 'test', ), - ).resolves.toBe(join('packages', 'core', 'coverage', 'core', 'lcov.info')); + ).resolves.toBe( + path.join('packages', 'core', 'coverage', 'core', 'lcov.info'), + ); }); it('should throw for unsupported executor (only vitest and jest are supported)', async () => { @@ -142,7 +144,7 @@ describe('getCoveragePathForTarget', () => { getCoveragePathsForTarget( { name: 'ui', - root: join('apps', 'ui'), + root: path.join('apps', 'ui'), targets: { 'component-test': { executor: '@nx/cypress', @@ -173,12 +175,12 @@ describe('getCoveragePathForVitest', () => { await expect( getCoveragePathForVitest( { configFile: 'vitest-valid.config.unit.ts' }, - { name: 'cli', root: join('packages', 'cli') }, + { name: 'cli', root: path.join('packages', 'cli') }, 'unit-test', ), ).resolves.toEqual({ - pathToProject: join('packages', 'cli'), - resultsPath: join('packages', 'cli', 'coverage', 'cli', 'lcov.info'), + pathToProject: path.join('packages', 'cli'), + resultsPath: path.join('packages', 'cli', 'coverage', 'cli', 'lcov.info'), } satisfies CoverageResult); }); @@ -186,7 +188,7 @@ describe('getCoveragePathForVitest', () => { await expect(() => getCoveragePathForVitest( { configFile: 'vitest-no-dir.config.integration.ts' }, - { name: 'cli', root: join('packages', 'cli') }, + { name: 'cli', root: path.join('packages', 'cli') }, 'integration-test', ), ).rejects.toThrow( @@ -199,7 +201,7 @@ describe('getCoveragePathForVitest', () => { getCoveragePathForVitest( { configFile: 'vitest-valid.config.unit.ts', - reportsDirectory: join( + reportsDirectory: path.join( '..', '..', 'dist', @@ -208,12 +210,18 @@ describe('getCoveragePathForVitest', () => { 'coverage', ), }, - { name: 'cli', root: join('packages', 'cli') }, + { name: 'cli', root: path.join('packages', 'cli') }, 'unit-test', ), ).resolves.toEqual({ - pathToProject: join('packages', 'cli'), - resultsPath: join('dist', 'packages', 'cli', 'coverage', 'lcov.info'), + pathToProject: path.join('packages', 'cli'), + resultsPath: path.join( + 'dist', + 'packages', + 'cli', + 'coverage', + 'lcov.info', + ), } satisfies CoverageResult); }); @@ -221,7 +229,7 @@ describe('getCoveragePathForVitest', () => { await expect(() => getCoveragePathForVitest( { configFile: 'vitest-no-lcov.config.integration.ts' }, - { name: 'core', root: join('packages', 'core') }, + { name: 'core', root: path.join('packages', 'core') }, 'integration-test', ), ).rejects.toThrow(/configuration .* does not include LCOV report format/); @@ -232,13 +240,18 @@ describe('getCoveragePathForVitest', () => { getCoveragePathForVitest( { configFile: 'vitest-valid.config.unit.ts', - reportsDirectory: join(process.cwd(), 'coverage', 'packages', 'cli'), + reportsDirectory: path.join( + process.cwd(), + 'coverage', + 'packages', + 'cli', + ), }, - { name: 'cli', root: join('packages', 'cli') }, + { name: 'cli', root: path.join('packages', 'cli') }, 'unit-test', ), ).resolves.toBe( - join(process.cwd(), 'coverage', 'packages', 'cli', 'lcov.info'), + path.join(process.cwd(), 'coverage', 'packages', 'cli', 'lcov.info'), ); }); }); @@ -260,17 +273,19 @@ describe('getCoveragePathForJest', () => { await expect( getCoveragePathForJest( { jestConfig: 'jest-valid.config.unit.ts' }, - { name: 'cli', root: join('packages', 'cli') }, + { name: 'cli', root: path.join('packages', 'cli') }, 'unit-test', ), - ).resolves.toBe(join('packages', 'cli', 'coverage', 'core', 'lcov.info')); + ).resolves.toBe( + path.join('packages', 'cli', 'coverage', 'core', 'lcov.info'), + ); }); it('should throw when coverageDirectory is not set in Jest config', async () => { await expect(() => getCoveragePathForJest( { jestConfig: 'jest-no-dir.config.integration.ts' }, - { name: 'core', root: join('packages', 'core') }, + { name: 'core', root: path.join('packages', 'core') }, 'integration-test', ), ).rejects.toThrow( @@ -283,7 +298,7 @@ describe('getCoveragePathForJest', () => { getCoveragePathForJest( { jestConfig: 'jest-valid.config.unit.ts', - coverageDirectory: join( + coverageDirectory: path.join( '..', '..', 'dist', @@ -292,17 +307,19 @@ describe('getCoveragePathForJest', () => { 'coverage', ), }, - { name: 'cli', root: join('packages', 'cli') }, + { name: 'cli', root: path.join('packages', 'cli') }, 'unit-test', ), - ).resolves.toBe(join('dist', 'packages', 'cli', 'coverage', 'lcov.info')); + ).resolves.toBe( + path.join('dist', 'packages', 'cli', 'coverage', 'lcov.info'), + ); }); it('should throw when Jest config does not include lcov reporter', async () => { await expect(() => getCoveragePathForJest( { jestConfig: 'jest-no-lcov.config.integration.ts' }, - { name: 'core', root: join('packages', 'core') }, + { name: 'core', root: path.join('packages', 'core') }, 'integration-test', ), ).rejects.toThrow(/configuration .* does not include LCOV report format/); @@ -315,7 +332,7 @@ describe('getCoveragePathForJest', () => { jestConfig: 'jest-no-lcov.config.integration.ts', coverageReporters: ['lcov'], }, - { name: 'core', root: join('packages', 'core') }, + { name: 'core', root: path.join('packages', 'core') }, 'integration-test', ), ).resolves.toBeTypeOf('string'); @@ -328,7 +345,7 @@ describe('getCoveragePathForJest', () => { jestConfig: 'jest-valid.config.integration.ts', coverageReporters: ['text', 'html'], }, - { name: 'core', root: join('packages', 'core') }, + { name: 'core', root: path.join('packages', 'core') }, 'integration-test', ), ).rejects.toThrow(/configuration .* does not include LCOV report format/); @@ -341,7 +358,7 @@ describe('getCoveragePathForJest', () => { jestConfig: 'jest-valid.config.integration.ts', coverageReporters: ['lcov'], }, - { name: 'core', root: join('packages', 'core') }, + { name: 'core', root: path.join('packages', 'core') }, 'integration-test', ), ).resolves.toBeTypeOf('string'); @@ -351,7 +368,7 @@ describe('getCoveragePathForJest', () => { await expect( getCoveragePathForJest( { jestConfig: 'jest-preset.config.ts' }, - { name: 'core', root: join('packages', 'core') }, + { name: 'core', root: path.join('packages', 'core') }, 'test', ), ).resolves.toBeTypeOf('string'); @@ -362,13 +379,18 @@ describe('getCoveragePathForJest', () => { getCoveragePathForJest( { jestConfig: 'jest-valid.config.unit.ts', - coverageDirectory: join(process.cwd(), 'coverage', 'packages', 'cli'), + coverageDirectory: path.join( + process.cwd(), + 'coverage', + 'packages', + 'cli', + ), }, - { name: 'cli', root: join('packages', 'cli') }, + { name: 'cli', root: path.join('packages', 'cli') }, 'unit-test', ), ).resolves.toBe( - join(process.cwd(), 'coverage', 'packages', 'cli', 'lcov.info'), + path.join(process.cwd(), 'coverage', 'packages', 'cli', 'lcov.info'), ); }); }); diff --git a/packages/plugin-coverage/src/lib/runner/constants.ts b/packages/plugin-coverage/src/lib/runner/constants.ts index 3b6a925e0..ea888685e 100644 --- a/packages/plugin-coverage/src/lib/runner/constants.ts +++ b/packages/plugin-coverage/src/lib/runner/constants.ts @@ -1,9 +1,9 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { pluginWorkDir } from '@code-pushup/utils'; export const WORKDIR = pluginWorkDir('coverage'); -export const RUNNER_OUTPUT_PATH = join(WORKDIR, 'runner-output.json'); -export const PLUGIN_CONFIG_PATH = join( +export const RUNNER_OUTPUT_PATH = path.join(WORKDIR, 'runner-output.json'); +export const PLUGIN_CONFIG_PATH = path.join( process.cwd(), WORKDIR, 'plugin-config.json', diff --git a/packages/plugin-coverage/src/lib/runner/index.ts b/packages/plugin-coverage/src/lib/runner/index.ts index 07b23b82a..89710e224 100644 --- a/packages/plugin-coverage/src/lib/runner/index.ts +++ b/packages/plugin-coverage/src/lib/runner/index.ts @@ -1,6 +1,6 @@ import { bold } from 'ansis'; import { writeFile } from 'node:fs/promises'; -import { dirname } from 'node:path'; +import path from 'node:path'; import type { AuditOutputs, RunnerConfig } from '@code-pushup/models'; import { ProcessError, @@ -41,7 +41,7 @@ export async function executeRunner(): Promise { // Calculate coverage from LCOV results const auditOutputs = await lcovResultsToAuditOutputs(reports, coverageTypes); - await ensureDirectoryExists(dirname(RUNNER_OUTPUT_PATH)); + await ensureDirectoryExists(path.dirname(RUNNER_OUTPUT_PATH)); await writeFile(RUNNER_OUTPUT_PATH, JSON.stringify(auditOutputs)); } @@ -50,7 +50,7 @@ export async function createRunnerConfig( config: FinalCoveragePluginConfig, ): Promise { // Create JSON config for executeRunner - await ensureDirectoryExists(dirname(PLUGIN_CONFIG_PATH)); + await ensureDirectoryExists(path.dirname(PLUGIN_CONFIG_PATH)); await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config)); const threshold = config.perfectScoreThreshold; diff --git a/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.integration.test.ts b/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.integration.test.ts index 21d706e1d..a151c3534 100644 --- a/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.integration.test.ts +++ b/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.integration.test.ts @@ -1,4 +1,4 @@ -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import { osAgnosticAuditOutputs } from '@code-pushup/test-utils'; @@ -15,8 +15,8 @@ describe('lcovResultsToAuditOutputs', () => { const results = await lcovResultsToAuditOutputs( [ { - resultsPath: join( - fileURLToPath(dirname(import.meta.url)), + resultsPath: path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', '..', '..', @@ -36,8 +36,8 @@ describe('lcovResultsToAuditOutputs', () => { const results = await lcovResultsToAuditOutputs( [ { - resultsPath: join( - fileURLToPath(dirname(import.meta.url)), + resultsPath: path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', '..', '..', diff --git a/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.ts b/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.ts index e0bad2f08..3a5a2c447 100644 --- a/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.ts +++ b/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import type { LCOVRecord } from 'parse-lcov'; import type { AuditOutputs } from '@code-pushup/models'; import { exists, readTextFile, toUnixNewlines, ui } from '@code-pushup/utils'; @@ -71,7 +71,7 @@ export async function parseLcovFiles( file: typeof result === 'string' || result.pathToProject == null ? record.file - : join(result.pathToProject, record.file), + : path.join(result.pathToProject, record.file), })); }), ); diff --git a/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.unit.test.ts b/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.unit.test.ts index cec536989..2ab6bcd2f 100644 --- a/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.unit.test.ts +++ b/packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.unit.test.ts @@ -1,5 +1,5 @@ import { vol } from 'memfs'; -import { join } from 'node:path'; +import path from 'node:path'; import { describe, expect, it } from 'vitest'; import { getLogMessages } from '@code-pushup/test-utils'; import { ui } from '@code-pushup/utils'; @@ -8,7 +8,7 @@ import { parseLcovFiles } from './lcov-runner.js'; describe('parseLcovFiles', () => { const UTILS_REPORT = ` TN: -SF:${join('common', 'utils.ts')} +SF:${path.join('common', 'utils.ts')} FNF:0 FNH:0 DA:1,1 @@ -23,7 +23,7 @@ end_of_record const CONSTANTS_REPORT = ` TN: -SF:${join('src', 'lib', 'constants.ts')} +SF:${path.join('src', 'lib', 'constants.ts')} FNF:0 FNH:0 DA:1,1 @@ -37,8 +37,8 @@ end_of_record beforeEach(() => { vol.fromJSON( { - [join('integration-tests', 'lcov.info')]: UTILS_REPORT, // file name value under SF used in tests - [join('unit-tests', 'lcov.info')]: CONSTANTS_REPORT, // file name value under SF used in tests + [path.join('integration-tests', 'lcov.info')]: UTILS_REPORT, // file name value under SF used in tests + [path.join('unit-tests', 'lcov.info')]: CONSTANTS_REPORT, // file name value under SF used in tests 'lcov.info': '', // empty report file }, 'coverage', @@ -47,9 +47,9 @@ end_of_record it('should identify coverage path passed as a string', async () => { await expect( - parseLcovFiles([join('coverage', 'integration-tests', 'lcov.info')]), + parseLcovFiles([path.join('coverage', 'integration-tests', 'lcov.info')]), ).resolves.toEqual([ - expect.objectContaining({ file: join('common', 'utils.ts') }), + expect.objectContaining({ file: path.join('common', 'utils.ts') }), ]); }); @@ -57,13 +57,13 @@ end_of_record await expect( parseLcovFiles([ { - resultsPath: join('coverage', 'unit-tests', 'lcov.info'), - pathToProject: join('packages', 'cli'), + resultsPath: path.join('coverage', 'unit-tests', 'lcov.info'), + pathToProject: path.join('packages', 'cli'), }, ]), ).resolves.toEqual([ expect.objectContaining({ - file: join('packages', 'cli', 'src', 'lib', 'constants.ts'), + file: path.join('packages', 'cli', 'src', 'lib', 'constants.ts'), }), ]); }); @@ -72,35 +72,35 @@ end_of_record await expect( parseLcovFiles([ { - resultsPath: join('coverage', 'unit-tests', 'lcov.info'), - pathToProject: join('packages', 'cli'), + resultsPath: path.join('coverage', 'unit-tests', 'lcov.info'), + pathToProject: path.join('packages', 'cli'), }, - join('coverage', 'integration-tests', 'lcov.info'), + path.join('coverage', 'integration-tests', 'lcov.info'), ]), ).resolves.toEqual([ expect.objectContaining({ - file: join('packages', 'cli', 'src', 'lib', 'constants.ts'), + file: path.join('packages', 'cli', 'src', 'lib', 'constants.ts'), }), expect.objectContaining({ - file: join('common', 'utils.ts'), + file: path.join('common', 'utils.ts'), }), ]); }); it('should throw for only empty reports', async () => { await expect(() => - parseLcovFiles([join('coverage', 'lcov.info')]), + parseLcovFiles([path.join('coverage', 'lcov.info')]), ).rejects.toThrow('All provided results are empty.'); }); it('should warn about an empty lcov file', async () => { await parseLcovFiles([ - join('coverage', 'integration-tests', 'lcov.info'), - join('coverage', 'lcov.info'), + path.join('coverage', 'integration-tests', 'lcov.info'), + path.join('coverage', 'lcov.info'), ]); expect(getLogMessages(ui().logger)[0]).toContain( - `Coverage plugin: Empty lcov report file detected at ${join( + `Coverage plugin: Empty lcov report file detected at ${path.join( 'coverage', 'lcov.info', )}`, diff --git a/packages/plugin-coverage/src/lib/runner/runner.integration.test.ts b/packages/plugin-coverage/src/lib/runner/runner.integration.test.ts index 5e6603a49..cb1b64530 100644 --- a/packages/plugin-coverage/src/lib/runner/runner.integration.test.ts +++ b/packages/plugin-coverage/src/lib/runner/runner.integration.test.ts @@ -1,5 +1,5 @@ import { writeFile } from 'node:fs/promises'; -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, it } from 'vitest'; import type { @@ -53,8 +53,8 @@ describe('executeRunner', () => { it('should successfully execute runner', async () => { const config: FinalCoveragePluginConfig = { reports: [ - join( - fileURLToPath(dirname(import.meta.url)), + path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', '..', '..', diff --git a/packages/plugin-coverage/src/lib/utils.ts b/packages/plugin-coverage/src/lib/utils.ts index 7fb4e8cf1..371f36a63 100644 --- a/packages/plugin-coverage/src/lib/utils.ts +++ b/packages/plugin-coverage/src/lib/utils.ts @@ -23,10 +23,10 @@ export function applyMaxScoreAboveThreshold( ); } -/* eslint-disable no-magic-numbers */ export const coverageTypeWeightMapper: Record = { + /* eslint-disable @typescript-eslint/no-magic-numbers */ function: 6, branch: 3, line: 1, + /* eslint-enable @typescript-eslint/no-magic-numbers */ }; -/* eslint-enable no-magic-numbers */ diff --git a/packages/plugin-eslint/.eslintrc.json b/packages/plugin-eslint/.eslintrc.json deleted file mode 100644 index d4c551687..000000000 --- a/packages/plugin-eslint/.eslintrc.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["packages/plugin-eslint/tsconfig.*?.json"] - } - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": ["error"] - } - } - ] -} diff --git a/packages/plugin-eslint/eslint.config.js b/packages/plugin-eslint/eslint.config.js new file mode 100644 index 000000000..40165321a --- /dev/null +++ b/packages/plugin-eslint/eslint.config.js @@ -0,0 +1,21 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': 'error', + }, + }, +); diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/.eslintrc.json b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/.eslintrc.json deleted file mode 100644 index 983649501..000000000 --- a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/.eslintrc.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "root": true, - "ignorePatterns": ["**/*", "*.config*"], - "plugins": ["@nx"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": { - "@nx/enforce-module-boundaries": [ - "error", - { - "enforceBuildableLibDependency": true, - "allow": [], - "depConstraints": [ - { - "sourceTag": "*", - "onlyDependOnLibsWithTags": ["*"] - } - ] - } - ] - } - }, - { - "files": ["*.ts", "*.tsx"], - "extends": ["plugin:@nx/typescript"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "extends": ["plugin:@nx/javascript"], - "rules": {} - }, - { - "files": "*.json", - "parser": "jsonc-eslint-parser", - "rules": {} - } - ] -} diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/eslint.config.js b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/eslint.config.js new file mode 100644 index 000000000..6ef27db4c --- /dev/null +++ b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/eslint.config.js @@ -0,0 +1,41 @@ +const nx = require('@nx/eslint-plugin'); + +module.exports = [ + { + files: ['**/*.json'], + // Override or add rules here + rules: {}, + languageOptions: { + parser: require('jsonc-eslint-parser'), + }, + }, + ...nx.configs['flat/base'], + ...nx.configs['flat/typescript'], + ...nx.configs['flat/javascript'], + { + ignores: ['**/dist'], + }, + { + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], + rules: { + '@nx/enforce-module-boundaries': [ + 'warn', + { + enforceBuildableLibDependency: true, + allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?js$'], + depConstraints: [ + { + sourceTag: '*', + onlyDependOnLibsWithTags: ['*'], + }, + ], + }, + ], + }, + }, + { + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], + // Override or add rules here + rules: {}, + }, +]; diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/nx.json b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/nx.json index be05532ae..2b5514041 100644 --- a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/nx.json +++ b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/nx.json @@ -3,5 +3,6 @@ "@nrwl/js": { "analyzeSourceFiles": true } - } + }, + "useDaemonProcess": false } diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/package.json b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/package.json new file mode 100644 index 000000000..0fef86e33 --- /dev/null +++ b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/package.json @@ -0,0 +1,4 @@ +{ + "private": true, + "type": "commonjs" +} diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/cli/.eslintrc.json b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/cli/.eslintrc.json deleted file mode 100644 index f88293468..000000000 --- a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/cli/.eslintrc.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.ts", "*.tsx"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": ["error"] - } - } - ] -} diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/cli/eslint.config.js b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/cli/eslint.config.js new file mode 100644 index 000000000..9d2af7a3d --- /dev/null +++ b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/cli/eslint.config.js @@ -0,0 +1,19 @@ +const baseConfig = require('../../eslint.config.js'); + +module.exports = [ + ...baseConfig, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': [ + 'error', + { + ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'], + }, + ], + }, + languageOptions: { + parser: require('jsonc-eslint-parser'), + }, + }, +]; diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/cli/project.json b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/cli/project.json index 5c0452837..eb92b91ee 100644 --- a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/cli/project.json +++ b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/cli/project.json @@ -2,14 +2,9 @@ "name": "cli", "sourceRoot": "packages/cli/src", "projectType": "application", - "implicitDependencies": ["core"], "targets": { "lint": { - "executor": "@nx/linter:eslint", - "outputs": ["{options.outputFile}"], - "options": { - "lintFilePatterns": ["packages/cli/**/*.ts"] - } + "executor": "@nx/eslint:lint" } } } diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/core/.eslintrc.json b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/core/.eslintrc.json deleted file mode 100644 index f88293468..000000000 --- a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/core/.eslintrc.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.ts", "*.tsx"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": ["error"] - } - } - ] -} diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/core/eslint.config.js b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/core/eslint.config.js new file mode 100644 index 000000000..9d2af7a3d --- /dev/null +++ b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/core/eslint.config.js @@ -0,0 +1,19 @@ +const baseConfig = require('../../eslint.config.js'); + +module.exports = [ + ...baseConfig, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': [ + 'error', + { + ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'], + }, + ], + }, + languageOptions: { + parser: require('jsonc-eslint-parser'), + }, + }, +]; diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/core/project.json b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/core/project.json index cb8107127..c39496eb4 100644 --- a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/core/project.json +++ b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/core/project.json @@ -2,14 +2,9 @@ "name": "core", "sourceRoot": "packages/core/src", "projectType": "library", - "implicitDependencies": ["utils"], "targets": { "lint": { - "executor": "@nx/linter:eslint", - "outputs": ["{options.outputFile}"], - "options": { - "lintFilePatterns": ["packages/core/**/*.ts"] - } + "executor": "@nx/eslint:lint" } } } diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/nx-plugin/.eslintrc.json b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/nx-plugin/.eslintrc.json deleted file mode 100644 index f61728656..000000000 --- a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/nx-plugin/.eslintrc.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.ts", "*.tsx"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": ["error"] - } - }, - { - "files": ["./package.json", "./generators.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/nx-plugin-checks": "error" - } - } - ] -} diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/nx-plugin/eslint.config.js b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/nx-plugin/eslint.config.js new file mode 100644 index 000000000..9327f3f52 --- /dev/null +++ b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/nx-plugin/eslint.config.js @@ -0,0 +1,28 @@ +const baseConfig = require('../../eslint.config.js'); + +module.exports = [ + ...baseConfig, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': [ + 'error', + { + ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'], + }, + ], + }, + languageOptions: { + parser: require('jsonc-eslint-parser'), + }, + }, + { + files: ['**/package.json', '**/package.json', '**/generators.json'], + rules: { + '@nx/nx-plugin-checks': 'error', + }, + languageOptions: { + parser: require('jsonc-eslint-parser'), + }, + }, +]; diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/nx-plugin/project.json b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/nx-plugin/project.json index 45c7f7f1d..7c8b0a3d7 100644 --- a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/nx-plugin/project.json +++ b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/nx-plugin/project.json @@ -2,14 +2,9 @@ "name": "nx-plugin", "sourceRoot": "packages/nx-plugin/src", "projectType": "library", - "implicitDependencies": ["utils"], "targets": { "lint": { - "executor": "@nx/linter:eslint", - "outputs": ["{options.outputFile}"], - "options": { - "lintFilePatterns": ["packages/nx-plugin/**/*.ts"] - } + "executor": "@nx/eslint:lint" } } } diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/utils/.eslintrc.json b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/utils/.eslintrc.json deleted file mode 100644 index f88293468..000000000 --- a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/utils/.eslintrc.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.ts", "*.tsx"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": ["error"] - } - } - ] -} diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/utils/eslint.config.js b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/utils/eslint.config.js new file mode 100644 index 000000000..9d2af7a3d --- /dev/null +++ b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/utils/eslint.config.js @@ -0,0 +1,19 @@ +const baseConfig = require('../../eslint.config.js'); + +module.exports = [ + ...baseConfig, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': [ + 'error', + { + ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'], + }, + ], + }, + languageOptions: { + parser: require('jsonc-eslint-parser'), + }, + }, +]; diff --git a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/utils/project.json b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/utils/project.json index 0daa43e40..e13c52b59 100644 --- a/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/utils/project.json +++ b/packages/plugin-eslint/mocks/fixtures/nx-monorepo/packages/utils/project.json @@ -4,11 +4,7 @@ "projectType": "library", "targets": { "lint": { - "executor": "@nx/linter:eslint", - "outputs": ["{options.outputFile}"], - "options": { - "lintFilePatterns": ["packages/utils/**/*.ts"] - } + "executor": "@nx/eslint:lint" } } } diff --git a/packages/plugin-eslint/mocks/fixtures/todos-app/.eslintrc.js b/packages/plugin-eslint/mocks/fixtures/todos-app/.eslintrc.js deleted file mode 100644 index e05f2bb62..000000000 --- a/packages/plugin-eslint/mocks/fixtures/todos-app/.eslintrc.js +++ /dev/null @@ -1,69 +0,0 @@ -/** @type {import('eslint').ESLint.ConfigData} */ -module.exports = { - root: true, - env: { - browser: true, - es2021: true, - }, - plugins: ['react', 'react-hooks'], - overrides: [ - { - env: { - node: true, - }, - files: ['.eslintrc.{js,cjs}'], - parserOptions: { - sourceType: 'script', - }, - }, - ], - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - ecmaFeatures: { - jsx: true, - }, - }, - settings: { - react: { - version: 'detect', - }, - }, - rules: { - // https://eslint.org/docs/latest/rules/#possible-problems - 'no-cond-assign': 'warn', - 'no-const-assign': 'warn', - 'no-debugger': 'warn', - 'no-invalid-regexp': 'warn', - 'no-undef': 'warn', - 'no-unreachable-loop': 'warn', - 'no-unsafe-negation': 'warn', - 'no-unsafe-optional-chaining': 'warn', - 'no-unused-vars': 'warn', - 'use-isnan': 'warn', - 'valid-typeof': 'warn', - // https://eslint.org/docs/latest/rules/#suggestions - 'arrow-body-style': 'warn', - camelcase: 'warn', - curly: 'warn', - eqeqeq: 'warn', - 'max-lines-per-function': 'warn', - 'max-lines': 'warn', - 'no-shadow': 'warn', - 'no-var': 'warn', - 'object-shorthand': 'warn', - 'prefer-arrow-callback': 'warn', - 'prefer-const': 'warn', - 'prefer-object-spread': 'warn', - yoda: 'warn', - // https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules - 'react/jsx-key': 'warn', - 'react/prop-types': 'warn', - 'react/react-in-jsx-scope': 'warn', - 'react/jsx-uses-vars': 'warn', - 'react/jsx-uses-react': 'error', - // https://www.npmjs.com/package/eslint-plugin-react-hooks - 'react-hooks/rules-of-hooks': 'error', - 'react-hooks/exhaustive-deps': 'warn', - }, -}; diff --git a/packages/plugin-eslint/mocks/fixtures/todos-app/code-pushup.eslint.config.mjs b/packages/plugin-eslint/mocks/fixtures/todos-app/code-pushup.eslint.config.mjs new file mode 100644 index 000000000..488adb0b4 --- /dev/null +++ b/packages/plugin-eslint/mocks/fixtures/todos-app/code-pushup.eslint.config.mjs @@ -0,0 +1,3 @@ +import javascript from '@code-pushup/eslint-config'; + +export default javascript; diff --git a/packages/plugin-eslint/mocks/fixtures/todos-app/code-pushup.eslintrc.yml b/packages/plugin-eslint/mocks/fixtures/todos-app/code-pushup.eslintrc.yml deleted file mode 100644 index f7da37f07..000000000 --- a/packages/plugin-eslint/mocks/fixtures/todos-app/code-pushup.eslintrc.yml +++ /dev/null @@ -1,2 +0,0 @@ -root: true -extends: '@code-pushup' diff --git a/packages/plugin-eslint/mocks/fixtures/todos-app/eslint.config.js b/packages/plugin-eslint/mocks/fixtures/todos-app/eslint.config.js new file mode 100644 index 000000000..d4ca65827 --- /dev/null +++ b/packages/plugin-eslint/mocks/fixtures/todos-app/eslint.config.js @@ -0,0 +1,67 @@ +const react = require('eslint-plugin-react'); +const reactHooks = require('eslint-plugin-react-hooks'); +const globals = require('globals'); + +/** @type {import('eslint').Linter.Config[]} */ +module.exports = [ + { + files: ['**/*.jsx', '**/*.js'], + ignores: ['eslint.config.js'], + plugins: { + react, + 'react-hooks': reactHooks, + }, + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + globals: { + ...globals.browser, + }, + }, + settings: { + react: { + version: 'detect', + }, + }, + rules: { + // https://eslint.org/docs/latest/rules/#possible-problems + 'no-cond-assign': 'warn', + 'no-const-assign': 'warn', + 'no-debugger': 'warn', + 'no-invalid-regexp': 'warn', + 'no-undef': 'warn', + 'no-unreachable-loop': 'warn', + 'no-unsafe-negation': 'warn', + 'no-unsafe-optional-chaining': 'warn', + 'no-unused-vars': 'warn', + 'use-isnan': 'warn', + 'valid-typeof': 'warn', + // https://eslint.org/docs/latest/rules/#suggestions + 'arrow-body-style': 'warn', + camelcase: 'warn', + curly: 'warn', + eqeqeq: 'warn', + 'max-lines-per-function': 'warn', + 'max-lines': 'warn', + 'no-shadow': 'warn', + 'no-var': 'warn', + 'object-shorthand': 'warn', + 'prefer-arrow-callback': 'warn', + 'prefer-const': 'warn', + 'prefer-object-spread': 'warn', + yoda: 'warn', + // https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules + 'react/jsx-key': 'warn', + 'react/prop-types': 'warn', + 'react/react-in-jsx-scope': 'warn', + 'react/jsx-uses-vars': 'warn', + 'react/jsx-uses-react': 'error', + // https://www.npmjs.com/package/eslint-plugin-react-hooks + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + }, + }, +]; diff --git a/packages/plugin-eslint/mocks/fixtures/todos-app/package.json b/packages/plugin-eslint/mocks/fixtures/todos-app/package.json index 76fa0f9e9..5409f25c7 100644 --- a/packages/plugin-eslint/mocks/fixtures/todos-app/package.json +++ b/packages/plugin-eslint/mocks/fixtures/todos-app/package.json @@ -12,8 +12,9 @@ }, "devDependencies": { "esbuild": "^0.19.2", - "eslint": "^8.48.0", - "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0" + "eslint": "^9.16.0", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-hooks": "^5.1.0", + "globals": "^15.13.0" } } diff --git a/packages/plugin-eslint/src/index.ts b/packages/plugin-eslint/src/index.ts index b70b495c6..8e33e0c18 100644 --- a/packages/plugin-eslint/src/index.ts +++ b/packages/plugin-eslint/src/index.ts @@ -5,9 +5,8 @@ export default eslintPlugin; export type { ESLintPluginConfig } from './lib/config.js'; export { - eslintConfigFromNxProjectAndDeps, - // eslint-disable-next-line deprecation/deprecation - eslintConfigFromNxProjects, eslintConfigFromAllNxProjects, eslintConfigFromNxProject, + eslintConfigFromNxProjectAndDeps, + eslintConfigFromNxProjects, } from './lib/nx/index.js'; diff --git a/packages/plugin-eslint/src/lib/__snapshots__/eslint-plugin.integration.test.ts.snap b/packages/plugin-eslint/src/lib/__snapshots__/eslint-plugin.integration.test.ts.snap index ecb729eac..41d9175c0 100644 --- a/packages/plugin-eslint/src/lib/__snapshots__/eslint-plugin.integration.test.ts.snap +++ b/packages/plugin-eslint/src/lib/__snapshots__/eslint-plugin.integration.test.ts.snap @@ -6,7 +6,7 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` { "description": "ESLint rule **no-cond-assign**.", "docsUrl": "https://eslint.org/docs/latest/rules/no-cond-assign", - "slug": "no-cond-assign", + "slug": "no-cond-assign-4da1eac06e945b1a", "title": "Disallow assignment operators in conditional expressions", }, { @@ -24,31 +24,31 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` { "description": "ESLint rule **no-invalid-regexp**.", "docsUrl": "https://eslint.org/docs/latest/rules/no-invalid-regexp", - "slug": "no-invalid-regexp", + "slug": "no-invalid-regexp-9b874a94de5d65c6", "title": "Disallow invalid regular expression strings in \`RegExp\` constructors", }, { "description": "ESLint rule **no-undef**.", "docsUrl": "https://eslint.org/docs/latest/rules/no-undef", - "slug": "no-undef", + "slug": "no-undef-1c0a571cbd314966", "title": "Disallow the use of undeclared variables unless mentioned in \`/*global */\` comments", }, { "description": "ESLint rule **no-unreachable-loop**.", "docsUrl": "https://eslint.org/docs/latest/rules/no-unreachable-loop", - "slug": "no-unreachable-loop", + "slug": "no-unreachable-loop-6bd5152d46f5cba7", "title": "Disallow loops with a body that allows only one iteration", }, { "description": "ESLint rule **no-unsafe-negation**.", "docsUrl": "https://eslint.org/docs/latest/rules/no-unsafe-negation", - "slug": "no-unsafe-negation", + "slug": "no-unsafe-negation-1c3a548d96c1e95f", "title": "Disallow negating the left operand of relational operators", }, { "description": "ESLint rule **no-unsafe-optional-chaining**.", "docsUrl": "https://eslint.org/docs/latest/rules/no-unsafe-optional-chaining", - "slug": "no-unsafe-optional-chaining", + "slug": "no-unsafe-optional-chaining-0724d742fbe97cb2", "title": "Disallow use of optional chaining in contexts where the \`undefined\` value is not allowed", }, { @@ -60,31 +60,31 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` { "description": "ESLint rule **use-isnan**.", "docsUrl": "https://eslint.org/docs/latest/rules/use-isnan", - "slug": "use-isnan", + "slug": "use-isnan-20b8ba68b5cc1ea7", "title": "Require calls to \`isNaN()\` when checking for \`NaN\`", }, { "description": "ESLint rule **valid-typeof**.", "docsUrl": "https://eslint.org/docs/latest/rules/valid-typeof", - "slug": "valid-typeof", + "slug": "valid-typeof-19926c783be07085", "title": "Enforce comparing \`typeof\` expressions against valid strings", }, { "description": "ESLint rule **arrow-body-style**.", "docsUrl": "https://eslint.org/docs/latest/rules/arrow-body-style", - "slug": "arrow-body-style", + "slug": "arrow-body-style-61b676e8783b0a34", "title": "Require braces around arrow function bodies", }, { "description": "ESLint rule **camelcase**.", "docsUrl": "https://eslint.org/docs/latest/rules/camelcase", - "slug": "camelcase", + "slug": "camelcase-3568b92e56d6fcb0", "title": "Enforce camelcase naming convention", }, { "description": "ESLint rule **curly**.", "docsUrl": "https://eslint.org/docs/latest/rules/curly", - "slug": "curly", + "slug": "curly-da196f7d88e0421b", "title": "Enforce consistent brace style for all control statements", }, { @@ -108,7 +108,7 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` { "description": "ESLint rule **no-shadow**.", "docsUrl": "https://eslint.org/docs/latest/rules/no-shadow", - "slug": "no-shadow", + "slug": "no-shadow-b9e07f46bb0b5444", "title": "Disallow variable declarations from shadowing variables declared in the outer scope", }, { @@ -126,13 +126,13 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` { "description": "ESLint rule **prefer-arrow-callback**.", "docsUrl": "https://eslint.org/docs/latest/rules/prefer-arrow-callback", - "slug": "prefer-arrow-callback", + "slug": "prefer-arrow-callback-81999cc2a4967394", "title": "Require using arrow functions for callbacks", }, { "description": "ESLint rule **prefer-const**.", "docsUrl": "https://eslint.org/docs/latest/rules/prefer-const", - "slug": "prefer-const", + "slug": "prefer-const-519d0b1dc698e7d6", "title": "Require \`const\` declarations for variables that are never reassigned after declared", }, { @@ -144,7 +144,7 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` { "description": "ESLint rule **yoda**.", "docsUrl": "https://eslint.org/docs/latest/rules/yoda", - "slug": "yoda", + "slug": "yoda-e0d68df5f54f65ab", "title": "Require or disallow "Yoda" conditions", }, { @@ -197,7 +197,7 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` "description": "Code that either will cause an error or may cause confusing behavior. Developers should consider this a high priority to resolve.", "refs": [ { - "slug": "no-cond-assign", + "slug": "no-cond-assign-4da1eac06e945b1a", "weight": 1, }, { @@ -209,23 +209,23 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` "weight": 1, }, { - "slug": "no-invalid-regexp", + "slug": "no-invalid-regexp-9b874a94de5d65c6", "weight": 1, }, { - "slug": "no-undef", + "slug": "no-undef-1c0a571cbd314966", "weight": 1, }, { - "slug": "no-unreachable-loop", + "slug": "no-unreachable-loop-6bd5152d46f5cba7", "weight": 1, }, { - "slug": "no-unsafe-negation", + "slug": "no-unsafe-negation-1c3a548d96c1e95f", "weight": 1, }, { - "slug": "no-unsafe-optional-chaining", + "slug": "no-unsafe-optional-chaining-0724d742fbe97cb2", "weight": 1, }, { @@ -233,11 +233,11 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` "weight": 1, }, { - "slug": "use-isnan", + "slug": "use-isnan-20b8ba68b5cc1ea7", "weight": 1, }, { - "slug": "valid-typeof", + "slug": "valid-typeof-19926c783be07085", "weight": 1, }, { @@ -252,15 +252,15 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` "description": "Something that could be done in a better way but no errors will occur if the code isn't changed.", "refs": [ { - "slug": "arrow-body-style", + "slug": "arrow-body-style-61b676e8783b0a34", "weight": 1, }, { - "slug": "camelcase", + "slug": "camelcase-3568b92e56d6fcb0", "weight": 1, }, { - "slug": "curly", + "slug": "curly-da196f7d88e0421b", "weight": 1, }, { @@ -276,7 +276,7 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` "weight": 1, }, { - "slug": "no-shadow", + "slug": "no-shadow-b9e07f46bb0b5444", "weight": 1, }, { @@ -288,11 +288,11 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` "weight": 1, }, { - "slug": "prefer-arrow-callback", + "slug": "prefer-arrow-callback-81999cc2a4967394", "weight": 1, }, { - "slug": "prefer-const", + "slug": "prefer-const-519d0b1dc698e7d6", "weight": 1, }, { @@ -300,7 +300,7 @@ exports[`eslintPlugin > should initialize ESLint plugin for React application 1` "weight": 1, }, { - "slug": "yoda", + "slug": "yoda-e0d68df5f54f65ab", "weight": 1, }, { diff --git a/packages/plugin-eslint/src/lib/__snapshots__/runner.integration.test.ts.snap b/packages/plugin-eslint/src/lib/__snapshots__/runner.integration.test.ts.snap index 0c9001469..a1ff1b6b3 100644 --- a/packages/plugin-eslint/src/lib/__snapshots__/runner.integration.test.ts.snap +++ b/packages/plugin-eslint/src/lib/__snapshots__/runner.integration.test.ts.snap @@ -8,7 +8,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "no-cond-assign", + "slug": "no-cond-assign-4da1eac06e945b1a", "value": 0, }, { @@ -35,7 +35,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "no-invalid-regexp", + "slug": "no-invalid-regexp-9b874a94de5d65c6", "value": 0, }, { @@ -44,7 +44,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "no-undef", + "slug": "no-undef-1c0a571cbd314966", "value": 0, }, { @@ -53,7 +53,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "no-unreachable-loop", + "slug": "no-unreachable-loop-6bd5152d46f5cba7", "value": 0, }, { @@ -62,7 +62,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "no-unsafe-negation", + "slug": "no-unsafe-negation-1c3a548d96c1e95f", "value": 0, }, { @@ -71,7 +71,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "no-unsafe-optional-chaining", + "slug": "no-unsafe-optional-chaining-0724d742fbe97cb2", "value": 0, }, { @@ -103,7 +103,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "use-isnan", + "slug": "use-isnan-20b8ba68b5cc1ea7", "value": 0, }, { @@ -112,7 +112,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "valid-typeof", + "slug": "valid-typeof-19926c783be07085", "value": 0, }, { @@ -135,7 +135,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "1 warning", "score": 0, - "slug": "arrow-body-style", + "slug": "arrow-body-style-61b676e8783b0a34", "value": 1, }, { @@ -144,7 +144,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "camelcase", + "slug": "camelcase-3568b92e56d6fcb0", "value": 0, }, { @@ -153,7 +153,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "curly", + "slug": "curly-da196f7d88e0421b", "value": 0, }, { @@ -257,7 +257,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "3 warnings", "score": 0, - "slug": "no-shadow", + "slug": "no-shadow-b9e07f46bb0b5444", "value": 3, }, { @@ -324,7 +324,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "prefer-arrow-callback", + "slug": "prefer-arrow-callback-81999cc2a4967394", "value": 0, }, { @@ -347,7 +347,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "1 warning", "score": 0, - "slug": "prefer-const", + "slug": "prefer-const-519d0b1dc698e7d6", "value": 1, }, { @@ -365,7 +365,7 @@ exports[`executeRunner > should execute ESLint and create audit results for Reac }, "displayValue": "passed", "score": 1, - "slug": "yoda", + "slug": "yoda-e0d68df5f54f65ab", "value": 0, }, { diff --git a/packages/plugin-eslint/src/lib/eslint-plugin.integration.test.ts b/packages/plugin-eslint/src/lib/eslint-plugin.integration.test.ts index 7420cf36f..afb82191a 100644 --- a/packages/plugin-eslint/src/lib/eslint-plugin.integration.test.ts +++ b/packages/plugin-eslint/src/lib/eslint-plugin.integration.test.ts @@ -1,5 +1,5 @@ import os from 'node:os'; -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import type { MockInstance } from 'vitest'; import type { Audit, PluginConfig, RunnerConfig } from '@code-pushup/models'; @@ -7,9 +7,9 @@ import { toUnixPath } from '@code-pushup/utils'; import { eslintPlugin } from './eslint-plugin.js'; describe('eslintPlugin', () => { - const thisDir = fileURLToPath(dirname(import.meta.url)); + const thisDir = fileURLToPath(path.dirname(import.meta.url)); - const fixturesDir = join(thisDir, '..', '..', 'mocks', 'fixtures'); + const fixturesDir = path.join(thisDir, '..', '..', 'mocks', 'fixtures'); let cwdSpy: MockInstance<[], string>; let platformSpy: MockInstance<[], NodeJS.Platform>; @@ -19,7 +19,7 @@ describe('eslintPlugin', () => { runner: { ...(plugin.runner as RunnerConfig), args: (plugin.runner as RunnerConfig).args?.map(arg => - toUnixPath(arg.replace(dirname(thisDir), '')), + toUnixPath(arg.replace(path.dirname(thisDir), '')), ), outputFile: toUnixPath((plugin.runner as RunnerConfig).outputFile), }, @@ -37,9 +37,9 @@ describe('eslintPlugin', () => { }); it('should initialize ESLint plugin for React application', async () => { - cwdSpy.mockReturnValue(join(fixturesDir, 'todos-app')); + cwdSpy.mockReturnValue(path.join(fixturesDir, 'todos-app')); const plugin = await eslintPlugin({ - eslintrc: '.eslintrc.js', + eslintrc: 'eslint.config.js', patterns: ['src/**/*.js', 'src/**/*.jsx'], }); @@ -49,13 +49,13 @@ describe('eslintPlugin', () => { }); it('should initialize ESLint plugin for Nx project', async () => { - cwdSpy.mockReturnValue(join(fixturesDir, 'nx-monorepo')); + cwdSpy.mockReturnValue(path.join(fixturesDir, 'nx-monorepo')); const plugin = await eslintPlugin({ - eslintrc: './packages/utils/.eslintrc.json', - patterns: ['packages/utils/**/*.ts', 'packages/utils/**/*.json'], + eslintrc: './packages/nx-plugin/eslint.config.js', + patterns: ['packages/nx-plugin/**/*.ts', 'packages/nx-plugin/**/*.json'], }); - // expect rule from extended base .eslintrc.json + // expect rule from extended base eslint.config.js expect(plugin.audits).toContainEqual( expect.objectContaining({ slug: expect.stringMatching(/^nx-enforce-module-boundaries/), @@ -63,10 +63,10 @@ describe('eslintPlugin', () => { description: expect.stringContaining('sourceTag'), }), ); - // expect rule from utils project's .eslintrc.json + // expect rule from nx-plugin project's eslint.config.js expect(plugin.audits).toContainEqual( expect.objectContaining>({ - slug: 'nx-dependency-checks', + slug: 'nx-nx-plugin-checks', }), ); }); @@ -81,6 +81,6 @@ describe('eslintPlugin', () => { it("should throw if eslintrc file doesn't exist", async () => { await expect( eslintPlugin({ eslintrc: '.eslintrc.yml', patterns: '**/*.js' }), - ).rejects.toThrow('Cannot read config file'); + ).rejects.toThrow(/Failed to load url .*\.eslintrc.yml/); }); }); diff --git a/packages/plugin-eslint/src/lib/eslint-plugin.ts b/packages/plugin-eslint/src/lib/eslint-plugin.ts index a4350d11a..1393bad09 100644 --- a/packages/plugin-eslint/src/lib/eslint-plugin.ts +++ b/packages/plugin-eslint/src/lib/eslint-plugin.ts @@ -1,5 +1,5 @@ import { createRequire } from 'node:module'; -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import type { PluginConfig } from '@code-pushup/models'; import { type ESLintPluginConfig, eslintPluginConfigSchema } from './config.js'; @@ -33,8 +33,8 @@ export async function eslintPlugin( const { audits, groups } = await listAuditsAndGroups(targets); - const runnerScriptPath = join( - fileURLToPath(dirname(import.meta.url)), + const runnerScriptPath = path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', 'bin.js', ); diff --git a/packages/plugin-eslint/src/lib/meta/groups.ts b/packages/plugin-eslint/src/lib/meta/groups.ts index e2c84f9ef..b7250aca2 100644 --- a/packages/plugin-eslint/src/lib/meta/groups.ts +++ b/packages/plugin-eslint/src/lib/meta/groups.ts @@ -1,7 +1,7 @@ import type { Rule } from 'eslint'; import type { Group, GroupRef } from '@code-pushup/models'; import { objectToKeys, slugify } from '@code-pushup/utils'; -import { ruleIdToSlug } from './hash.js'; +import { ruleToSlug } from './hash.js'; import { type RuleData, parseRuleId } from './parse.js'; type RuleType = NonNullable; @@ -32,12 +32,15 @@ export function groupsFromRuleTypes(rules: RuleData[]): Group[] { const allTypes = objectToKeys(typeGroups); const auditSlugsMap = rules.reduce>>( - (acc, { meta: { type }, ruleId, options }) => - type == null + (acc, rule) => + rule.meta.type == null ? acc : { ...acc, - [type]: [...(acc[type] ?? []), ruleIdToSlug(ruleId, options)], + [rule.meta.type]: [ + ...(acc[rule.meta.type] ?? []), + ruleToSlug(rule), + ], }, {}, ); @@ -54,21 +57,18 @@ export function groupsFromRuleTypes(rules: RuleData[]): Group[] { export function groupsFromRuleCategories(rules: RuleData[]): Group[] { const categoriesMap = rules.reduce>>( - (acc, { meta: { docs }, ruleId, options }) => { + (acc, rule) => { // meta.docs.category still used by some popular plugins (e.g. import, react, functional) - const category = docs?.category; + const category = rule.meta.docs?.category; if (!category) { return acc; } - const { plugin = '' } = parseRuleId(ruleId); + const { plugin = '' } = parseRuleId(rule.id); return { ...acc, [plugin]: { ...acc[plugin], - [category]: [ - ...(acc[plugin]?.[category] ?? []), - ruleIdToSlug(ruleId, options), - ], + [category]: [...(acc[plugin]?.[category] ?? []), ruleToSlug(rule)], }, }; }, diff --git a/packages/plugin-eslint/src/lib/meta/groups.unit.test.ts b/packages/plugin-eslint/src/lib/meta/groups.unit.test.ts index fda1d7d85..1b9070ee8 100644 --- a/packages/plugin-eslint/src/lib/meta/groups.unit.test.ts +++ b/packages/plugin-eslint/src/lib/meta/groups.unit.test.ts @@ -4,7 +4,7 @@ import type { RuleData } from './parse.js'; const eslintRules: RuleData[] = [ { - ruleId: 'no-var', + id: 'no-var', meta: { docs: { description: 'Require `let` or `const` instead of `var`', @@ -21,7 +21,7 @@ const eslintRules: RuleData[] = [ options: [], }, { - ruleId: 'no-const-assign', + id: 'no-const-assign', meta: { docs: { description: 'Disallow reassigning `const` variables', @@ -37,7 +37,7 @@ const eslintRules: RuleData[] = [ options: [], }, { - ruleId: 'no-debugger', + id: 'no-debugger', meta: { type: 'problem', docs: { @@ -53,7 +53,7 @@ const eslintRules: RuleData[] = [ options: [], }, { - ruleId: 'react/jsx-key', + id: 'react/jsx-key', meta: { docs: { category: 'Possible Errors', @@ -66,7 +66,7 @@ const eslintRules: RuleData[] = [ options: [], }, { - ruleId: 'react/react-in-jsx-scope', + id: 'react/react-in-jsx-scope', meta: { docs: { description: 'Disallow missing React when using JSX', @@ -82,7 +82,7 @@ const eslintRules: RuleData[] = [ options: [], }, { - ruleId: 'react/no-deprecated', + id: 'react/no-deprecated', meta: { docs: { description: 'Disallow usage of deprecated methods', @@ -99,7 +99,7 @@ const eslintRules: RuleData[] = [ options: [], }, { - ruleId: '@typescript-eslint/no-array-constructor', + id: '@typescript-eslint/no-array-constructor', meta: { type: 'suggestion', docs: { diff --git a/packages/plugin-eslint/src/lib/meta/hash.ts b/packages/plugin-eslint/src/lib/meta/hash.ts index a5849ecf8..bf8fa30fe 100644 --- a/packages/plugin-eslint/src/lib/meta/hash.ts +++ b/packages/plugin-eslint/src/lib/meta/hash.ts @@ -1,5 +1,10 @@ import { createHash } from 'node:crypto'; import { slugify } from '@code-pushup/utils'; +import { type RuleData, resolveRuleOptions } from './parse.js'; + +export function ruleToSlug(rule: RuleData): string { + return ruleIdToSlug(rule.id, resolveRuleOptions(rule)); +} export function ruleIdToSlug( ruleId: string, diff --git a/packages/plugin-eslint/src/lib/meta/parse.ts b/packages/plugin-eslint/src/lib/meta/parse.ts index 31523a39c..b3debe9c4 100644 --- a/packages/plugin-eslint/src/lib/meta/parse.ts +++ b/packages/plugin-eslint/src/lib/meta/parse.ts @@ -2,14 +2,14 @@ import type { Linter, Rule } from 'eslint'; import { toArray } from '@code-pushup/utils'; export type RuleData = { - ruleId: string; + id: string; meta: Rule.RuleMetaData; options: unknown[] | undefined; }; export function parseRuleId(ruleId: string): { plugin?: string; name: string } { const i = ruleId.lastIndexOf('/'); - if (i < 0) { + if (i === -1) { return { name: ruleId }; } return { @@ -38,3 +38,10 @@ export function optionsFromRuleEntry( ): unknown[] { return toArray(entry).slice(1); } + +export function resolveRuleOptions(rule: RuleData): unknown[] | undefined { + if (rule.options?.length) { + return rule.options; + } + return rule.meta.defaultOptions; +} diff --git a/packages/plugin-eslint/src/lib/meta/parse.unit.test.ts b/packages/plugin-eslint/src/lib/meta/parse.unit.test.ts index 980437f12..c1b00da9b 100644 --- a/packages/plugin-eslint/src/lib/meta/parse.unit.test.ts +++ b/packages/plugin-eslint/src/lib/meta/parse.unit.test.ts @@ -1,5 +1,10 @@ import type { Linter } from 'eslint'; -import { isRuleOff, optionsFromRuleEntry, parseRuleId } from './parse.js'; +import { + isRuleOff, + optionsFromRuleEntry, + parseRuleId, + resolveRuleOptions, +} from './parse.js'; describe('parseRuleId', () => { it.each([ @@ -83,3 +88,31 @@ describe('optionsFromRuleEntry', () => { expect(optionsFromRuleEntry(['warn'])).toEqual([]); }); }); + +describe('resolveRuleOptions', () => { + it('should prioritize custom options', () => { + expect( + resolveRuleOptions({ + id: 'arrow-body-style', + options: ['always'], + meta: { defaultOptions: ['as-needed'] }, + }), + ).toEqual(['always']); + }); + + it('should fallback to default options if no custom options', () => { + expect( + resolveRuleOptions({ + id: 'arrow-body-style', + options: [], + meta: { defaultOptions: ['as-needed'] }, + }), + ).toEqual(['as-needed']); + }); + + it('should return undefined if neither custom not default options are set', () => { + expect( + resolveRuleOptions({ id: 'require-await', options: [], meta: {} }), + ).toBeUndefined(); + }); +}); diff --git a/packages/plugin-eslint/src/lib/meta/rules.unit.test.ts b/packages/plugin-eslint/src/lib/meta/rules.integration.test.ts similarity index 84% rename from packages/plugin-eslint/src/lib/meta/rules.unit.test.ts rename to packages/plugin-eslint/src/lib/meta/rules.integration.test.ts index dd7a71a89..6526243d7 100644 --- a/packages/plugin-eslint/src/lib/meta/rules.unit.test.ts +++ b/packages/plugin-eslint/src/lib/meta/rules.integration.test.ts @@ -1,4 +1,4 @@ -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import type { MockInstance } from 'vitest'; import type { ESLintTarget } from '../config.js'; @@ -6,8 +6,8 @@ import type { RuleData } from './parse.js'; import { listRules } from './rules.js'; describe('listRules', () => { - const fixturesDir = join( - fileURLToPath(dirname(import.meta.url)), + const fixturesDir = path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', '..', '..', @@ -26,8 +26,8 @@ describe('listRules', () => { }); describe('React app', () => { - const appRootDir = join(fixturesDir, 'todos-app'); - const eslintrc = join(appRootDir, '.eslintrc.js'); + const appRootDir = path.join(fixturesDir, 'todos-app'); + const eslintrc = path.join(appRootDir, 'eslint.config.js'); const patterns = ['src/**/*.js', 'src/**/*.jsx']; const targets: ESLintTarget[] = [{ eslintrc, patterns }]; @@ -42,7 +42,7 @@ describe('listRules', () => { it('should include explicitly set built-in rule', async () => { await expect(listRules(targets)).resolves.toContainEqual({ - ruleId: 'no-const-assign', + id: 'no-const-assign', meta: { docs: { description: 'Disallow reassigning `const` variables', @@ -61,7 +61,7 @@ describe('listRules', () => { it('should include explicitly set plugin rule', async () => { await expect(listRules(targets)).resolves.toContainEqual({ - ruleId: 'react/jsx-key', + id: 'react/jsx-key', meta: { docs: { category: 'Possible Errors', @@ -89,8 +89,8 @@ describe('listRules', () => { }); describe('Nx monorepo project', () => { - const nxRootDir = join(fixturesDir, 'nx-monorepo'); - const eslintrc = join(nxRootDir, 'packages/utils/.eslintrc.json'); + const nxRootDir = path.join(fixturesDir, 'nx-monorepo'); + const eslintrc = path.join(nxRootDir, 'packages/utils/eslint.config.js'); const patterns = ['packages/utils/**/*.ts', 'packages/utils/**/*.json']; const targets: ESLintTarget[] = [{ eslintrc, patterns }]; @@ -105,13 +105,12 @@ describe('listRules', () => { }); it('should include explicitly set plugin rule with custom options', async () => { - // set in root .eslintrc.json + // set in root eslint.config.js await expect(listRules(targets)).resolves.toContainEqual({ - ruleId: '@nx/enforce-module-boundaries', + id: '@nx/enforce-module-boundaries', meta: expect.any(Object), options: [ - { - allow: [], + expect.objectContaining({ depConstraints: [ { onlyDependOnLibsWithTags: ['*'], @@ -119,7 +118,7 @@ describe('listRules', () => { }, ], enforceBuildableLibDependency: true, - }, + }), ], } satisfies RuleData); }); @@ -127,7 +126,7 @@ describe('listRules', () => { it('should include built-in rule set implicitly by extending recommended config', async () => { // extended via @nx/typescript -> @typescript-eslint/eslint-recommended await expect(listRules(targets)).resolves.toContainEqual({ - ruleId: 'no-var', + id: 'no-var', meta: expect.any(Object), options: [], } as RuleData); @@ -136,7 +135,7 @@ describe('listRules', () => { it('should include plugin rule set implicitly by extending recommended config', async () => { // extended via @nx/typescript -> @typescript-eslint/recommended await expect(listRules(targets)).resolves.toContainEqual({ - ruleId: '@typescript-eslint/no-unused-vars', + id: '@typescript-eslint/no-unused-vars', meta: expect.any(Object), options: [], } satisfies RuleData); @@ -146,17 +145,17 @@ describe('listRules', () => { // extended TypeScript config sets "no-unused-semi": "off" await expect(listRules(targets)).resolves.not.toContainEqual( expect.objectContaining({ - ruleId: 'no-unused-vars', + id: 'no-unused-expressions', } satisfies Partial), ); }); it('should include rule added to root config by project config', async () => { - // set only in packages/utils/.eslintrc.json + // set only in packages/utils/eslint.config.js await expect(listRules(targets)).resolves.toContainEqual({ - ruleId: '@nx/dependency-checks', + id: '@nx/dependency-checks', meta: expect.any(Object), - options: [], + options: expect.any(Array), } satisfies RuleData); }); }); diff --git a/packages/plugin-eslint/src/lib/meta/rules.ts b/packages/plugin-eslint/src/lib/meta/rules.ts index ec89c2cd7..2750a7b8a 100644 --- a/packages/plugin-eslint/src/lib/meta/rules.ts +++ b/packages/plugin-eslint/src/lib/meta/rules.ts @@ -21,8 +21,8 @@ export async function listRules(targets: ESLintTarget[]): Promise { function mergeRuleIntoMap(map: RulesMap, rule: RuleData): RulesMap { return { ...map, - [rule.ruleId]: { - ...map[rule.ruleId], + [rule.id]: { + ...map[rule.id], [jsonHash(rule.options)]: rule, }, }; diff --git a/packages/plugin-eslint/src/lib/meta/transform.ts b/packages/plugin-eslint/src/lib/meta/transform.ts index 9c986dd75..459ca554c 100644 --- a/packages/plugin-eslint/src/lib/meta/transform.ts +++ b/packages/plugin-eslint/src/lib/meta/transform.ts @@ -1,28 +1,28 @@ import type { Audit } from '@code-pushup/models'; import { truncateDescription, truncateTitle } from '@code-pushup/utils'; -import { ruleIdToSlug } from './hash.js'; +import { ruleToSlug } from './hash.js'; import type { RuleData } from './parse.js'; -export function ruleToAudit({ ruleId, meta, options }: RuleData): Audit { - const name = ruleId.split('/').at(-1) ?? ruleId; +export function ruleToAudit(rule: RuleData): Audit { + const name = rule.id.split('/').at(-1) ?? rule.id; const plugin = - name === ruleId ? null : ruleId.slice(0, ruleId.lastIndexOf('/')); + name === rule.id ? null : rule.id.slice(0, rule.id.lastIndexOf('/')); const pluginContext = plugin ? `, from _${plugin}_ plugin` : ''; const lines: string[] = [ `ESLint rule **${name}**${pluginContext}.`, - ...(options?.length ? ['Custom options:'] : []), - ...(options?.map(option => + ...(rule.options?.length ? ['Custom options:'] : []), + ...(rule.options?.map(option => ['```json', JSON.stringify(option, null, 2), '```'].join('\n'), ) ?? []), ]; return { - slug: ruleIdToSlug(ruleId, options), - title: truncateTitle(meta.docs?.description ?? name), + slug: ruleToSlug(rule), + title: truncateTitle(rule.meta.docs?.description ?? name), description: truncateDescription(lines.join('\n\n')), - ...(meta.docs?.url && { - docsUrl: meta.docs.url, + ...(rule.meta.docs?.url && { + docsUrl: rule.meta.docs.url, }), }; } diff --git a/packages/plugin-eslint/src/lib/meta/transform.unit.test.ts b/packages/plugin-eslint/src/lib/meta/transform.unit.test.ts index 4855a2e5b..8d3b2951a 100644 --- a/packages/plugin-eslint/src/lib/meta/transform.unit.test.ts +++ b/packages/plugin-eslint/src/lib/meta/transform.unit.test.ts @@ -5,7 +5,7 @@ describe('ruleToAudit', () => { it('built-in rule without custom options', () => { expect( ruleToAudit({ - ruleId: 'no-invalid-regexp', + id: 'no-invalid-regexp', meta: { docs: { description: @@ -27,7 +27,7 @@ describe('ruleToAudit', () => { it('plugin rule without custom options', () => { expect( ruleToAudit({ - ruleId: '@typescript-eslint/no-explicit-any', + id: '@typescript-eslint/no-explicit-any', meta: { docs: { description: 'Disallow the `any` type.', @@ -48,7 +48,7 @@ describe('ruleToAudit', () => { it('plugin rule with custom options object', () => { expect( ruleToAudit({ - ruleId: 'max-lines', + id: 'max-lines', meta: { docs: { description: 'Enforce a maximum number of lines per file', @@ -84,7 +84,7 @@ Custom options: it('built-in rule with custom options array', () => { expect( ruleToAudit({ - ruleId: 'no-restricted-imports', + id: 'no-restricted-imports', meta: { docs: { description: 'Disallow specified modules when loaded by import', @@ -114,7 +114,7 @@ Custom options: it('plugin rule with custom options', () => { expect( ruleToAudit({ - ruleId: 'import/extensions', + id: 'import/extensions', meta: { docs: { description: @@ -148,7 +148,7 @@ Custom options: it('rule with overlong description -> title is truncated', () => { expect( ruleToAudit({ - ruleId: '@angular-eslint/template/mouse-events-have-key-events', + id: '@angular-eslint/template/mouse-events-have-key-events', meta: { docs: { description: diff --git a/packages/plugin-eslint/src/lib/meta/versions/flat.integration.test.ts b/packages/plugin-eslint/src/lib/meta/versions/flat.integration.test.ts index a8ff222ae..757f16718 100644 --- a/packages/plugin-eslint/src/lib/meta/versions/flat.integration.test.ts +++ b/packages/plugin-eslint/src/lib/meta/versions/flat.integration.test.ts @@ -1,11 +1,11 @@ import type { ESLint, Linter, Rule } from 'eslint'; import { mkdir, rm, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import type { RuleData } from '../parse.js'; import { loadRulesForFlatConfig } from './flat.js'; describe('loadRulesForFlatConfig', () => { - const workDir = join( + const workDir = path.join( process.cwd(), 'tmp', 'plugin-eslint', @@ -22,14 +22,14 @@ describe('loadRulesForFlatConfig', () => { }); it('should load built-in rules from implicit flat config location', async () => { - const config: Linter.FlatConfig = { + const config: Linter.Config = { rules: { 'no-unused-vars': 'error', 'prefer-const': 'warn', }, }; await writeFile( - join(workDir, 'eslint.config.js'), + path.join(workDir, 'eslint.config.js'), `export default ${JSON.stringify(config, null, 2)}`, ); @@ -40,8 +40,8 @@ describe('loadRulesForFlatConfig', () => { }), }); await expect(loadRulesForFlatConfig({})).resolves.toEqual([ - { ruleId: 'no-unused-vars', meta: expectedMeta, options: [] }, - { ruleId: 'prefer-const', meta: expectedMeta, options: [] }, + { id: 'no-unused-vars', meta: expectedMeta, options: [] }, + { id: 'prefer-const', meta: expectedMeta, options: [] }, ] satisfies RuleData[]); }); @@ -78,7 +78,7 @@ describe('loadRulesForFlatConfig', () => { } as Rule.RuleModule, }, } as ESLint.Plugin; - const config: Linter.FlatConfig[] = [ + const config: Linter.Config[] = [ { plugins: { '@typescript-eslint': tseslint, @@ -102,7 +102,7 @@ describe('loadRulesForFlatConfig', () => { }, ]; await writeFile( - join(workDir, 'code-pushup.eslint.config.js'), + path.join(workDir, 'code-pushup.eslint.config.js'), `export default ${JSON.stringify(config, null, 2)}`, ); @@ -110,7 +110,7 @@ describe('loadRulesForFlatConfig', () => { loadRulesForFlatConfig({ eslintrc: 'code-pushup.eslint.config.js' }), ).resolves.toEqual([ { - ruleId: '@typescript-eslint/no-explicit-any', + id: '@typescript-eslint/no-explicit-any', meta: { docs: { description: 'Disallow the `any` type', @@ -120,7 +120,7 @@ describe('loadRulesForFlatConfig', () => { options: [], }, { - ruleId: 'react-hooks/rules-of-hooks', + id: 'react-hooks/rules-of-hooks', meta: { docs: { description: 'enforces the Rules of Hooks', @@ -130,7 +130,7 @@ describe('loadRulesForFlatConfig', () => { options: [], }, { - ruleId: '@typescript-eslint/no-unsafe-call', + id: '@typescript-eslint/no-unsafe-call', meta: { docs: { description: 'Disallow calling a value with type `any`', @@ -143,7 +143,7 @@ describe('loadRulesForFlatConfig', () => { }); it('should load custom rule options', async () => { - const config: Linter.FlatConfig[] = [ + const config: Linter.Config[] = [ { rules: { complexity: ['warn', 30], @@ -152,18 +152,18 @@ describe('loadRulesForFlatConfig', () => { }, ]; await writeFile( - join(workDir, 'eslint.config.cjs'), + path.join(workDir, 'eslint.config.cjs'), `module.exports = ${JSON.stringify(config, null, 2)}`, ); await expect(loadRulesForFlatConfig({})).resolves.toEqual([ { - ruleId: 'complexity', + id: 'complexity', meta: expect.any(Object), options: [30], }, { - ruleId: 'eqeqeq', + id: 'eqeqeq', meta: expect.any(Object), options: ['always', { null: 'never' }], }, @@ -171,7 +171,7 @@ describe('loadRulesForFlatConfig', () => { }); it('should create multiple rule instances when different options used', async () => { - const config: Linter.FlatConfig[] = [ + const config: Linter.Config[] = [ { rules: { 'max-lines': ['warn', { max: 300 }], @@ -185,18 +185,18 @@ describe('loadRulesForFlatConfig', () => { }, ]; await writeFile( - join(workDir, 'eslint.config.mjs'), + path.join(workDir, 'eslint.config.mjs'), `export default ${JSON.stringify(config, null, 2)}`, ); await expect(loadRulesForFlatConfig({})).resolves.toEqual([ { - ruleId: 'max-lines', + id: 'max-lines', meta: expect.any(Object), options: [{ max: 300 }], }, { - ruleId: 'max-lines', + id: 'max-lines', meta: expect.any(Object), options: [{ max: 500 }], }, diff --git a/packages/plugin-eslint/src/lib/meta/versions/flat.ts b/packages/plugin-eslint/src/lib/meta/versions/flat.ts index f3be89417..80e9377ca 100644 --- a/packages/plugin-eslint/src/lib/meta/versions/flat.ts +++ b/packages/plugin-eslint/src/lib/meta/versions/flat.ts @@ -1,7 +1,6 @@ import type { Linter, Rule } from 'eslint'; -// eslint-disable-next-line import/no-deprecated import { builtinRules } from 'eslint/use-at-your-own-risk'; -import { isAbsolute, join } from 'node:path'; +import path from 'node:path'; import { pathToFileURL } from 'node:url'; import { exists, findNearestFile, toArray, ui } from '@code-pushup/utils'; import type { ESLintTarget } from '../../config.js'; @@ -24,9 +23,9 @@ export async function loadRulesForFlatConfig({ const rules = findEnabledRulesWithOptions(configs); return rules .map(rule => { - const meta = findRuleMeta(rule.ruleId, configs); + const meta = findRuleMeta(rule.id, configs); if (!meta) { - ui().logger.warning(`Cannot find metadata for rule ${rule.ruleId}`); + ui().logger.warning(`Cannot find metadata for rule ${rule.id}`); return null; } return { ...rule, meta }; @@ -34,7 +33,7 @@ export async function loadRulesForFlatConfig({ .filter(exists); } -type FlatConfig = Linter.FlatConfig | Linter.FlatConfig[]; +type FlatConfig = Linter.Config | Linter.Config[]; async function loadConfigByDefaultLocation(): Promise { const flatConfigFileNames = [ @@ -54,27 +53,29 @@ async function loadConfigByDefaultLocation(): Promise { ); } -async function loadConfigByPath(path: string): Promise { - const absolutePath = isAbsolute(path) ? path : join(process.cwd(), path); +async function loadConfigByPath(configPath: string): Promise { + const absolutePath = path.isAbsolute(configPath) + ? configPath + : path.join(process.cwd(), configPath); const url = pathToFileURL(absolutePath).toString(); const mod = (await import(url)) as FlatConfig | { default: FlatConfig }; return 'default' in mod ? mod.default : mod; } function findEnabledRulesWithOptions( - configs: Linter.FlatConfig[], + configs: Linter.Config[], ): Omit[] { const enabledRules = configs .flatMap(({ rules }) => Object.entries(rules ?? {})) .filter(([, entry]) => entry != null && !isRuleOff(entry)) - .map(([ruleId, entry]) => ({ - ruleId, + .map(([id, entry]) => ({ + id, options: entry ? optionsFromRuleEntry(entry) : [], })); const uniqueRulesMap = new Map( - enabledRules.map(({ ruleId, options }) => [ - `${ruleId}::${jsonHash(options)}`, - { ruleId, options }, + enabledRules.map(({ id, options }) => [ + `${id}::${jsonHash(options)}`, + { id, options }, ]), ); return [...uniqueRulesMap.values()]; @@ -82,7 +83,7 @@ function findEnabledRulesWithOptions( function findRuleMeta( ruleId: string, - configs: Linter.FlatConfig[], + configs: Linter.Config[], ): Rule.RuleMetaData | undefined { const { plugin, name } = parseRuleId(ruleId); if (!plugin) { @@ -92,7 +93,6 @@ function findRuleMeta( } function findBuiltinRuleMeta(name: string): Rule.RuleMetaData | undefined { - // eslint-disable-next-line import/no-deprecated, deprecation/deprecation const rule = builtinRules.get(name); return rule?.meta; } @@ -100,7 +100,7 @@ function findBuiltinRuleMeta(name: string): Rule.RuleMetaData | undefined { function findPluginRuleMeta( plugin: string, name: string, - configs: Linter.FlatConfig[], + configs: Linter.Config[], ): Rule.RuleMetaData | undefined { const config = configs.find(({ plugins = {} }) => plugin in plugins); const rule = config?.plugins?.[plugin]?.rules?.[name]; diff --git a/packages/plugin-eslint/src/lib/meta/versions/legacy.ts b/packages/plugin-eslint/src/lib/meta/versions/legacy.ts index 304b3dc3e..e44f5145c 100644 --- a/packages/plugin-eslint/src/lib/meta/versions/legacy.ts +++ b/packages/plugin-eslint/src/lib/meta/versions/legacy.ts @@ -13,9 +13,9 @@ export async function loadRulesForLegacyConfig({ const configs = await toArray(patterns).reduce( async (acc, pattern) => [ ...(await acc), - (await eslint.calculateConfigForFile(pattern)) as Linter.Config, + (await eslint.calculateConfigForFile(pattern)) as Linter.LegacyConfig, ], - Promise.resolve([]), + Promise.resolve([]), ); const rulesIds = distinct( @@ -31,21 +31,19 @@ export async function loadRulesForLegacyConfig({ return configs .flatMap(config => Object.entries(config.rules ?? {})) - .map(([ruleId, ruleEntry]): RuleData | null => { - if (ruleEntry == null || isRuleOff(ruleEntry)) { + .map(([id, entry]): RuleData | null => { + if (entry == null || isRuleOff(entry)) { return null; } - const meta = rulesMeta[ruleId]; - if (!meta) { - ui().logger.warning(`Metadata not found for ESLint rule ${ruleId}`); + const ruleMeta = rulesMeta[id]; + if (!ruleMeta) { + ui().logger.warning(`Metadata not found for ESLint rule ${id}`); return null; } - const options = optionsFromRuleEntry(ruleEntry); - return { - ruleId, - meta, - options, - }; + // ignoring meta.defaultOptions to match legacy config handling in calculateConfigForFile + const { defaultOptions: _, ...meta } = ruleMeta; + const options = optionsFromRuleEntry(entry); + return { id, meta, options }; }) .filter(exists); } diff --git a/packages/plugin-eslint/src/lib/nx.integration.test.ts b/packages/plugin-eslint/src/lib/nx.integration.test.ts index c8c45794d..0874b39d5 100644 --- a/packages/plugin-eslint/src/lib/nx.integration.test.ts +++ b/packages/plugin-eslint/src/lib/nx.integration.test.ts @@ -1,7 +1,7 @@ -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { setWorkspaceRoot, workspaceRoot } from 'nx/src/utils/workspace-root'; import type { MockInstance } from 'vitest'; +import { executeProcess } from '@code-pushup/utils'; import type { ESLintTarget } from './config.js'; import { eslintConfigFromNxProject } from './nx/find-project-without-deps.js'; import { @@ -9,16 +9,14 @@ import { eslintConfigFromNxProjectAndDeps, } from './nx/index.js'; -const ALL_PROJECTS = ['cli', 'core', 'nx-plugin', 'utils'] as const; -type Project = (typeof ALL_PROJECTS)[number]; +type Project = 'cli' | 'core' | 'nx-plugin' | 'utils'; describe('Nx helpers', () => { let cwdSpy: MockInstance<[], string>; - const originalWorkspaceRoot = workspaceRoot; - beforeAll(() => { - const workspaceDir = join( - fileURLToPath(dirname(import.meta.url)), + beforeAll(async () => { + const workspaceDir = path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', '..', 'mocks', @@ -27,59 +25,35 @@ describe('Nx helpers', () => { ); cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue(workspaceDir); - // eslint-disable-next-line functional/immutable-data - process.env['NX_DAEMON'] = 'false'; - - setWorkspaceRoot(workspaceDir); + // HACK: somehow prevents "Failed to process project graph" errors + await executeProcess({ + command: 'npx nx graph --file=.nx/graph.json', + cwd: workspaceDir, + }); }); afterAll(() => { cwdSpy.mockRestore(); - setWorkspaceRoot(originalWorkspaceRoot); }); describe('create config from all Nx projects', () => { it('should include eslintrc and patterns of each project', async () => { await expect(eslintConfigFromAllNxProjects()).resolves.toEqual([ { - eslintrc: './packages/cli/.eslintrc.json', - patterns: [ - 'packages/cli/**/*.ts', - 'packages/cli/*.spec.ts', - 'packages/cli/*.cy.ts', - 'packages/cli/*.stories.ts', - 'packages/cli/.storybook/main.ts', - ], + eslintrc: './packages/cli/eslint.config.js', + patterns: ['packages/cli'], }, { - eslintrc: './packages/core/.eslintrc.json', - patterns: [ - 'packages/core/**/*.ts', - 'packages/core/*.spec.ts', - 'packages/core/*.cy.ts', - 'packages/core/*.stories.ts', - 'packages/core/.storybook/main.ts', - ], + eslintrc: './packages/core/eslint.config.js', + patterns: ['packages/core'], }, { - eslintrc: './packages/nx-plugin/.eslintrc.json', - patterns: [ - 'packages/nx-plugin/**/*.ts', - 'packages/nx-plugin/*.spec.ts', - 'packages/nx-plugin/*.cy.ts', - 'packages/nx-plugin/*.stories.ts', - 'packages/nx-plugin/.storybook/main.ts', - ], + eslintrc: './packages/nx-plugin/eslint.config.js', + patterns: ['packages/nx-plugin'], }, { - eslintrc: './packages/utils/.eslintrc.json', - patterns: [ - 'packages/utils/**/*.ts', - 'packages/utils/*.spec.ts', - 'packages/utils/*.cy.ts', - 'packages/utils/*.stories.ts', - 'packages/utils/.storybook/main.ts', - ], + eslintrc: './packages/utils/eslint.config.js', + patterns: ['packages/utils'], }, ] satisfies ESLintTarget[]); }); @@ -89,14 +63,8 @@ describe('Nx helpers', () => { eslintConfigFromAllNxProjects({ exclude: ['cli', 'core', 'utils'] }), ).resolves.toEqual([ { - eslintrc: './packages/nx-plugin/.eslintrc.json', - patterns: [ - 'packages/nx-plugin/**/*.ts', - 'packages/nx-plugin/*.spec.ts', - 'packages/nx-plugin/*.cy.ts', - 'packages/nx-plugin/*.stories.ts', - 'packages/nx-plugin/.storybook/main.ts', - ], + eslintrc: './packages/nx-plugin/eslint.config.js', + patterns: ['packages/nx-plugin'], }, ] satisfies ESLintTarget[]); }); @@ -130,8 +98,8 @@ describe('Nx helpers', () => { expect(targets).toEqual( expectedProjects.map( (p): ESLintTarget => ({ - eslintrc: `./packages/${p}/.eslintrc.json`, - patterns: expect.arrayContaining([`packages/${p}/**/*.ts`]), + eslintrc: `./packages/${p}/eslint.config.js`, + patterns: [`packages/${p}`], }), ), ); @@ -160,8 +128,8 @@ describe('Nx helpers', () => { const targets = await eslintConfigFromNxProject(project); expect(targets).toEqual({ - eslintrc: `./packages/${project}/.eslintrc.json`, - patterns: expect.arrayContaining([`packages/${project}/**/*.ts`]), + eslintrc: `./packages/${project}/eslint.config.js`, + patterns: [`packages/${project}`], }); }, ); diff --git a/packages/plugin-eslint/src/lib/nx/index.ts b/packages/plugin-eslint/src/lib/nx/index.ts index 37c3ba1f4..900692698 100644 --- a/packages/plugin-eslint/src/lib/nx/index.ts +++ b/packages/plugin-eslint/src/lib/nx/index.ts @@ -1,7 +1,6 @@ export { - // eslint-disable-next-line deprecation/deprecation - eslintConfigFromNxProjects, eslintConfigFromAllNxProjects, + eslintConfigFromNxProjects, } from './find-all-projects.js'; -export { eslintConfigFromNxProject } from './find-project-without-deps.js'; export { eslintConfigFromNxProjectAndDeps } from './find-project-with-deps.js'; +export { eslintConfigFromNxProject } from './find-project-without-deps.js'; diff --git a/packages/plugin-eslint/src/lib/nx/utils.ts b/packages/plugin-eslint/src/lib/nx/utils.ts index 45bd134be..17564f2b6 100644 --- a/packages/plugin-eslint/src/lib/nx/utils.ts +++ b/packages/plugin-eslint/src/lib/nx/utils.ts @@ -1,5 +1,5 @@ import type { ProjectConfiguration } from '@nx/devkit'; -import { join } from 'node:path'; +import path from 'node:path'; import { fileExists, toArray } from '@code-pushup/utils'; import type { ConfigFormat } from '../meta/index.js'; @@ -96,7 +96,7 @@ async function findProjectFile( // eslint-disable-next-line functional/no-loop-statements for (const ext of file.extensions) { const filename = `./${project.root}/${name}.${ext}`; - if (await fileExists(join(process.cwd(), filename))) { + if (await fileExists(path.join(process.cwd(), filename))) { return filename; } } diff --git a/packages/plugin-eslint/src/lib/runner.integration.test.ts b/packages/plugin-eslint/src/lib/runner.integration.test.ts index c4eed29d8..5f78884e5 100644 --- a/packages/plugin-eslint/src/lib/runner.integration.test.ts +++ b/packages/plugin-eslint/src/lib/runner.integration.test.ts @@ -1,5 +1,5 @@ import os from 'node:os'; -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { type MockInstance, describe, expect, it } from 'vitest'; import type { AuditOutput, AuditOutputs, Issue } from '@code-pushup/models'; @@ -24,8 +24,8 @@ describe('executeRunner', () => { await createRunnerConfig('bin.js', audits, targets); }; - const appDir = join( - fileURLToPath(dirname(import.meta.url)), + const appDir = path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', '..', 'mocks', @@ -45,7 +45,7 @@ describe('executeRunner', () => { }); it('should execute ESLint and create audit results for React application', async () => { - await createPluginConfig('.eslintrc.js'); + await createPluginConfig('eslint.config.js'); await executeRunner(); const json = await readJsonFile(RUNNER_OUTPUT_PATH); @@ -53,7 +53,7 @@ describe('executeRunner', () => { }); it('should execute runner with custom config using @code-pushup/eslint-config', async () => { - await createPluginConfig('code-pushup.eslintrc.yml'); + await createPluginConfig('code-pushup.eslint.config.mjs'); await executeRunner(); const json = await readJsonFile(RUNNER_OUTPUT_PATH); @@ -69,7 +69,7 @@ describe('executeRunner', () => { message: 'Filename is not in kebab case. Rename it to `use-todos.js`.', source: expect.objectContaining({ - file: join(appDir, 'src', 'hooks', 'useTodos.js'), + file: path.join(appDir, 'src', 'hooks', 'useTodos.js'), }), }, ]), diff --git a/packages/plugin-eslint/src/lib/runner/index.ts b/packages/plugin-eslint/src/lib/runner/index.ts index 894ee7e1d..fc60360e9 100644 --- a/packages/plugin-eslint/src/lib/runner/index.ts +++ b/packages/plugin-eslint/src/lib/runner/index.ts @@ -1,5 +1,5 @@ import { writeFile } from 'node:fs/promises'; -import { dirname, join } from 'node:path'; +import path from 'node:path'; import type { Audit, AuditOutput, RunnerConfig } from '@code-pushup/models'; import { ensureDirectoryExists, @@ -13,8 +13,8 @@ import { lintResultsToAudits, mergeLinterOutputs } from './transform.js'; import type { LinterOutput } from './types.js'; export const WORKDIR = pluginWorkDir('eslint'); -export const RUNNER_OUTPUT_PATH = join(WORKDIR, 'runner-output.json'); -export const PLUGIN_CONFIG_PATH = join( +export const RUNNER_OUTPUT_PATH = path.join(WORKDIR, 'runner-output.json'); +export const PLUGIN_CONFIG_PATH = path.join( process.cwd(), WORKDIR, 'plugin-config.json', @@ -42,7 +42,7 @@ export async function executeRunner(): Promise { }, ); - await ensureDirectoryExists(dirname(RUNNER_OUTPUT_PATH)); + await ensureDirectoryExists(path.dirname(RUNNER_OUTPUT_PATH)); await writeFile(RUNNER_OUTPUT_PATH, JSON.stringify(audits)); } @@ -55,7 +55,7 @@ export async function createRunnerConfig( targets, slugs: audits.map(audit => audit.slug), }; - await ensureDirectoryExists(dirname(PLUGIN_CONFIG_PATH)); + await ensureDirectoryExists(path.dirname(PLUGIN_CONFIG_PATH)); await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config)); return { diff --git a/packages/plugin-eslint/src/lib/runner/lint.unit.test.ts b/packages/plugin-eslint/src/lib/runner/lint.unit.test.ts index f5e390296..c0166b1c4 100644 --- a/packages/plugin-eslint/src/lib/runner/lint.unit.test.ts +++ b/packages/plugin-eslint/src/lib/runner/lint.unit.test.ts @@ -32,7 +32,6 @@ vi.mock('eslint', () => ({ vi.mock('@code-pushup/utils', async () => { const utils = await vi.importActual('@code-pushup/utils'); - // eslint-disable-next-line @typescript-eslint/naming-convention const testUtils: { MEMFS_VOLUME: string } = await vi.importActual( '@code-pushup/test-utils', ); diff --git a/packages/plugin-js-packages/.eslintrc.json b/packages/plugin-js-packages/.eslintrc.json deleted file mode 100644 index f594ad62e..000000000 --- a/packages/plugin-js-packages/.eslintrc.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["packages/plugin-js-packages/tsconfig.*?.json"] - } - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": ["error"] - } - } - ] -} diff --git a/packages/plugin-js-packages/eslint.config.js b/packages/plugin-js-packages/eslint.config.js new file mode 100644 index 000000000..40165321a --- /dev/null +++ b/packages/plugin-js-packages/eslint.config.js @@ -0,0 +1,21 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': 'error', + }, + }, +); diff --git a/packages/plugin-js-packages/src/lib/constants.ts b/packages/plugin-js-packages/src/lib/constants.ts index 823913069..8f51db12d 100644 --- a/packages/plugin-js-packages/src/lib/constants.ts +++ b/packages/plugin-js-packages/src/lib/constants.ts @@ -22,13 +22,13 @@ export const dependencyGroupToLong: Record< optional: 'optionalDependencies', }; -/* eslint-disable no-magic-numbers */ export const dependencyGroupWeights: Record = { + /* eslint-disable @typescript-eslint/no-magic-numbers */ prod: 80, dev: 15, optional: 5, + /* eslint-enable @typescript-eslint/no-magic-numbers */ }; -/* eslint-enable no-magic-numbers */ export const dependencyDocs: Record = { prod: 'https://classic.yarnpkg.com/docs/dependency-types#toc-dependencies', diff --git a/packages/plugin-js-packages/src/lib/js-packages-plugin.ts b/packages/plugin-js-packages/src/lib/js-packages-plugin.ts index 3e232c217..97e5b97e9 100644 --- a/packages/plugin-js-packages/src/lib/js-packages-plugin.ts +++ b/packages/plugin-js-packages/src/lib/js-packages-plugin.ts @@ -1,5 +1,5 @@ import { createRequire } from 'node:module'; -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import type { Audit, Group, PluginConfig } from '@code-pushup/models'; import { @@ -37,8 +37,8 @@ export async function jsPackagesPlugin( const { packageManager, checks, depGroups, ...jsPackagesPluginConfigRest } = await normalizeConfig(config); - const runnerScriptPath = join( - fileURLToPath(dirname(import.meta.url)), + const runnerScriptPath = path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', 'bin.js', ); diff --git a/packages/plugin-js-packages/src/lib/package-managers/derive-package-manager.ts b/packages/plugin-js-packages/src/lib/package-managers/derive-package-manager.ts index b327ad81b..e527603e5 100644 --- a/packages/plugin-js-packages/src/lib/package-managers/derive-package-manager.ts +++ b/packages/plugin-js-packages/src/lib/package-managers/derive-package-manager.ts @@ -1,5 +1,5 @@ import { readFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { fileExists } from '@code-pushup/utils'; import type { PackageManagerId } from '../config.js'; import { deriveYarnVersion } from './derive-yarn.js'; @@ -7,9 +7,9 @@ import { deriveYarnVersion } from './derive-yarn.js'; export async function derivePackageManagerInPackageJson( currentDir = process.cwd(), ) { - if (await fileExists(join(currentDir, 'package.json'))) { + if (await fileExists(path.join(currentDir, 'package.json'))) { const content = JSON.parse( - (await readFile(join('package.json'))).toString(), + (await readFile(path.join('package.json'))).toString(), ) as { packageManager?: string }; const { packageManager: packageManagerData = '' } = content; @@ -39,11 +39,11 @@ export async function derivePackageManager( } // Check for lock files - if (await fileExists(join(currentDir, 'package-lock.json'))) { + if (await fileExists(path.join(currentDir, 'package-lock.json'))) { return 'npm'; - } else if (await fileExists(join(currentDir, 'pnpm-lock.yaml'))) { + } else if (await fileExists(path.join(currentDir, 'pnpm-lock.yaml'))) { return 'pnpm'; - } else if (await fileExists(join(currentDir, 'yarn.lock'))) { + } else if (await fileExists(path.join(currentDir, 'yarn.lock'))) { const yarnVersion = await deriveYarnVersion(); if (yarnVersion) { return yarnVersion; diff --git a/packages/plugin-js-packages/src/lib/package-managers/derive-package-manager.unit.test.ts b/packages/plugin-js-packages/src/lib/package-managers/derive-package-manager.unit.test.ts index 7f7e2724a..ab4dc90fd 100644 --- a/packages/plugin-js-packages/src/lib/package-managers/derive-package-manager.unit.test.ts +++ b/packages/plugin-js-packages/src/lib/package-managers/derive-package-manager.unit.test.ts @@ -16,6 +16,7 @@ describe('derivePackageManagerInPackageJson', () => { fileExistsSpy.mockClear(); executeProcessSpy.mockClear(); }); + afterAll(() => { executeProcessSpy.mockRestore(); }); @@ -105,6 +106,7 @@ describe('derivePackageManager', () => { fileExistsSpy.mockClear(); deriveYarnVersionSpy.mockClear(); }); + afterAll(() => { fileExistsSpy.mockRestore(); deriveYarnVersionSpy.mockRestore(); diff --git a/packages/plugin-js-packages/src/lib/package-managers/derive-yarn.unit.test.ts b/packages/plugin-js-packages/src/lib/package-managers/derive-yarn.unit.test.ts index 868c8b9ed..b8ee0bc88 100644 --- a/packages/plugin-js-packages/src/lib/package-managers/derive-yarn.unit.test.ts +++ b/packages/plugin-js-packages/src/lib/package-managers/derive-yarn.unit.test.ts @@ -9,6 +9,7 @@ describe('deriveYarnVersion', () => { beforeEach(() => { executeProcessSpy.mockClear(); }); + afterAll(() => { executeProcessSpy.mockRestore(); }); diff --git a/packages/plugin-js-packages/src/lib/package-managers/npm/audit-result.ts b/packages/plugin-js-packages/src/lib/package-managers/npm/audit-result.ts index 172448d98..ba04e3d46 100644 --- a/packages/plugin-js-packages/src/lib/package-managers/npm/audit-result.ts +++ b/packages/plugin-js-packages/src/lib/package-managers/npm/audit-result.ts @@ -48,7 +48,7 @@ export function npmToFixInformation( export function npmToAdvisory( name: string, vulnerabilities: NpmVulnerabilities, - prevNodes: Set = new Set(), + prevNodes = new Set(), ): NpmAdvisory | null { const advisory = vulnerabilities[name]?.via; diff --git a/packages/plugin-js-packages/src/lib/package-managers/pnpm/types.ts b/packages/plugin-js-packages/src/lib/package-managers/pnpm/types.ts index de7f6d8a2..80c2aa40d 100644 --- a/packages/plugin-js-packages/src/lib/package-managers/pnpm/types.ts +++ b/packages/plugin-js-packages/src/lib/package-managers/pnpm/types.ts @@ -2,7 +2,6 @@ import type { PackageAuditLevel } from '../../config.js'; import type { DependencyGroupLong } from '../../runner/outdated/types.js'; -/* eslint-disable @typescript-eslint/naming-convention */ export type PnpmAuditAdvisory = { module_name: string; id: number; @@ -13,7 +12,6 @@ export type PnpmAuditAdvisory = { url: string; findings: { paths: string[] }[]; }; -/* eslint-enable @typescript-eslint/naming-convention */ export type PnpmAuditResultJson = { advisories: Record; diff --git a/packages/plugin-js-packages/src/lib/package-managers/yarn-classic/outdated-result.unit.test.ts b/packages/plugin-js-packages/src/lib/package-managers/yarn-classic/outdated-result.unit.test.ts index bc6877f63..c47113e64 100644 --- a/packages/plugin-js-packages/src/lib/package-managers/yarn-classic/outdated-result.unit.test.ts +++ b/packages/plugin-js-packages/src/lib/package-managers/yarn-classic/outdated-result.unit.test.ts @@ -14,6 +14,7 @@ import type { Yarnv1FieldName } from './types.js'; describe('yarnv1ToOutdatedResult', () => { const yarnInfo = { type: 'info', data: 'Colours' }; + it('should transform Yarn v1 outdated to unified outdated result', () => { const table = { type: 'table', diff --git a/packages/plugin-js-packages/src/lib/package-managers/yarn-classic/types.ts b/packages/plugin-js-packages/src/lib/package-managers/yarn-classic/types.ts index a465417b5..24bc4e29e 100644 --- a/packages/plugin-js-packages/src/lib/package-managers/yarn-classic/types.ts +++ b/packages/plugin-js-packages/src/lib/package-managers/yarn-classic/types.ts @@ -8,7 +8,6 @@ export type Yarnv1AuditAdvisory = { id: number; path: string; }; - /* eslint-disable @typescript-eslint/naming-convention */ advisory: { module_name: string; severity: PackageAuditLevel; @@ -17,7 +16,6 @@ export type Yarnv1AuditAdvisory = { title: string; url: string; }; - /* eslint-enable @typescript-eslint/naming-convention */ }; }; diff --git a/packages/plugin-js-packages/src/lib/package-managers/yarn-modern/types.ts b/packages/plugin-js-packages/src/lib/package-managers/yarn-modern/types.ts index 87f46cfaa..3dcc164ad 100644 --- a/packages/plugin-js-packages/src/lib/package-managers/yarn-modern/types.ts +++ b/packages/plugin-js-packages/src/lib/package-managers/yarn-modern/types.ts @@ -2,7 +2,6 @@ import type { PackageAuditLevel } from '../../config.js'; import type { DependencyGroupLong } from '../../runner/outdated/types.js'; -/* eslint-disable @typescript-eslint/naming-convention */ export type Yarnv2AuditAdvisory = { module_name: string; severity: PackageAuditLevel; @@ -12,7 +11,6 @@ export type Yarnv2AuditAdvisory = { url: string; findings: { paths: string[] }[]; // TODO indirect? }; -/* eslint-enable @typescript-eslint/naming-convention */ export type Yarnv2AuditResultJson = { advisories: Record; diff --git a/packages/plugin-js-packages/src/lib/runner/audit/constants.ts b/packages/plugin-js-packages/src/lib/runner/audit/constants.ts index 99b6daca6..1c9350eb7 100644 --- a/packages/plugin-js-packages/src/lib/runner/audit/constants.ts +++ b/packages/plugin-js-packages/src/lib/runner/audit/constants.ts @@ -1,11 +1,11 @@ import type { PackageAuditLevel } from '../../config.js'; -/* eslint-disable no-magic-numbers */ export const auditScoreModifiers: Record = { + /* eslint-disable @typescript-eslint/no-magic-numbers */ critical: 1, high: 0.1, moderate: 0.05, low: 0.02, info: 0.01, + /* eslint-enable @typescript-eslint/no-magic-numbers */ }; -/* eslint-enable no-magic-numbers */ diff --git a/packages/plugin-js-packages/src/lib/runner/constants.ts b/packages/plugin-js-packages/src/lib/runner/constants.ts index e2bcec21b..19d0f6f7d 100644 --- a/packages/plugin-js-packages/src/lib/runner/constants.ts +++ b/packages/plugin-js-packages/src/lib/runner/constants.ts @@ -1,9 +1,9 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { pluginWorkDir } from '@code-pushup/utils'; export const WORKDIR = pluginWorkDir('js-packages'); -export const RUNNER_OUTPUT_PATH = join(WORKDIR, 'runner-output.json'); -export const PLUGIN_CONFIG_PATH = join( +export const RUNNER_OUTPUT_PATH = path.join(WORKDIR, 'runner-output.json'); +export const PLUGIN_CONFIG_PATH = path.join( process.cwd(), WORKDIR, 'plugin-config.json', diff --git a/packages/plugin-js-packages/src/lib/runner/index.ts b/packages/plugin-js-packages/src/lib/runner/index.ts index 0285e2731..2c5f9a9e7 100644 --- a/packages/plugin-js-packages/src/lib/runner/index.ts +++ b/packages/plugin-js-packages/src/lib/runner/index.ts @@ -1,5 +1,5 @@ import { writeFile } from 'node:fs/promises'; -import { dirname } from 'node:path'; +import path from 'node:path'; import type { RunnerConfig } from '@code-pushup/models'; import { ensureDirectoryExists, @@ -30,7 +30,7 @@ export async function createRunnerConfig( scriptPath: string, config: FinalJSPackagesPluginConfig, ): Promise { - await ensureDirectoryExists(dirname(PLUGIN_CONFIG_PATH)); + await ensureDirectoryExists(path.dirname(PLUGIN_CONFIG_PATH)); await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config)); return { @@ -58,7 +58,7 @@ export async function executeRunner(): Promise { : []; const checkResults = [...auditResults, ...outdatedResults]; - await ensureDirectoryExists(dirname(RUNNER_OUTPUT_PATH)); + await ensureDirectoryExists(path.dirname(RUNNER_OUTPUT_PATH)); await writeFile(RUNNER_OUTPUT_PATH, JSON.stringify(checkResults)); } @@ -129,7 +129,7 @@ async function processAudit( const rejected = auditResults.filter(isPromiseRejectedResult); if (rejected.length > 0) { - rejected.map(result => { + rejected.forEach(result => { console.error(result.reason); }); diff --git a/packages/plugin-js-packages/src/lib/runner/utils.ts b/packages/plugin-js-packages/src/lib/runner/utils.ts index f0aca616b..db0f318e6 100644 --- a/packages/plugin-js-packages/src/lib/runner/utils.ts +++ b/packages/plugin-js-packages/src/lib/runner/utils.ts @@ -1,4 +1,4 @@ -import { sep } from 'node:path'; +import path from 'node:path'; import { crawlFileSystem, objectFromEntries, @@ -62,10 +62,10 @@ export async function findAllPackageJson(): Promise { pattern: /(^|[\\/])package\.json$/, }) ).filter( - path => - !path.startsWith(`node_modules${sep}`) && - !path.includes(`${sep}node_modules${sep}`) && - !path.startsWith(`.nx${sep}`), + filePath => + !filePath.startsWith(`node_modules${path.sep}`) && + !filePath.includes(`${path.sep}node_modules${path.sep}`) && + !filePath.startsWith(`.nx${path.sep}`), ); } diff --git a/packages/plugin-js-packages/src/lib/runner/utils.unit.test.ts b/packages/plugin-js-packages/src/lib/runner/utils.unit.test.ts index 2aff7d72c..321606a30 100644 --- a/packages/plugin-js-packages/src/lib/runner/utils.unit.test.ts +++ b/packages/plugin-js-packages/src/lib/runner/utils.unit.test.ts @@ -1,5 +1,5 @@ import { vol } from 'memfs'; -import { join } from 'node:path'; +import path from 'node:path'; import { describe, expect, it } from 'vitest'; import { MEMFS_VOLUME } from '@code-pushup/test-utils'; import type { AuditResult, Vulnerability } from './audit/types.js'; @@ -15,11 +15,11 @@ describe('findAllPackageJson', () => { vol.fromJSON( { 'package.json': '', - [join('ui', 'package.json')]: '', - [join('ui', 'ng-package.json')]: '', // non-exact file match should be excluded - [join('.nx', 'cache', 'ui', 'package.json')]: '', // nx cache should be excluded - [join('node_modules', 'eslint', 'package.json')]: '', // root node_modules should be excluded - [join('ui', 'node_modules', 'eslint', 'package.json')]: '', // project node_modules should be excluded + [path.join('ui', 'package.json')]: '', + [path.join('ui', 'ng-package.json')]: '', // non-exact file match should be excluded + [path.join('.nx', 'cache', 'ui', 'package.json')]: '', // nx cache should be excluded + [path.join('node_modules', 'eslint', 'package.json')]: '', // root node_modules should be excluded + [path.join('ui', 'node_modules', 'eslint', 'package.json')]: '', // project node_modules should be excluded }, MEMFS_VOLUME, ); @@ -28,7 +28,7 @@ describe('findAllPackageJson', () => { it('should return all valid package.json files (exclude .nx, node_modules)', async () => { await expect(findAllPackageJson()).resolves.toEqual([ 'package.json', - join('ui', 'package.json'), + path.join('ui', 'package.json'), ]); }); }); @@ -45,7 +45,7 @@ describe('getTotalDependencies', () => { vitest: '1.3.1', }, } satisfies PackageJson), - [join('ui', 'package.json')]: JSON.stringify({ + [path.join('ui', 'package.json')]: JSON.stringify({ dependencies: { '@code-pushup/eslint-config': '1.0.0', '@typescript-eslint/eslint-plugin': '2.0.0', @@ -64,7 +64,7 @@ describe('getTotalDependencies', () => { it('should return correct number of dependencies', async () => { await expect( - getTotalDependencies([join(MEMFS_VOLUME, 'package.json')]), + getTotalDependencies([path.join(MEMFS_VOLUME, 'package.json')]), ).resolves.toStrictEqual({ dependencies: 1, devDependencies: 3, @@ -75,8 +75,8 @@ describe('getTotalDependencies', () => { it('should merge dependencies for multiple package.json files', async () => { await expect( getTotalDependencies([ - join(MEMFS_VOLUME, 'package.json'), - join(MEMFS_VOLUME, 'ui', 'package.json'), + path.join(MEMFS_VOLUME, 'package.json'), + path.join(MEMFS_VOLUME, 'ui', 'package.json'), ]), ).resolves.toStrictEqual({ dependencies: 2, diff --git a/packages/plugin-lighthouse/.eslintrc.json b/packages/plugin-lighthouse/.eslintrc.json deleted file mode 100644 index 8af3ca090..000000000 --- a/packages/plugin-lighthouse/.eslintrc.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["packages/plugin-lighthouse/tsconfig.*?.json"] - } - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": ["error"] - } - } - ] -} diff --git a/packages/plugin-lighthouse/eslint.config.js b/packages/plugin-lighthouse/eslint.config.js new file mode 100644 index 000000000..40165321a --- /dev/null +++ b/packages/plugin-lighthouse/eslint.config.js @@ -0,0 +1,21 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': 'error', + }, + }, +); diff --git a/packages/plugin-lighthouse/src/lib/constants.ts b/packages/plugin-lighthouse/src/lib/constants.ts index 12f1d6d5d..5d3cce15c 100644 --- a/packages/plugin-lighthouse/src/lib/constants.ts +++ b/packages/plugin-lighthouse/src/lib/constants.ts @@ -1,12 +1,12 @@ import { DEFAULT_FLAGS } from 'chrome-launcher/dist/flags.js'; -import { join } from 'node:path'; +import path from 'node:path'; import { DEFAULT_PERSIST_OUTPUT_DIR } from '@code-pushup/models'; // headless is needed to pass CI on Linux and Windows (locally it works without headless too) export const DEFAULT_CHROME_FLAGS = [...DEFAULT_FLAGS, '--headless']; export const LIGHTHOUSE_PLUGIN_SLUG = 'lighthouse'; -export const LIGHTHOUSE_OUTPUT_PATH = join( +export const LIGHTHOUSE_OUTPUT_PATH = path.join( DEFAULT_PERSIST_OUTPUT_DIR, LIGHTHOUSE_PLUGIN_SLUG, ); diff --git a/packages/plugin-lighthouse/src/lib/normalize-flags.unit.test.ts b/packages/plugin-lighthouse/src/lib/normalize-flags.unit.test.ts index f027b84c4..31c817d02 100644 --- a/packages/plugin-lighthouse/src/lib/normalize-flags.unit.test.ts +++ b/packages/plugin-lighthouse/src/lib/normalize-flags.unit.test.ts @@ -1,5 +1,5 @@ import { bold, yellow } from 'ansis'; -import { join } from 'node:path'; +import path from 'node:path'; import { describe, expect, it } from 'vitest'; import { getLogMessages } from '@code-pushup/test-utils'; import { ui } from '@code-pushup/utils'; @@ -18,6 +18,7 @@ describe('logUnsupportedFlagsInUse', () => { )} used unsupported flags: ${bold('list-all-audits')}`, ); }); + it('should log only 3 details of unsupported entries', () => { const unsupportedFlags = { 'list-all-audits': true, @@ -54,8 +55,9 @@ describe('normalizeFlags', () => { // custom overwrites in favour of the plugin quiet: true, output: ['json'], - outputPath: join(LIGHTHOUSE_OUTPUT_PATH, LIGHTHOUSE_REPORT_NAME), + outputPath: path.join(LIGHTHOUSE_OUTPUT_PATH, LIGHTHOUSE_REPORT_NAME), }; + it('should fill defaults with undefined flags', () => { expect(normalizeFlags()).toStrictEqual(normalizedDefaults); }); diff --git a/packages/plugin-lighthouse/src/lib/runner/constants.ts b/packages/plugin-lighthouse/src/lib/runner/constants.ts index 1b5f82672..e57a7ef55 100644 --- a/packages/plugin-lighthouse/src/lib/runner/constants.ts +++ b/packages/plugin-lighthouse/src/lib/runner/constants.ts @@ -5,7 +5,7 @@ import { type Audit as LHAudit, defaultConfig, } from 'lighthouse'; -import { join } from 'node:path'; +import path from 'node:path'; import type { Audit, Group } from '@code-pushup/models'; import { DEFAULT_CHROME_FLAGS, LIGHTHOUSE_OUTPUT_PATH } from '../constants.js'; @@ -75,8 +75,8 @@ async function loadLighthouseAudit( // shape: string // otherwise it is a JS object maintaining a `path` property // shape: { path: string, options?: {}; } - const path = typeof value === 'string' ? value : value.path; - const module = (await import(`lighthouse/core/audits/${path}.js`)) as { + const file = typeof value === 'string' ? value : value.path; + const module = (await import(`lighthouse/core/audits/${file}.js`)) as { default: typeof LHAudit; }; return module.default; @@ -101,5 +101,5 @@ export const DEFAULT_CLI_FLAGS = { skipAudits: [], onlyCategories: [], output: ['json'], - outputPath: join(LIGHTHOUSE_OUTPUT_PATH, LIGHTHOUSE_REPORT_NAME), + outputPath: path.join(LIGHTHOUSE_OUTPUT_PATH, LIGHTHOUSE_REPORT_NAME), } satisfies Partial; diff --git a/packages/plugin-lighthouse/src/lib/runner/details/item-value.ts b/packages/plugin-lighthouse/src/lib/runner/details/item-value.ts index 49560850c..87de82abc 100644 --- a/packages/plugin-lighthouse/src/lib/runner/details/item-value.ts +++ b/packages/plugin-lighthouse/src/lib/runner/details/item-value.ts @@ -58,7 +58,7 @@ export function formatTableItemPropertyValue( const parsedItemValue = parseTableItemPropertyValue(itemValue); - /* eslint-disable no-magic-numbers */ + /* eslint-disable @typescript-eslint/no-magic-numbers */ switch (itemValueFormat) { case 'bytes': return formatBytes(Number(parsedItemValue)); @@ -94,7 +94,7 @@ export function formatTableItemPropertyValue( ui().logger.info(`Format type ${bold('thumbnail')} is not implemented`); return ''; } - /* eslint-enable no-magic-numbers */ + /* eslint-enable @typescript-eslint/no-magic-numbers */ return itemValue; } diff --git a/packages/plugin-lighthouse/src/lib/runner/runner.ts b/packages/plugin-lighthouse/src/lib/runner/runner.ts index e52c24044..a750dd992 100644 --- a/packages/plugin-lighthouse/src/lib/runner/runner.ts +++ b/packages/plugin-lighthouse/src/lib/runner/runner.ts @@ -1,6 +1,6 @@ import type { RunnerResult } from 'lighthouse'; import { runLighthouse } from 'lighthouse/cli/run.js'; -import { dirname } from 'node:path'; +import path from 'node:path'; import type { AuditOutputs, RunnerFunction } from '@code-pushup/models'; import { ensureDirectoryExists } from '@code-pushup/utils'; import { DEFAULT_CLI_FLAGS } from './constants.js'; @@ -28,7 +28,7 @@ export function createRunnerFunction( const config = await getConfig({ configPath, preset }); if (outputPath) { - await ensureDirectoryExists(dirname(outputPath)); + await ensureDirectoryExists(path.dirname(outputPath)); } const enrichedFlags = { diff --git a/packages/plugin-lighthouse/src/lib/runner/runner.unit.test.ts b/packages/plugin-lighthouse/src/lib/runner/runner.unit.test.ts index 6b97d704b..c0d2474bf 100644 --- a/packages/plugin-lighthouse/src/lib/runner/runner.unit.test.ts +++ b/packages/plugin-lighthouse/src/lib/runner/runner.unit.test.ts @@ -39,7 +39,7 @@ vi.mock('lighthouse/cli/run.js', async () => { config, lhr: { audits: { - ['cumulative-layout-shift']: { + 'cumulative-layout-shift': { id: 'cumulative-layout-shift', title: 'Cumulative Layout Shift', description: diff --git a/packages/plugin-lighthouse/src/lib/runner/utils.unit.test.ts b/packages/plugin-lighthouse/src/lib/runner/utils.unit.test.ts index 5e0aee4b9..8f2aff376 100644 --- a/packages/plugin-lighthouse/src/lib/runner/utils.unit.test.ts +++ b/packages/plugin-lighthouse/src/lib/runner/utils.unit.test.ts @@ -4,7 +4,7 @@ import log from 'lighthouse-logger'; import type Details from 'lighthouse/types/lhr/audit-details'; import type { Result } from 'lighthouse/types/lhr/audit-result'; import { vol } from 'memfs'; -import { join } from 'node:path'; +import path from 'node:path'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { type AuditOutput, @@ -376,7 +376,7 @@ describe('getConfig', () => { it('should return undefined and log if configPath has wrong extension', async () => { await expect( - getConfig({ configPath: join('wrong.not') }), + getConfig({ configPath: path.join('wrong.not') }), ).resolves.toBeUndefined(); expect(getLogMessages(ui().logger).at(0)).toMatch( 'Format of file wrong.not not supported', @@ -386,6 +386,7 @@ describe('getConfig', () => { describe('determineAndSetLogLevel', () => { const debugLib = debug as { enabled: (flag: string) => boolean }; + beforeEach(() => { log.setLevel('info'); }); diff --git a/packages/utils/.eslintrc.json b/packages/utils/.eslintrc.json deleted file mode 100644 index 7d0618efc..000000000 --- a/packages/utils/.eslintrc.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "parserOptions": { - "project": ["packages/utils/tsconfig.*?.json"] - }, - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": [ - "error", - { - "ignoredDependencies": ["esbuild"] // esbuild is a peer dependency of bundle-require - } - ] - } - } - ] -} diff --git a/packages/utils/eslint.config.js b/packages/utils/eslint.config.js new file mode 100644 index 000000000..1ad01224a --- /dev/null +++ b/packages/utils/eslint.config.js @@ -0,0 +1,24 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config( + ...baseConfig, + { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': [ + 'error', + { ignoredDependencies: ['esbuild'] }, // esbuild is a peer dependency of bundle-require + ], + }, + }, +); diff --git a/packages/utils/package.json b/packages/utils/package.json index af18362a9..a8c1daa8b 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -23,6 +23,9 @@ "access": "public" }, "type": "module", + "engines": { + "node": ">=17.0.0" + }, "dependencies": { "@code-pushup/models": "0.56.0", "@isaacs/cliui": "^8.0.2", diff --git a/packages/utils/perf/crawl-file-system/index.ts b/packages/utils/perf/crawl-file-system/index.ts index 4f27f8a6c..e158ab050 100644 --- a/packages/utils/perf/crawl-file-system/index.ts +++ b/packages/utils/perf/crawl-file-system/index.ts @@ -1,5 +1,5 @@ import * as Benchmark from 'benchmark'; -import { join } from 'node:path'; +import path from 'node:path'; import { type CrawlFileSystemOptions, crawlFileSystem, @@ -21,7 +21,7 @@ const suite = new Benchmark.Suite('report-scoring'); const TARGET_DIRECTORY = PROCESS_ARGUMENT_TARGET_DIRECTORY || - join(process.cwd(), '..', '..', '..', 'node_modules'); + path.join(process.cwd(), '..', '..', '..', 'node_modules'); const PATTERN = PROCESS_ARGUMENT_PATTERN || /.json$/; // ================== @@ -30,7 +30,7 @@ const start = performance.now(); // Add listener const listeners = { - cycle: function (event: Benchmark.Event) { + cycle(event: Benchmark.Event) { console.info(String(event.target)); }, complete: () => { @@ -87,7 +87,7 @@ function wrapWithDefer( ) { return { defer: true, // important for async functions - fn: function (deferred: { resolve: () => void }) { + fn(deferred: { resolve: () => void }) { return asyncFn(options) .catch(() => []) .then((result: unknown[]) => { diff --git a/packages/utils/perf/score-report/index.ts b/packages/utils/perf/score-report/index.ts index 45547cf5c..3f477e915 100644 --- a/packages/utils/perf/score-report/index.ts +++ b/packages/utils/perf/score-report/index.ts @@ -34,7 +34,6 @@ const PROCESS_ARGUMENT_NUM_GROUPS_P2 = Number.parseInt( 10, ); -// eslint-disable-next-line import/no-named-as-default-member const suite = new Benchmark.Suite('report-scoring'); const AUDIT_PREFIX = 'a-'; @@ -53,7 +52,7 @@ const NUM_GROUPS_P2 = PROCESS_ARGUMENT_NUM_GROUPS_P2 || NUM_AUDITS_P2 / 2; // Add listener const listeners = { - cycle: function (event: Benchmark.Event) { + cycle(event: Benchmark.Event) { console.info(String(event.target)); }, complete: () => { @@ -139,7 +138,7 @@ function minimalReport(opt?: MinimalReportOptions): Report { packageName: 'perf-benchmark', version: '0', commit: { - date: date, + date, message: 'perf: benchmark score report', author: 'me', hash: 'mock_hash', diff --git a/packages/utils/perf/score-report/optimized1.ts b/packages/utils/perf/score-report/optimized1.ts index 6659b9eb6..cd2ec77eb 100644 --- a/packages/utils/perf/score-report/optimized1.ts +++ b/packages/utils/perf/score-report/optimized1.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-assignment */ // Note: The plugins of the ScoredReport are not structured correctly, hence the ESLint disables. import type { Report } from '@code-pushup/models'; import { GroupRefInvalidError } from '../../src/lib/reports/scoring.js'; diff --git a/packages/utils/perf/score-report/optimized2.ts b/packages/utils/perf/score-report/optimized2.ts index 11a0b886c..e48a8fd39 100644 --- a/packages/utils/perf/score-report/optimized2.ts +++ b/packages/utils/perf/score-report/optimized2.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-assignment */ // Note: The plugins of the ScoredReport are not structured correctly, hence the ESLint disables. import type { CategoryRef, GroupRef, Report } from '@code-pushup/models'; import { GroupRefInvalidError } from '../../src/lib/reports/scoring.js'; diff --git a/packages/utils/src/lib/file-system.integration.test.ts b/packages/utils/src/lib/file-system.integration.test.ts index 88688648b..74e969225 100644 --- a/packages/utils/src/lib/file-system.integration.test.ts +++ b/packages/utils/src/lib/file-system.integration.test.ts @@ -1,14 +1,20 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { describe, expect, it } from 'vitest'; import { importModule } from './file-system.js'; describe('importModule', () => { - const mockDir = join(process.cwd(), 'packages', 'utils', 'mocks', 'fixtures'); + const mockDir = path.join( + process.cwd(), + 'packages', + 'utils', + 'mocks', + 'fixtures', + ); it('should load a valid ES module', async () => { await expect( importModule({ - filepath: join(mockDir, 'valid-es-module-export.mjs'), + filepath: path.join(mockDir, 'valid-es-module-export.mjs'), }), ).resolves.toBe('valid-es-module-export'); }); @@ -16,7 +22,7 @@ describe('importModule', () => { it('should load a valid CommonJS module', async () => { await expect( importModule({ - filepath: join(mockDir, 'valid-cjs-module-export.cjs'), + filepath: path.join(mockDir, 'valid-cjs-module-export.cjs'), }), ).resolves.toBe('valid-cjs-module-export'); }); @@ -24,7 +30,7 @@ describe('importModule', () => { it('should load an ES module without default export', async () => { await expect( importModule({ - filepath: join(mockDir, 'no-default-export.mjs'), + filepath: path.join(mockDir, 'no-default-export.mjs'), }), ).resolves.toEqual( expect.objectContaining({ exportedVar: 'exported-variable' }), diff --git a/packages/utils/src/lib/file-system.ts b/packages/utils/src/lib/file-system.ts index 6234c17f5..966dbc536 100644 --- a/packages/utils/src/lib/file-system.ts +++ b/packages/utils/src/lib/file-system.ts @@ -1,33 +1,33 @@ import { bold, gray } from 'ansis'; import { type Options, bundleRequire } from 'bundle-require'; import { mkdir, readFile, readdir, rm, stat } from 'node:fs/promises'; -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { formatBytes } from './formatting.js'; import { logMultipleResults } from './log-results.js'; import { ui } from './logging.js'; -export async function readTextFile(path: string): Promise { - const buffer = await readFile(path); +export async function readTextFile(filePath: string): Promise { + const buffer = await readFile(filePath); return buffer.toString(); } -export async function readJsonFile(path: string): Promise { - const text = await readTextFile(path); +export async function readJsonFile(filePath: string): Promise { + const text = await readTextFile(filePath); return JSON.parse(text) as T; } -export async function fileExists(path: string): Promise { +export async function fileExists(filePath: string): Promise { try { - const stats = await stat(path); + const stats = await stat(filePath); return stats.isFile(); } catch { return false; } } -export async function directoryExists(path: string): Promise { +export async function directoryExists(filePath: string): Promise { try { - const stats = await stat(path); + const stats = await stat(filePath); return stats.isDirectory(); } catch { return false; @@ -85,7 +85,7 @@ export async function importModule(options: Options): Promise { } export function pluginWorkDir(slug: string): string { - return join('node_modules', '.code-pushup', slug); + return path.join('node_modules', '.code-pushup', slug); } export type CrawlFileSystemOptions = { @@ -104,7 +104,7 @@ export async function crawlFileSystem( const files = await readdir(directory); const promises = files.map(async (file): Promise => { - const filePath = join(directory, file); + const filePath = path.join(directory, file); const stats = await stat(filePath); if (stats.isDirectory()) { @@ -128,13 +128,13 @@ export async function findNearestFile( for ( // eslint-disable-next-line functional/no-let let directory = cwd; - directory !== dirname(directory); - directory = dirname(directory) + directory !== path.dirname(directory); + directory = path.dirname(directory) ) { // eslint-disable-next-line functional/no-loop-statements for (const file of fileNames) { - if (await fileExists(join(directory, file))) { - return join(directory, file); + if (await fileExists(path.join(directory, file))) { + return path.join(directory, file); } } } @@ -151,9 +151,9 @@ export function findLineNumberInText( return lineNumber === 0 ? null : lineNumber; // If the package isn't found, return null } -export function filePathToCliArg(path: string): string { +export function filePathToCliArg(filePath: string): string { // needs to be escaped if spaces included - return `"${path}"`; + return `"${filePath}"`; } export function projectToFilename(project: string): string { diff --git a/packages/utils/src/lib/file-system.unit.test.ts b/packages/utils/src/lib/file-system.unit.test.ts index 3e63d1a39..dfb76ee06 100644 --- a/packages/utils/src/lib/file-system.unit.test.ts +++ b/packages/utils/src/lib/file-system.unit.test.ts @@ -1,6 +1,6 @@ import { vol } from 'memfs'; import { stat } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { MEMFS_VOLUME } from '@code-pushup/test-utils'; import { @@ -19,7 +19,7 @@ describe('ensureDirectoryExists', () => { it('should create a nested folder', async () => { vol.fromJSON({}, MEMFS_VOLUME); - const dir = join(MEMFS_VOLUME, 'sub', 'dir'); + const dir = path.join(MEMFS_VOLUME, 'sub', 'dir'); await ensureDirectoryExists(dir); await expect( @@ -73,8 +73,8 @@ describe('crawlFileSystem', () => { }), ).resolves.toEqual([ expect.stringContaining('README.md'), - expect.stringContaining(join('src', 'README.md')), - expect.stringContaining(join('src', 'index.ts')), + expect.stringContaining(path.join('src', 'README.md')), + expect.stringContaining(path.join('src', 'index.ts')), ]); }); @@ -86,7 +86,7 @@ describe('crawlFileSystem', () => { }), ).resolves.toEqual([ expect.stringContaining('README.md'), - expect.stringContaining(join('src', 'README.md')), + expect.stringContaining(path.join('src', 'README.md')), ]); }); @@ -120,7 +120,7 @@ describe('findNearestFile', () => { MEMFS_VOLUME, ); await expect(findNearestFile(['eslint.config.js'])).resolves.toBe( - join(MEMFS_VOLUME, 'eslint.config.js'), + path.join(MEMFS_VOLUME, 'eslint.config.js'), ); }); @@ -138,7 +138,7 @@ describe('findNearestFile', () => { 'eslint.config.cjs', 'eslint.config.mjs', ]), - ).resolves.toBe(join(MEMFS_VOLUME, 'eslint.config.cjs')); + ).resolves.toBe(path.join(MEMFS_VOLUME, 'eslint.config.cjs')); }); it('should resolve to undefined if file not found', async () => { @@ -163,9 +163,9 @@ describe('findNearestFile', () => { await expect( findNearestFile( ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs'], - join(MEMFS_VOLUME, 'e2e'), + path.join(MEMFS_VOLUME, 'e2e'), ), - ).resolves.toBe(join(MEMFS_VOLUME, 'eslint.config.js')); + ).resolves.toBe(path.join(MEMFS_VOLUME, 'eslint.config.js')); }); it('should find file in directory multiple levels up', async () => { @@ -179,9 +179,9 @@ describe('findNearestFile', () => { await expect( findNearestFile( ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs'], - join(MEMFS_VOLUME, 'packages/core'), + path.join(MEMFS_VOLUME, 'packages/core'), ), - ).resolves.toBe(join(MEMFS_VOLUME, 'eslint.config.cjs')); + ).resolves.toBe(path.join(MEMFS_VOLUME, 'eslint.config.cjs')); }); it("should find file that's nearest to current folder", async () => { @@ -196,9 +196,9 @@ describe('findNearestFile', () => { await expect( findNearestFile( ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs'], - join(MEMFS_VOLUME, 'packages/core'), + path.join(MEMFS_VOLUME, 'packages/core'), ), - ).resolves.toBe(join(MEMFS_VOLUME, 'packages/core/eslint.config.js')); + ).resolves.toBe(path.join(MEMFS_VOLUME, 'packages/core/eslint.config.js')); }); it('should not find file in sub-folders of current folder', async () => { diff --git a/packages/utils/src/lib/formatting.ts b/packages/utils/src/lib/formatting.ts index efe7bef44..86bc91301 100644 --- a/packages/utils/src/lib/formatting.ts +++ b/packages/utils/src/lib/formatting.ts @@ -35,7 +35,7 @@ export function formatBytes(bytes: number, decimals = 2) { } const k = 1024; - const dm = decimals < 0 ? 0 : decimals; + const dm = Math.max(decimals, 0); const sizes = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; const i = Math.floor(Math.log(positiveBytes) / Math.log(k)); diff --git a/packages/utils/src/lib/git/git.commits-and-tags.integration.test.ts b/packages/utils/src/lib/git/git.commits-and-tags.integration.test.ts index 03894d1a0..1c30292e0 100644 --- a/packages/utils/src/lib/git/git.commits-and-tags.integration.test.ts +++ b/packages/utils/src/lib/git/git.commits-and-tags.integration.test.ts @@ -1,5 +1,5 @@ import { mkdir, rm } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { type SimpleGit, simpleGit } from 'simple-git'; import { afterAll, beforeAll, describe, expect } from 'vitest'; import { commitFile, initGitRepo } from '@code-pushup/test-utils'; @@ -18,7 +18,7 @@ async function getAllCommits(git: SimpleGit) { } describe('getCurrentBranchOrTag', () => { - const baseDir = join(process.cwd(), 'tmp', 'git-tests'); + const baseDir = path.join(process.cwd(), 'tmp', 'git-tests'); let currentBranchOrTagGitMock: SimpleGit; beforeAll(async () => { @@ -59,7 +59,7 @@ describe('getCurrentBranchOrTag', () => { }); describe('getLatestCommit', () => { - const baseDir = join(process.cwd(), 'tmp', 'git', 'latest-commit'); + const baseDir = path.join(process.cwd(), 'tmp', 'git', 'latest-commit'); let emptyGit: SimpleGit; beforeAll(async () => { @@ -92,7 +92,7 @@ describe('getLatestCommit', () => { }); describe('getHashes', () => { - const baseDir = join(process.cwd(), 'tmp', 'utils-git-get-hashes'); + const baseDir = path.join(process.cwd(), 'tmp', 'utils-git-get-hashes'); let gitMock: SimpleGit; beforeAll(async () => { @@ -113,6 +113,7 @@ describe('getHashes', () => { describe('with a branch and commits clean', () => { let commits: { hash: string; message: string }[]; + beforeAll(async () => { await commitFile(gitMock, { baseDir, commitMsg: 'Create README' }); await commitFile(gitMock, { baseDir, commitMsg: 'Update README 1' }); @@ -173,7 +174,7 @@ describe('getHashes', () => { }); describe('getSemverTags', () => { - const baseDir = join(process.cwd(), 'tmp', 'git', 'get-semver-tags'); + const baseDir = path.join(process.cwd(), 'tmp', 'git', 'get-semver-tags'); let gitSemverTagsMock: SimpleGit; beforeAll(async () => { diff --git a/packages/utils/src/lib/git/git.commits-and-tags.ts b/packages/utils/src/lib/git/git.commits-and-tags.ts index e13c4d8c9..42099f8f5 100644 --- a/packages/utils/src/lib/git/git.commits-and-tags.ts +++ b/packages/utils/src/lib/git/git.commits-and-tags.ts @@ -51,7 +51,7 @@ export function filterLogs( const { from, to, maxCount } = opt; const finIndex = (tagName?: string, fallback?: T) => { const idx = allTags.indexOf(tagName ?? ''); - if (idx > -1) { + if (idx !== -1) { return idx; } return fallback; diff --git a/packages/utils/src/lib/git/git.integration.test.ts b/packages/utils/src/lib/git/git.integration.test.ts index 1c914910b..0851b01b7 100644 --- a/packages/utils/src/lib/git/git.integration.test.ts +++ b/packages/utils/src/lib/git/git.integration.test.ts @@ -1,5 +1,5 @@ import { mkdir, rm, stat, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { type SimpleGit, simpleGit } from 'simple-git'; import { afterAll, beforeAll, beforeEach, describe, expect } from 'vitest'; import { toUnixPath } from '../transform.js'; @@ -11,7 +11,7 @@ import { } from './git.js'; describe('git utils in a git repo', () => { - const baseDir = join(process.cwd(), 'tmp', 'git-tests'); + const baseDir = path.join(process.cwd(), 'tmp', 'git-tests'); let emptyGit: SimpleGit; beforeAll(async () => { @@ -34,7 +34,7 @@ describe('git utils in a git repo', () => { describe('with a branch and commits clean', () => { beforeAll(async () => { - await writeFile(join(baseDir, 'README.md'), '# hello-world\n'); + await writeFile(path.join(baseDir, 'README.md'), '# hello-world\n'); await emptyGit.add('README.md'); await emptyGit.commit('Create README'); @@ -53,13 +53,13 @@ describe('git utils in a git repo', () => { it('should convert absolute path to relative Git path', async () => { await expect( - toGitPath(join(baseDir, 'src', 'utils.ts'), emptyGit), + toGitPath(path.join(baseDir, 'src', 'utils.ts'), emptyGit), ).resolves.toBe('src/utils.ts'); }); it('should convert relative Windows path to relative Git path', async () => { await expect( - toGitPath('Backend\\API\\Startup.cs', emptyGit), + toGitPath(String.raw`Backend\API\Startup.cs`, emptyGit), ).resolves.toBe('../../Backend/API/Startup.cs'); }); @@ -92,10 +92,10 @@ describe('git utils in a git repo', () => { }); describe('with a branch and commits dirty', () => { - const newFilePath = join(baseDir, 'new-file.md'); + const newFilePath = path.join(baseDir, 'new-file.md'); beforeAll(async () => { - await writeFile(join(baseDir, 'README.md'), '# hello-world\n'); + await writeFile(path.join(baseDir, 'README.md'), '# hello-world\n'); await emptyGit.add('README.md'); await emptyGit.commit('Create README'); diff --git a/packages/utils/src/lib/git/git.ts b/packages/utils/src/lib/git/git.ts index 5588451d4..f706129f0 100644 --- a/packages/utils/src/lib/git/git.ts +++ b/packages/utils/src/lib/git/git.ts @@ -1,4 +1,4 @@ -import { isAbsolute, join, relative } from 'node:path'; +import path from 'node:path'; import { type StatusResult, simpleGit } from 'simple-git'; import { ui } from '../logging.js'; import { toUnixPath } from '../transform.js'; @@ -7,18 +7,20 @@ export function getGitRoot(git = simpleGit()): Promise { return git.revparse('--show-toplevel'); } -export function formatGitPath(path: string, gitRoot: string): string { - const absolutePath = isAbsolute(path) ? path : join(process.cwd(), path); - const relativePath = relative(gitRoot, absolutePath); +export function formatGitPath(filePath: string, gitRoot: string): string { + const absolutePath = path.isAbsolute(filePath) + ? filePath + : path.join(process.cwd(), filePath); + const relativePath = path.relative(gitRoot, absolutePath); return toUnixPath(relativePath); } export async function toGitPath( - path: string, + filePath: string, git = simpleGit(), ): Promise { const gitRoot = await getGitRoot(git); - return formatGitPath(path, gitRoot); + return formatGitPath(filePath, gitRoot); } export class GitStatusError extends Error { diff --git a/packages/utils/src/lib/progress.ts b/packages/utils/src/lib/progress.ts index d598881a2..a7d33276f 100644 --- a/packages/utils/src/lib/progress.ts +++ b/packages/utils/src/lib/progress.ts @@ -29,7 +29,6 @@ let mpb: MultiProgressBars; export function getSingletonProgressBars( options?: Partial, ): MultiProgressBars { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!mpb) { mpb = new MultiProgressBars({ progressWidth: TERMINAL_WIDTH, diff --git a/packages/utils/src/lib/reports/constants.ts b/packages/utils/src/lib/reports/constants.ts index c8aaf5b4d..07801403e 100644 --- a/packages/utils/src/lib/reports/constants.ts +++ b/packages/utils/src/lib/reports/constants.ts @@ -1,12 +1,12 @@ // https://stackoverflow.com/questions/4651012/why-is-the-default-terminal-width-80-characters/4651037#4651037 export const TERMINAL_WIDTH = 80; -/* eslint-disable no-magic-numbers */ export const SCORE_COLOR_RANGE = { + /* eslint-disable @typescript-eslint/no-magic-numbers */ GREEN_MIN: 0.9, YELLOW_MIN: 0.5, + /* eslint-enable @typescript-eslint/no-magic-numbers */ }; -/* eslint-enable no-magic-numbers */ export const FOOTER_PREFIX = 'Made with ❤ by'; // replace ❤️ with ❤, because ❤️ has output issues in terminal export const CODE_PUSHUP_DOMAIN = 'code-pushup.dev'; diff --git a/packages/utils/src/lib/reports/formatting.ts b/packages/utils/src/lib/reports/formatting.ts index 594c093bd..08e90a8b3 100644 --- a/packages/utils/src/lib/reports/formatting.ts +++ b/packages/utils/src/lib/reports/formatting.ts @@ -4,7 +4,7 @@ import { MarkdownDocument, md, } from 'build-md'; -import { posix as pathPosix } from 'node:path'; +import path from 'node:path'; import type { AuditReport, SourceFileLocation, @@ -143,7 +143,7 @@ export function formatFileLink( position: SourceFileLocation['position'], outputDir: string, ): string { - const relativePath = pathPosix.relative(outputDir, file); + const relativePath = path.posix.relative(outputDir, file); const env = getEnvironmentType(); switch (env) { diff --git a/packages/utils/src/lib/reports/generate-md-reports-diff.ts b/packages/utils/src/lib/reports/generate-md-reports-diff.ts index 013b5a871..8ac158566 100644 --- a/packages/utils/src/lib/reports/generate-md-reports-diff.ts +++ b/packages/utils/src/lib/reports/generate-md-reports-diff.ts @@ -127,7 +127,7 @@ function createDiffCategoriesSection( return new MarkdownDocument() .heading(HIERARCHY.level_2, !skipHeading && '🏷️ Categories') .table(columns, rows) - .paragraph(added.length > 0 && md.italic('(\\*) New category.')) + .paragraph(added.length > 0 && md.italic(String.raw`(\*) New category.`)) .paragraph( skipUnchanged && unchanged.length > 0 && @@ -153,9 +153,9 @@ function createCategoriesTable( ]), ...added.map(category => [ formatTitle(category), - md.italic('n/a (\\*)'), + md.italic(String.raw`n/a (\*)`), formatScoreWithColor(category.score), - md.italic('n/a (\\*)'), + md.italic(String.raw`n/a (\*)`), ]), ...(skipUnchanged ? [] diff --git a/packages/utils/src/lib/reports/load-report.ts b/packages/utils/src/lib/reports/load-report.ts index a5fc4217d..0f94d2b4f 100644 --- a/packages/utils/src/lib/reports/load-report.ts +++ b/packages/utils/src/lib/reports/load-report.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { type Format, type PersistConfig, @@ -20,7 +20,7 @@ export async function loadReport( ): Promise> { const { outputDir, filename, format } = options; await ensureDirectoryExists(outputDir); - const filePath = join(outputDir, `${filename}.${format}`); + const filePath = path.join(outputDir, `${filename}.${format}`); if (format === 'json') { const content = await readJsonFile(filePath); diff --git a/packages/utils/src/lib/reports/log-stdout-summary.ts b/packages/utils/src/lib/reports/log-stdout-summary.ts index 27b9924b8..93d71f149 100644 --- a/packages/utils/src/lib/reports/log-stdout-summary.ts +++ b/packages/utils/src/lib/reports/log-stdout-summary.ts @@ -81,14 +81,14 @@ function logRow(score: number, title: string, value?: string): void { }, { text: title, - // eslint-disable-next-line no-magic-numbers + // eslint-disable-next-line @typescript-eslint/no-magic-numbers padding: [0, 3, 0, 0], }, ...(value ? [ { text: cyanBright(value), - // eslint-disable-next-line no-magic-numbers + // eslint-disable-next-line @typescript-eslint/no-magic-numbers width: 20, padding: [0, 0, 0, 0], }, @@ -109,7 +109,7 @@ export function logCategories({ countCategoryAudits(refs, plugins), ]); const table = ui().table(); - // eslint-disable-next-line no-magic-numbers + // eslint-disable-next-line @typescript-eslint/no-magic-numbers table.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]); table.head( REPORT_RAW_OVERVIEW_TABLE_HEADERS.map((heading, idx) => ({ diff --git a/packages/utils/src/lib/reports/utils.ts b/packages/utils/src/lib/reports/utils.ts index 5d5f0b14c..7dfaba833 100644 --- a/packages/utils/src/lib/reports/utils.ts +++ b/packages/utils/src/lib/reports/utils.ts @@ -237,34 +237,31 @@ export function compareIssues(a: Issue, b: Issue): number { if (a.severity !== b.severity) { return -compareIssueSeverity(a.severity, b.severity); } - if (!a.source && b.source) { return -1; } - if (a.source && !b.source) { return 1; } - if (a.source?.file !== b.source?.file) { return a.source?.file.localeCompare(b.source?.file || '') ?? 0; } + return compareSourceFilePosition(a.source?.position, b.source?.position); +} - if (!a.source?.position && b.source?.position) { +function compareSourceFilePosition( + a: NonNullable['position'], + b: NonNullable['position'], +): number { + if (!a && b) { return -1; } - - if (a.source?.position && !b.source?.position) { + if (a && !b) { return 1; } - - if (a.source?.position?.startLine !== b.source?.position?.startLine) { - return ( - (a.source?.position?.startLine ?? 0) - - (b.source?.position?.startLine ?? 0) - ); + if (a?.startLine !== b?.startLine) { + return (a?.startLine ?? 0) - (b?.startLine ?? 0); } - return 0; } diff --git a/packages/utils/src/lib/reports/utils.unit.test.ts b/packages/utils/src/lib/reports/utils.unit.test.ts index 43c406e15..965807a35 100644 --- a/packages/utils/src/lib/reports/utils.unit.test.ts +++ b/packages/utils/src/lib/reports/utils.unit.test.ts @@ -488,9 +488,11 @@ describe('targetScoreIcon', () => { it('should return target score icon "✅" for passed score', () => { expect(targetScoreIcon(0.42, 0.4)).toBe('✅'); }); + it('should return target score icon "❌" for failed score', () => { expect(targetScoreIcon(0.42, 0.5)).toBe('❌'); }); + it('should return prefixed target score icon if prefix is provided', () => { expect( targetScoreIcon(0.42, 0.1, { @@ -498,6 +500,7 @@ describe('targetScoreIcon', () => { }), ).toBe('<✅'); }); + it('should return prefixed target score icon if postfix is provided', () => { expect( targetScoreIcon(0.42, 0.1, { @@ -505,6 +508,7 @@ describe('targetScoreIcon', () => { }), ).toBe('✅>'); }); + it('should return pre and postfixed target score icon if both are provided', () => { expect( targetScoreIcon(0.42, 0.1, { @@ -513,6 +517,7 @@ describe('targetScoreIcon', () => { }), ).toBe('<✅>'); }); + it('should return no target score icon if no targetScore is provided', () => { expect(targetScoreIcon(0.42)).toBe(''); }); @@ -693,7 +698,7 @@ describe('countCategoryAudits', () => { weight: 1, }, ], - [{ slug: 'coverage', groups: groups }] as ScoredReport['plugins'], + [{ slug: 'coverage', groups }] as ScoredReport['plugins'], ), ).toBe(0); }, diff --git a/packages/utils/src/lib/text-formats/constants.ts b/packages/utils/src/lib/text-formats/constants.ts index 78ff1971d..70ea49db7 100644 --- a/packages/utils/src/lib/text-formats/constants.ts +++ b/packages/utils/src/lib/text-formats/constants.ts @@ -2,13 +2,13 @@ export const NEW_LINE = '\n'; export const TAB = ' '; export const SPACE = ' '; -/* eslint-disable no-magic-numbers */ export const HIERARCHY = { + /* eslint-disable @typescript-eslint/no-magic-numbers */ level_1: 1, level_2: 2, level_3: 3, level_4: 4, level_5: 5, level_6: 6, + /* eslint-enable @typescript-eslint/no-magic-numbers */ } as const; -/* eslint-enable no-magic-numbers */ diff --git a/packages/utils/src/lib/transform.ts b/packages/utils/src/lib/transform.ts index 4a94d4033..d3943a0c4 100644 --- a/packages/utils/src/lib/transform.ts +++ b/packages/utils/src/lib/transform.ts @@ -47,8 +47,7 @@ export function factorOf(items: T[], filterFn: (i: T) => boolean): number { type ArgumentValue = number | string | boolean | string[]; export type CliArgsObject> = T extends never - ? // eslint-disable-next-line @typescript-eslint/naming-convention - Record | { _: string } + ? Record | { _: string } : T; /** @@ -61,7 +60,6 @@ export type CliArgsObject> = * formats: ['json', 'md'] // --format=json --format=md * }); */ -// eslint-disable-next-line sonarjs/cognitive-complexity export function objectToCliArgs< T extends object = Record, >(params?: CliArgsObject): string[] { @@ -69,11 +67,9 @@ export function objectToCliArgs< return []; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return Object.entries(params).flatMap(([key, value]) => { // process/file/script if (key === '_') { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return Array.isArray(value) ? value : [`${value}`]; } const prefix = key.length === 1 ? '-' : '--'; @@ -144,8 +140,8 @@ export function toNumberPrecision( ); } -/* eslint-disable no-magic-numbers */ export function toOrdinal(value: number): string { + /* eslint-disable @typescript-eslint/no-magic-numbers */ if (value % 10 === 1 && value % 100 !== 11) { return `${value}st`; } @@ -157,8 +153,7 @@ export function toOrdinal(value: number): string { if (value % 10 === 3 && value % 100 !== 13) { return `${value}rd`; } + /* eslint-enable @typescript-eslint/no-magic-numbers */ return `${value}th`; } - -/* eslint-enable no-magic-numbers */ diff --git a/packages/utils/src/lib/transform.unit.test.ts b/packages/utils/src/lib/transform.unit.test.ts index 3aa72f937..1478f8d0d 100644 --- a/packages/utils/src/lib/transform.unit.test.ts +++ b/packages/utils/src/lib/transform.unit.test.ts @@ -238,7 +238,7 @@ describe('toUnixPath', () => { ['src/main.ts', 'src/main.ts'], ['../../relative/unix/path/index.ts', '../../relative/unix/path/index.ts'], [ - '..\\..\\relative\\windows\\path\\index.ts', + String.raw`..\..\relative\windows\path\index.ts`, '../../relative/windows/path/index.ts', ], ])('should transform "%s" to valid slug "%s"', (path, unixPath) => { diff --git a/packages/utils/src/lib/types.ts b/packages/utils/src/lib/types.ts index a32d7b031..03b53ea77 100644 --- a/packages/utils/src/lib/types.ts +++ b/packages/utils/src/lib/types.ts @@ -4,7 +4,7 @@ export type ExcludeNullableProps = { export type ItemOrArray = T | T[]; -export type ExtractArray = T extends Array ? T : never; +export type ExtractArray = T extends unknown[] ? T : never; export type ExtractArrays> = { [K in keyof T]: ExtractArray; diff --git a/testing/test-nx-utils/.eslintrc.json b/testing/test-nx-utils/.eslintrc.json deleted file mode 100644 index d0614e23a..000000000 --- a/testing/test-nx-utils/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "parserOptions": { - "project": ["testing/test-nx-utils/tsconfig.*?.json"] - } - } - ] -} diff --git a/testing/test-nx-utils/eslint.config.js b/testing/test-nx-utils/eslint.config.js new file mode 100644 index 000000000..2656b27cb --- /dev/null +++ b/testing/test-nx-utils/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, +}); diff --git a/testing/test-nx-utils/src/lib/utils/nx-plugin.ts b/testing/test-nx-utils/src/lib/utils/nx-plugin.ts index 37cea76ab..30d9706ba 100644 --- a/testing/test-nx-utils/src/lib/utils/nx-plugin.ts +++ b/testing/test-nx-utils/src/lib/utils/nx-plugin.ts @@ -1,5 +1,4 @@ import type { - // eslint-disable-next-line import/no-deprecated CreateNodes, CreateNodesContext, CreateNodesContextV2, @@ -33,7 +32,6 @@ export async function invokeCreateNodesOnVirtualFiles< T extends Record | undefined, >( // FIXME: refactor this to use the V2 api & remove the eslint disable on the whole file - // eslint-disable-next-line import/no-deprecated,deprecation/deprecation createNodes: CreateNodes, context: CreateNodesContext, createNodeOptions: T, diff --git a/testing/test-nx-utils/src/lib/utils/nx.ts b/testing/test-nx-utils/src/lib/utils/nx.ts index 7a662ff74..e6e700068 100644 --- a/testing/test-nx-utils/src/lib/utils/nx.ts +++ b/testing/test-nx-utils/src/lib/utils/nx.ts @@ -8,7 +8,7 @@ import { } from '@nx/devkit'; import { libraryGenerator } from '@nx/js'; import type { LibraryGeneratorSchema } from '@nx/js/src/utils/schema'; -import { join } from 'node:path'; +import path from 'node:path'; import { createTreeWithEmptyWorkspace } from 'nx/src/generators/testing-utils/create-tree-with-empty-workspace'; import { executeProcess } from '@code-pushup/utils'; @@ -46,7 +46,7 @@ export async function generateWorkspaceAndProject( typeof options === 'string' ? { name: options } : options; await libraryGenerator(tree, { name, - directory: join('libs', name), + directory: path.join('libs', name), tags: 'scope:plugin', linter: 'none', unitTestRunner: 'none', diff --git a/testing/test-nx-utils/src/lib/utils/tree.integration.test.ts b/testing/test-nx-utils/src/lib/utils/tree.integration.test.ts index 5cf76ea64..0e06aabf6 100644 --- a/testing/test-nx-utils/src/lib/utils/tree.integration.test.ts +++ b/testing/test-nx-utils/src/lib/utils/tree.integration.test.ts @@ -1,21 +1,21 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { createTreeWithEmptyWorkspace } from 'nx/src/generators/testing-utils/create-tree-with-empty-workspace'; import { describe, expect, it } from 'vitest'; import { readJsonFile } from '@code-pushup/utils'; import { materializeTree } from './tree.js'; describe('materializeTree', () => { - const baseDir = join('tmp', 'materialize-tree'); + const baseDir = path.join('tmp', 'materialize-tree'); it('should create files from tree', async () => { - const root = join(baseDir, 'materialize'); + const root = path.join(baseDir, 'materialize'); const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); expect(tree.exists('nx.json')).toBe(true); await materializeTree(tree, root); - await expect(readJsonFile(join(root, 'nx.json'))).resolves.toEqual({ + await expect(readJsonFile(path.join(root, 'nx.json'))).resolves.toEqual({ affected: { defaultBase: 'main', }, diff --git a/testing/test-nx-utils/src/lib/utils/tree.ts b/testing/test-nx-utils/src/lib/utils/tree.ts index 488d7423b..cb2dfc0dd 100644 --- a/testing/test-nx-utils/src/lib/utils/tree.ts +++ b/testing/test-nx-utils/src/lib/utils/tree.ts @@ -1,17 +1,17 @@ import type { Tree } from '@nx/devkit'; import { writeFile } from 'node:fs/promises'; -import { dirname, join } from 'node:path'; +import path from 'node:path'; import { ensureDirectoryExists } from '@code-pushup/test-utils'; export async function materializeTree(tree: Tree, targetFolder: string) { const changes = tree.listChanges(); await Promise.all( changes.map(async change => { - const filePath = join(targetFolder, change.path); + const filePath = path.join(targetFolder, change.path); if (change.type === 'CREATE' || change.type === 'UPDATE') { try { - await ensureDirectoryExists(dirname(filePath)); + await ensureDirectoryExists(path.dirname(filePath)); await writeFile(filePath, change.content?.toString() ?? ''); } catch (error) { console.error(`Failed to process file ${filePath}:`, error); diff --git a/testing/test-setup/.eslintrc.json b/testing/test-setup/.eslintrc.json deleted file mode 100644 index a5332fd93..000000000 --- a/testing/test-setup/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "parserOptions": { - "project": ["testing/test-setup/tsconfig.*?.json"] - } - } - ] -} diff --git a/testing/test-setup/eslint.config.js b/testing/test-setup/eslint.config.js new file mode 100644 index 000000000..2656b27cb --- /dev/null +++ b/testing/test-setup/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, +}); diff --git a/testing/test-utils/.eslintrc.json b/testing/test-utils/.eslintrc.json deleted file mode 100644 index d1cea98c7..000000000 --- a/testing/test-utils/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*", "*.config*", "src/lib/fixtures/configs"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "parserOptions": { - "project": ["testing/test-utils/tsconfig.*?.json"] - } - } - ] -} diff --git a/testing/test-utils/eslint.config.js b/testing/test-utils/eslint.config.js new file mode 100644 index 000000000..2656b27cb --- /dev/null +++ b/testing/test-utils/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, +}); diff --git a/testing/test-utils/src/lib/fixtures/configs/code-pushup.invalid.config.ts b/testing/test-utils/src/lib/fixtures/configs/code-pushup.invalid.config.ts index 77380331b..7396948b9 100644 --- a/testing/test-utils/src/lib/fixtures/configs/code-pushup.invalid.config.ts +++ b/testing/test-utils/src/lib/fixtures/configs/code-pushup.invalid.config.ts @@ -1,4 +1,4 @@ -import { type CoreConfig } from '@code-pushup/models'; +import type { CoreConfig } from '@code-pushup/models'; export default { persist: { outputDir: 'tmp' }, diff --git a/testing/test-utils/src/lib/fixtures/configs/code-pushup.needs-tsconfig.config.ts b/testing/test-utils/src/lib/fixtures/configs/code-pushup.needs-tsconfig.config.ts index 376cc48cb..44b4d0a2d 100644 --- a/testing/test-utils/src/lib/fixtures/configs/code-pushup.needs-tsconfig.config.ts +++ b/testing/test-utils/src/lib/fixtures/configs/code-pushup.needs-tsconfig.config.ts @@ -1,5 +1,6 @@ // the point is to test runtime import which relies on alias defined in tsconfig.json "paths" // non-type import from '@example/custom-plugin' wouldn't work without --tsconfig +// eslint-disable-next-line import/no-unresolved import customPlugin from '@example/custom-plugin'; const config = { diff --git a/testing/test-utils/src/lib/fixtures/configs/progress-bar.config.mock.ts b/testing/test-utils/src/lib/fixtures/configs/progress-bar.config.mock.ts index c3a812435..ee22a3d7d 100644 --- a/testing/test-utils/src/lib/fixtures/configs/progress-bar.config.mock.ts +++ b/testing/test-utils/src/lib/fixtures/configs/progress-bar.config.mock.ts @@ -4,7 +4,7 @@ * Usage: * npx ./dist/packages/cli collect --config=./testing/test-utils/src/lib/fixtures/configs/progress-bar.config.mock.ts */ -import { dirname, join } from 'path'; +import path from 'node:path'; import { fileURLToPath } from 'url'; // Small hack to control the number of plugins while debugging @@ -16,8 +16,8 @@ const numPlugins = parseInt( ); const outputDir = './tmp'; -const pluginProcess = join( - fileURLToPath(dirname(import.meta.url)), +const pluginProcess = path.join( + fileURLToPath(path.dirname(import.meta.url)), '..', '..', 'utils', @@ -30,7 +30,7 @@ const pluginTitle = (end: string): string => 'Async Plugin ' + end; const auditTitle = (end: string): string => 'Async Audit ' + end; const asyncPlugin = (pId: string, duration = 1000) => { const aId = '0'; - const outputFile = join(outputDir, `${pluginSlug(pId)}-output.json`); + const outputFile = path.join(outputDir, `${pluginSlug(pId)}-output.json`); return { slug: pluginSlug(pId), title: pluginTitle(pId), diff --git a/testing/test-utils/src/lib/utils/create-npm-workshpace.ts b/testing/test-utils/src/lib/utils/create-npm-workshpace.ts index 741c90e4a..cd9d87637 100644 --- a/testing/test-utils/src/lib/utils/create-npm-workshpace.ts +++ b/testing/test-utils/src/lib/utils/create-npm-workshpace.ts @@ -1,10 +1,10 @@ import { mkdir, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; export async function createNpmWorkspace(cwd: string) { await mkdir(cwd, { recursive: true }); await writeFile( - join(cwd, 'package.json'), + path.join(cwd, 'package.json'), JSON.stringify( { name: 'create-npm-workspace', diff --git a/testing/test-utils/src/lib/utils/dynamic-mocks/eslint-plugin.mock.ts b/testing/test-utils/src/lib/utils/dynamic-mocks/eslint-plugin.mock.ts index fef734a59..672f32fec 100644 --- a/testing/test-utils/src/lib/utils/dynamic-mocks/eslint-plugin.mock.ts +++ b/testing/test-utils/src/lib/utils/dynamic-mocks/eslint-plugin.mock.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import type { Audit, AuditReport, @@ -53,7 +53,7 @@ export function eslintPluginConfigMock(outputDir = 'tmp'): PluginConfig { ...ESLINT_PLUGIN_META, runner: echoRunnerConfigMock( Object.values(ESLINT_AUDITS_MAP), - join(outputDir, 'eslint-out.json'), + path.join(outputDir, 'eslint-out.json'), ), audits, }; diff --git a/testing/test-utils/src/lib/utils/dynamic-mocks/lighthouse-plugin.mock.ts b/testing/test-utils/src/lib/utils/dynamic-mocks/lighthouse-plugin.mock.ts index 1360ba8fc..ea3b43edd 100644 --- a/testing/test-utils/src/lib/utils/dynamic-mocks/lighthouse-plugin.mock.ts +++ b/testing/test-utils/src/lib/utils/dynamic-mocks/lighthouse-plugin.mock.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import type { Audit, AuditReport, @@ -61,7 +61,7 @@ export function lighthousePluginConfigMock(outputDir = 'tmp'): PluginConfig { ...LH_PLUGIN_META, runner: echoRunnerConfigMock( Object.values(LIGHTHOUSE_AUDITS_MAP), - join(outputDir, 'lighthouse-out.json'), + path.join(outputDir, 'lighthouse-out.json'), ), audits, groups: [LH_PLUGIN_GROUP_PERFORMANCE], diff --git a/testing/test-utils/src/lib/utils/dynamic-mocks/plugin-config.mock.ts b/testing/test-utils/src/lib/utils/dynamic-mocks/plugin-config.mock.ts index c7e37dd2a..da9cf94fa 100644 --- a/testing/test-utils/src/lib/utils/dynamic-mocks/plugin-config.mock.ts +++ b/testing/test-utils/src/lib/utils/dynamic-mocks/plugin-config.mock.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import path from 'node:path'; import { type Audit, type AuditReport, @@ -14,7 +14,7 @@ export function pluginConfigMock( opt?: Partial & { outputDir?: string; outputFile?: string }, ): PluginConfig { const { outputDir, outputFile } = opt || {}; - const pluginOutputFile = join( + const pluginOutputFile = path.join( outputDir || 'tmp', outputFile || `out.${Date.now()}.json`, ); diff --git a/testing/test-utils/src/lib/utils/execute-process-helper.mock.ts b/testing/test-utils/src/lib/utils/execute-process-helper.mock.ts index 1995c7526..0f9290acb 100644 --- a/testing/test-utils/src/lib/utils/execute-process-helper.mock.ts +++ b/testing/test-utils/src/lib/utils/execute-process-helper.mock.ts @@ -1,6 +1,6 @@ -import { join } from 'path'; +import path from 'node:path'; -const asyncProcessPath = join(__dirname, './execute-process.mock.mjs'); +const asyncProcessPath = path.join(__dirname, './execute-process.mock.mjs'); /** * Helps to get an async process runner config for testing. diff --git a/testing/test-utils/src/lib/utils/file-system.unit.test.ts b/testing/test-utils/src/lib/utils/file-system.unit.test.ts index 4ce6dc381..68dc4671c 100644 --- a/testing/test-utils/src/lib/utils/file-system.unit.test.ts +++ b/testing/test-utils/src/lib/utils/file-system.unit.test.ts @@ -1,6 +1,6 @@ import { vol } from 'memfs'; import { stat } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import { type MockInstance, afterEach, @@ -36,7 +36,7 @@ describe('ensureDirectoryExists', () => { it('should create a nested folder', async () => { vol.fromJSON({}, MEMFS_VOLUME); - const dir = join(MEMFS_VOLUME, 'sub', 'dir'); + const dir = path.join(MEMFS_VOLUME, 'sub', 'dir'); await ensureDirectoryExists(dir); await expect( @@ -52,7 +52,7 @@ describe('ensureDirectoryExists', () => { MEMFS_VOLUME, ); - const dir = join(MEMFS_VOLUME, 'sub'); + const dir = path.join(MEMFS_VOLUME, 'sub'); await ensureDirectoryExists(dir); await expect( diff --git a/testing/test-utils/src/lib/utils/git.ts b/testing/test-utils/src/lib/utils/git.ts index 408ed430a..785b92cfe 100644 --- a/testing/test-utils/src/lib/utils/git.ts +++ b/testing/test-utils/src/lib/utils/git.ts @@ -1,5 +1,5 @@ import { mkdir, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import type { FetchResult, Response, @@ -42,7 +42,7 @@ export async function commitFile( } = opt ?? {}; const { name = 'README.md', content = `# hello-world-${Math.random()}\n` } = file ?? {}; - await writeFile(join(baseDir, name), content); + await writeFile(path.join(baseDir, name), content); await git.add(name); if (tagName) { await git.tag([tagName]); @@ -54,7 +54,6 @@ export async function commitFile( } export async function simulateGitFetch(git: SimpleGit) { - // eslint-disable-next-line functional/no-let let fetchHead: string = await git.branchLocal().then(resp => resp.current); vi.spyOn(git, 'fetch').mockImplementation((...args) => { diff --git a/testing/test-utils/src/lib/utils/omit-report-data.ts b/testing/test-utils/src/lib/utils/omit-report-data.ts index 832bc87dd..70c21c7ea 100644 --- a/testing/test-utils/src/lib/utils/omit-report-data.ts +++ b/testing/test-utils/src/lib/utils/omit-report-data.ts @@ -28,7 +28,6 @@ export function omitVariablePluginData( }, ) { const { omitAuditData } = options ?? {}; - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return { ...pluginReport, audits: audits.map(plugin => diff --git a/testing/test-utils/src/lib/utils/os-agnostic-paths.unit.test.ts b/testing/test-utils/src/lib/utils/os-agnostic-paths.unit.test.ts index 1824fe5a5..4925084fd 100644 --- a/testing/test-utils/src/lib/utils/os-agnostic-paths.unit.test.ts +++ b/testing/test-utils/src/lib/utils/os-agnostic-paths.unit.test.ts @@ -22,6 +22,7 @@ describe('osAgnosticPath', () => { beforeEach(() => { cwdSpy.mockReturnValue(unixCwd); }); + afterEach(() => { cwdSpy.mockReset(); }); @@ -70,11 +71,12 @@ describe('osAgnosticPath', () => { }); describe('Windows', () => { - const windowsCWD = 'D:\\users\\jerry'; + const windowsCWD = String.raw`D:\users\jerry`; beforeEach(() => { cwdSpy.mockReturnValue(windowsCWD); }); + afterEach(() => { cwdSpy.mockReset(); }); @@ -94,26 +96,27 @@ describe('osAgnosticPath', () => { }); it('should handle absolute paths correctly on Windows', () => { - expect(osAgnosticPath('\\.code-pushup\\.code-pushup.config.ts')).toBe( - '/.code-pushup/.code-pushup.config.ts', - ); + expect( + osAgnosticPath(String.raw`\.code-pushup\.code-pushup.config.ts`), + ).toBe('/.code-pushup/.code-pushup.config.ts'); }); it('should handle paths with CWD shorthand "." correctly on Windows', () => { - expect(osAgnosticPath('.\\.code-pushup\\.code-pushup.config.ts')).toBe( - './.code-pushup/.code-pushup.config.ts', - ); + expect( + osAgnosticPath(String.raw`.\.code-pushup\.code-pushup.config.ts`), + ).toBe('./.code-pushup/.code-pushup.config.ts'); }); it('should handle relative paths correctly on Windows', () => { expect( - osAgnosticPath('..\\..\\.code-pushup\\.code-pushup.config.ts'), + osAgnosticPath(String.raw`..\..\.code-pushup\.code-pushup.config.ts`), ).toBe('../../.code-pushup/.code-pushup.config.ts'); }); + it('should handle path segments correctly on Windows', () => { - expect(osAgnosticPath('.code-pushup\\.code-pushup.config.ts')).toBe( - '.code-pushup/.code-pushup.config.ts', - ); + expect( + osAgnosticPath(String.raw`.code-pushup\.code-pushup.config.ts`), + ).toBe('.code-pushup/.code-pushup.config.ts'); }); }); }); diff --git a/testing/test-utils/src/lib/utils/progress-bar-process.mock.mjs b/testing/test-utils/src/lib/utils/progress-bar-process.mock.mjs index 9f2a069ed..37894cfb8 100644 --- a/testing/test-utils/src/lib/utils/progress-bar-process.mock.mjs +++ b/testing/test-utils/src/lib/utils/progress-bar-process.mock.mjs @@ -1,5 +1,5 @@ import { existsSync, mkdirSync, writeFileSync } from 'fs'; -import { join } from 'path'; +import path from 'node:path'; /** * Custom runner implementation that simulates asynchronous situations. @@ -52,7 +52,7 @@ let auditPostfix = const pluginSlug = 'progress-mock-plugin-' + pluginPostfix; const auditSlug = pluginSlug + '-a' + auditPostfix; const auditTitle = 'Async Audit ' + auditPostfix; -const outputFile = './' + join(outputDir, `${pluginSlug}-output.json`); +const outputFile = './' + path.join(outputDir, `${pluginSlug}-output.json`); (async () => { if (verbose) { diff --git a/tools/.eslintrc.json b/tools/.eslintrc.json deleted file mode 100644 index 8f1ce3691..000000000 --- a/tools/.eslintrc.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "extends": "../.eslintrc.json", - "ignorePatterns": ["!**/*", "*.config*"], - "overrides": [ - { - "files": "*.ts", - "parserOptions": { - "project": ["tools/tsconfig.tools.json"] - }, - "rules": { - "@nx/enforce-module-boundaries": "off" - } - } - ] -} diff --git a/tools/eslint.config.js b/tools/eslint.config.js new file mode 100644 index 000000000..2ac01fcbb --- /dev/null +++ b/tools/eslint.config.js @@ -0,0 +1,12 @@ +import tseslint from 'typescript-eslint'; +import baseConfig from '../eslint.config.js'; + +export default tseslint.config(...baseConfig, { + files: ['**/*.ts'], + parserOptions: { + project: ['tools/tsconfig.tools.json'], + }, + rules: { + '@nx/enforce-module-boundaries': 'off', + }, +}); diff --git a/tools/src/debug/utils.ts b/tools/src/debug/utils.ts index 37d637d78..9e258024d 100644 --- a/tools/src/debug/utils.ts +++ b/tools/src/debug/utils.ts @@ -1,6 +1,6 @@ import { execSync } from 'node:child_process'; import { readFile, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; +import path from 'node:path'; import * as os from 'os'; export type PID = string | number; @@ -77,7 +77,7 @@ export function listProcess({ pid, commandMatch }: ProcessListOption = {}): { pid, command: command .replace(process.cwd(), '.') - .replace(`node ./${join('node_modules', '.bin')}/`, ''), + .replace(`node ./${path.join('node_modules', '.bin')}/`, ''), })) .filter(({ pid, command }) => { if (pids.length === 0 && commands.length === 0) {