diff --git a/packages/pretty-reporter/src/format-single-diagnostic.ts b/packages/pretty-reporter/src/format-single-diagnostic.ts index f6f81db..df2c6ec 100644 --- a/packages/pretty-reporter/src/format-single-diagnostic.ts +++ b/packages/pretty-reporter/src/format-single-diagnostic.ts @@ -2,7 +2,6 @@ import { relative } from 'node:path' import figures from 'figures' import terminalLink from 'terminal-link' import chalk from 'chalk' - import type { Diagnostic } from '@steiger/types' export function formatSingleDiagnostic(d: Diagnostic, cwd: string): string { diff --git a/packages/pretty-reporter/src/index.ts b/packages/pretty-reporter/src/index.ts index c73f875..2cdf8a2 100644 --- a/packages/pretty-reporter/src/index.ts +++ b/packages/pretty-reporter/src/index.ts @@ -1,7 +1,7 @@ import chalk from 'chalk' import figures from 'figures' - import type { Diagnostic } from '@steiger/types' + import { formatSingleDiagnostic } from './format-single-diagnostic.js' import { s } from './pluralization.js' @@ -46,5 +46,3 @@ export function formatPretty(diagnostics: Array, cwd: string) { export function reportPretty(diagnostics: Array, cwd: string) { console.error(formatPretty(diagnostics, cwd)) } - -export type { Diagnostic } diff --git a/packages/steiger-plugin-fsd/package.json b/packages/steiger-plugin-fsd/package.json index 8b920c2..caeaf83 100644 --- a/packages/steiger-plugin-fsd/package.json +++ b/packages/steiger-plugin-fsd/package.json @@ -39,8 +39,8 @@ }, "devDependencies": { "@steiger/eslint-config": "workspace:*", + "@steiger/toolkit": "workspace:*", "@steiger/tsconfig": "workspace:*", - "@steiger/types": "workspace:*", "@total-typescript/ts-reset": "^0.5.1", "@types/lodash-es": "^4.17.12", "@types/pluralize": "^0.0.33", diff --git a/packages/steiger-plugin-fsd/src/_lib/index-source-files.ts b/packages/steiger-plugin-fsd/src/_lib/index-source-files.ts index dd6d4fa..c112c6d 100644 --- a/packages/steiger-plugin-fsd/src/_lib/index-source-files.ts +++ b/packages/steiger-plugin-fsd/src/_lib/index-source-files.ts @@ -1,7 +1,5 @@ import { getIndex, getLayers, getSegments, getSlices, isSliced, type LayerName } from '@feature-sliced/filesystem' -import type { File, Folder } from '@steiger/types' - -import { joinFromRoot, parseIntoFsdRoot } from './prepare-test.js' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot, type File, type Folder } from '@steiger/toolkit' type SourceFile = { file: File diff --git a/packages/steiger-plugin-fsd/src/ambiguous-slice-names/index.spec.ts b/packages/steiger-plugin-fsd/src/ambiguous-slice-names/index.spec.ts index 51b4879..d2ceba6 100644 --- a/packages/steiger-plugin-fsd/src/ambiguous-slice-names/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/ambiguous-slice-names/index.spec.ts @@ -2,7 +2,7 @@ import { join } from 'node:path' import { expect, it } from 'vitest' import ambiguousSliceNames from './index.js' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' it('reports no errors on a project without slice names that match some segment name in Shared', () => { const root = parseIntoFsdRoot(` diff --git a/packages/steiger-plugin-fsd/src/ambiguous-slice-names/index.ts b/packages/steiger-plugin-fsd/src/ambiguous-slice-names/index.ts index cfd54ce..37ca03c 100644 --- a/packages/steiger-plugin-fsd/src/ambiguous-slice-names/index.ts +++ b/packages/steiger-plugin-fsd/src/ambiguous-slice-names/index.ts @@ -1,11 +1,11 @@ import { basename, sep } from 'node:path' import { getAllSlices, getLayers, getSegments, type LayerName } from '@feature-sliced/filesystem' -import type { PartialDiagnostic, Folder, Rule } from '@steiger/types' +import type { PartialDiagnostic, Folder, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' /** Forbid slice names that match some segment’s name in shared (e.g., theme, i18n) */ const ambiguousSliceNames = { - name: `${NAMESPACE}/ambiguous-slice-names`, + name: `${NAMESPACE}/ambiguous-slice-names` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/excessive-slicing/index.spec.ts b/packages/steiger-plugin-fsd/src/excessive-slicing/index.spec.ts index 54ba725..5e0ae1b 100644 --- a/packages/steiger-plugin-fsd/src/excessive-slicing/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/excessive-slicing/index.spec.ts @@ -1,6 +1,6 @@ import { expect, it } from 'vitest' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' import excessiveSlicing from './index.js' it('reports no errors on projects with moderate slicing', () => { diff --git a/packages/steiger-plugin-fsd/src/excessive-slicing/index.ts b/packages/steiger-plugin-fsd/src/excessive-slicing/index.ts index 18d838e..9885024 100644 --- a/packages/steiger-plugin-fsd/src/excessive-slicing/index.ts +++ b/packages/steiger-plugin-fsd/src/excessive-slicing/index.ts @@ -1,6 +1,6 @@ import { join } from 'node:path' import { getLayers, getSlices, isSliced } from '@feature-sliced/filesystem' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { groupSlices } from '../_lib/group-slices.js' import { NAMESPACE } from '../constants.js' @@ -14,7 +14,7 @@ const THRESHOLDS = { /** Warn about excessive amounts of ungrouped entities/features/widgets/pages. */ const excessiveSlicing = { - name: `${NAMESPACE}/excessive-slicing`, + name: `${NAMESPACE}/excessive-slicing` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/forbidden-imports/index.spec.ts b/packages/steiger-plugin-fsd/src/forbidden-imports/index.spec.ts index 4572e12..dbedd80 100644 --- a/packages/steiger-plugin-fsd/src/forbidden-imports/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/forbidden-imports/index.spec.ts @@ -1,6 +1,6 @@ import { expect, it, vi } from 'vitest' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' import forbiddenImports from './index.js' vi.mock('tsconfck', async (importOriginal) => { @@ -23,7 +23,7 @@ vi.mock('tsconfck', async (importOriginal) => { vi.mock('node:fs', async (importOriginal) => { const originalFs = await importOriginal() - const { createFsMocks } = await import('../_lib/prepare-test.js') + const { createFsMocks } = await import('@steiger/toolkit') return createFsMocks( { diff --git a/packages/steiger-plugin-fsd/src/forbidden-imports/index.ts b/packages/steiger-plugin-fsd/src/forbidden-imports/index.ts index 1fbbb4e..04fc4ce 100644 --- a/packages/steiger-plugin-fsd/src/forbidden-imports/index.ts +++ b/packages/steiger-plugin-fsd/src/forbidden-imports/index.ts @@ -4,13 +4,13 @@ import { layerSequence, resolveImport, isCrossImportPublicApi } from '@feature-s import precinct from 'precinct' const { paperwork } = precinct import { parse as parseNearestTsConfig } from 'tsconfck' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { indexSourceFiles } from '../_lib/index-source-files.js' import { NAMESPACE } from '../constants.js' const forbiddenImports = { - name: `${NAMESPACE}/forbidden-imports`, + name: `${NAMESPACE}/forbidden-imports` as const, async check(root) { const diagnostics: Array = [] const { tsconfig } = await parseNearestTsConfig(root.path) diff --git a/packages/steiger-plugin-fsd/src/forbidden-imports/precinct.d.ts b/packages/steiger-plugin-fsd/src/forbidden-imports/precinct.d.ts deleted file mode 100644 index 2654f56..0000000 --- a/packages/steiger-plugin-fsd/src/forbidden-imports/precinct.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Track https://github.com/dependents/node-precinct/issues/130 to remove this file -declare module 'precinct' { - export = precinct - - /** - * Finds the list of dependencies for the given file - * - * @param {String|Object} content - File's content or AST - * @param {Object} [options] - * @param {String} [options.type] - The type of content being passed in. Useful if you want to use a non-js detective - * @return {String[]} - */ - declare function precinct( - content: string | Record, - options?: { - type?: string - }, - ): string[] - declare namespace precinct { - /** - * Returns the dependencies for the given file path - * - * @param {String} filename - * @param {Object} [options] - * @param {Boolean} [options.includeCore=true] - Whether or not to include core modules in the dependency list - * @param {Object} [options.fileSystem=undefined] - An alternative fs implementation to use for reading the file path. - * @return {String[]} - */ - function paperwork( - filename: string, - options?: { - includeCore?: boolean - fileSystem?: Record - }, - ): string[] - } -} diff --git a/packages/steiger-plugin-fsd/src/import-locality/index.spec.ts b/packages/steiger-plugin-fsd/src/import-locality/index.spec.ts index 2025b8d..8a25fa8 100644 --- a/packages/steiger-plugin-fsd/src/import-locality/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/import-locality/index.spec.ts @@ -1,6 +1,6 @@ import { expect, it, vi } from 'vitest' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' import importLocality from './index.js' vi.mock('tsconfck', async (importOriginal) => { @@ -12,7 +12,7 @@ vi.mock('tsconfck', async (importOriginal) => { vi.mock('node:fs', async (importOriginal) => { const originalFs = await importOriginal() - const { createFsMocks } = await import('../_lib/prepare-test.js') + const { createFsMocks } = await import('@steiger/toolkit') return createFsMocks( { diff --git a/packages/steiger-plugin-fsd/src/import-locality/index.ts b/packages/steiger-plugin-fsd/src/import-locality/index.ts index 9d663f5..ad7646c 100644 --- a/packages/steiger-plugin-fsd/src/import-locality/index.ts +++ b/packages/steiger-plugin-fsd/src/import-locality/index.ts @@ -3,7 +3,7 @@ import { resolveImport } from '@feature-sliced/filesystem' import precinct from 'precinct' const { paperwork } = precinct import { parse as parseNearestTsConfig } from 'tsconfck' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { indexSourceFiles } from '../_lib/index-source-files.js' import { NAMESPACE } from '../constants.js' diff --git a/packages/steiger-plugin-fsd/src/inconsistent-naming/index.spec.ts b/packages/steiger-plugin-fsd/src/inconsistent-naming/index.spec.ts index 3e7b606..60aec51 100644 --- a/packages/steiger-plugin-fsd/src/inconsistent-naming/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/inconsistent-naming/index.spec.ts @@ -1,6 +1,6 @@ import { expect, it } from 'vitest' -import { compareMessages, parseIntoFsdRoot, joinFromRoot } from '../_lib/prepare-test.js' +import { compareMessages, joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' import inconsistentNaming from './index.js' it('reports no errors on slice names that are pluralized consistently', () => { diff --git a/packages/steiger-plugin-fsd/src/inconsistent-naming/index.ts b/packages/steiger-plugin-fsd/src/inconsistent-naming/index.ts index 7b6aa8e..9fd3af1 100644 --- a/packages/steiger-plugin-fsd/src/inconsistent-naming/index.ts +++ b/packages/steiger-plugin-fsd/src/inconsistent-naming/index.ts @@ -3,14 +3,14 @@ import { partition } from 'lodash-es' import pluralize from 'pluralize' const { isPlural, plural, singular } = pluralize import { getLayers, getSlices } from '@feature-sliced/filesystem' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { groupSlices } from '../_lib/group-slices.js' import { NAMESPACE } from '../constants.js' /** Detect inconsistent naming of slices on layers (singular vs plural) */ const inconsistentNaming = { - name: `${NAMESPACE}/inconsistent-naming`, + name: `${NAMESPACE}/inconsistent-naming` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/index.ts b/packages/steiger-plugin-fsd/src/index.ts index e1ce72a..53fefaf 100644 --- a/packages/steiger-plugin-fsd/src/index.ts +++ b/packages/steiger-plugin-fsd/src/index.ts @@ -1,4 +1,4 @@ -import { Config, Rule, Plugin, ConfigObject } from '@steiger/types' +import { enableAllRules, type ConfigObjectOf, createPlugin, createConfigs } from '@steiger/toolkit' import ambiguousSliceNames from './ambiguous-slice-names/index.js' import excessiveSlicing from './excessive-slicing/index.js' @@ -19,7 +19,7 @@ import typoInLayerName from './typo-in-layer-name/index.js' import noProcesses from './no-processes/index.js' import packageJson from '../package.json' -const allRules: Array = [ +const rules = [ ambiguousSliceNames, excessiveSlicing, forbiddenImports, @@ -39,23 +39,21 @@ const allRules: Array = [ noProcesses, ] -const allRulesEnabledConfig: ConfigObject = { - rules: allRules.reduce((acc, rule) => ({ ...acc, [rule.name]: 'error' }), {}), -} - -const plugin: Plugin = { +const plugin = createPlugin({ meta: { name: 'steiger-plugin-fsd', version: packageJson.version, }, - ruleDefinitions: allRules, -} + ruleDefinitions: rules, +}) -const configs: Record = { - recommended: [plugin, allRulesEnabledConfig], -} +const configs = createConfigs({ + recommended: enableAllRules(plugin), +}) export default { plugin, configs, } + +export type FSDConfigObject = ConfigObjectOf diff --git a/packages/steiger-plugin-fsd/src/insignificant-slice/index.spec.ts b/packages/steiger-plugin-fsd/src/insignificant-slice/index.spec.ts index cf3f819..e71832e 100644 --- a/packages/steiger-plugin-fsd/src/insignificant-slice/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/insignificant-slice/index.spec.ts @@ -1,7 +1,7 @@ import { expect, it, vi } from 'vitest' import { join } from 'node:path' -import { compareMessages, joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { compareMessages, joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' import insignificantSlice from './index.js' vi.mock('tsconfck', async (importOriginal) => { @@ -13,7 +13,7 @@ vi.mock('tsconfck', async (importOriginal) => { vi.mock('node:fs', async (importOriginal) => { const originalFs = await importOriginal() - const { createFsMocks } = await import('../_lib/prepare-test.js') + const { createFsMocks } = await import('@steiger/toolkit') return createFsMocks( { diff --git a/packages/steiger-plugin-fsd/src/insignificant-slice/index.ts b/packages/steiger-plugin-fsd/src/insignificant-slice/index.ts index 83186b3..5266cc3 100644 --- a/packages/steiger-plugin-fsd/src/insignificant-slice/index.ts +++ b/packages/steiger-plugin-fsd/src/insignificant-slice/index.ts @@ -2,7 +2,7 @@ import * as fs from 'node:fs' import { sep, join } from 'node:path' import { parse as parseNearestTsConfig } from 'tsconfck' import { isSliced, resolveImport, unslicedLayers, type LayerName } from '@feature-sliced/filesystem' -import type { Folder, PartialDiagnostic, Rule } from '@steiger/types' +import type { Folder, PartialDiagnostic, Rule } from '@steiger/toolkit' import precinct from 'precinct' const { paperwork } = precinct @@ -10,7 +10,7 @@ import { indexSourceFiles } from '../_lib/index-source-files.js' import { NAMESPACE } from '../constants.js' const insignificantSlice = { - name: `${NAMESPACE}/insignificant-slice`, + name: `${NAMESPACE}/insignificant-slice` as const, async check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/no-file-segments/index.spec.ts b/packages/steiger-plugin-fsd/src/no-file-segments/index.spec.ts index 5e186a2..925b26b 100644 --- a/packages/steiger-plugin-fsd/src/no-file-segments/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/no-file-segments/index.spec.ts @@ -1,6 +1,6 @@ import { expect, it } from 'vitest' -import { compareMessages, joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { compareMessages, joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' import noFileSegments from './index.js' it('reports no errors on a project with only folder segments', async () => { diff --git a/packages/steiger-plugin-fsd/src/no-file-segments/index.ts b/packages/steiger-plugin-fsd/src/no-file-segments/index.ts index 1543149..60b739c 100644 --- a/packages/steiger-plugin-fsd/src/no-file-segments/index.ts +++ b/packages/steiger-plugin-fsd/src/no-file-segments/index.ts @@ -1,6 +1,6 @@ import { basename } from 'node:path' import { getLayers, getSlices, isSliced } from '@feature-sliced/filesystem' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' const noFileSegments = { diff --git a/packages/steiger-plugin-fsd/src/no-layer-public-api/index.spec.ts b/packages/steiger-plugin-fsd/src/no-layer-public-api/index.spec.ts index e29060b..617b506 100644 --- a/packages/steiger-plugin-fsd/src/no-layer-public-api/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/no-layer-public-api/index.spec.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest' import noLayerPublicApi from './index.js' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' it('reports no errors on a project without index files on layer level', () => { const root = parseIntoFsdRoot(` diff --git a/packages/steiger-plugin-fsd/src/no-layer-public-api/index.ts b/packages/steiger-plugin-fsd/src/no-layer-public-api/index.ts index ac6ad5c..7b741c8 100644 --- a/packages/steiger-plugin-fsd/src/no-layer-public-api/index.ts +++ b/packages/steiger-plugin-fsd/src/no-layer-public-api/index.ts @@ -1,5 +1,5 @@ import { getIndex, getLayers } from '@feature-sliced/filesystem' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' /** Layers that are allowed to have an index file. */ @@ -7,7 +7,7 @@ const exceptionLayers = ['app'] /** Forbid index files on layer level. */ const noLayerPublicApi = { - name: `${NAMESPACE}/no-layer-public-api`, + name: `${NAMESPACE}/no-layer-public-api` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/no-processes/index.spec.ts b/packages/steiger-plugin-fsd/src/no-processes/index.spec.ts index 169f940..b3a0090 100644 --- a/packages/steiger-plugin-fsd/src/no-processes/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/no-processes/index.spec.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest' import noProcesses from './index.js' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' it('reports no errors on a project without the Processes layer', () => { const root = parseIntoFsdRoot(` diff --git a/packages/steiger-plugin-fsd/src/no-processes/index.ts b/packages/steiger-plugin-fsd/src/no-processes/index.ts index 95c9f9e..ae4badb 100644 --- a/packages/steiger-plugin-fsd/src/no-processes/index.ts +++ b/packages/steiger-plugin-fsd/src/no-processes/index.ts @@ -1,9 +1,9 @@ import { basename } from 'node:path' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' const noProcesses = { - name: `${NAMESPACE}/no-processes`, + name: `${NAMESPACE}/no-processes` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/no-public-api-sidestep/index.spec.ts b/packages/steiger-plugin-fsd/src/no-public-api-sidestep/index.spec.ts index 519224a..163c7a8 100644 --- a/packages/steiger-plugin-fsd/src/no-public-api-sidestep/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/no-public-api-sidestep/index.spec.ts @@ -1,6 +1,6 @@ import { expect, it, vi } from 'vitest' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' import noPublicApiSidestep from './index.js' vi.mock('tsconfck', async (importOriginal) => { @@ -12,7 +12,7 @@ vi.mock('tsconfck', async (importOriginal) => { vi.mock('node:fs', async (importOriginal) => { const originalFs = await importOriginal() - const { createFsMocks } = await import('../_lib/prepare-test.js') + const { createFsMocks } = await import('@steiger/toolkit') return createFsMocks( { diff --git a/packages/steiger-plugin-fsd/src/no-public-api-sidestep/index.ts b/packages/steiger-plugin-fsd/src/no-public-api-sidestep/index.ts index 8f3a281..3e45559 100644 --- a/packages/steiger-plugin-fsd/src/no-public-api-sidestep/index.ts +++ b/packages/steiger-plugin-fsd/src/no-public-api-sidestep/index.ts @@ -10,14 +10,14 @@ import { resolveImport, crossReferenceToken, } from '@feature-sliced/filesystem' -import type { Folder, File, PartialDiagnostic, Rule } from '@steiger/types' +import type { Folder, File, PartialDiagnostic, Rule } from '@steiger/toolkit' import { indexSourceFiles } from '../_lib/index-source-files.js' import { NAMESPACE } from '../constants.js' /** Restrict imports that go inside the slice, sidestepping the public API. */ const noPublicApiSidestep = { - name: `${NAMESPACE}/no-public-api-sidestep`, + name: `${NAMESPACE}/no-public-api-sidestep` as const, async check(root) { const diagnostics: Array = [] const { tsconfig } = await parseNearestTsConfig(root.path) diff --git a/packages/steiger-plugin-fsd/src/no-reserved-folder-names/index.spec.ts b/packages/steiger-plugin-fsd/src/no-reserved-folder-names/index.spec.ts index 1c3ea7b..5551f45 100644 --- a/packages/steiger-plugin-fsd/src/no-reserved-folder-names/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/no-reserved-folder-names/index.spec.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest' import noReservedFolderNames from './index.js' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' it('reports no errors on a project without subfolders in segments that use reserved names', () => { const root = parseIntoFsdRoot(` diff --git a/packages/steiger-plugin-fsd/src/no-reserved-folder-names/index.ts b/packages/steiger-plugin-fsd/src/no-reserved-folder-names/index.ts index d6599f4..7c53af3 100644 --- a/packages/steiger-plugin-fsd/src/no-reserved-folder-names/index.ts +++ b/packages/steiger-plugin-fsd/src/no-reserved-folder-names/index.ts @@ -1,13 +1,12 @@ import { basename } from 'node:path' import { getAllSegments, conventionalSegmentNames, crossReferenceToken } from '@feature-sliced/filesystem' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import { findAllRecursively, type PartialDiagnostic, type Rule } from '@steiger/toolkit' -import { findAllRecursively } from '../_lib/find-all-recursively.js' import { NAMESPACE } from '../constants.js' /** Forbid subfolders in segments that have names of common segments (e.g., shared/lib/ui). */ const noReservedFolderNames = { - name: `${NAMESPACE}/no-reserved-folder-names`, + name: `${NAMESPACE}/no-reserved-folder-names` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/no-segmentless-slices/index.spec.ts b/packages/steiger-plugin-fsd/src/no-segmentless-slices/index.spec.ts index db8404c..a12f755 100644 --- a/packages/steiger-plugin-fsd/src/no-segmentless-slices/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/no-segmentless-slices/index.spec.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest' import noSegmentlessSlices from './index.js' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' it('reports no errors on a project where every slice has at least one segment', () => { const root = parseIntoFsdRoot(` diff --git a/packages/steiger-plugin-fsd/src/no-segmentless-slices/index.ts b/packages/steiger-plugin-fsd/src/no-segmentless-slices/index.ts index f516f4d..4a23394 100644 --- a/packages/steiger-plugin-fsd/src/no-segmentless-slices/index.ts +++ b/packages/steiger-plugin-fsd/src/no-segmentless-slices/index.ts @@ -1,9 +1,9 @@ import { getLayers, isSlice, isSliced } from '@feature-sliced/filesystem' -import type { Folder, PartialDiagnostic, Rule } from '@steiger/types' +import type { Folder, PartialDiagnostic, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' const noSegmentlessSlices = { - name: `${NAMESPACE}/no-segmentless-slices`, + name: `${NAMESPACE}/no-segmentless-slices` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/no-segments-on-sliced-layers/index.spec.ts b/packages/steiger-plugin-fsd/src/no-segments-on-sliced-layers/index.spec.ts index 113110a..00b911c 100644 --- a/packages/steiger-plugin-fsd/src/no-segments-on-sliced-layers/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/no-segments-on-sliced-layers/index.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest' import noSegmentsOnSlicedLayers from './index.js' -import { joinFromRoot, parseIntoFsdRoot, compareMessages } from '../_lib/prepare-test.js' +import { compareMessages, joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' describe('no-segments-on-sliced-layers rule', () => { it('reports no errors on a project where the sliced layers has no segments in direct children', () => { diff --git a/packages/steiger-plugin-fsd/src/no-segments-on-sliced-layers/index.ts b/packages/steiger-plugin-fsd/src/no-segments-on-sliced-layers/index.ts index 9eac787..8a0d95b 100644 --- a/packages/steiger-plugin-fsd/src/no-segments-on-sliced-layers/index.ts +++ b/packages/steiger-plugin-fsd/src/no-segments-on-sliced-layers/index.ts @@ -1,11 +1,11 @@ import { basename } from 'node:path' import { getLayers, isSliced, conventionalSegmentNames } from '@feature-sliced/filesystem' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' const noSegmentsOnSlicedLayers = { - name: `${NAMESPACE}/no-segments-on-sliced-layers`, + name: `${NAMESPACE}/no-segments-on-sliced-layers` as const, check(root) { const diagnostics: Array = [] const layers = Object.values(getLayers(root)) diff --git a/packages/steiger-plugin-fsd/src/no-ui-in-app/index.spec.ts b/packages/steiger-plugin-fsd/src/no-ui-in-app/index.spec.ts index 461a79e..fd19313 100644 --- a/packages/steiger-plugin-fsd/src/no-ui-in-app/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/no-ui-in-app/index.spec.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' import noUiInApp from './index.js' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' it('reports no errors on a project without the "ui" segment on the "app" layer', () => { const root = parseIntoFsdRoot(` diff --git a/packages/steiger-plugin-fsd/src/no-ui-in-app/index.ts b/packages/steiger-plugin-fsd/src/no-ui-in-app/index.ts index 44310ef..62287fa 100644 --- a/packages/steiger-plugin-fsd/src/no-ui-in-app/index.ts +++ b/packages/steiger-plugin-fsd/src/no-ui-in-app/index.ts @@ -1,9 +1,9 @@ -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' import { getLayers, getSegments } from '@feature-sliced/filesystem' const noUiInApp = { - name: `${NAMESPACE}/no-ui-in-app`, + name: `${NAMESPACE}/no-ui-in-app` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/public-api/index.spec.ts b/packages/steiger-plugin-fsd/src/public-api/index.spec.ts index 2a29c37..810abc0 100644 --- a/packages/steiger-plugin-fsd/src/public-api/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/public-api/index.spec.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest' import publicApi from './index.js' -import { compareMessages, joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { compareMessages, joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' it('reports no errors on a project with all the required public APIs', () => { const root = parseIntoFsdRoot(` diff --git a/packages/steiger-plugin-fsd/src/public-api/index.ts b/packages/steiger-plugin-fsd/src/public-api/index.ts index f3c9d67..c7ef651 100644 --- a/packages/steiger-plugin-fsd/src/public-api/index.ts +++ b/packages/steiger-plugin-fsd/src/public-api/index.ts @@ -1,11 +1,11 @@ import { join } from 'node:path' import { getLayers, getSegments, isSliced, getIndex, getSlices } from '@feature-sliced/filesystem' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' /** Require slices (or segments on sliceless layers) to have a public API. */ const publicApi = { - name: `${NAMESPACE}/public-api`, + name: `${NAMESPACE}/public-api` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/repetitive-naming/index.spec.ts b/packages/steiger-plugin-fsd/src/repetitive-naming/index.spec.ts index 817dd9e..683dc67 100644 --- a/packages/steiger-plugin-fsd/src/repetitive-naming/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/repetitive-naming/index.spec.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest' import repetitiveNaming from './index.js' -import { compareMessages, joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { compareMessages, joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' it('reports no errors on a project with no repetitive words in slices', () => { const root = parseIntoFsdRoot(` diff --git a/packages/steiger-plugin-fsd/src/repetitive-naming/index.ts b/packages/steiger-plugin-fsd/src/repetitive-naming/index.ts index cc440fe..1516125 100644 --- a/packages/steiger-plugin-fsd/src/repetitive-naming/index.ts +++ b/packages/steiger-plugin-fsd/src/repetitive-naming/index.ts @@ -1,5 +1,5 @@ import { getLayers, getSlices, isSliced } from '@feature-sliced/filesystem' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' /** @@ -16,7 +16,7 @@ const wordPattern = /(?:[A-Z]+|[a-z]+)[a-z]*/g /** Warn about repetitive parts in slice names (e.g. adding page to every slice on Pages) */ const repetitiveNaming = { - name: `${NAMESPACE}/repetitive-naming`, + name: `${NAMESPACE}/repetitive-naming` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/segments-by-purpose/index.spec.ts b/packages/steiger-plugin-fsd/src/segments-by-purpose/index.spec.ts index c29a6cb..a76a5b6 100644 --- a/packages/steiger-plugin-fsd/src/segments-by-purpose/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/segments-by-purpose/index.spec.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest' import segmentsByPurpose from './index.js' -import { compareMessages, joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { compareMessages, joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' it('reports no errors on a project with good segments', () => { const root = parseIntoFsdRoot(` diff --git a/packages/steiger-plugin-fsd/src/segments-by-purpose/index.ts b/packages/steiger-plugin-fsd/src/segments-by-purpose/index.ts index 98f956d..9f09f10 100644 --- a/packages/steiger-plugin-fsd/src/segments-by-purpose/index.ts +++ b/packages/steiger-plugin-fsd/src/segments-by-purpose/index.ts @@ -1,12 +1,12 @@ import { getLayers, getSegments, getSlices, isSliced } from '@feature-sliced/filesystem' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' const BAD_NAMES = ['components', 'hooks', 'helpers', 'utils', 'modals', 'types', 'constants', 'consts', 'const'] /** Discourage the use of segment names that group code by its essence, and instead encourage grouping by purpose. */ const segmentsByPurpose = { - name: `${NAMESPACE}/segments-by-purpose`, + name: `${NAMESPACE}/segments-by-purpose` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/shared-lib-grouping/index.spec.ts b/packages/steiger-plugin-fsd/src/shared-lib-grouping/index.spec.ts index 0c1d00e..791d2b6 100644 --- a/packages/steiger-plugin-fsd/src/shared-lib-grouping/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/shared-lib-grouping/index.spec.ts @@ -1,6 +1,6 @@ import { expect, it } from 'vitest' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' import excessiveSlicing from './index.js' it('reports no errors on projects with no shared/lib', () => { diff --git a/packages/steiger-plugin-fsd/src/shared-lib-grouping/index.ts b/packages/steiger-plugin-fsd/src/shared-lib-grouping/index.ts index 2fd35e9..bf7fec6 100644 --- a/packages/steiger-plugin-fsd/src/shared-lib-grouping/index.ts +++ b/packages/steiger-plugin-fsd/src/shared-lib-grouping/index.ts @@ -1,12 +1,12 @@ import { getLayers, getSegments } from '@feature-sliced/filesystem' -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' const THRESHOLD = 15 /** Warn about too much stuff in shared/lib. */ const sharedLibGrouping = { - name: `${NAMESPACE}/shared-lib-grouping`, + name: `${NAMESPACE}/shared-lib-grouping` as const, check(root) { const diagnostics: Array = [] diff --git a/packages/steiger-plugin-fsd/src/typo-in-layer-name/index.spec.ts b/packages/steiger-plugin-fsd/src/typo-in-layer-name/index.spec.ts index 18fb481..181e07a 100644 --- a/packages/steiger-plugin-fsd/src/typo-in-layer-name/index.spec.ts +++ b/packages/steiger-plugin-fsd/src/typo-in-layer-name/index.spec.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest' +import { joinFromRoot, parseIntoFolder as parseIntoFsdRoot } from '@steiger/toolkit' import typoInLayerName from './index.js' -import { joinFromRoot, parseIntoFsdRoot } from '../_lib/prepare-test.js' it('reports no errors on a project without typos in layer names', () => { const root = parseIntoFsdRoot(` diff --git a/packages/steiger-plugin-fsd/src/typo-in-layer-name/index.ts b/packages/steiger-plugin-fsd/src/typo-in-layer-name/index.ts index 75d35a1..93d9aa6 100644 --- a/packages/steiger-plugin-fsd/src/typo-in-layer-name/index.ts +++ b/packages/steiger-plugin-fsd/src/typo-in-layer-name/index.ts @@ -1,14 +1,13 @@ -import type { PartialDiagnostic, Rule } from '@steiger/types' +import type { PartialDiagnostic, Rule } from '@steiger/toolkit' import { NAMESPACE } from '../constants.js' import { LayerName, layerSequence } from '@feature-sliced/filesystem' import { distance } from 'fastest-levenshtein' -import { basename } from 'node:path' -import { joinFromRoot } from '../_lib/prepare-test.js' +import { basename, join } from 'node:path' const LEVENSHTEIN_DISTANCE_UPPER_BOUND = 3 const typoInLayerName = { - name: `${NAMESPACE}/typo-in-layer-name`, + name: `${NAMESPACE}/typo-in-layer-name` as const, check(root) { const diagnostics: Array = [] @@ -57,7 +56,7 @@ const typoInLayerName = { diagnostics.push({ message: `Layer "${layer.input}" potentially contains a typo. Did you mean "${layer.suggestion}"?`, - location: { path: joinFromRoot(layer.input) }, + location: { path: join(root.path, layer.input) }, }) }) diff --git a/packages/steiger/package.json b/packages/steiger/package.json index a633df5..d0b85de 100644 --- a/packages/steiger/package.json +++ b/packages/steiger/package.json @@ -39,7 +39,6 @@ "README.md" ], "dependencies": { - "@feature-sliced/filesystem": "^2.4.0", "@feature-sliced/steiger-plugin": "workspace:*", "chokidar": "^3.6.0", "cosmiconfig": "^9.0.0", @@ -57,6 +56,7 @@ "devDependencies": { "@steiger/eslint-config": "workspace:*", "@steiger/pretty-reporter": "workspace:*", + "@steiger/toolkit": "workspace:*", "@steiger/tsconfig": "workspace:*", "@steiger/types": "workspace:*", "@total-typescript/ts-reset": "^0.5.1", diff --git a/packages/steiger/src/_lib/prepare-test.ts b/packages/steiger/src/_lib/prepare-test.ts deleted file mode 100644 index 70cbe7f..0000000 --- a/packages/steiger/src/_lib/prepare-test.ts +++ /dev/null @@ -1,235 +0,0 @@ -import { join, sep } from 'node:path' -import type { readFileSync, existsSync } from 'node:fs' -import type { FsdRoot } from '@feature-sliced/filesystem' -import type { Folder, File, PartialDiagnostic } from '@steiger/types' -import { vi } from 'vitest' - -/** Parse a multi-line indented string with emojis for files and folders into an FSD root. - * @param fsMarkup - a file system tree represented in markup using file and folder emojis - * @param mountTo - virtually make the passed markup a subtree of the mountTo folder - * */ -export function parseIntoFsdRoot(fsMarkup: string, mountTo?: string): FsdRoot { - function parseFolder(lines: Array, path: string): Folder { - const children: Array = [] - - lines.forEach((line, index) => { - if (line.startsWith('πŸ“‚ ')) { - let nestedLines = lines.slice(index + 1) - const nextIndex = nestedLines.findIndex((line) => !line.startsWith(' ')) - nestedLines = nestedLines.slice(0, nextIndex === -1 ? nestedLines.length : nextIndex) - const folder = parseFolder( - nestedLines.map((line) => line.slice(' '.length)), - join(path, line.slice('πŸ“‚ '.length)), - ) - children.push(folder) - } else if (line.startsWith('πŸ“„ ')) { - children.push({ type: 'file', path: join(path, line.slice('πŸ“„ '.length)) }) - } - }) - - return { type: 'folder', path, children } - } - - const lines = fsMarkup - .split('\n') - .filter(Boolean) - .map((line, _i, lines) => line.slice(lines[0].search(/\S/))) - .filter(Boolean) - - return parseFolder(lines, mountTo ?? joinFromRoot()) -} - -export function compareMessages(a: PartialDiagnostic, b: PartialDiagnostic): number { - return a.message.localeCompare(b.message) || a.location.path.localeCompare(b.location.path) -} - -export function joinFromRoot(...segments: Array) { - return join('/', ...segments) -} - -export function createFsMocks(mockedFiles: Record, original: typeof import('fs')): typeof import('fs') { - const normalizedMockedFiles = Object.fromEntries( - Object.entries(mockedFiles).map(([path, content]) => [path.replace(/\//g, sep), content]), - ) - - return { - ...original, - readFileSync: vi.fn(((path, options) => { - const normalizedPath = typeof path === 'string' ? path.replace(/\//g, sep) : path - if (typeof normalizedPath === 'string' && normalizedPath in normalizedMockedFiles) { - return normalizedMockedFiles[normalizedPath as keyof typeof normalizedMockedFiles] - } else { - return original.readFileSync(normalizedPath, options) - } - }) as typeof readFileSync), - existsSync: vi.fn(((path) => { - const normalizedPath = typeof path === 'string' ? path.replace(/\//g, sep) : path - return Object.keys(normalizedMockedFiles).some( - (key) => key === normalizedPath || key.startsWith(normalizedPath + sep), - ) - }) as typeof existsSync), - } as typeof import('fs') -} - -if (import.meta.vitest) { - const { test, expect } = import.meta.vitest - - test('parseIntoFsdRoot', () => { - const root = parseIntoFsdRoot(` - πŸ“‚ entities - πŸ“‚ users - πŸ“‚ ui - πŸ“„ index.ts - πŸ“‚ posts - πŸ“‚ ui - πŸ“„ index.ts - πŸ“‚ shared - πŸ“‚ ui - πŸ“„ index.ts - πŸ“„ Button.tsx - `) - - expect(root).toEqual({ - type: 'folder', - path: joinFromRoot(), - children: [ - { - type: 'folder', - path: joinFromRoot('entities'), - children: [ - { - type: 'folder', - path: joinFromRoot('entities', 'users'), - children: [ - { - type: 'folder', - path: joinFromRoot('entities', 'users', 'ui'), - children: [], - }, - { - type: 'file', - path: joinFromRoot('entities', 'users', 'index.ts'), - }, - ], - }, - { - type: 'folder', - path: joinFromRoot('entities', 'posts'), - children: [ - { - type: 'folder', - path: joinFromRoot('entities', 'posts', 'ui'), - children: [], - }, - { - type: 'file', - path: joinFromRoot('entities', 'posts', 'index.ts'), - }, - ], - }, - ], - }, - { - type: 'folder', - path: joinFromRoot('shared'), - children: [ - { - type: 'folder', - path: joinFromRoot('shared', 'ui'), - children: [ - { - type: 'file', - path: joinFromRoot('shared', 'ui', 'index.ts'), - }, - { - type: 'file', - path: joinFromRoot('shared', 'ui', 'Button.tsx'), - }, - ], - }, - ], - }, - ], - }) - }) - - test('it should return a nested root folder when the optional rootPath argument is passed', () => { - const markup = ` - πŸ“‚ entities - πŸ“‚ users - πŸ“‚ ui - πŸ“„ index.ts - πŸ“‚ posts - πŸ“‚ ui - πŸ“„ index.ts - πŸ“‚ shared - πŸ“‚ ui - πŸ“„ index.ts - πŸ“„ Button.tsx - ` - const root = parseIntoFsdRoot(markup, joinFromRoot('src')) - - expect(root).toEqual({ - type: 'folder', - path: joinFromRoot('src'), - children: [ - { - type: 'folder', - path: joinFromRoot('src', 'entities'), - children: [ - { - type: 'folder', - path: joinFromRoot('src', 'entities', 'users'), - children: [ - { - type: 'folder', - path: joinFromRoot('src', 'entities', 'users', 'ui'), - children: [], - }, - { - type: 'file', - path: joinFromRoot('src', 'entities', 'users', 'index.ts'), - }, - ], - }, - { - type: 'folder', - path: joinFromRoot('src', 'entities', 'posts'), - children: [ - { - type: 'folder', - path: joinFromRoot('src', 'entities', 'posts', 'ui'), - children: [], - }, - { - type: 'file', - path: joinFromRoot('src', 'entities', 'posts', 'index.ts'), - }, - ], - }, - ], - }, - { - type: 'folder', - path: joinFromRoot('src', 'shared'), - children: [ - { - type: 'folder', - path: joinFromRoot('src', 'shared', 'ui'), - children: [ - { - type: 'file', - path: joinFromRoot('src', 'shared', 'ui', 'index.ts'), - }, - { - type: 'file', - path: joinFromRoot('src', 'shared', 'ui', 'Button.tsx'), - }, - ], - }, - ], - }, - ], - }) - }) -} diff --git a/packages/steiger/src/app.ts b/packages/steiger/src/app.ts index b7bdcf6..dccbad6 100644 --- a/packages/steiger/src/app.ts +++ b/packages/steiger/src/app.ts @@ -1,6 +1,6 @@ import { createEffect, sample } from 'effector' import { debounce, not } from 'patronum' -import { Config, Folder, Rule } from '@steiger/types' +import type { Config, Folder, Rule } from '@steiger/types' import { scan, createWatcher } from './features/transfer-fs-to-vfs' import { defer } from './shared/defer' @@ -66,6 +66,6 @@ export const linter = { }, } -export function defineConfig(config: Config) { +export function defineConfig = Array>(config: Config) { return config } diff --git a/packages/steiger/src/features/calculate-diagnostic-severities/calculate-final-severity.spec.ts b/packages/steiger/src/features/calculate-diagnostic-severities/calculate-final-severity.spec.ts index fa7fd54..1985d7a 100644 --- a/packages/steiger/src/features/calculate-diagnostic-severities/calculate-final-severity.spec.ts +++ b/packages/steiger/src/features/calculate-diagnostic-severities/calculate-final-severity.spec.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest' +import { joinFromRoot, parseIntoFolder } from '@steiger/toolkit' -import { joinFromRoot, parseIntoFsdRoot } from '../../_lib/prepare-test' import calculateFinalSeverities from './calculate-final-severity' import { GlobGroupWithSeverity } from '../../models/config' @@ -16,7 +16,7 @@ vi.mock('../../models/config', async () => { describe('calculateFinalSeverity', () => { it('should return severities for paths', () => { - const vfs = parseIntoFsdRoot( + const vfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ ui diff --git a/packages/steiger/src/features/remove-global-ignores-from-vfs/remove-global-ignores-from-vfs.spec.ts b/packages/steiger/src/features/remove-global-ignores-from-vfs/remove-global-ignores-from-vfs.spec.ts index 64dd8e8..c634280 100644 --- a/packages/steiger/src/features/remove-global-ignores-from-vfs/remove-global-ignores-from-vfs.spec.ts +++ b/packages/steiger/src/features/remove-global-ignores-from-vfs/remove-global-ignores-from-vfs.spec.ts @@ -1,11 +1,11 @@ import { expect, it, describe } from 'vitest' +import { joinFromRoot, parseIntoFolder } from '@steiger/toolkit' import removeGlobalIgnoresFromVfs from './remove-global-ignores-from-vfs' -import { joinFromRoot, parseIntoFsdRoot } from '../../_lib/prepare-test' describe('removeGlobalIgnoresFromVfs', () => { it('should remove nodes that match global ignores from VFS', () => { - const vfs = parseIntoFsdRoot( + const vfs = parseIntoFolder( ` πŸ“‚ entities πŸ“‚ user @@ -17,11 +17,11 @@ describe('removeGlobalIgnoresFromVfs', () => { πŸ“‚ ui πŸ“„ UserAvatar.tsx πŸ“„ UserAvatar.test.tsx - πŸ“„ index.ts + πŸ“„ index.ts `, joinFromRoot('src'), ) - const expectedVfs = parseIntoFsdRoot( + const expectedVfs = parseIntoFolder( ` πŸ“‚ entities πŸ“‚ user @@ -29,7 +29,7 @@ describe('removeGlobalIgnoresFromVfs', () => { πŸ“„ store.ts πŸ“‚ ui πŸ“„ UserAvatar.tsx - πŸ“„ index.ts + πŸ“„ index.ts `, joinFromRoot('src'), ) diff --git a/packages/steiger/src/features/run-rule/prepare-vfs-for-rule-run.spec.ts b/packages/steiger/src/features/run-rule/prepare-vfs-for-rule-run.spec.ts index 4518d21..612600e 100644 --- a/packages/steiger/src/features/run-rule/prepare-vfs-for-rule-run.spec.ts +++ b/packages/steiger/src/features/run-rule/prepare-vfs-for-rule-run.spec.ts @@ -1,12 +1,12 @@ -import { prepareVfsForRuleRun } from './prepare-vfs-for-rule-run' - import { describe, it, expect } from 'vitest' -import { joinFromRoot, parseIntoFsdRoot } from '../../_lib/prepare-test' +import { joinFromRoot, parseIntoFolder } from '@steiger/toolkit' + +import { prepareVfsForRuleRun } from './prepare-vfs-for-rule-run' import { GlobGroupWithSeverity } from '../../models/config' describe('prepareVfsForRuleRun', () => { it('should return vfs without off nodes', () => { - const vfs = parseIntoFsdRoot( + const vfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ ui @@ -36,7 +36,7 @@ describe('prepareVfsForRuleRun', () => { joinFromRoot('src'), ) - const expectedVfs = parseIntoFsdRoot( + const expectedVfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ ui diff --git a/packages/steiger/src/features/run-rule/run-rule.ts b/packages/steiger/src/features/run-rule/run-rule.ts index 514efbd..e471994 100644 --- a/packages/steiger/src/features/run-rule/run-rule.ts +++ b/packages/steiger/src/features/run-rule/run-rule.ts @@ -12,5 +12,5 @@ export async function runRule(vfs: Folder, rule: Rule) { return Promise.resolve({ diagnostics: [] }) } - return Promise.resolve(rule.check(finalVfs, getRuleOptions(rule.name) || undefined)) + return Promise.resolve(rule.check(finalVfs, getRuleOptions(rule.name) ?? {})) } diff --git a/packages/steiger/src/models/config/create-rule-instructions.spec.ts b/packages/steiger/src/models/config/create-rule-instructions.spec.ts index 0ebb297..a0508b5 100644 --- a/packages/steiger/src/models/config/create-rule-instructions.spec.ts +++ b/packages/steiger/src/models/config/create-rule-instructions.spec.ts @@ -1,11 +1,11 @@ import { describe, expect, it } from 'vitest' -import { Config } from '@steiger/types' +import type { Config, Rule } from '@steiger/types' import createRuleInstructions from './create-rule-instructions' describe('createRuleInstructions', () => { it('should create rule instructions for each rule present in the config', () => { - const config: Config = [ + const config: Config> = [ { rules: { rule1: 'warn', @@ -56,7 +56,7 @@ describe('createRuleInstructions', () => { }) it('should add several glob groups for a rule if they are provided in the config', () => { - const config: Config = [ + const config: Config> = [ { rules: { rule1: 'warn', diff --git a/packages/steiger/src/models/config/create-rule-instructions.ts b/packages/steiger/src/models/config/create-rule-instructions.ts index 0e0c0c4..163c262 100644 --- a/packages/steiger/src/models/config/create-rule-instructions.ts +++ b/packages/steiger/src/models/config/create-rule-instructions.ts @@ -1,4 +1,4 @@ -import { Config, ConfigObject, Severity } from '@steiger/types' +import type { Rule, Config, ConfigObject } from '@steiger/types' import { RuleInstructions } from './types' import { getOptions, getSeverity, isConfigObject } from './raw-config' @@ -10,11 +10,11 @@ function createEmptyInstructions(): RuleInstructions { } } -function extractRuleNames(configObject: ConfigObject) { +function extractRuleNames(configObject: ConfigObject>) { return Object.keys(configObject.rules) } -function preCreateRuleInstructions(config: Config) { +function preCreateRuleInstructions(config: Config>) { return config .filter(isConfigObject) .flatMap(extractRuleNames) @@ -27,26 +27,28 @@ function preCreateRuleInstructions(config: Config) { ) } -export default function createRuleInstructions(config: Config): Record { +export default function createRuleInstructions(config: Config>): Record { const ruleNameToInstructions: Record = preCreateRuleInstructions(config) return config.reduce((acc: Record, item) => { if (isConfigObject(item)) { - Object.entries(item.rules).forEach( - ([ruleName, severityOrTuple]: [string, Severity | [Severity, Record]]) => { - const ruleOptions: Record | null = getOptions(severityOrTuple) - - if (ruleOptions) { - acc[ruleName].options = ruleOptions - } - - acc[ruleName].globGroups.push({ - severity: getSeverity(severityOrTuple), - files: item.files, - ignores: item.ignores, - }) - }, - ) + Object.entries(item.rules).forEach(([ruleName, severityOrTuple]) => { + if (severityOrTuple === undefined) { + return + } + + const ruleOptions: Record | null = getOptions(severityOrTuple) + + if (ruleOptions) { + acc[ruleName].options = ruleOptions + } + + acc[ruleName].globGroups.push({ + severity: getSeverity(severityOrTuple), + files: item.files, + ignores: item.ignores, + }) + }) } return acc diff --git a/packages/steiger/src/models/config/index.ts b/packages/steiger/src/models/config/index.ts index c59e931..53f78a5 100644 --- a/packages/steiger/src/models/config/index.ts +++ b/packages/steiger/src/models/config/index.ts @@ -1,5 +1,5 @@ import { combine, createEvent, createStore } from 'effector' -import { Config, GlobalIgnore, Plugin } from '@steiger/types' +import type { Config, GlobalIgnore, Plugin, Rule } from '@steiger/types' import createRuleInstructions from './create-rule-instructions' import { RuleInstructions } from './types' @@ -31,7 +31,7 @@ export const $enabledRules = combine($ruleInstructions, $plugins, (ruleInstructi return allRules.filter((rule) => rulesThatHaveInstructions.includes(rule.name)) }) -export function processConfiguration(rawConfig: Config, configLocationFolder: string | null) { +export function processConfiguration(rawConfig: Config>, configLocationFolder: string | null) { const validatedConfig = validateConfig(rawConfig) const plugins = rawConfig.filter(isPlugin) const configTransformedGlobs = transformGlobs(validatedConfig, configLocationFolder) diff --git a/packages/steiger/src/models/config/raw-config.ts b/packages/steiger/src/models/config/raw-config.ts index 16b4b70..07fe835 100644 --- a/packages/steiger/src/models/config/raw-config.ts +++ b/packages/steiger/src/models/config/raw-config.ts @@ -1,10 +1,12 @@ -import { BaseRuleOptions, ConfigObject, GlobalIgnore, Plugin, Severity } from '@steiger/types' +import type { BaseRuleOptions, ConfigObject, GlobalIgnore, Plugin, Severity, Rule } from '@steiger/types' export function getSeverity(severityOrTuple: Severity | [Severity, BaseRuleOptions]): Severity { return Array.isArray(severityOrTuple) ? severityOrTuple[0] : severityOrTuple } -export function getOptions(severityOrTuple: Severity | [Severity, BaseRuleOptions]): BaseRuleOptions | null { +export function getOptions( + severityOrTuple: Severity | [Severity, BaseRuleOptions] | undefined, +): BaseRuleOptions | null { return Array.isArray(severityOrTuple) ? severityOrTuple[1] : null } @@ -12,7 +14,7 @@ export function isGlobalIgnore(obj: unknown): obj is GlobalIgnore { return typeof obj === 'object' && obj !== null && 'ignores' in obj && Object.keys(obj).length === 1 } -export function isConfigObject(obj: unknown): obj is ConfigObject { +export function isConfigObject(obj: unknown): obj is ConfigObject> { return typeof obj === 'object' && obj !== null && 'rules' in obj } @@ -20,6 +22,6 @@ export function isPlugin(obj: unknown): obj is Plugin { return typeof obj === 'object' && obj !== null && 'ruleDefinitions' in obj } -export function isConfiguration(obj: unknown): obj is ConfigObject | GlobalIgnore { +export function isConfiguration(obj: unknown): obj is ConfigObject> | GlobalIgnore { return isConfigObject(obj) || isGlobalIgnore(obj) } diff --git a/packages/steiger/src/models/config/transform-globs.spec.ts b/packages/steiger/src/models/config/transform-globs.spec.ts index d4efa2d..ecdf29b 100644 --- a/packages/steiger/src/models/config/transform-globs.spec.ts +++ b/packages/steiger/src/models/config/transform-globs.spec.ts @@ -1,12 +1,12 @@ import { describe, it, expect } from 'vitest' -import { Config } from '@steiger/types' +import type { Config, Rule } from '@steiger/types' +import { joinFromRoot } from '@steiger/toolkit' -import { joinFromRoot } from '../../_lib/prepare-test' import { transformGlobs } from './transform-globs' describe('transformGlobs', () => { it('should convert relative globs to absolute', () => { - const config: Config = [ + const config: Config> = [ { ignores: ['./src/entities/**'], }, @@ -34,7 +34,7 @@ describe('transformGlobs', () => { }) it('should strip trailing slashes', () => { - const config: Config = [ + const config: Config> = [ { ignores: ['./src/entities/', '**/shared/'], }, @@ -62,7 +62,7 @@ describe('transformGlobs', () => { }) it("should correctly transform globs that are relative but don't start with a dot", () => { - const config: Config = [ + const config: Config> = [ { ignores: ['src/entities/**'], }, @@ -90,7 +90,7 @@ describe('transformGlobs', () => { }) it('should correctly transform negated globs', () => { - const config: Config = [ + const config: Config> = [ { ignores: ['!src/entities/**'], }, diff --git a/packages/steiger/src/models/config/transform-globs.ts b/packages/steiger/src/models/config/transform-globs.ts index 32755e1..5d0d966 100644 --- a/packages/steiger/src/models/config/transform-globs.ts +++ b/packages/steiger/src/models/config/transform-globs.ts @@ -1,5 +1,5 @@ import { posix, sep, isAbsolute } from 'node:path' -import { Config } from '@steiger/types' +import { Config, Rule } from '@steiger/types' import { isConfigObject, isConfiguration } from './raw-config' import { getGlobPath, replaceGlobPath } from '../../shared/globs' @@ -27,7 +27,7 @@ function stripTrailingSlashes(globs: Array) { return globs.map((ignore) => ignore.replace(/\/$/, '')) } -export function transformGlobs(config: Config, configLocationPath: string | null) { +export function transformGlobs(config: Config>, configLocationPath: string | null) { if (!configLocationPath) { return config } diff --git a/packages/steiger/src/models/config/validate-config.spec.ts b/packages/steiger/src/models/config/validate-config.spec.ts index 19d7bbc..a2c20ca 100644 --- a/packages/steiger/src/models/config/validate-config.spec.ts +++ b/packages/steiger/src/models/config/validate-config.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { Config, Plugin } from '@steiger/types' +import { Config, Plugin, Rule } from '@steiger/types' import { buildValidationScheme, validateConfig } from './validate-config' import { isPlugin } from './raw-config' @@ -8,7 +8,7 @@ import { isPlugin } from './raw-config' // but with the check method expected to be just any function. // The reason why it's needed is that the original check method's .toString method returns [Function: check], // but after the validation it changes to [Function: anonymous] and fails the test. -function expectCheckToBeAnyFunction(config: Config) { +function expectCheckToBeAnyFunction(config: Config>) { return config.map((configObject) => { if (isPlugin(configObject)) { return { @@ -54,7 +54,7 @@ describe('buildValidationScheme', () => { rule2: 'warn', }, }, - ] as Config + ] as Config> const scheme = buildValidationScheme(config) expect(scheme.parse(config)).toEqual(expectCheckToBeAnyFunction(config)) @@ -69,7 +69,7 @@ describe('buildValidationScheme', () => { rule2: ['error', {}], }, }, - ] as Config + ] as Config> const scheme = buildValidationScheme(config) expect(scheme.parse(config)).toEqual(expectCheckToBeAnyFunction(config)) @@ -88,7 +88,7 @@ describe('buildValidationScheme', () => { rule2: 'warn', }, }, - ] as Config + ] as Config> const scheme = buildValidationScheme(config) @@ -110,7 +110,7 @@ describe('buildValidationScheme', () => { rule2: 'warn', }, }, - ] as Config + ] as Config> const scheme = buildValidationScheme(config) expect(scheme.parse(config)).toEqual(expectCheckToBeAnyFunction(config)) @@ -132,7 +132,7 @@ describe('buildValidationScheme', () => { rule2: 'warn', }, }, - ] as Config + ] as Config> const scheme = buildValidationScheme(config) expect(scheme.parse(config)).toEqual(expectCheckToBeAnyFunction(config)) @@ -155,7 +155,7 @@ describe('buildValidationScheme', () => { rule2: 'warn', }, }, - ] as Config + ] as Config> const scheme = buildValidationScheme(config) expect(scheme.parse(config)).toEqual(expectCheckToBeAnyFunction(config)) @@ -168,21 +168,21 @@ describe('buildValidationScheme', () => { rule1: 'off', }, }, - ] as Config + ] as Config> expect(() => buildValidationScheme(config)).toThrow('At least one rule must be provided by plugins!') }) it('should throw an error if no config objects are provided', () => { - const config = [dummyPlugin] as Config + const config = [dummyPlugin] as Config> const scheme = buildValidationScheme(config) expect(() => scheme.parse(config)).toThrow('At least one config object must be provided!') }) it('should throw an error if a config object without rules is provided', () => { - const config = [dummyPlugin, {}] as Config - const config1 = [dummyPlugin, { files: ['/shared'] }] as Config - const config2 = [dummyPlugin, { ignores: ['**/*.test.ts'] }] as Config + const config = [dummyPlugin, {}] as Config> + const config1 = [dummyPlugin, { files: ['/shared'] }] as Config> + const config2 = [dummyPlugin, { ignores: ['**/*.test.ts'] }] as Config> const scheme = buildValidationScheme(config) const scheme1 = buildValidationScheme(config1) @@ -202,7 +202,7 @@ describe('buildValidationScheme', () => { rule3: 'warn', }, }, - ] as Config + ] as Config> const scheme = buildValidationScheme(config) expect(() => scheme.parse(config)).toThrow() @@ -245,7 +245,7 @@ describe('buildValidationScheme', () => { }) it('should successfully validate when the config provides a rule with multiple but identical options', () => { - const config: Config = [ + const config: Config> = [ dummyPlugin, { rules: { @@ -266,7 +266,7 @@ describe('buildValidationScheme', () => { }) it('should throw an error when the config provides a rule with multiple but different options', () => { - const config: Config = [ + const config: Config> = [ dummyPlugin, { rules: { @@ -292,7 +292,7 @@ describe('buildValidationScheme', () => { }) it('should throw an error when duplicate rule definition are provided', () => { - const config: Config = [ + const config: Config> = [ dummyPlugin, dummyPlugin, { diff --git a/packages/steiger/src/models/config/validate-config.ts b/packages/steiger/src/models/config/validate-config.ts index 1ce05c0..e25453d 100644 --- a/packages/steiger/src/models/config/validate-config.ts +++ b/packages/steiger/src/models/config/validate-config.ts @@ -1,6 +1,6 @@ import z from 'zod' -import { BaseRuleOptions, Config, Plugin, Severity } from '@steiger/types' +import { BaseRuleOptions, Config, Plugin, Rule } from '@steiger/types' import { getOptions, isConfigObject, isPlugin } from './raw-config' import { isEqual } from '../../shared/objects' @@ -17,7 +17,7 @@ function getAllRuleNames(plugins: Array) { return allRules.map((rule) => rule.name) } -function validateConfigObjectsNumber(value: Config, ctx: z.RefinementCtx) { +function validateConfigObjectsNumber(value: Config>, ctx: z.RefinementCtx) { const configObjects = value.filter(isConfigObject) if (configObjects.length === 0) { @@ -28,7 +28,7 @@ function validateConfigObjectsNumber(value: Config, ctx: z.RefinementCtx) { } } -function validateRuleUniqueness(value: Config, ctx: z.RefinementCtx) { +function validateRuleUniqueness(value: Config>, ctx: z.RefinementCtx) { const allRuleNames = getAllRuleNames(value.filter(isPlugin)) const uniqueNames = new Set(allRuleNames) @@ -42,34 +42,32 @@ function validateRuleUniqueness(value: Config, ctx: z.RefinementCtx) { } } -function validateRuleOptions(value: Config, ctx: z.RefinementCtx) { +function validateRuleOptions(value: Config>, ctx: z.RefinementCtx) { const ruleToOptions: Record = {} value.forEach((configObject) => { if (isConfigObject(configObject)) { - Object.entries(configObject.rules).forEach( - ([ruleName, severityOrTuple]: [string, Severity | [Severity, Record]]) => { - const prevOptions = ruleToOptions[ruleName] - const ruleOptions: BaseRuleOptions | null = getOptions(severityOrTuple) - - if (!prevOptions) { - ruleToOptions[ruleName] = ruleOptions - return - } - - if (ruleOptions && prevOptions && !isEqual(ruleOptions, prevOptions)) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: ` - Rule "${ruleName}" has multiple options provided! - ${JSON.stringify(ruleToOptions[ruleName])} + Object.entries(configObject.rules).forEach(([ruleName, severityOrTuple]) => { + const prevOptions = ruleToOptions[ruleName] + const ruleOptions: BaseRuleOptions | null = getOptions(severityOrTuple) + + if (!prevOptions) { + ruleToOptions[ruleName] = ruleOptions + return + } + + if (ruleOptions && prevOptions && !isEqual(ruleOptions, prevOptions)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: ` + Rule "${ruleName}" has multiple options provided! + ${JSON.stringify(ruleToOptions[ruleName])} and ${JSON.stringify(ruleOptions)}. You can only provide options for a rule once.`, - }) - } - }, - ) + }) + } + }) } }) } @@ -77,7 +75,7 @@ function validateRuleOptions(value: Config, ctx: z.RefinementCtx) { /** * Dynamically build a validation scheme based on the rules provided by plugins. * */ -export function buildValidationScheme(rawConfig: Config) { +export function buildValidationScheme(rawConfig: Config>) { const allRuleNames = getAllRuleNames(rawConfig.filter(isPlugin)) // Make sure there's at least one rule registered by plugins @@ -133,7 +131,7 @@ export function buildValidationScheme(rawConfig: Config) { .superRefine(validateRuleUniqueness) } -export function validateConfig(rawConfig: Config) { +export function validateConfig(rawConfig: Config>) { const isOldConfig = typeof rawConfig === 'object' && !Array.isArray(rawConfig) const isWrongShape = !Array.isArray(rawConfig) diff --git a/packages/steiger/src/shared/globs/apply-exclusion.spec.ts b/packages/steiger/src/shared/globs/apply-exclusion.spec.ts index 196f92a..7f36a88 100644 --- a/packages/steiger/src/shared/globs/apply-exclusion.spec.ts +++ b/packages/steiger/src/shared/globs/apply-exclusion.spec.ts @@ -1,12 +1,12 @@ import { describe, expect, it } from 'vitest' +import { joinFromRoot, parseIntoFolder } from '@steiger/toolkit' import { applyExclusion } from './apply-exclusion' import { not } from './not' -import { joinFromRoot, parseIntoFsdRoot } from '../../_lib/prepare-test' describe('applyExclusion', () => { it('should apply exclusions with a normal glob group', () => { - const vfs = parseIntoFsdRoot( + const vfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ ui @@ -31,7 +31,7 @@ describe('applyExclusion', () => { joinFromRoot('src'), ) - const expectedVfs = parseIntoFsdRoot( + const expectedVfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ lib @@ -58,7 +58,7 @@ describe('applyExclusion', () => { }) it('should correctly apply exclusions with an inverted glob group', () => { - const vfs = parseIntoFsdRoot( + const vfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ ui @@ -83,7 +83,7 @@ describe('applyExclusion', () => { joinFromRoot('src'), ) - const expectedVfs = parseIntoFsdRoot( + const expectedVfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ lib @@ -106,7 +106,7 @@ describe('applyExclusion', () => { }) it('should correctly apply exclusions with several normal and inverted glob groups', () => { - const vfs = parseIntoFsdRoot( + const vfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ ui @@ -136,7 +136,7 @@ describe('applyExclusion', () => { joinFromRoot('src'), ) - const expectedVfs = parseIntoFsdRoot( + const expectedVfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ lib @@ -170,7 +170,7 @@ describe('applyExclusion', () => { it('should correctly apply exclusions for an empty glob group', () => { const globs = [not({}), { files: ['/src/shared/ui/Button.ts'], ignores: [] }] - const vfs = parseIntoFsdRoot( + const vfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ ui @@ -200,7 +200,7 @@ describe('applyExclusion', () => { joinFromRoot('src'), ) - const expectedVfs = parseIntoFsdRoot( + const expectedVfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ ui @@ -215,7 +215,7 @@ describe('applyExclusion', () => { it('should correctly apply exclusions for brace sets', () => { const globs = [{}, not({ files: ['**/*.spec.{ts,tsx}'] })] - const vfs = parseIntoFsdRoot( + const vfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ ui @@ -245,7 +245,7 @@ describe('applyExclusion', () => { joinFromRoot('src'), ) - const expectedVfs = parseIntoFsdRoot( + const expectedVfs = parseIntoFolder( ` πŸ“‚ shared πŸ“‚ ui diff --git a/packages/steiger/steiger.config.ts b/packages/steiger/steiger.config.ts deleted file mode 100644 index 2f0dd2f..0000000 --- a/packages/steiger/steiger.config.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - rules: { - // 'public-api': 'off', - }, -} diff --git a/packages/steiger/tsup.config.ts b/packages/steiger/tsup.config.ts index 2ded5b4..a8adc11 100644 --- a/packages/steiger/tsup.config.ts +++ b/packages/steiger/tsup.config.ts @@ -1,4 +1,3 @@ -import { exec } from 'node:child_process' import { defineConfig } from 'tsup' export default defineConfig({ @@ -7,6 +6,7 @@ export default defineConfig({ outExtension: () => ({ js: '.mjs' }), dts: { entry: 'src/app.ts', + resolve: true, }, treeshake: true, clean: true, @@ -14,7 +14,4 @@ export default defineConfig({ esbuildOptions(options) { options.define = { 'import.meta.vitest': 'undefined' } }, - onSuccess: async () => { - exec('tsc --emitDeclarationOnly') - }, }) diff --git a/packages/toolkit/.prettierignore b/packages/toolkit/.prettierignore new file mode 100644 index 0000000..1521c8b --- /dev/null +++ b/packages/toolkit/.prettierignore @@ -0,0 +1 @@ +dist diff --git a/packages/toolkit/eslint.config.mjs b/packages/toolkit/eslint.config.mjs new file mode 100644 index 0000000..0cab055 --- /dev/null +++ b/packages/toolkit/eslint.config.mjs @@ -0,0 +1 @@ +export { default } from '@steiger/eslint-config' diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json new file mode 100644 index 0000000..7ec101d --- /dev/null +++ b/packages/toolkit/package.json @@ -0,0 +1,49 @@ +{ + "name": "@steiger/toolkit", + "description": "Toolkit for writing rules for Steiger", + "version": "0.1.0", + "scripts": { + "lint": "eslint .", + "format": "prettier --write . --cache", + "check-formatting": "prettier --check . --cache", + "dev": "tsup --watch", + "build": "tsup", + "test": "vitest run", + "typecheck": "tsc --noEmit" + }, + "exports": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "type": "module", + "license": "MIT", + "authors": [ + { + "name": "Anton Medvedev", + "email": "unordinarity@yandex.ru", + "url": "https://github.com/unordinarity" + }, + { + "name": "Lev Chelyadinov", + "email": "leva181777@gmail.com", + "url": "https://github.com/illright" + } + ], + "peerDependencies": { + "vitest": "^1.6.0" + }, + "peerDependenciesMeta": { + "vitest": { + "optional": true + } + }, + "devDependencies": { + "@steiger/eslint-config": "workspace:*", + "@steiger/tsconfig": "workspace:*", + "@steiger/types": "workspace:*", + "@total-typescript/ts-reset": "^0.5.1", + "tsup": "^8.3.0", + "typescript": "^5.6.3", + "vitest": "^1.6.0" + } +} diff --git a/packages/toolkit/reset.d.ts b/packages/toolkit/reset.d.ts new file mode 100644 index 0000000..e3f32cd --- /dev/null +++ b/packages/toolkit/reset.d.ts @@ -0,0 +1,2 @@ +// Do not add any other lines of code to this file! +import '@total-typescript/ts-reset' diff --git a/packages/toolkit/src/config-object-of.ts b/packages/toolkit/src/config-object-of.ts new file mode 100644 index 0000000..6f9844e --- /dev/null +++ b/packages/toolkit/src/config-object-of.ts @@ -0,0 +1,4 @@ +import type { ConfigObject, Plugin } from '@steiger/types' + +/** Accepts a plugin as a type parameter and returns the config object for these rules. */ +export type ConfigObjectOf = ThisPlugin extends Plugin ? ConfigObject : never diff --git a/packages/toolkit/src/create-configs.ts b/packages/toolkit/src/create-configs.ts new file mode 100644 index 0000000..8c24255 --- /dev/null +++ b/packages/toolkit/src/create-configs.ts @@ -0,0 +1,21 @@ +import type { Config, ConfigObject, OptionsForRule, Plugin, Rule, RuleNames, Severity } from '@steiger/types' + +export function enableAllRules>( + plugin: Plugin, + options?: { severity: Exclude }, +): [Plugin, ConfigObject] { + return [ + plugin, + { + rules: Object.fromEntries(plugin.ruleDefinitions.map((rule) => [rule.name, options?.severity ?? 'error'])) as { + [key in RuleNames]?: Severity | [Severity, OptionsForRule] + }, + }, + ] +} + +export function createConfigs = Array, Keys extends string = string>( + configs: Record>, +): Record> { + return configs +} diff --git a/packages/toolkit/src/create-plugin.ts b/packages/toolkit/src/create-plugin.ts new file mode 100644 index 0000000..dd93e48 --- /dev/null +++ b/packages/toolkit/src/create-plugin.ts @@ -0,0 +1,7 @@ +import type { Plugin, Rule } from '@steiger/types' + +export function createPlugin>>( + plugin: Plugin, +): Plugin { + return plugin +} diff --git a/packages/steiger-plugin-fsd/src/_lib/find-all-recursively.ts b/packages/toolkit/src/find-all-recursively.ts similarity index 94% rename from packages/steiger-plugin-fsd/src/_lib/find-all-recursively.ts rename to packages/toolkit/src/find-all-recursively.ts index 4ead104..4517c57 100644 --- a/packages/steiger-plugin-fsd/src/_lib/find-all-recursively.ts +++ b/packages/toolkit/src/find-all-recursively.ts @@ -1,7 +1,7 @@ import { basename } from 'node:path' import type { Folder, File } from '@steiger/types' -import { joinFromRoot, parseIntoFsdRoot as parseIntoFolder } from './prepare-test.js' +import { joinFromRoot, parseIntoFolder } from './prepare-test.js' /** Recursively walk through a folder and return all entries that satisfy the predicate in a flat array. */ export function findAllRecursively(folder: Folder, predicate: (entry: Folder | File) => boolean): Array { diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts new file mode 100644 index 0000000..7ff1a6b --- /dev/null +++ b/packages/toolkit/src/index.ts @@ -0,0 +1,8 @@ +export type * from '@steiger/types' + +export { findAllRecursively } from './find-all-recursively.js' +export { enableAllRules, createConfigs } from './create-configs.js' +export { createPlugin } from './create-plugin.js' +export type { ConfigObjectOf } from './config-object-of.js' + +export { compareMessages, createFsMocks, joinFromRoot, parseIntoFolder } from './prepare-test.js' diff --git a/packages/steiger-plugin-fsd/src/_lib/prepare-test.ts b/packages/toolkit/src/prepare-test.ts similarity index 93% rename from packages/steiger-plugin-fsd/src/_lib/prepare-test.ts rename to packages/toolkit/src/prepare-test.ts index 70cbe7f..d4b85a0 100644 --- a/packages/steiger-plugin-fsd/src/_lib/prepare-test.ts +++ b/packages/toolkit/src/prepare-test.ts @@ -1,14 +1,14 @@ import { join, sep } from 'node:path' import type { readFileSync, existsSync } from 'node:fs' -import type { FsdRoot } from '@feature-sliced/filesystem' import type { Folder, File, PartialDiagnostic } from '@steiger/types' import { vi } from 'vitest' /** Parse a multi-line indented string with emojis for files and folders into an FSD root. - * @param fsMarkup - a file system tree represented in markup using file and folder emojis - * @param mountTo - virtually make the passed markup a subtree of the mountTo folder - * */ -export function parseIntoFsdRoot(fsMarkup: string, mountTo?: string): FsdRoot { + * + * @param fsMarkup a file system tree represented in markup using file and folder emojis + * @param mountTo virtually make the passed markup a subtree of the mountTo folder + */ +export function parseIntoFolder(fsMarkup: string, mountTo?: string): Folder { function parseFolder(lines: Array, path: string): Folder { const children: Array = [] @@ -74,8 +74,8 @@ export function createFsMocks(mockedFiles: Record, original: typ if (import.meta.vitest) { const { test, expect } = import.meta.vitest - test('parseIntoFsdRoot', () => { - const root = parseIntoFsdRoot(` + test('parseIntoFolder', () => { + const root = parseIntoFolder(` πŸ“‚ entities πŸ“‚ users πŸ“‚ ui @@ -167,7 +167,7 @@ if (import.meta.vitest) { πŸ“„ index.ts πŸ“„ Button.tsx ` - const root = parseIntoFsdRoot(markup, joinFromRoot('src')) + const root = parseIntoFolder(markup, joinFromRoot('src')) expect(root).toEqual({ type: 'folder', diff --git a/packages/toolkit/tsconfig.json b/packages/toolkit/tsconfig.json new file mode 100644 index 0000000..41beec0 --- /dev/null +++ b/packages/toolkit/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@steiger/tsconfig/base.json", + "compilerOptions": { + "types": ["vitest/importMeta"], + "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" + }, + "include": ["./src", "./reset.d.ts"] +} diff --git a/packages/toolkit/tsup.config.ts b/packages/toolkit/tsup.config.ts new file mode 100644 index 0000000..3113b51 --- /dev/null +++ b/packages/toolkit/tsup.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm'], + dts: { + entry: 'src/index.ts', + resolve: true, + }, + treeshake: true, + clean: true, + esbuildOptions(options) { + options.define = { 'import.meta.vitest': 'undefined' } + }, +}) diff --git a/packages/toolkit/vitest.config.ts b/packages/toolkit/vitest.config.ts new file mode 100644 index 0000000..a93210a --- /dev/null +++ b/packages/toolkit/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + includeSource: ['src/**/*.{js,ts}'], + }, +}) diff --git a/packages/types/index.ts b/packages/types/index.ts index 5d823c4..a7c331a 100644 --- a/packages/types/index.ts +++ b/packages/types/index.ts @@ -11,10 +11,14 @@ export interface Folder { export type BaseRuleOptions = Record -export interface Rule { +export interface Rule< + Context = unknown, + RuleOptions extends BaseRuleOptions = BaseRuleOptions, + RuleName extends string | number | symbol = string, +> { /** Short code name for the rule. */ - name: string - check: (this: Context, root: Folder, ruleOptions?: RuleOptions) => RuleResult | Promise + name: RuleName + check: (this: Context, root: Folder, ruleOptions: RuleOptions) => RuleResult | Promise } export interface RuleResult { @@ -63,27 +67,50 @@ export type Fix = content: string } -export type Config = Array +export type Config> = Array | Plugin | GlobalIgnore> export type Severity = 'off' | 'warn' | 'error' -export type ConfigObject = { - /** Globs of files to check */ +/** + * Extracts a union of rule names from a given tuple of `Rule` objects. + * + * @example + * type Context = unknown + * type Options = BaseRuleOptions + * type Result = RuleNames<[Rule, Rule]> + * // Result is 'foo' | 'bar' + */ +export type RuleNames> = Rules[number]['name'] + +type LookUpByName = Union extends { name: Name } ? Union : never + +/** + * Looks up a rule in a tuple of rules by name and then extracts that rule's options type. + * + * @example + * type Context = unknown + * type Rules = [Rule, Rule] + * type Result = OptionsForRule + * // Result is { foo: string } + */ +export type OptionsForRule, RuleName extends RuleNames> = + LookUpByName extends Rule ? Options : never + +export interface ConfigObject> { + /** Globs of files to check. */ files?: Array - /** Globs of files to ignore */ + /** Globs of files to ignore. */ ignores?: Array /** Severity of rules and individual rule options. */ - rules: { - [ruleName: string]: Severity | [Severity, BaseRuleOptions] - } + rules: { [key in RuleNames]?: Severity | [Severity, OptionsForRule] } } export type GlobalIgnore = { ignores: Array } -export interface Plugin { +export interface Plugin> = Array> { meta: { name: string version: string } - ruleDefinitions: Array + ruleDefinitions: Rules } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1cf838a..4f11c48 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,9 +78,6 @@ importers: packages/steiger: dependencies: - '@feature-sliced/filesystem': - specifier: ^2.4.0 - version: 2.4.0 '@feature-sliced/steiger-plugin': specifier: workspace:* version: link:../steiger-plugin-fsd @@ -127,6 +124,9 @@ importers: '@steiger/pretty-reporter': specifier: workspace:* version: link:../pretty-reporter + '@steiger/toolkit': + specifier: workspace:* + version: link:../toolkit '@steiger/tsconfig': specifier: workspace:* version: link:../../tooling/tsconfig @@ -144,7 +144,7 @@ importers: version: 17.0.33 tsup: specifier: ^8.3.0 - version: 8.3.0(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3)(yaml@2.5.1) + version: 8.3.0(@microsoft/api-extractor@7.47.7(@types/node@20.16.11))(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3)(yaml@2.5.1) typescript: specifier: ^5.6.3 version: 5.6.3 @@ -176,12 +176,12 @@ importers: '@steiger/eslint-config': specifier: workspace:* version: link:../../tooling/eslint-config + '@steiger/toolkit': + specifier: workspace:* + version: link:../toolkit '@steiger/tsconfig': specifier: workspace:* version: link:../../tooling/tsconfig - '@steiger/types': - specifier: workspace:* - version: link:../types '@total-typescript/ts-reset': specifier: ^0.5.1 version: 0.5.1 @@ -193,7 +193,31 @@ importers: version: 0.0.33 tsup: specifier: ^8.3.0 - version: 8.3.0(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3)(yaml@2.5.1) + version: 8.3.0(@microsoft/api-extractor@7.47.7(@types/node@20.16.11))(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3)(yaml@2.5.1) + typescript: + specifier: ^5.6.3 + version: 5.6.3 + vitest: + specifier: ^1.6.0 + version: 1.6.0(@types/node@20.16.11) + + packages/toolkit: + devDependencies: + '@steiger/eslint-config': + specifier: workspace:* + version: link:../../tooling/eslint-config + '@steiger/tsconfig': + specifier: workspace:* + version: link:../../tooling/tsconfig + '@steiger/types': + specifier: workspace:* + version: link:../types + '@total-typescript/ts-reset': + specifier: ^0.5.1 + version: 0.5.1 + tsup: + specifier: ^8.3.0 + version: 8.3.0(@microsoft/api-extractor@7.47.7(@types/node@20.16.11))(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3)(yaml@2.5.1) typescript: specifier: ^5.6.3 version: 5.6.3 @@ -716,6 +740,19 @@ packages: resolution: {integrity: sha512-SkAyKAByB9l93Slyg8AUHGuM2kjvWioUTCckT/03J09jYnfEzMO/wSXmEhnKGYs6qx9De8TH4yJCl0Y9lRgnyQ==} engines: {node: '>=14.18.0'} + '@microsoft/api-extractor-model@7.29.6': + resolution: {integrity: sha512-gC0KGtrZvxzf/Rt9oMYD2dHvtN/1KPEYsrQPyMKhLHnlVuO/f4AFN3E4toqZzD2pt4LhkKoYmL2H9tX3yCOyRw==} + + '@microsoft/api-extractor@7.47.7': + resolution: {integrity: sha512-fNiD3G55ZJGhPOBPMKD/enozj8yxJSYyVJWxRWdcUtw842rvthDHJgUWq9gXQTensFlMHv2wGuCjjivPv53j0A==} + hasBin: true + + '@microsoft/tsdoc-config@0.17.0': + resolution: {integrity: sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==} + + '@microsoft/tsdoc@0.15.0': + resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -904,6 +941,28 @@ packages: cpu: [x64] os: [win32] + '@rushstack/node-core-library@5.7.0': + resolution: {integrity: sha512-Ff9Cz/YlWu9ce4dmqNBZpA45AEya04XaBFIjV7xTVeEf+y/kTjEasmozqFELXlNG4ROdevss75JrrZ5WgufDkQ==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rig-package@0.5.3': + resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==} + + '@rushstack/terminal@0.14.0': + resolution: {integrity: sha512-juTKMAMpTIJKudeFkG5slD8Z/LHwNwGZLtU441l/u82XdTBfsP+LbGKJLCNwP5se+DMCT55GB8x9p6+C4UL7jw==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/ts-command-line@4.22.6': + resolution: {integrity: sha512-QSRqHT/IfoC5nk9zn6+fgyqOPXHME0BfchII9EUPR19pocsNp/xSbeBCbD3PIR2Lg+Q5qk7OFqk1VhWPMdKHJg==} + '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -925,6 +984,9 @@ packages: '@tsconfig/node-lts@20.1.3': resolution: {integrity: sha512-m3b7EP2U+h5tNSpaBMfcTuHmHn04wrgRPQQrfKt75YIPq6kPs2153/KfPHdqkEWGx5pEBvS6rnvToT+yTtC1iw==} + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} @@ -1084,9 +1146,31 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + + ajv@8.13.0: + resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -1595,6 +1679,9 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + get-amd-module-type@6.0.0: resolution: {integrity: sha512-hFM7oivtlgJ3d6XWD6G47l8Wyh/C6vFw5G24Kk1Tbq85yh5gcM8Fne5/lFhiuxB+RT6+SI7I1ThB9lG4FBh3jw==} engines: {node: '>=18'} @@ -1676,6 +1763,10 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} @@ -1714,6 +1805,10 @@ packages: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -1728,6 +1823,10 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1816,6 +1915,9 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -1877,6 +1979,9 @@ packages: lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} @@ -1895,6 +2000,10 @@ packages: lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} @@ -1937,6 +2046,9 @@ packages: resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} engines: {node: 20 || >=22} + minimatch@3.0.8: + resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2095,6 +2207,9 @@ packages: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} engines: {node: '>=12'} + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} @@ -2261,6 +2376,10 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -2275,6 +2394,10 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + responselike@3.0.0: resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} engines: {node: '>=14.16'} @@ -2313,6 +2436,11 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + semver@7.6.2: resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} engines: {node: '>=10'} @@ -2448,10 +2576,18 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + supports-hyperlinks@2.3.0: resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} engines: {node: '>=8'} + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -2602,6 +2738,11 @@ packages: typescript: optional: true + typescript@5.4.2: + resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.6.3: resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} engines: {node: '>=14.17'} @@ -2728,6 +2869,9 @@ packages: yallist@2.1.2: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.5.1: resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} engines: {node: '>= 14'} @@ -3214,6 +3358,45 @@ snapshots: jju: 1.4.0 read-yaml-file: 1.1.0 + '@microsoft/api-extractor-model@7.29.6(@types/node@20.16.11)': + dependencies: + '@microsoft/tsdoc': 0.15.0 + '@microsoft/tsdoc-config': 0.17.0 + '@rushstack/node-core-library': 5.7.0(@types/node@20.16.11) + transitivePeerDependencies: + - '@types/node' + optional: true + + '@microsoft/api-extractor@7.47.7(@types/node@20.16.11)': + dependencies: + '@microsoft/api-extractor-model': 7.29.6(@types/node@20.16.11) + '@microsoft/tsdoc': 0.15.0 + '@microsoft/tsdoc-config': 0.17.0 + '@rushstack/node-core-library': 5.7.0(@types/node@20.16.11) + '@rushstack/rig-package': 0.5.3 + '@rushstack/terminal': 0.14.0(@types/node@20.16.11) + '@rushstack/ts-command-line': 4.22.6(@types/node@20.16.11) + lodash: 4.17.21 + minimatch: 3.0.8 + resolve: 1.22.8 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.4.2 + transitivePeerDependencies: + - '@types/node' + optional: true + + '@microsoft/tsdoc-config@0.17.0': + dependencies: + '@microsoft/tsdoc': 0.15.0 + ajv: 8.12.0 + jju: 1.4.0 + resolve: 1.22.8 + optional: true + + '@microsoft/tsdoc@0.15.0': + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -3337,6 +3520,44 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.24.0': optional: true + '@rushstack/node-core-library@5.7.0(@types/node@20.16.11)': + dependencies: + ajv: 8.13.0 + ajv-draft-04: 1.0.0(ajv@8.13.0) + ajv-formats: 3.0.1(ajv@8.13.0) + fs-extra: 7.0.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.8 + semver: 7.5.4 + optionalDependencies: + '@types/node': 20.16.11 + optional: true + + '@rushstack/rig-package@0.5.3': + dependencies: + resolve: 1.22.8 + strip-json-comments: 3.1.1 + optional: true + + '@rushstack/terminal@0.14.0(@types/node@20.16.11)': + dependencies: + '@rushstack/node-core-library': 5.7.0(@types/node@20.16.11) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 20.16.11 + optional: true + + '@rushstack/ts-command-line@4.22.6(@types/node@20.16.11)': + dependencies: + '@rushstack/terminal': 0.14.0(@types/node@20.16.11) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + optional: true + '@sinclair/typebox@0.27.8': {} '@sindresorhus/is@5.6.0': {} @@ -3351,6 +3572,9 @@ snapshots: '@tsconfig/node-lts@20.1.3': {} + '@types/argparse@1.0.38': + optional: true + '@types/estree@1.0.5': {} '@types/estree@1.0.6': {} @@ -3555,6 +3779,16 @@ snapshots: acorn@8.12.1: {} + ajv-draft-04@1.0.0(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + optional: true + + ajv-formats@3.0.1(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + optional: true + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -3562,6 +3796,22 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.12.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + optional: true + + ajv@8.13.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + optional: true + ansi-colors@4.1.3: {} ansi-escapes@5.0.0: @@ -4124,6 +4374,9 @@ snapshots: fsevents@2.3.3: optional: true + function-bind@1.1.2: + optional: true + get-amd-module-type@6.0.0: dependencies: ast-module-types: 6.0.0 @@ -4209,6 +4462,11 @@ snapshots: has-flag@4.0.0: {} + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + optional: true + http-cache-semantics@4.1.1: {} http2-wrapper@2.2.1: @@ -4237,6 +4495,9 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + import-lazy@4.0.0: + optional: true + imurmurhash@0.1.4: {} ini@1.3.8: {} @@ -4247,6 +4508,11 @@ snapshots: dependencies: binary-extensions: 2.3.0 + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + optional: true + is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -4310,6 +4576,9 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: + optional: true + json-stable-stringify-without-jsonify@1.0.1: {} jsonfile@4.0.0: @@ -4378,6 +4647,9 @@ snapshots: lodash.startcase@4.4.0: {} + lodash@4.17.21: + optional: true + log-update@6.1.0: dependencies: ansi-escapes: 7.0.0 @@ -4399,6 +4671,11 @@ snapshots: pseudomap: 1.0.2 yallist: 2.1.2 + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + optional: true + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -4431,6 +4708,11 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimatch@3.0.8: + dependencies: + brace-expansion: 1.1.11 + optional: true + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -4572,6 +4854,9 @@ snapshots: path-key@4.0.0: {} + path-parse@1.0.7: + optional: true + path-scurry@1.11.1: dependencies: lru-cache: 10.2.2 @@ -4716,6 +5001,9 @@ snapshots: require-directory@2.1.1: {} + require-from-string@2.0.2: + optional: true + resolve-alpn@1.2.1: {} resolve-from@4.0.0: {} @@ -4724,6 +5012,13 @@ snapshots: resolve-pkg-maps@1.0.0: {} + resolve@1.22.8: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + optional: true + responselike@3.0.0: dependencies: lowercase-keys: 3.0.0 @@ -4794,6 +5089,11 @@ snapshots: semver@6.3.1: {} + semver@7.5.4: + dependencies: + lru-cache: 6.0.0 + optional: true + semver@7.6.2: {} shebang-command@1.2.0: @@ -4910,11 +5210,19 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + optional: true + supports-hyperlinks@2.3.0: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 + supports-preserve-symlinks-flag@1.0.0: + optional: true + term-size@2.2.1: {} terminal-link@3.0.0: @@ -4969,7 +5277,7 @@ snapshots: optionalDependencies: typescript: 5.6.3 - tsup@8.3.0(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3)(yaml@2.5.1): + tsup@8.3.0(@microsoft/api-extractor@7.47.7(@types/node@20.16.11))(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3)(yaml@2.5.1): dependencies: bundle-require: 5.0.0(esbuild@0.23.1) cac: 6.7.14 @@ -4988,6 +5296,7 @@ snapshots: tinyglobby: 0.2.9 tree-kill: 1.2.2 optionalDependencies: + '@microsoft/api-extractor': 7.47.7(@types/node@20.16.11) postcss: 8.4.47 typescript: 5.6.3 transitivePeerDependencies: @@ -5049,6 +5358,9 @@ snapshots: - eslint - supports-color + typescript@5.4.2: + optional: true + typescript@5.6.3: {} ufo@1.5.3: {} @@ -5171,6 +5483,9 @@ snapshots: yallist@2.1.2: {} + yallist@4.0.0: + optional: true + yaml@2.5.1: {} yargs-parser@21.1.1: {} diff --git a/turbo.json b/turbo.json index 807de8a..7588723 100644 --- a/turbo.json +++ b/turbo.json @@ -32,7 +32,7 @@ "cache": false }, "test": { - "dependsOn": ["transit"] + "dependsOn": ["transit", "build"] } } }