@useinsider/eslint-config 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +27 -24
- package/dist/cli.cjs.map +1 -1
- package/dist/configs/typescript-dom/index.d.ts +13 -3
- package/dist/configs/vue2/index.d.ts +13 -3
- package/dist/configs/vue2-options-api/index.d.ts +13 -3
- package/dist/configs/vue3/index.d.ts +13 -3
- package/dist/configs/vue3-options-api/index.d.ts +13 -3
- package/dist/{javascript-DZ8AUcFu.js → javascript-9ynvToz3.js} +4 -4
- package/dist/{javascript-DZ8AUcFu.js.map → javascript-9ynvToz3.js.map} +1 -1
- package/dist/{javascript-9tQ9nS7g.cjs → javascript-VBXqjVi8.cjs} +7 -7
- package/dist/{javascript-9tQ9nS7g.cjs.map → javascript-VBXqjVi8.cjs.map} +1 -1
- package/dist/javascript-dom-5jRL_yKP.cjs +11 -0
- package/dist/{javascript-dom-Duiy5LQP.cjs.map → javascript-dom-5jRL_yKP.cjs.map} +1 -1
- package/dist/{javascript-dom-DaclGNp-.js → javascript-dom-B1wtLzJC.js} +3 -3
- package/dist/{javascript-dom-DaclGNp-.js.map → javascript-dom-B1wtLzJC.js.map} +1 -1
- package/dist/javascript-node-C1AU_4X2.cjs +11 -0
- package/dist/{javascript-node-42x75ZCz.cjs.map → javascript-node-C1AU_4X2.cjs.map} +1 -1
- package/dist/{javascript-node-CbCig5az.js → javascript-node-JaASOojH.js} +3 -3
- package/dist/{javascript-node-CbCig5az.js.map → javascript-node-JaASOojH.js.map} +1 -1
- package/dist/{jest-qLSuDnJy.cjs → jest-CHzjzBtf.cjs} +3 -3
- package/dist/{jest-qLSuDnJy.cjs.map → jest-CHzjzBtf.cjs.map} +1 -1
- package/dist/jest-zpwEzF4X.js.map +1 -1
- package/dist/jsdoc-based-ts-Cfahz0nn.cjs.map +1 -1
- package/dist/jsdoc-based-ts-D7tlky3K.js.map +1 -1
- package/dist/main.cjs +30 -28
- package/dist/main.cjs.map +1 -1
- package/dist/main.js +25 -23
- package/dist/main.js.map +1 -1
- package/dist/{merge-8QYHxTjU.cjs → merge-DJQU2y2U.cjs} +3 -3
- package/dist/merge-DJQU2y2U.cjs.map +1 -0
- package/dist/{merge-CXnhrNEt.js → merge-R5ZHqMsV.js} +3 -3
- package/dist/merge-R5ZHqMsV.js.map +1 -0
- package/dist/{mergeAllConfig-DD9aslCE.js → mergeAllConfig-B2rSXCdn.js} +2 -2
- package/dist/{mergeAllConfig-DD9aslCE.js.map → mergeAllConfig-B2rSXCdn.js.map} +1 -1
- package/dist/{mergeAllConfig-CJpF6W3i.cjs → mergeAllConfig-DjMWjBNg.cjs} +2 -2
- package/dist/{mergeAllConfig-CJpF6W3i.cjs.map → mergeAllConfig-DjMWjBNg.cjs.map} +1 -1
- package/dist/removePluginsFromConfigs-D0oSUj2C.cjs.map +1 -1
- package/dist/removePluginsFromConfigs-DzqX2Ju2.js.map +1 -1
- package/dist/{chunk-D6vf50IK.cjs → rolldown-runtime-VH7oDXx4.cjs} +1 -1
- package/dist/{strict-BvBxnZVo.js → strict-B2mCkeX_.js} +2 -2
- package/dist/{strict-BvBxnZVo.js.map → strict-B2mCkeX_.js.map} +1 -1
- package/dist/{strict-C7c_fc3d.cjs → strict-tmp-wKop.cjs} +2 -2
- package/dist/{strict-C7c_fc3d.cjs.map → strict-tmp-wKop.cjs.map} +1 -1
- package/dist/{stylistic-DwB496Pa.js → stylistic-B6j4I_aN.js} +2 -2
- package/dist/{stylistic-DwB496Pa.js.map → stylistic-B6j4I_aN.js.map} +1 -1
- package/dist/{stylistic-D9tHaeG4.cjs → stylistic-Dio0AM2E.cjs} +5 -5
- package/dist/{stylistic-D9tHaeG4.cjs.map → stylistic-Dio0AM2E.cjs.map} +1 -1
- package/dist/{typescript-BDc8Y6Hc.cjs → typescript-C1icmOxY.cjs} +9 -9
- package/dist/{typescript-BDc8Y6Hc.cjs.map → typescript-C1icmOxY.cjs.map} +1 -1
- package/dist/{typescript-CDhrDEoi.js → typescript-C7Xg57WE.js} +4 -4
- package/dist/{typescript-CDhrDEoi.js.map → typescript-C7Xg57WE.js.map} +1 -1
- package/dist/{typescript-dom-CexiRZAz.js → typescript-dom-ClE8ufzf.js} +3 -3
- package/dist/{typescript-dom-CexiRZAz.js.map → typescript-dom-ClE8ufzf.js.map} +1 -1
- package/dist/{typescript-dom-xYDL7O9g.cjs → typescript-dom-RUJn2WCZ.cjs} +5 -5
- package/dist/{typescript-dom-xYDL7O9g.cjs.map → typescript-dom-RUJn2WCZ.cjs.map} +1 -1
- package/dist/typescript-node-CBEzcc56.cjs +11 -0
- package/dist/{typescript-node-BjpTUbKF.cjs.map → typescript-node-CBEzcc56.cjs.map} +1 -1
- package/dist/{typescript-node-B8TpRcjd.js → typescript-node-CbViUIa8.js} +3 -3
- package/dist/{typescript-node-B8TpRcjd.js.map → typescript-node-CbViUIa8.js.map} +1 -1
- package/dist/{vitest-Xhsz8y7q.cjs → vitest-BomI8gtu.cjs} +3 -3
- package/dist/{vitest-Xhsz8y7q.cjs.map → vitest-BomI8gtu.cjs.map} +1 -1
- package/dist/vitest-Cdq7Gq88.js.map +1 -1
- package/dist/vue-2Tf2LIPf.cjs.map +1 -1
- package/dist/vue-Bg3R-yZT.js.map +1 -1
- package/dist/vue-options-api-9KoVvBkF.cjs.map +1 -1
- package/dist/vue-options-api-Bnzaw9Tz.js.map +1 -1
- package/dist/vue2-CVSRM5hR.js.map +1 -1
- package/dist/{vue2-G2Q58B8-.js → vue2-CYgPhPVM.js} +4 -4
- package/dist/{vue2-G2Q58B8-.js.map → vue2-CYgPhPVM.js.map} +1 -1
- package/dist/vue2-CwpD2fD-.cjs.map +1 -1
- package/dist/{vue2-DAi-TxWE.cjs → vue2-azKwRgaJ.cjs} +10 -10
- package/dist/{vue2-DAi-TxWE.cjs.map → vue2-azKwRgaJ.cjs.map} +1 -1
- package/dist/{vue2-options-api-CKnW7uvJ.js → vue2-options-api-Dvem0WyV.js} +4 -4
- package/dist/{vue2-options-api-CKnW7uvJ.js.map → vue2-options-api-Dvem0WyV.js.map} +1 -1
- package/dist/{vue2-options-api-DHnhmw3v.cjs → vue2-options-api-ma3J-bvM.cjs} +10 -10
- package/dist/{vue2-options-api-DHnhmw3v.cjs.map → vue2-options-api-ma3J-bvM.cjs.map} +1 -1
- package/dist/{vue2-typescript-DgXglO8R.js → vue2-typescript-BbhsVbsV.js} +4 -4
- package/dist/{vue2-typescript-DgXglO8R.js.map → vue2-typescript-BbhsVbsV.js.map} +1 -1
- package/dist/{vue2-typescript-CcuFB-YB.cjs → vue2-typescript-CZSHzp7I.cjs} +12 -12
- package/dist/{vue2-typescript-CcuFB-YB.cjs.map → vue2-typescript-CZSHzp7I.cjs.map} +1 -1
- package/dist/vue2-typescript-Cr8IshuA.cjs.map +1 -1
- package/dist/vue2-typescript-DWnsb3uv.js.map +1 -1
- package/dist/{vue2-typescript-options-api-DsejF_KX.js → vue2-typescript-options-api-A5jU6hdD.js} +4 -4
- package/dist/{vue2-typescript-options-api-DsejF_KX.js.map → vue2-typescript-options-api-A5jU6hdD.js.map} +1 -1
- package/dist/{vue2-typescript-options-api-2fugBIOF.cjs → vue2-typescript-options-api-B8OzZlje.cjs} +12 -12
- package/dist/{vue2-typescript-options-api-2fugBIOF.cjs.map → vue2-typescript-options-api-B8OzZlje.cjs.map} +1 -1
- package/dist/{vue3-CJAdAg73.cjs → vue3-Df5UQ1Ys.cjs} +10 -10
- package/dist/{vue3-CJAdAg73.cjs.map → vue3-Df5UQ1Ys.cjs.map} +1 -1
- package/dist/{vue3-CADfXE8I.js → vue3-haIiBEhv.js} +4 -4
- package/dist/{vue3-CADfXE8I.js.map → vue3-haIiBEhv.js.map} +1 -1
- package/dist/{vue3-options-api-D2Wub1Oy.cjs → vue3-options-api-Bx7yYHza.cjs} +10 -10
- package/dist/{vue3-options-api-D2Wub1Oy.cjs.map → vue3-options-api-Bx7yYHza.cjs.map} +1 -1
- package/dist/{vue3-options-api-DFWOX_UU.js → vue3-options-api-DkF4JagY.js} +4 -4
- package/dist/{vue3-options-api-DFWOX_UU.js.map → vue3-options-api-DkF4JagY.js.map} +1 -1
- package/dist/{vue3-typescript-DlncdT6z.js → vue3-typescript-BRN2iL98.js} +4 -4
- package/dist/{vue3-typescript-DlncdT6z.js.map → vue3-typescript-BRN2iL98.js.map} +1 -1
- package/dist/{vue3-typescript-DLfJPXTc.cjs → vue3-typescript-C0O1JZlk.cjs} +12 -12
- package/dist/{vue3-typescript-DLfJPXTc.cjs.map → vue3-typescript-C0O1JZlk.cjs.map} +1 -1
- package/dist/{vue3-typescript-options-api-CorDmhg0.js → vue3-typescript-options-api-BZ-QVwuD.js} +4 -4
- package/dist/{vue3-typescript-options-api-CorDmhg0.js.map → vue3-typescript-options-api-BZ-QVwuD.js.map} +1 -1
- package/dist/{vue3-typescript-options-api-Cn4u57BK.cjs → vue3-typescript-options-api-cPal4O-K.cjs} +12 -12
- package/dist/{vue3-typescript-options-api-Cn4u57BK.cjs.map → vue3-typescript-options-api-cPal4O-K.cjs.map} +1 -1
- package/package.json +40 -40
- package/dist/javascript-dom-Duiy5LQP.cjs +0 -11
- package/dist/javascript-node-42x75ZCz.cjs +0 -11
- package/dist/merge-8QYHxTjU.cjs.map +0 -1
- package/dist/merge-CXnhrNEt.js.map +0 -1
- package/dist/typescript-node-BjpTUbKF.cjs +0 -11
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","names":[],"sources":["../src/builders/configFiles.ts","../src/builders/jquery.ts","../src/builders/typescript.ts","../src/builders/vue.ts","../src/builders/scopeBlocks.ts","../src/builders/spellChecker.ts","../src/builders/strict.ts","../src/utils/ensureConfigStructure.ts","../src/utils/ensureDependencies.ts","../src/utils/silenceRules.ts","../src/main.ts"],"sourcesContent":["import type { Linter } from 'eslint';\n\n/**\n * Default globs treated as \"configuration files\" — root-level `*.config.*`\n * variants plus the canonical `eslint.config.*` flat-config filenames.\n *\n * These are intentionally conservative: only the top-level matches that the\n * v1 `config` preset was overwhelmingly applied to. Project-specific paths\n * (subdirectories, custom names) must be opted into by passing them via the\n * `configFiles: string[]` knob.\n */\nexport const DEFAULT_CONFIG_FILE_GLOBS: readonly string[] = [\n '*.config.{ts,mts,cts,js,mjs,cjs}',\n '*.config.*.{ts,mts,cts,js,mjs,cjs}',\n 'eslint.config.{mjs,js,cjs,ts}',\n];\n\nconst configFileRules: Linter.RulesRecord = {\n 'import-x/no-extraneous-dependencies': 'off',\n 'import-x/prefer-default-export': 'off',\n};\n\nfunction mergeGlobs (userGlobs: readonly string[]): string[] {\n const seen = new Set<string>();\n const merged: string[] = [];\n\n DEFAULT_CONFIG_FILE_GLOBS.forEach(glob => {\n if (!seen.has(glob)) {\n seen.add(glob);\n merged.push(glob);\n }\n });\n\n userGlobs.forEach(glob => {\n if (!seen.has(glob)) {\n seen.add(glob);\n merged.push(glob);\n }\n });\n\n return merged;\n}\n\nexport function buildConfigFilesBlocks (value: false | string[] | undefined): Linter.Config[] {\n if (value === false) {\n return [];\n }\n\n const files = value === undefined ? [...DEFAULT_CONFIG_FILE_GLOBS] : mergeGlobs(value);\n\n return [{\n files,\n rules: { ...configFileRules },\n }];\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('buildConfigFilesBlocks', () => {\n test('returns one default-only block when value is undefined (defaults on by default)', () => {\n const blocks = buildConfigFilesBlocks(undefined);\n\n expect(blocks).toHaveLength(1);\n expect(blocks[0].files).toStrictEqual([...DEFAULT_CONFIG_FILE_GLOBS]);\n expect(blocks[0].rules).toStrictEqual({\n 'import-x/no-extraneous-dependencies': 'off',\n 'import-x/prefer-default-export': 'off',\n });\n });\n\n test('returns empty array when value is false (explicit opt-out)', () => {\n expect(buildConfigFilesBlocks(false)).toStrictEqual([]);\n });\n\n test('returns one block with merged globs when value is a string[]', () => {\n const blocks = buildConfigFilesBlocks(['my.special.ts']);\n\n expect(blocks).toHaveLength(1);\n expect(blocks[0].files).toStrictEqual([\n ...DEFAULT_CONFIG_FILE_GLOBS,\n 'my.special.ts',\n ]);\n });\n\n test('user globs append after defaults in given order', () => {\n const blocks = buildConfigFilesBlocks(['z.ts', 'a.ts']);\n\n expect(blocks[0].files).toStrictEqual([\n ...DEFAULT_CONFIG_FILE_GLOBS,\n 'z.ts',\n 'a.ts',\n ]);\n });\n\n test('dedupes user globs that overlap with defaults', () => {\n const blocks = buildConfigFilesBlocks([\n '*.config.{ts,mts,cts,js,mjs,cjs}',\n 'extra.ts',\n ]);\n\n expect(blocks[0].files).toStrictEqual([\n ...DEFAULT_CONFIG_FILE_GLOBS,\n 'extra.ts',\n ]);\n });\n\n test('empty user list still emits default-only block', () => {\n const blocks = buildConfigFilesBlocks([]);\n\n expect(blocks).toHaveLength(1);\n expect(blocks[0].files).toStrictEqual([...DEFAULT_CONFIG_FILE_GLOBS]);\n });\n\n test('block has no languageOptions / parserOptions — only files + rules', () => {\n const [block] = buildConfigFilesBlocks(undefined);\n\n expect(block.languageOptions).toBeUndefined();\n expect(Object.keys(block)).toStrictEqual(['files', 'rules']);\n });\n });\n}\n","import type { Linter } from 'eslint';\nimport globals from 'globals';\n\nexport interface JqueryEntry {\n files: string[];\n}\n\nexport function buildJqueryBlocks (entries: readonly JqueryEntry[] | undefined): Linter.Config[] {\n if (!entries || entries.length === 0) {\n return [];\n }\n\n return entries.map(entry => ({\n files: entry.files,\n languageOptions: {\n globals: { ...globals.jquery },\n },\n }));\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('buildJqueryBlocks', () => {\n test('returns empty array when entries is undefined', () => {\n expect(buildJqueryBlocks(undefined)).toStrictEqual([]);\n });\n\n test('returns empty array when entries is empty', () => {\n expect(buildJqueryBlocks([])).toStrictEqual([]);\n });\n\n test('emits one block per entry with files set', () => {\n const blocks = buildJqueryBlocks([\n { files: ['a/**/*.js'] },\n { files: ['b/**/*.js'] },\n ]);\n\n expect(blocks).toHaveLength(2);\n expect(blocks[0].files).toStrictEqual(['a/**/*.js']);\n expect(blocks[1].files).toStrictEqual(['b/**/*.js']);\n });\n\n test('block exposes jquery globals ($, jQuery)', () => {\n const [block] = buildJqueryBlocks([{ files: ['legacy/**/*.js'] }]);\n const blockGlobals = block.languageOptions?.globals as Record<string, unknown>;\n\n expect(blockGlobals.$).toBeDefined();\n expect(blockGlobals.jQuery).toBeDefined();\n });\n });\n}\n","import type { Linter } from 'eslint';\n\ninterface TypescriptOverrideInput {\n tsconfigPaths: string[];\n}\n\ninterface ParserOptionsLike {\n project?: string[];\n tsconfigRootDir?: string;\n [key: string]: unknown;\n}\n\nexport function applyTypescriptOverrides<BlockType extends Linter.Config> (\n block: BlockType,\n input: TypescriptOverrideInput\n): BlockType {\n const languageOptions = block.languageOptions ?? {};\n const parserOptions = (languageOptions.parserOptions ?? {}) as ParserOptionsLike;\n\n parserOptions.project = input.tsconfigPaths;\n parserOptions.tsconfigRootDir = process.cwd();\n languageOptions.parserOptions = parserOptions;\n block.languageOptions = languageOptions;\n\n return block;\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('applyTypescriptOverrides', () => {\n test('sets parserOptions.project and tsconfigRootDir on a bare block', () => {\n const block: Linter.Config = {};\n\n applyTypescriptOverrides(block, { tsconfigPaths: ['tsconfig.lib.json'] });\n\n expect(block.languageOptions?.parserOptions).toMatchObject({\n project: ['tsconfig.lib.json'],\n tsconfigRootDir: process.cwd(),\n });\n });\n\n test('preserves existing parserOptions keys', () => {\n const block: Linter.Config = {\n languageOptions: {\n parserOptions: {\n sourceType: 'module',\n ecmaFeatures: { jsx: true },\n },\n },\n };\n\n applyTypescriptOverrides(block, { tsconfigPaths: ['tsconfig.json'] });\n\n expect(block.languageOptions?.parserOptions).toMatchObject({\n sourceType: 'module',\n ecmaFeatures: { jsx: true },\n project: ['tsconfig.json'],\n tsconfigRootDir: process.cwd(),\n });\n });\n\n test('overwrites any pre-existing project array', () => {\n const block: Linter.Config = {\n languageOptions: {\n parserOptions: {\n project: ['old.json'],\n },\n },\n };\n\n applyTypescriptOverrides(block, { tsconfigPaths: ['new.json'] });\n\n const parserOptions = block.languageOptions?.parserOptions as ParserOptionsLike;\n\n expect(parserOptions.project).toStrictEqual(['new.json']);\n });\n });\n}\n","import type { Linter } from 'eslint';\n\ninterface VueOverrideInput {\n globalComponents?: string[];\n}\n\nexport function escapeRegex (input: string): string {\n return input.replace(/[\\\\^$.*+?()[\\]{}|]/g, '\\\\$&');\n}\n\nexport function anchoredEscapeRegex (input: string): string {\n return `^${escapeRegex(input)}$`;\n}\n\nexport function applyVueOverrides<BlockType extends Linter.Config> (\n block: BlockType,\n input: VueOverrideInput\n): BlockType {\n if (!input.globalComponents || input.globalComponents.length === 0) {\n return block;\n }\n\n const rules = block.rules ?? {};\n\n rules['vue/no-undef-components'] = ['error', {\n ignorePatterns: input.globalComponents.map(anchoredEscapeRegex),\n }];\n block.rules = rules;\n\n return block;\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('escapeRegex', () => {\n test.each([\n ['plain', 'plain'],\n ['a.b', 'a\\\\.b'],\n ['$Foo', '\\\\$Foo'],\n ['a+b', 'a\\\\+b'],\n ['(x)', '\\\\(x\\\\)'],\n ['[a]', '\\\\[a\\\\]'],\n ['{a}', '\\\\{a\\\\}'],\n ['a|b', 'a\\\\|b'],\n ['a^b', 'a\\\\^b'],\n ['a*b', 'a\\\\*b'],\n ['a?b', 'a\\\\?b'],\n ['a\\\\b', 'a\\\\\\\\b'],\n ])('escapes %s → %s', (input, expected) => {\n expect(escapeRegex(input)).toBe(expected);\n });\n });\n\n describe('anchoredEscapeRegex', () => {\n test('wraps escaped output with ^…$', () => {\n expect(anchoredEscapeRegex('RouterLink')).toBe('^RouterLink$');\n expect(anchoredEscapeRegex('My.Component')).toBe('^My\\\\.Component$');\n });\n });\n\n describe('applyVueOverrides', () => {\n test('is a no-op when globalComponents is missing', () => {\n const block: Linter.Config = { rules: { 'vue/no-undef-components': ['error'] } };\n\n applyVueOverrides(block, {});\n\n expect(block.rules?.['vue/no-undef-components']).toStrictEqual(['error']);\n });\n\n test('is a no-op when globalComponents is empty', () => {\n const block: Linter.Config = { rules: { 'vue/no-undef-components': ['error'] } };\n\n applyVueOverrides(block, { globalComponents: [] });\n\n expect(block.rules?.['vue/no-undef-components']).toStrictEqual(['error']);\n });\n\n test('overrides rule with anchored escaped patterns', () => {\n const block: Linter.Config = {};\n\n applyVueOverrides(block, { globalComponents: ['RouterLink', 'NuxtLink'] });\n\n expect(block.rules?.['vue/no-undef-components']).toStrictEqual([\n 'error',\n { ignorePatterns: ['^RouterLink$', '^NuxtLink$'] },\n ]);\n });\n });\n}\n","import type { Linter } from 'eslint';\nimport { applyTypescriptOverrides } from './typescript';\nimport { applyVueOverrides } from './vue';\n\nexport interface CommonEntryFields {\n ignores?: string[];\n globals?: Linter.Globals;\n}\n\nexport interface JsEntry extends CommonEntryFields {\n files: string[];\n}\n\nexport interface TsEntry extends CommonEntryFields {\n files: string[];\n tsconfigPaths: string[];\n}\n\nexport interface VueEntry extends CommonEntryFields {\n files: string[];\n globalComponents?: string[];\n}\n\nexport interface VueTsEntry extends CommonEntryFields {\n files: string[];\n globalComponents?: string[];\n tsconfigPaths: string[];\n}\n\nexport interface TestEntry extends CommonEntryFields {\n files: string[];\n}\n\nexport type ScopeEntry = JsEntry | TsEntry | VueEntry | VueTsEntry | TestEntry;\n\nconst scopeMap = {\n async javascript () {\n return import('../configs/javascript');\n },\n async javascriptDom () {\n return import('../configs/javascript-dom');\n },\n async javascriptNode () {\n return import('../configs/javascript-node');\n },\n async typescript () {\n return import('../configs/typescript');\n },\n async typescriptDom () {\n return import('../configs/typescript-dom');\n },\n async typescriptNode () {\n return import('../configs/typescript-node');\n },\n async vue2 () {\n return import('../configs/vue2');\n },\n async vue3 () {\n return import('../configs/vue3');\n },\n async vue2Typescript () {\n return import('../configs/vue2-typescript');\n },\n async vue3Typescript () {\n return import('../configs/vue3-typescript');\n },\n async vue2OptionsApi () {\n return import('../configs/vue2-options-api');\n },\n async vue3OptionsApi () {\n return import('../configs/vue3-options-api');\n },\n async vue2TypescriptOptionsApi () {\n return import('../configs/vue2-typescript-options-api');\n },\n async vue3TypescriptOptionsApi () {\n return import('../configs/vue3-typescript-options-api');\n },\n async jest () {\n return import('../configs/jest');\n },\n async vitest () {\n return import('../configs/vitest');\n },\n async jsdocBasedTs () {\n return import('../configs/jsdoc-based-ts');\n },\n} satisfies Record<string, () => Promise<{ default: Linter.Config }>>;\n\nexport type ScopeName = keyof typeof scopeMap;\n\nconst tsScopes = new Set<ScopeName>([\n 'typescript',\n 'typescriptDom',\n 'typescriptNode',\n 'vue2Typescript',\n 'vue3Typescript',\n 'vue2TypescriptOptionsApi',\n 'vue3TypescriptOptionsApi',\n 'jsdocBasedTs',\n]);\n\nconst vueScopes = new Set<ScopeName>([\n 'vue2',\n 'vue3',\n 'vue2Typescript',\n 'vue3Typescript',\n 'vue2OptionsApi',\n 'vue3OptionsApi',\n 'vue2TypescriptOptionsApi',\n 'vue3TypescriptOptionsApi',\n]);\n\ntype LanguageOptions = NonNullable<Linter.Config['languageOptions']>;\n\nfunction cloneLanguageOptions (languageOptions: LanguageOptions): LanguageOptions {\n const clone: LanguageOptions = { ...languageOptions };\n\n if (languageOptions.parserOptions) {\n clone.parserOptions = { ...languageOptions.parserOptions };\n }\n\n if (languageOptions.globals) {\n clone.globals = { ...languageOptions.globals };\n }\n\n return clone;\n}\n\n// ESLint's flat-config schema validates every OWN key, so `languageOptions: undefined`\n// fails with `Key \"languageOptions\": Expected an object.` — absent keys must stay absent.\nfunction clonePreset (preset: Linter.Config): Linter.Config {\n const { languageOptions, rules, settings, ...rest } = preset;\n const clone: Linter.Config = { ...rest };\n\n if (languageOptions) {\n clone.languageOptions = cloneLanguageOptions(languageOptions);\n }\n\n if (rules) {\n clone.rules = { ...rules };\n }\n\n if (settings) {\n clone.settings = { ...settings };\n }\n\n return clone;\n}\n\nexport function isScopeName (key: string): key is ScopeName {\n return Object.keys(scopeMap).includes(key);\n}\n\nfunction applyCommonEntryFields (block: Linter.Config, entry: CommonEntryFields): void {\n if (entry.ignores && entry.ignores.length > 0) {\n block.ignores = [...entry.ignores];\n }\n\n if (entry.globals) {\n const languageOptions = block.languageOptions ?? {};\n const existingGlobals = languageOptions.globals ?? {};\n\n languageOptions.globals = { ...existingGlobals, ...entry.globals };\n block.languageOptions = languageOptions;\n }\n}\n\nexport async function buildScopeBlocks (\n scope: ScopeName,\n entries: readonly ScopeEntry[]\n): Promise<Linter.Config[]> {\n const preset = (await scopeMap[scope]()).default;\n\n return entries.map(entry => {\n const block = clonePreset(preset);\n\n block.files = entry.files;\n\n if (tsScopes.has(scope)) {\n const { tsconfigPaths } = entry as TsEntry;\n\n if (tsconfigPaths) {\n applyTypescriptOverrides(block, { tsconfigPaths });\n }\n }\n\n if (vueScopes.has(scope)) {\n const { globalComponents } = entry as VueEntry;\n\n applyVueOverrides(block, { globalComponents });\n }\n\n applyCommonEntryFields(block, entry);\n\n return block;\n });\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n interface ParserOptionsShape {\n project?: string[];\n tsconfigRootDir?: string;\n [key: string]: unknown;\n }\n\n describe('buildScopeBlocks — ignores', () => {\n test('sets block.ignores when entry.ignores is non-empty', async () => {\n const [block] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['scripts/**/*.js'],\n ignores: ['scripts/legacy/**'],\n },\n ]);\n\n expect(block.ignores).toStrictEqual(['scripts/legacy/**']);\n });\n\n test('omits block.ignores when entry.ignores is missing', async () => {\n const [block] = await buildScopeBlocks('javascriptNode', [\n { files: ['scripts/**/*.js'] },\n ]);\n\n expect(block.ignores).toBeUndefined();\n });\n\n test('omits block.ignores when entry.ignores is an empty array', async () => {\n const [block] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['scripts/**/*.js'],\n ignores: [],\n },\n ]);\n\n expect(block.ignores).toBeUndefined();\n });\n\n test('copies the ignores array (caller mutation does not leak into block)', async () => {\n const ignores = ['scripts/legacy/**'];\n const [block] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['scripts/**/*.js'],\n ignores,\n },\n ]);\n\n ignores.push('mutated/**');\n\n expect(block.ignores).toStrictEqual(['scripts/legacy/**']);\n });\n });\n\n describe('buildScopeBlocks — globals', () => {\n test('merges user globals on top of preset globals (user wins on conflict)', async () => {\n const [block] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['**/*.js'],\n globals: { MyCustomGlobal: 'readonly' },\n },\n ]);\n\n const blockGlobals = block.languageOptions?.globals as Record<string, unknown>;\n\n expect(blockGlobals.MyCustomGlobal).toBe('readonly');\n });\n\n test('preserves preset globals when entry.globals is absent', async () => {\n const [withUserGlobals] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['**/*.js'],\n globals: { MyCustomGlobal: 'readonly' },\n },\n ]);\n const [withoutUserGlobals] = await buildScopeBlocks('javascriptNode', [\n { files: ['**/*.js'] },\n ]);\n\n const presetGlobals = withoutUserGlobals.languageOptions?.globals as Record<string, unknown>;\n const mergedGlobals = withUserGlobals.languageOptions?.globals as Record<string, unknown>;\n\n Object.keys(presetGlobals).forEach(key => {\n expect(mergedGlobals[key]).toBe(presetGlobals[key]);\n });\n });\n\n test('user globals override preset globals on key conflict', async () => {\n const [presetBlock] = await buildScopeBlocks('javascriptNode', [\n { files: ['**/*.js'] },\n ]);\n const presetGlobals = presetBlock.languageOptions?.globals as Record<string, unknown>;\n const [sharedKey] = Object.keys(presetGlobals);\n\n const [overrideBlock] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['**/*.js'],\n globals: { [sharedKey]: 'writable' },\n },\n ]);\n const overrideGlobals = overrideBlock.languageOptions?.globals as Record<string, unknown>;\n\n expect(overrideGlobals[sharedKey]).toBe('writable');\n });\n });\n\n describe('buildScopeBlocks — ignores + typescript override interaction', () => {\n test('TS scope: ignores and tsconfigPaths both applied, ignores not overwritten', async () => {\n const [block] = await buildScopeBlocks('typescriptNode', [\n {\n files: ['*.ts', 'scripts/**/*.ts', 'e2e/**/*.ts'],\n tsconfigPaths: ['./tsconfig.node.json'],\n ignores: ['e2e/**/*.test.ts', 'scripts/design-system.d.ts'],\n },\n ]);\n\n const parserOptions = block.languageOptions?.parserOptions as ParserOptionsShape;\n\n expect(parserOptions.project).toStrictEqual(['./tsconfig.node.json']);\n expect(block.ignores).toStrictEqual([\n 'e2e/**/*.test.ts',\n 'scripts/design-system.d.ts',\n ]);\n });\n });\n\n describe('buildScopeBlocks — ignores + vue override interaction', () => {\n test('Vue scope: ignores and globalComponents both applied', async () => {\n const [block] = await buildScopeBlocks('vue3', [\n {\n files: ['src/**/*.vue'],\n globalComponents: ['RouterLink'],\n ignores: ['src/legacy/**/*.vue'],\n },\n ]);\n\n expect(block.ignores).toStrictEqual(['src/legacy/**/*.vue']);\n expect(block.rules?.['vue/no-undef-components']).toStrictEqual([\n 'error',\n { ignorePatterns: ['^RouterLink$'] },\n ]);\n });\n });\n\n describe('buildScopeBlocks — no undefined-valued own keys', () => {\n const scopeNames = Object.keys(scopeMap) as ScopeName[];\n\n test.each(scopeNames)('%s block exposes no own key set to undefined', async scope => {\n const entry = {\n files: ['src/**/*'],\n tsconfigPaths: ['./tsconfig.json'],\n };\n const [block] = await buildScopeBlocks(scope, [entry]);\n\n const undefinedKeys = Object.entries(block)\n .filter(([, value]) => value === undefined)\n .map(([key]) => key);\n\n expect(undefinedKeys).toStrictEqual([]);\n });\n\n test('a preset declaring neither key keeps both absent instead of undefined', async () => {\n const [block] = await buildScopeBlocks('vitest', [{ files: ['**/*.spec.js'] }]);\n\n expect('languageOptions' in block).toBe(false);\n expect('settings' in block).toBe(false);\n });\n\n test('every preset that declares languageOptions leaves no undefined key inside it', async () => {\n const inspected: string[] = [];\n\n await Promise.all(scopeNames.map(async scope => {\n const [block] = await buildScopeBlocks(scope, [{\n files: ['src/**/*'],\n tsconfigPaths: ['./tsconfig.json'],\n }]);\n\n if (!block.languageOptions) {\n return;\n }\n\n inspected.push(scope);\n\n const undefinedKeys = Object.entries(block.languageOptions)\n .filter(([, value]) => value === undefined)\n .map(([key]) => key);\n\n expect(undefinedKeys).toStrictEqual([]);\n }));\n\n expect(inspected.length).toBeGreaterThan(0);\n });\n });\n\n describe('buildScopeBlocks — real-world repro (MB-2937 / mindbehind-fe)', () => {\n test('typescriptNode entry with design-system.d.ts in ignores emits matching block.ignores', async () => {\n const [, secondBlock] = await buildScopeBlocks('typescriptNode', [\n {\n files: ['e2e/**/*.test.ts'],\n tsconfigPaths: ['./tsconfig.node.json'],\n },\n {\n files: ['*.ts', 'scripts/**/*.ts', 'e2e/**/*.ts'],\n tsconfigPaths: ['./tsconfig.node.json'],\n ignores: ['e2e/**/*.test.ts', 'scripts/design-system.d.ts'],\n },\n ]);\n\n expect(secondBlock.files).toStrictEqual(['*.ts', 'scripts/**/*.ts', 'e2e/**/*.ts']);\n expect(secondBlock.ignores).toContain('scripts/design-system.d.ts');\n expect(secondBlock.ignores).toContain('e2e/**/*.test.ts');\n });\n });\n}\n","import type { Linter } from 'eslint';\n\ninterface SpellCheckerInput {\n ignoredPaths?: string[];\n customWordListFile?: string;\n}\n\nexport function buildSpellCheckerBlocks (input: SpellCheckerInput | undefined): Linter.Config[] {\n if (!input) {\n return [];\n }\n\n const blocks: Linter.Config[] = [];\n\n if (input.customWordListFile) {\n blocks.push({\n rules: {\n '@cspell/spellchecker': ['error', { customWordListFile: input.customWordListFile }],\n },\n });\n }\n\n if (input.ignoredPaths && input.ignoredPaths.length > 0) {\n blocks.push({\n files: input.ignoredPaths,\n rules: {\n '@cspell/spellchecker': 'off',\n },\n });\n }\n\n return blocks;\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('buildSpellCheckerBlocks', () => {\n test('returns empty array when input is undefined', () => {\n expect(buildSpellCheckerBlocks(undefined)).toStrictEqual([]);\n });\n\n test('returns empty array when input has neither option', () => {\n expect(buildSpellCheckerBlocks({})).toStrictEqual([]);\n });\n\n test('returns customWordListFile block alone', () => {\n const blocks = buildSpellCheckerBlocks({ customWordListFile: './words.json' });\n\n expect(blocks).toStrictEqual([\n {\n rules: {\n '@cspell/spellchecker': ['error', { customWordListFile: './words.json' }],\n },\n },\n ]);\n });\n\n test('returns ignoredPaths off block alone', () => {\n const blocks = buildSpellCheckerBlocks({ ignoredPaths: ['vendor/**'] });\n\n expect(blocks).toStrictEqual([\n {\n files: ['vendor/**'],\n rules: { '@cspell/spellchecker': 'off' },\n },\n ]);\n });\n\n test('emits customWordListFile first then ignoredPaths so ignore wins on overlap', () => {\n const blocks = buildSpellCheckerBlocks({\n customWordListFile: './cspell.json',\n ignoredPaths: ['vendor/**', 'dist/**'],\n });\n\n expect(blocks).toHaveLength(2);\n expect(blocks[0].rules?.['@cspell/spellchecker']).toStrictEqual([\n 'error',\n { customWordListFile: './cspell.json' },\n ]);\n expect(blocks[1].files).toStrictEqual(['vendor/**', 'dist/**']);\n expect(blocks[1].rules?.['@cspell/spellchecker']).toBe('off');\n });\n\n test('ignores empty ignoredPaths array', () => {\n const blocks = buildSpellCheckerBlocks({ ignoredPaths: [] });\n\n expect(blocks).toStrictEqual([]);\n });\n });\n}\n","import type { Linter } from 'eslint';\n\nexport async function buildStrictBlocks (value: boolean | string[] | undefined): Promise<Linter.Config[]> {\n if (!value) {\n return [];\n }\n\n const isFileScoped = Array.isArray(value);\n\n if (isFileScoped && value.length === 0) {\n return [];\n }\n\n const { default: strictConfig } = await import('../configs/strict');\n const clone: Linter.Config = { ...strictConfig };\n\n if (clone.rules) {\n clone.rules = { ...clone.rules };\n }\n\n if (isFileScoped) {\n clone.files = value;\n } else {\n delete clone.files;\n }\n\n return [clone];\n}\n","import { readFileSync, realpathSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, resolve as resolvePath } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { AST_NODE_TYPES, type TSESTree } from '@typescript-eslint/types';\n\nexport interface ConfigStructureViolation {\n line: number;\n column: number;\n reason: string;\n}\n\nexport interface ValidateProgramStructureOptions {\n isInsiderImportSource?: (source: string, configPath: string) => boolean;\n}\n\nconst CONFIG_FILE_REGEX = /eslint\\.config\\.[mc]?[jt]s(?:$|\\?)/;\nconst OPT_OUT_ENV = 'INSIDER_SKIP_CONFIG_VALIDATION';\nconst FILE_URL_PREFIX = 'file://';\nconst TARGET_CALLEE_NAME = 'useInsider';\nconst INSIDER_IMPORT_SOURCE = '@useinsider/eslint-config';\nlet bypassWarnEmitted = false;\n\nfunction computeSelfModulePath (): string {\n const raw = fileURLToPath(import.meta.url);\n\n try {\n return realpathSync(raw);\n } catch {\n return raw;\n }\n}\n\nconst SELF_MODULE_PATH = computeSelfModulePath();\n\nfunction isPathSpecifier (source: string): boolean {\n return source.startsWith('.') || source.startsWith('/');\n}\n\nexport function defaultIsInsiderImportSource (source: string, configPath: string): boolean {\n if (source === INSIDER_IMPORT_SOURCE) {\n return true;\n }\n\n if (!configPath || !isPathSpecifier(source)) {\n return false;\n }\n\n try {\n const absolute = resolvePath(dirname(configPath), source);\n const canonical = realpathSync(absolute);\n\n return canonical === SELF_MODULE_PATH;\n } catch {\n return false;\n }\n}\n\nfunction locOf (node: TSESTree.Node): { line: number; column: number } {\n return { line: node.loc.start.line, column: node.loc.start.column };\n}\n\nfunction validateCallExpression (\n callExpr: TSESTree.CallExpression\n): ConfigStructureViolation[] {\n const { callee } = callExpr;\n\n if (callee.type !== AST_NODE_TYPES.Identifier || callee.name !== TARGET_CALLEE_NAME) {\n return [{\n ...locOf(callExpr),\n reason: 'Default export must call `useInsider(...)` directly.',\n }];\n }\n\n return [];\n}\n\nfunction validateExportedCall (\n callee: TSESTree.Expression,\n fallbackLoc: { line: number; column: number }\n): ConfigStructureViolation[] {\n let target: TSESTree.Expression = callee;\n\n if (target.type === AST_NODE_TYPES.AwaitExpression) {\n target = target.argument;\n }\n\n if (target.type === AST_NODE_TYPES.CallExpression) {\n return validateCallExpression(target);\n }\n\n if (target.type === AST_NODE_TYPES.ArrayExpression) {\n return [{\n ...locOf(target),\n reason: 'Default export must not wrap `useInsider(...)` in an array. Use the typed options object instead.',\n }];\n }\n\n const isGated = target.type === AST_NODE_TYPES.LogicalExpression\n || target.type === AST_NODE_TYPES.ConditionalExpression;\n\n if (isGated) {\n const reason = [\n 'Default export must not gate `useInsider(...)` behind a logical/conditional expression.',\n 'Use the typed options object instead.',\n ].join(' ');\n\n return [{ ...locOf(target), reason }];\n }\n\n return [{\n ...fallbackLoc,\n reason: 'Default export must be a direct call to `useInsider(...)`.',\n }];\n}\n\ninterface IgnoreClassification {\n type: 'ignore';\n}\n\ninterface ExportClassification {\n type: 'export';\n violations: ConfigStructureViolation[];\n}\n\ninterface DuplicateExportClassification {\n type: 'duplicateExport';\n violation: ConfigStructureViolation;\n}\n\ninterface DisallowedClassification {\n type: 'disallowed';\n violation: ConfigStructureViolation;\n}\n\ntype StatementClassification = DisallowedClassification | DuplicateExportClassification | ExportClassification | IgnoreClassification; // eslint-disable-line @stylistic/max-len -- type alias union; splitting hits operator-linebreak rule.\n\nfunction collectInsiderBindings (\n node: TSESTree.ImportDeclaration,\n bindings: Set<string>,\n isInsiderSource: (source: string) => boolean\n): void {\n if (!isInsiderSource(node.source.value)) {\n return;\n }\n\n node.specifiers.forEach(specifier => {\n bindings.add(specifier.local.name);\n });\n}\n\nfunction classifyExpressionStatement (\n node: TSESTree.ExpressionStatement,\n insiderBindings: ReadonlySet<string>\n): StatementClassification {\n const { expression } = node;\n const isInsiderCall = expression.type === AST_NODE_TYPES.CallExpression\n && expression.callee.type === AST_NODE_TYPES.Identifier\n && insiderBindings.has(expression.callee.name);\n\n if (isInsiderCall) {\n return { type: 'ignore' };\n }\n\n const reason = [\n 'Disallowed top-level statement `ExpressionStatement`.',\n `Only call expressions whose callee was imported from \\`${INSIDER_IMPORT_SOURCE}\\` are allowed at top level.`,\n ].join(' ');\n\n return {\n type: 'disallowed',\n violation: { ...locOf(node), reason },\n };\n}\n\nfunction classifyStatement (\n node: TSESTree.ProgramStatement,\n exportSeen: boolean,\n insiderBindings: ReadonlySet<string>\n): StatementClassification {\n if (node.type === AST_NODE_TYPES.ImportDeclaration || node.type === AST_NODE_TYPES.VariableDeclaration) {\n return { type: 'ignore' };\n }\n\n if (node.type === AST_NODE_TYPES.ExpressionStatement) {\n return classifyExpressionStatement(node, insiderBindings);\n }\n\n if (node.type === AST_NODE_TYPES.ExportDefaultDeclaration) {\n if (exportSeen) {\n return {\n type: 'duplicateExport',\n violation: { ...locOf(node), reason: 'Only one default export is allowed.' },\n };\n }\n\n const declaration = node.declaration as TSESTree.Expression;\n\n return { type: 'export', violations: validateExportedCall(declaration, locOf(node)) };\n }\n\n const reason = [\n `Disallowed top-level statement \\`${node.type}\\`.`,\n 'Only imports, variable declarations, helper calls from `@useinsider/eslint-config`,',\n 'and one `export default useInsider(...)` are allowed.',\n ].join(' ');\n\n return {\n type: 'disallowed',\n violation: { ...locOf(node), reason },\n };\n}\n\nexport function validateProgramStructure (\n program: TSESTree.Program,\n configPath = '',\n options: ValidateProgramStructureOptions = {}\n): ConfigStructureViolation[] {\n const violations: ConfigStructureViolation[] = [];\n const insiderBindings = new Set<string>();\n const resolver = options.isInsiderImportSource ?? defaultIsInsiderImportSource;\n const isInsiderSource = (source: string): boolean => resolver(source, configPath);\n let exportSeen = false;\n\n program.body.forEach(node => {\n if (node.type === AST_NODE_TYPES.ImportDeclaration) {\n collectInsiderBindings(node, insiderBindings, isInsiderSource);\n }\n\n const result = classifyStatement(node, exportSeen, insiderBindings);\n\n if (result.type === 'ignore') {\n return;\n }\n\n if (result.type === 'export') {\n exportSeen = true;\n violations.push(...result.violations);\n\n return;\n }\n\n if (result.type === 'duplicateExport') {\n violations.push(result.violation);\n\n return;\n }\n\n violations.push(result.violation);\n });\n\n if (!exportSeen) {\n violations.push({\n line: 1,\n column: 0,\n reason: 'Missing `export default useInsider(...)`.',\n });\n }\n\n return violations;\n}\n\ninterface CallSiteLike {\n getFileName: (this: void) => string | null;\n}\n\nfunction readStackFrames (): readonly CallSiteLike[] {\n // eslint-disable-next-line @typescript-eslint/unbound-method -- Error.prepareStackTrace is a value slot, not a method call.\n const original = Error.prepareStackTrace;\n\n Error.prepareStackTrace = (_, frames) => frames;\n\n const holder: { stack?: unknown } = {};\n\n Error.captureStackTrace(holder, readStackFrames);\n\n const { stack } = holder;\n\n Error.prepareStackTrace = original;\n\n if (!Array.isArray(stack)) {\n return [];\n }\n\n return stack as readonly CallSiteLike[];\n}\n\nfunction normalizeFrameFilename (raw: string | null): string | null {\n if (!raw) {\n return null;\n }\n\n const withoutPrefix = raw.startsWith(FILE_URL_PREFIX) ? raw.slice(FILE_URL_PREFIX.length) : raw;\n const queryIndex = withoutPrefix.indexOf('?');\n\n return queryIndex === -1 ? withoutPrefix : withoutPrefix.slice(0, queryIndex);\n}\n\nfunction extractMatchesFromStack (frames: readonly CallSiteLike[]): string[] {\n const matches = new Set<string>();\n\n frames.forEach(frame => {\n const name = normalizeFrameFilename(frame.getFileName());\n\n if (name && CONFIG_FILE_REGEX.test(name)) {\n matches.add(name);\n }\n });\n\n return Array.from(matches);\n}\n\nfunction argvCandidateAt (argv: readonly string[], index: number): string | null {\n const arg = argv[index];\n\n if (arg === '--config' || arg === '-c') {\n return argv[index + 1] ?? null;\n }\n\n if (arg.startsWith('--config=')) {\n return arg.slice('--config='.length);\n }\n\n return null;\n}\n\nfunction extractConfigFromArgv (argv: readonly string[]): string | null {\n for (let i = 0; i < argv.length; i += 1) {\n const candidate = argvCandidateAt(argv, i);\n\n if (candidate && CONFIG_FILE_REGEX.test(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\nexport function getConsumerConfigPath (\n frames: readonly CallSiteLike[] = readStackFrames(),\n argv: readonly string[] = process.argv\n): string | null {\n const stackMatches = extractMatchesFromStack(frames);\n\n if (stackMatches.length === 1) {\n return stackMatches[0];\n }\n\n const fromArgv = extractConfigFromArgv(argv);\n\n if (fromArgv) {\n return fromArgv;\n }\n\n return null;\n}\n\nfunction formatReport (configPath: string, violations: readonly ConfigStructureViolation[]): string {\n const header = [\n '@useinsider/eslint-config: protected configuration structure violated in:',\n ` ${configPath}`,\n ].join('\\n');\n const lines = violations.map(violation => ` - [${violation.line}:${violation.column}] ${violation.reason}`);\n const footer = [\n '',\n 'Allowed structure:',\n ' - import statements',\n ' - const/let/var declarations',\n ' - exactly one `export default useInsider({ ... })`',\n '',\n 'Emergency bypass: set INSIDER_SKIP_CONFIG_VALIDATION=true (do not commit).',\n ].join('\\n');\n\n return `${header}\\n${lines.join('\\n')}\\n${footer}`;\n}\n\nfunction emitBypassWarning (): void {\n if (bypassWarnEmitted) {\n return;\n }\n\n bypassWarnEmitted = true;\n\n const message = [\n '@useinsider/eslint-config: structure validation BYPASSED',\n 'via INSIDER_SKIP_CONFIG_VALIDATION — emergency use only.',\n ].join(' ');\n\n // eslint-disable-next-line no-console -- diagnostic surface, mirrors ensureDependencies.ts.\n console.warn(message);\n}\n\nclass ConfigStructureExit extends Error {}\n\nfunction fail (message: string): never {\n // eslint-disable-next-line no-console -- diagnostic surface, mirrors ensureDependencies.ts.\n console.error(message);\n\n const exit: (code: number) => void = process.exit.bind(process);\n\n exit(1);\n\n throw new ConfigStructureExit(message);\n}\n\nfunction readConsumerSource (configPath: string): string {\n try {\n return readFileSync(configPath, 'utf8');\n } catch (readError) {\n const { message } = readError as Error;\n\n return fail(`@useinsider/eslint-config: failed to read consumer config at ${configPath}: ${message}`);\n }\n}\n\nconst lazyRequire = createRequire(import.meta.url);\n\nfunction loadParser (): typeof import('@typescript-eslint/parser').parse {\n const mod = lazyRequire('@typescript-eslint/parser') as typeof import('@typescript-eslint/parser');\n\n return mod.parse;\n}\n\nfunction parseConsumerSource (configPath: string, source: string): TSESTree.Program {\n const parse = loadParser();\n\n try {\n return parse(source, { sourceType: 'module', range: true, loc: true });\n } catch (parseError) {\n const { message } = parseError as Error;\n\n return fail(`@useinsider/eslint-config: failed to parse consumer config at ${configPath}: ${message}`);\n }\n}\n\nfunction runValidation (overridePath?: string | null): void {\n const configPath = overridePath === undefined ? getConsumerConfigPath() : overridePath;\n\n if (!configPath) {\n fail([\n '@useinsider/eslint-config: unable to locate consumer `eslint.config.*` file for structure validation.',\n 'If this is an unexpected environment, set INSIDER_SKIP_CONFIG_VALIDATION=true (do not commit).',\n ].join('\\n'));\n }\n\n const source = readConsumerSource(configPath);\n const program = parseConsumerSource(configPath, source);\n const violations = validateProgramStructure(program, configPath);\n\n if (violations.length === 0) {\n return;\n }\n\n fail(formatReport(configPath, violations));\n}\n\nexport function ensureConfigStructure (overridePath?: string | null): void {\n if (process.env[OPT_OUT_ENV] === 'true') {\n emitBypassWarning();\n\n return;\n }\n\n try {\n runValidation(overridePath);\n } catch (error) {\n if (error instanceof ConfigStructureExit) {\n return;\n }\n\n throw error;\n }\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n function parseSource (source: string): TSESTree.Program {\n return loadParser()(source, { sourceType: 'module', range: true, loc: true });\n }\n\n describe('validateProgramStructure', () => {\n test('accepts `export default await useInsider(...)` (useInsider returns a Promise)', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'export default await useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('rejects await around a non-useInsider call', () => {\n const program = parseSource([\n \"import { other } from 'x';\",\n 'export default await other({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/must call `useInsider/);\n });\n\n test('accepts a valid baseline: import + const + export default useInsider(...)', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n \"const tsconfigPaths = ['./tsconfig.json'];\",\n \"export default useInsider({ typescript: [{ files: ['**/*.ts'], tsconfigPaths }] });\",\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('accepts let / var declarations alongside const', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'let a = 1;',\n 'var b = 2;',\n 'const c = 3;',\n 'export default useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('rejects spread sibling around useInsider() in array', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'export default [...useInsider(), { rules: {} }];',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/must not wrap `useInsider/);\n });\n\n test('rejects array wrapping the useInsider call', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'export default [useInsider()];',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/must not wrap `useInsider/);\n });\n\n test('rejects an extra top-level statement when callee is not from @useinsider/eslint-config', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n \"console.log('boot');\",\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Disallowed top-level statement `ExpressionStatement`/);\n });\n\n test('accepts top-level `silenceDependencyWarning(true);` when imported from @useinsider/eslint-config', () => {\n const program = parseSource([\n \"import { useInsider, silenceDependencyWarning } from '@useinsider/eslint-config';\",\n 'silenceDependencyWarning(true);',\n 'export default await useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('accepts top-level `silenceRules([...]);` when imported from @useinsider/eslint-config', () => {\n const program = parseSource([\n \"import { useInsider, silenceRules } from '@useinsider/eslint-config';\",\n \"silenceRules(['no-unused-vars']);\",\n 'export default await useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('accepts a renamed import binding via `as` alias when calling the alias at top level', () => {\n const program = parseSource([\n \"import { silenceDependencyWarning as silence, useInsider } from '@useinsider/eslint-config';\",\n 'silence(true);',\n 'export default await useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('rejects top-level call whose callee identifier is imported from a different module', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n \"import { somethingElse } from 'other-package';\",\n 'somethingElse(true);',\n 'export default await useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Disallowed top-level statement `ExpressionStatement`/);\n });\n\n test('rejects multiple exports (named export alongside default)', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'export const foo = 1;',\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations.length).toBeGreaterThanOrEqual(1);\n expect(violations[0].reason).toMatch(/Disallowed top-level statement `ExportNamedDeclaration`/);\n });\n\n test('rejects two default exports — second one flagged as duplicate', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'export default useInsider({});',\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Only one default export/);\n });\n\n test('rejects export default with a non-useInsider callee', () => {\n const program = parseSource([\n \"import { someOther } from 'x';\",\n 'export default someOther({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/must call `useInsider/);\n });\n\n test('rejects export default as a logical expression around the call', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'const flag = true;',\n 'export default flag && useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/logical\\/conditional/);\n });\n\n test('rejects export default as a conditional expression', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'const flag = true;',\n 'export default flag ? useInsider({}) : [];',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/logical\\/conditional/);\n });\n\n test('rejects missing default export', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'const a = 1;',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Missing `export default useInsider/);\n });\n\n test('rejects export default of a plain object literal', () => {\n const program = parseSource('export default { rules: {} };');\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/direct call to `useInsider/);\n });\n });\n\n describe('validateProgramStructure — self-relative imports', () => {\n const acceptSelfRelative = (source: string, configPath: string): boolean => {\n if (source === INSIDER_IMPORT_SOURCE) {\n return true;\n }\n\n return configPath === '/repo/packages/eslint-config/eslint.config.js'\n && source === './dist/main.js';\n };\n\n test('accepts top-level helper call when relative import resolves to the validator module', () => {\n const program = parseSource([\n \"import { useInsider, silenceDependencyWarning } from './dist/main.js';\",\n 'silenceDependencyWarning(true);',\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(\n program,\n '/repo/packages/eslint-config/eslint.config.js',\n { isInsiderImportSource: acceptSelfRelative }\n );\n\n expect(violations).toStrictEqual([]);\n });\n\n test('rejects top-level call when relative import points to a different module', () => {\n const program = parseSource([\n \"import { silenceDependencyWarning } from './other/path.js';\",\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'silenceDependencyWarning(true);',\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(\n program,\n '/repo/packages/eslint-config/eslint.config.js',\n { isInsiderImportSource: acceptSelfRelative }\n );\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Disallowed top-level statement `ExpressionStatement`/);\n });\n\n test('treats `@useinsider/eslint-config` as Insider regardless of configPath', () => {\n const program = parseSource([\n \"import { useInsider, silenceDependencyWarning } from '@useinsider/eslint-config';\",\n 'silenceDependencyWarning(true);',\n 'export default useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program, '/elsewhere/eslint.config.mjs'))\n .toStrictEqual([]);\n });\n\n test('default resolver: relative path that does not resolve falls back to non-Insider (no crash)', () => {\n const program = parseSource([\n \"import { silenceDependencyWarning } from './does/not/exist.js';\",\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'silenceDependencyWarning(true);',\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program, '/no/such/eslint.config.js');\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Disallowed top-level statement `ExpressionStatement`/);\n });\n\n test('back-compat: calls without configPath still validate against the canonical source', () => {\n const program = parseSource([\n \"import { useInsider, silenceDependencyWarning } from '@useinsider/eslint-config';\",\n 'silenceDependencyWarning(true);',\n 'export default useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n });\n\n describe('defaultIsInsiderImportSource', () => {\n test('returns true for the canonical package source', () => {\n expect(defaultIsInsiderImportSource('@useinsider/eslint-config', '')).toBe(true);\n expect(defaultIsInsiderImportSource('@useinsider/eslint-config', '/any/eslint.config.js')).toBe(true);\n });\n\n test('returns false for unrelated package sources', () => {\n expect(defaultIsInsiderImportSource('other-package', '/any/eslint.config.js')).toBe(false);\n });\n\n test('returns false for relative path that cannot be realpath-resolved', () => {\n expect(defaultIsInsiderImportSource('./missing.js', '/does/not/exist/eslint.config.js'))\n .toBe(false);\n });\n\n test('returns false for relative source when configPath is empty', () => {\n expect(defaultIsInsiderImportSource('./dist/main.js', '')).toBe(false);\n });\n });\n\n describe('getConsumerConfigPath', () => {\n test('returns single matching stack frame', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => '/repo/node_modules/@useinsider/eslint-config/dist/main.js' },\n { getFileName: () => '/repo/eslint.config.mjs' },\n { getFileName: () => 'node:internal/process' },\n ];\n\n expect(getConsumerConfigPath(frames, [])).toBe('/repo/eslint.config.mjs');\n });\n\n test('strips ESM cache-buster query string (`?mtime=...`) from stack frame', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => 'file:///repo/eslint.config.mjs?mtime=1779282184336' },\n ];\n\n expect(getConsumerConfigPath(frames, [])).toBe('/repo/eslint.config.mjs');\n });\n\n test('strips file:// prefix from stack frame', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => 'file:///repo/eslint.config.ts' },\n ];\n\n expect(getConsumerConfigPath(frames, [])).toBe('/repo/eslint.config.ts');\n });\n\n test('falls back to argv when stack has 0 matches', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => 'node:internal/process' },\n ];\n const argv = ['node', 'eslint', '--config', '/abs/eslint.config.mjs', 'src'];\n\n expect(getConsumerConfigPath(frames, argv)).toBe('/abs/eslint.config.mjs');\n });\n\n test('falls back to argv when stack has multiple matches', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => '/repo/a/eslint.config.mjs' },\n { getFileName: () => '/repo/b/eslint.config.mjs' },\n ];\n const argv = ['node', 'eslint', '-c', '/cli/eslint.config.cts', 'src'];\n\n expect(getConsumerConfigPath(frames, argv)).toBe('/cli/eslint.config.cts');\n });\n\n test('supports --config=<path> argv form', () => {\n const frames: CallSiteLike[] = [];\n const argv = ['node', 'eslint', '--config=/cli/eslint.config.mts'];\n\n expect(getConsumerConfigPath(frames, argv)).toBe('/cli/eslint.config.mts');\n });\n\n test('returns null when nothing matches in stack or argv', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => 'node:internal/process' },\n { getFileName: () => null },\n ];\n\n expect(getConsumerConfigPath(frames, ['node', 'eslint', 'src'])).toBeNull();\n });\n\n test('default arguments: stack walk + process.argv probe runs without throwing', () => {\n const result = getConsumerConfigPath();\n\n expect(result === null || typeof result === 'string').toBe(true);\n });\n\n test('detection regex covers all six config extensions', () => {\n const cases = [\n 'eslint.config.js',\n 'eslint.config.mjs',\n 'eslint.config.cjs',\n 'eslint.config.ts',\n 'eslint.config.mts',\n 'eslint.config.cts',\n ];\n\n cases.forEach(name => {\n const frames: CallSiteLike[] = [{ getFileName: () => `/repo/${name}` }];\n\n expect(getConsumerConfigPath(frames, [])).toBe(`/repo/${name}`);\n });\n });\n });\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { installPackage } from '@antfu/install-pkg';\nimport semver from 'semver';\n\nlet turnOff = false;\nlet scanned = false;\nconst silencedDependencies = new Set<string>();\nconst fixedDependencies = [\n 'eslint',\n 'globals',\n];\nconst require = createRequire(import.meta.url);\n\nfunction getPackageJSONPath (currentDir: string) {\n const fileLocation = resolve(currentDir, 'package.json');\n\n if (existsSync(fileLocation)) {\n return fileLocation;\n }\n\n if (currentDir === process.cwd()) {\n throw new Error(`Unable to locate package.json in ${currentDir}`);\n }\n\n const parentDir = resolve(currentDir, '..');\n\n return getPackageJSONPath(parentDir);\n}\n\nfunction getPackageJSON (fileLocation: string) {\n const packageJsonContent = readFileSync(fileLocation, 'utf8');\n\n return JSON.parse(packageJsonContent) as Record<string, Record<string, string>>;\n}\n\nfunction getExpectedDependencies (): Record<string, string> {\n const path = getPackageJSONPath(fileURLToPath(new URL(/* @vite-ignore */ import.meta.url)));\n const packageJson = getPackageJSON(path);\n\n return packageJson.dependencies;\n}\n\nfunction getDependencyVersionFromConsumer (depName: string, baseDir: string = process.cwd()): string | undefined {\n try {\n const pkgJsonPath = require.resolve(`${depName}/package.json`, { paths: [baseDir] });\n const pkgContent = readFileSync(pkgJsonPath, 'utf8');\n const pkg: { version?: string } = JSON.parse(pkgContent) as Record<string, Record<string, string>>;\n\n return pkg.version;\n } catch {\n return undefined;\n }\n}\n\nexport async function ensureDependencies () {\n if (turnOff || scanned) {\n return;\n }\n\n scanned = true;\n\n const expectedDependencies = Object.entries(getExpectedDependencies());\n const missingDependencyEntries = expectedDependencies.filter(\n ([expectedDependency, expectedVersion]) => {\n if (!fixedDependencies.includes(expectedDependency) || silencedDependencies.has(expectedDependency)) {\n return false;\n }\n\n const installedVersion = getDependencyVersionFromConsumer(expectedDependency);\n\n if (!installedVersion) {\n return true;\n }\n\n return semver.lt(installedVersion, expectedVersion);\n }\n );\n\n if (!missingDependencyEntries.length) {\n return;\n }\n\n const missingDependencies = missingDependencyEntries.map(([dependency, version]) => `${dependency}@${version.replace(/^\\W+/, '')}`);\n\n // eslint-disable-next-line no-console\n console.info('Missing dependencies detected:');\n // eslint-disable-next-line no-console\n console.info(missingDependencies.map(dependency => ` - ${dependency}`).join('\\n'));\n // eslint-disable-next-line no-console\n console.info('\\nInstalling dependencies:');\n\n await installPackage(missingDependencies, { dev: true, additionalArgs: ['-E'] });\n\n // eslint-disable-next-line no-process-exit\n process.exit(1);\n}\n\nexport function silenceDependencyWarning (\n /**\n * Pass `true` to silence all dependency warnings.\n */\n all: boolean\n): void;\n\nexport function silenceDependencyWarning (dependencies: string[]): void;\n\nexport function silenceDependencyWarning (input: boolean | string[]) {\n if (typeof input === 'boolean') {\n turnOff = input;\n\n return;\n }\n\n silencedDependencies.clear();\n input.forEach(dependency => silencedDependencies.add(String(dependency)));\n}\n","import { warn } from 'node:console';\nimport type { Linter } from 'eslint';\n\nconst globallySilencedRules = new Set<string>();\n\n/**\n * Silence rules globally.\n * This will affect all configs scopes.\n */\nexport function silenceRules (rules: string[]) {\n rules.forEach(rule => {\n globallySilencedRules.add(rule);\n });\n}\n\nfunction silenceRule (config: Linter.RuleEntry) {\n const [, ...restConfig] = Array.isArray(config) ? config : [];\n\n return ['warn', ...restConfig as Record<string, unknown>[]] as Linter.RuleSeverityAndOptions;\n}\n\nexport function filterSilencedRules (\n rules: Partial<Linter.RulesRecord> | Readonly<Linter.RulesRecord> | undefined,\n silencedRules?: string[] | Set<string>\n) {\n let targetSilencedRules = silencedRules || globallySilencedRules;\n\n if (Array.isArray(targetSilencedRules)) {\n targetSilencedRules = new Set(targetSilencedRules);\n }\n\n if (!rules || !targetSilencedRules.size) {\n return rules;\n }\n\n if (silencedRules) {\n targetSilencedRules.forEach(rule => {\n if (rules[rule]) {\n return;\n }\n\n warn(`Unknown rule \"${rule}\". Please remove it from the silenced rules list.`);\n });\n }\n\n return Object.entries(rules).reduce((newRules, [key, value]) => {\n if (value) {\n newRules[key] = targetSilencedRules.has(key) ? silenceRule(value) : value;\n }\n\n return newRules;\n }, {} as Linter.RulesRecord);\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { vi, describe, test, expect, afterEach } = import.meta.vitest;\n\n const mocks = vi.hoisted(() => ({\n warn: vi.fn(),\n }));\n\n vi.mock('node:console', () => mocks);\n\n afterEach(() => {\n globallySilencedRules.clear();\n });\n\n describe('silenceRules', () => {\n test('should register given rule keys as silenced', () => {\n const rule = '@stylistic/type-generic-spacing';\n\n expect(globallySilencedRules.has(rule)).toBe(false);\n silenceRules([rule]);\n expect(globallySilencedRules.has(rule)).toBe(true);\n });\n });\n\n describe('filterSilencedRules', () => {\n test('should return the same rules object if no rules are silenced', () => {\n const rules = {\n 'no-console': 'warn',\n 'no-debugger': 'warn',\n } as Linter.RulesRecord;\n\n expect(filterSilencedRules(rules)).toBe(rules);\n });\n\n test('should return a new rules object with silenced rules silenced and other rules unchanged', () => {\n silenceRules(['no-console']);\n\n const rules = {\n 'no-console': 'error',\n 'no-debugger': 'warn',\n 'no-alert': 'error',\n } as Linter.RulesRecord;\n\n expect(filterSilencedRules(rules)).toStrictEqual({\n 'no-console': ['warn'],\n 'no-debugger': 'warn',\n 'no-alert': 'error',\n });\n });\n\n test('should return a new rules object with silenced rules silenced and other rules unchanged', () => {\n silenceRules(['no-bitwise', 'new-cap']);\n\n const rules = {\n 'max-len': 'off',\n 'new-cap': ['error'],\n 'no-alert': ['error'],\n 'no-bitwise': ['error', {\n allow: ['~'],\n }],\n } as Linter.RulesRecord;\n\n expect(filterSilencedRules(rules)).toStrictEqual({\n 'max-len': 'off',\n 'new-cap': ['warn'],\n 'no-alert': ['error'],\n 'no-bitwise': ['warn', {\n allow: ['~'],\n }],\n });\n });\n\n test('should not warn about unknown rules', () => {\n silenceRules(['no-console', 'no-bitwise']);\n\n const rules = {\n 'no-console': 'error',\n 'no-debugger': 'warn',\n 'no-alert': 'error',\n } as Linter.RulesRecord;\n\n filterSilencedRules(rules);\n\n expect(mocks.warn).not.toHaveBeenCalled();\n expect(mocks.warn).not.toHaveBeenCalled();\n });\n\n test('should warn about unknown rules', () => {\n const rules = {\n 'no-console': 'error',\n 'no-debugger': 'warn',\n 'no-alert': 'error',\n } as Linter.RulesRecord;\n\n filterSilencedRules(rules, ['no-console', 'no-bitwise']);\n\n expect(mocks.warn).toHaveBeenCalledTimes(1);\n expect(mocks.warn).toHaveBeenCalledWith(\n 'Unknown rule \"no-bitwise\". Please remove it from the silenced rules list.'\n );\n });\n\n test.each([\n [['@typescript-eslint/no-unsafe-call'], ['warn']],\n [new Set(['@typescript-eslint/no-unsafe-call']), ['warn']],\n [[], 'error'],\n ])(\n '#%# should filter scope-related rules and ignore globally silenced rules',\n (silencedRules, expectation) => {\n silenceRules(['@typescript-eslint/no-unsafe-assignment']);\n\n const rules = {\n '@typescript-eslint/no-unsafe-assignment': 'error',\n '@typescript-eslint/no-unsafe-member-access': 'error',\n '@typescript-eslint/no-unsafe-call': 'error',\n } as Linter.RulesRecord;\n\n expect(filterSilencedRules(rules, silencedRules)).toStrictEqual({\n '@typescript-eslint/no-unsafe-assignment': 'error',\n '@typescript-eslint/no-unsafe-member-access': 'error',\n '@typescript-eslint/no-unsafe-call': expectation,\n });\n }\n );\n });\n}\n","import type { Linter } from 'eslint';\nimport { defineConfig } from 'eslint/config';\nimport { buildConfigFilesBlocks } from './builders/configFiles';\nimport { buildJqueryBlocks, type JqueryEntry } from './builders/jquery';\nimport { buildScopeBlocks, isScopeName, type ScopeEntry } from './builders/scopeBlocks';\nimport { buildSpellCheckerBlocks } from './builders/spellChecker';\nimport { buildStrictBlocks } from './builders/strict';\nimport { ensureConfigStructure } from './utils/ensureConfigStructure';\nimport { ensureDependencies } from './utils/ensureDependencies';\nimport { filterSilencedRules } from './utils/silenceRules';\n\ninterface CommonEntryFields {\n ignores?: string[];\n globals?: Linter.Globals;\n}\n\ninterface JsEntry extends CommonEntryFields {\n files: string[];\n}\n\ninterface TsEntry extends CommonEntryFields {\n files: string[];\n tsconfigPaths: string[];\n}\n\ninterface VueEntry extends CommonEntryFields {\n files: string[];\n globalComponents?: string[];\n}\n\ninterface VueTsEntry extends CommonEntryFields {\n files: string[];\n globalComponents?: string[];\n tsconfigPaths: string[];\n}\n\ninterface TestEntry extends CommonEntryFields {\n files: string[];\n}\n\ninterface SpellCheckerOptions {\n ignoredPaths?: string[];\n customWordListFile?: string;\n}\n\nexport interface Config {\n ignores?: string[];\n\n javascript?: JsEntry[];\n javascriptDom?: JsEntry[];\n javascriptNode?: JsEntry[];\n typescript?: TsEntry[];\n typescriptDom?: TsEntry[];\n typescriptNode?: TsEntry[];\n vue2?: VueEntry[];\n vue3?: VueEntry[];\n vue2Typescript?: VueTsEntry[];\n vue3Typescript?: VueTsEntry[];\n vue2OptionsApi?: VueEntry[];\n vue3OptionsApi?: VueEntry[];\n vue2TypescriptOptionsApi?: VueTsEntry[];\n vue3TypescriptOptionsApi?: VueTsEntry[];\n jest?: TestEntry[];\n vitest?: TestEntry[];\n jsdocBasedTs?: TsEntry[];\n\n jquery?: JqueryEntry[];\n strict?: boolean | string[];\n spellChecker?: SpellCheckerOptions;\n silenceRules?: string[];\n configFiles?: false | string[];\n}\n\nconst reservedKeys = new Set([\n 'ignores',\n 'jquery',\n 'strict',\n 'spellChecker',\n 'silenceRules',\n 'configFiles',\n]);\n\nconst removedKeys = new Map<string, string>([\n [\n 'config',\n 'use `configFiles: string[]` instead. Its block is emitted after every scope block, so'\n + ' the overrides actually win; the `config` scope was emitted before them and was silently'\n + ' overridden for any file a language scope also matched.',\n ],\n]);\n\nfunction assertNoRemovedKeys (config: Config): void {\n const used = Object.keys(config).filter(key => removedKeys.has(key));\n\n if (used.length === 0) {\n return;\n }\n\n const details = used.map(key => ` - \\`${key}\\`: ${removedKeys.get(key)}`).join('\\n');\n\n throw new Error(`@useinsider/eslint-config: removed option(s) passed to useInsider():\\n${details}`);\n}\n\nfunction isProcessableScopeKey (key: string): boolean {\n return !reservedKeys.has(key) && isScopeName(key);\n}\n\nasync function collectScopeBlocks (config: Config): Promise<Linter.Config[]> {\n const scopeKeys = Object.keys(config).filter(isProcessableScopeKey);\n\n return scopeKeys.reduce<Promise<Linter.Config[]>>(async (accumulator, key) => {\n const blocks = await accumulator;\n const entries = config[key as keyof Config] as ScopeEntry[] | undefined;\n\n if (!entries || entries.length === 0) {\n return blocks;\n }\n\n const scopeBlocks = await buildScopeBlocks(key as Parameters<typeof buildScopeBlocks>[0], entries);\n\n return [...blocks, ...scopeBlocks];\n }, Promise.resolve([]));\n}\n\nfunction applySilenceRules (blocks: Linter.Config[], perCallSilence: string[] | undefined): void {\n blocks.forEach(block => {\n if (!block.rules) {\n return;\n }\n\n block.rules = perCallSilence\n ? filterSilencedRules(block.rules, perCallSilence)\n : filterSilencedRules(block.rules);\n });\n}\n\n/** Resolved return type of {@link useInsider}: a Promise of an ESLint flat-config array. */\nexport type UseInsiderResult = Promise<Linter.Config[]>;\n\nfunction buildIgnoresBlocks (ignores: string[] | undefined): Linter.Config[] {\n if (!ignores || ignores.length === 0) {\n return [];\n }\n\n return [{ ignores: [...ignores] }];\n}\n\nexport async function useInsider (config: Config): UseInsiderResult {\n assertNoRemovedKeys(config);\n ensureConfigStructure();\n await ensureDependencies();\n\n const ignoresBlocks = buildIgnoresBlocks(config.ignores);\n const scopeBlocks = await collectScopeBlocks(config);\n const jqueryBlocks = buildJqueryBlocks(config.jquery);\n const configFilesBlocks = buildConfigFilesBlocks(config.configFiles);\n const strictBlocks = await buildStrictBlocks(config.strict);\n const spellCheckerBlocks = buildSpellCheckerBlocks(config.spellChecker);\n\n const blocks: Linter.Config[] = [\n ...ignoresBlocks,\n ...scopeBlocks,\n ...jqueryBlocks,\n ...configFilesBlocks,\n ...strictBlocks,\n ...spellCheckerBlocks,\n ];\n\n applySilenceRules(blocks, config.silenceRules);\n\n return defineConfig(blocks);\n}\n\nexport { merge } from './utils/merge';\n\nexport { mergeAll } from './utils/mergeAllConfig';\n\nexport { silenceDependencyWarning } from './utils/ensureDependencies';\n\nexport { ensureConfigStructure } from './utils/ensureConfigStructure';\n\nexport { silenceRules } from './utils/silenceRules';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"main.js","names":[],"sources":["../src/builders/configFiles.ts","../src/builders/jquery.ts","../src/builders/typescript.ts","../src/builders/vue.ts","../src/builders/scopeBlocks.ts","../src/builders/spellChecker.ts","../src/builders/strict.ts","../src/utils/ensureConfigStructure.ts","../src/utils/ensureDependencies.ts","../src/utils/silenceRules.ts","../src/main.ts"],"sourcesContent":["import type { Linter } from 'eslint';\n\n/**\n * Default globs treated as \"configuration files\" — root-level `*.config.*`\n * variants plus the canonical `eslint.config.*` flat-config filenames.\n *\n * These are intentionally conservative: only the top-level matches that the\n * v1 `config` preset was overwhelmingly applied to. Project-specific paths\n * (subdirectories, custom names) must be opted into by passing them via the\n * `configFiles: string[]` knob.\n */\nexport const DEFAULT_CONFIG_FILE_GLOBS: readonly string[] = [\n '*.config.{ts,mts,cts,js,mjs,cjs}',\n '*.config.*.{ts,mts,cts,js,mjs,cjs}',\n 'eslint.config.{mjs,js,cjs,ts}',\n];\n\nconst configFileRules: Linter.RulesRecord = {\n 'import-x/no-extraneous-dependencies': 'off',\n 'import-x/prefer-default-export': 'off',\n};\n\nfunction mergeGlobs (userGlobs: readonly string[]): string[] {\n const seen = new Set<string>();\n const merged: string[] = [];\n\n DEFAULT_CONFIG_FILE_GLOBS.forEach(glob => {\n if (!seen.has(glob)) {\n seen.add(glob);\n merged.push(glob);\n }\n });\n\n userGlobs.forEach(glob => {\n if (!seen.has(glob)) {\n seen.add(glob);\n merged.push(glob);\n }\n });\n\n return merged;\n}\n\nexport function buildConfigFilesBlocks (value: false | string[] | undefined): Linter.Config[] {\n if (value === false) {\n return [];\n }\n\n const files = value === undefined ? [...DEFAULT_CONFIG_FILE_GLOBS] : mergeGlobs(value);\n\n return [{\n files,\n rules: { ...configFileRules },\n }];\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('buildConfigFilesBlocks', () => {\n test('returns one default-only block when value is undefined (defaults on by default)', () => {\n const blocks = buildConfigFilesBlocks(undefined);\n\n expect(blocks).toHaveLength(1);\n expect(blocks[0].files).toStrictEqual([...DEFAULT_CONFIG_FILE_GLOBS]);\n expect(blocks[0].rules).toStrictEqual({\n 'import-x/no-extraneous-dependencies': 'off',\n 'import-x/prefer-default-export': 'off',\n });\n });\n\n test('returns empty array when value is false (explicit opt-out)', () => {\n expect(buildConfigFilesBlocks(false)).toStrictEqual([]);\n });\n\n test('returns one block with merged globs when value is a string[]', () => {\n const blocks = buildConfigFilesBlocks(['my.special.ts']);\n\n expect(blocks).toHaveLength(1);\n expect(blocks[0].files).toStrictEqual([\n ...DEFAULT_CONFIG_FILE_GLOBS,\n 'my.special.ts',\n ]);\n });\n\n test('user globs append after defaults in given order', () => {\n const blocks = buildConfigFilesBlocks(['z.ts', 'a.ts']);\n\n expect(blocks[0].files).toStrictEqual([\n ...DEFAULT_CONFIG_FILE_GLOBS,\n 'z.ts',\n 'a.ts',\n ]);\n });\n\n test('dedupes user globs that overlap with defaults', () => {\n const blocks = buildConfigFilesBlocks([\n '*.config.{ts,mts,cts,js,mjs,cjs}',\n 'extra.ts',\n ]);\n\n expect(blocks[0].files).toStrictEqual([\n ...DEFAULT_CONFIG_FILE_GLOBS,\n 'extra.ts',\n ]);\n });\n\n test('empty user list still emits default-only block', () => {\n const blocks = buildConfigFilesBlocks([]);\n\n expect(blocks).toHaveLength(1);\n expect(blocks[0].files).toStrictEqual([...DEFAULT_CONFIG_FILE_GLOBS]);\n });\n\n test('block has no languageOptions / parserOptions — only files + rules', () => {\n const [block] = buildConfigFilesBlocks(undefined);\n\n expect(block.languageOptions).toBeUndefined();\n expect(Object.keys(block)).toStrictEqual(['files', 'rules']);\n });\n });\n}\n","import type { Linter } from 'eslint';\nimport globals from 'globals';\n\nexport interface JqueryEntry {\n files: string[];\n}\n\nexport function buildJqueryBlocks (entries: readonly JqueryEntry[] | undefined): Linter.Config[] {\n if (!entries || entries.length === 0) {\n return [];\n }\n\n return entries.map(entry => ({\n files: entry.files,\n languageOptions: {\n globals: { ...globals.jquery },\n },\n }));\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('buildJqueryBlocks', () => {\n test('returns empty array when entries is undefined', () => {\n expect(buildJqueryBlocks(undefined)).toStrictEqual([]);\n });\n\n test('returns empty array when entries is empty', () => {\n expect(buildJqueryBlocks([])).toStrictEqual([]);\n });\n\n test('emits one block per entry with files set', () => {\n const blocks = buildJqueryBlocks([\n { files: ['a/**/*.js'] },\n { files: ['b/**/*.js'] },\n ]);\n\n expect(blocks).toHaveLength(2);\n expect(blocks[0].files).toStrictEqual(['a/**/*.js']);\n expect(blocks[1].files).toStrictEqual(['b/**/*.js']);\n });\n\n test('block exposes jquery globals ($, jQuery)', () => {\n const [block] = buildJqueryBlocks([{ files: ['legacy/**/*.js'] }]);\n const blockGlobals = block.languageOptions?.globals as Record<string, unknown>;\n\n expect(blockGlobals.$).toBeDefined();\n expect(blockGlobals.jQuery).toBeDefined();\n });\n });\n}\n","import type { Linter } from 'eslint';\n\ninterface TypescriptOverrideInput {\n tsconfigPaths: string[];\n}\n\ninterface ParserOptionsLike {\n project?: string[];\n tsconfigRootDir?: string;\n [key: string]: unknown;\n}\n\nexport function applyTypescriptOverrides<BlockType extends Linter.Config> (\n block: BlockType,\n input: TypescriptOverrideInput\n): BlockType {\n const languageOptions = block.languageOptions ?? {};\n const parserOptions = (languageOptions.parserOptions ?? {}) as ParserOptionsLike;\n\n parserOptions.project = input.tsconfigPaths;\n parserOptions.tsconfigRootDir = process.cwd();\n languageOptions.parserOptions = parserOptions;\n block.languageOptions = languageOptions;\n\n return block;\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('applyTypescriptOverrides', () => {\n test('sets parserOptions.project and tsconfigRootDir on a bare block', () => {\n const block: Linter.Config = {};\n\n applyTypescriptOverrides(block, { tsconfigPaths: ['tsconfig.lib.json'] });\n\n expect(block.languageOptions?.parserOptions).toMatchObject({\n project: ['tsconfig.lib.json'],\n tsconfigRootDir: process.cwd(),\n });\n });\n\n test('preserves existing parserOptions keys', () => {\n const block: Linter.Config = {\n languageOptions: {\n parserOptions: {\n sourceType: 'module',\n ecmaFeatures: { jsx: true },\n },\n },\n };\n\n applyTypescriptOverrides(block, { tsconfigPaths: ['tsconfig.json'] });\n\n expect(block.languageOptions?.parserOptions).toMatchObject({\n sourceType: 'module',\n ecmaFeatures: { jsx: true },\n project: ['tsconfig.json'],\n tsconfigRootDir: process.cwd(),\n });\n });\n\n test('overwrites any pre-existing project array', () => {\n const block: Linter.Config = {\n languageOptions: {\n parserOptions: {\n project: ['old.json'],\n },\n },\n };\n\n applyTypescriptOverrides(block, { tsconfigPaths: ['new.json'] });\n\n const parserOptions = block.languageOptions?.parserOptions as ParserOptionsLike;\n\n expect(parserOptions.project).toStrictEqual(['new.json']);\n });\n });\n}\n","import type { Linter } from 'eslint';\n\ninterface VueOverrideInput {\n globalComponents?: string[];\n}\n\nexport function escapeRegex (input: string): string {\n return input.replace(/[\\\\^$.*+?()[\\]{}|]/g, '\\\\$&');\n}\n\nexport function anchoredEscapeRegex (input: string): string {\n return `^${escapeRegex(input)}$`;\n}\n\nexport function applyVueOverrides<BlockType extends Linter.Config> (\n block: BlockType,\n input: VueOverrideInput\n): BlockType {\n if (!input.globalComponents || input.globalComponents.length === 0) {\n return block;\n }\n\n const rules = block.rules ?? {};\n\n rules['vue/no-undef-components'] = ['error', {\n ignorePatterns: input.globalComponents.map(anchoredEscapeRegex),\n }];\n block.rules = rules;\n\n return block;\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('escapeRegex', () => {\n test.each([\n ['plain', 'plain'],\n ['a.b', 'a\\\\.b'],\n ['$Foo', '\\\\$Foo'],\n ['a+b', 'a\\\\+b'],\n ['(x)', '\\\\(x\\\\)'],\n ['[a]', '\\\\[a\\\\]'],\n ['{a}', '\\\\{a\\\\}'],\n ['a|b', 'a\\\\|b'],\n ['a^b', 'a\\\\^b'],\n ['a*b', 'a\\\\*b'],\n ['a?b', 'a\\\\?b'],\n ['a\\\\b', 'a\\\\\\\\b'],\n ])('escapes %s → %s', (input, expected) => {\n expect(escapeRegex(input)).toBe(expected);\n });\n });\n\n describe('anchoredEscapeRegex', () => {\n test('wraps escaped output with ^…$', () => {\n expect(anchoredEscapeRegex('RouterLink')).toBe('^RouterLink$');\n expect(anchoredEscapeRegex('My.Component')).toBe('^My\\\\.Component$');\n });\n });\n\n describe('applyVueOverrides', () => {\n test('is a no-op when globalComponents is missing', () => {\n const block: Linter.Config = { rules: { 'vue/no-undef-components': ['error'] } };\n\n applyVueOverrides(block, {});\n\n expect(block.rules?.['vue/no-undef-components']).toStrictEqual(['error']);\n });\n\n test('is a no-op when globalComponents is empty', () => {\n const block: Linter.Config = { rules: { 'vue/no-undef-components': ['error'] } };\n\n applyVueOverrides(block, { globalComponents: [] });\n\n expect(block.rules?.['vue/no-undef-components']).toStrictEqual(['error']);\n });\n\n test('overrides rule with anchored escaped patterns', () => {\n const block: Linter.Config = {};\n\n applyVueOverrides(block, { globalComponents: ['RouterLink', 'NuxtLink'] });\n\n expect(block.rules?.['vue/no-undef-components']).toStrictEqual([\n 'error',\n { ignorePatterns: ['^RouterLink$', '^NuxtLink$'] },\n ]);\n });\n });\n}\n","import type { Linter } from 'eslint';\nimport { applyTypescriptOverrides } from './typescript';\nimport { applyVueOverrides } from './vue';\n\nexport interface CommonEntryFields {\n ignores?: string[];\n globals?: Linter.Globals;\n}\n\nexport interface JsEntry extends CommonEntryFields {\n files: string[];\n}\n\nexport interface TsEntry extends CommonEntryFields {\n files: string[];\n tsconfigPaths: string[];\n}\n\nexport interface VueEntry extends CommonEntryFields {\n files: string[];\n globalComponents?: string[];\n}\n\nexport interface VueTsEntry extends CommonEntryFields {\n files: string[];\n globalComponents?: string[];\n tsconfigPaths: string[];\n}\n\nexport interface TestEntry extends CommonEntryFields {\n files: string[];\n}\n\nexport type ScopeEntry = JsEntry | TsEntry | VueEntry | VueTsEntry | TestEntry;\n\nconst scopeMap = {\n async javascript () {\n return import('../configs/javascript');\n },\n async javascriptDom () {\n return import('../configs/javascript-dom');\n },\n async javascriptNode () {\n return import('../configs/javascript-node');\n },\n async typescript () {\n return import('../configs/typescript');\n },\n async typescriptDom () {\n return import('../configs/typescript-dom');\n },\n async typescriptNode () {\n return import('../configs/typescript-node');\n },\n async vue2 () {\n return import('../configs/vue2');\n },\n async vue3 () {\n return import('../configs/vue3');\n },\n async vue2Typescript () {\n return import('../configs/vue2-typescript');\n },\n async vue3Typescript () {\n return import('../configs/vue3-typescript');\n },\n async vue2OptionsApi () {\n return import('../configs/vue2-options-api');\n },\n async vue3OptionsApi () {\n return import('../configs/vue3-options-api');\n },\n async vue2TypescriptOptionsApi () {\n return import('../configs/vue2-typescript-options-api');\n },\n async vue3TypescriptOptionsApi () {\n return import('../configs/vue3-typescript-options-api');\n },\n async jest () {\n return import('../configs/jest');\n },\n async vitest () {\n return import('../configs/vitest');\n },\n async jsdocBasedTs () {\n return import('../configs/jsdoc-based-ts');\n },\n} satisfies Record<string, () => Promise<{ default: Linter.Config }>>;\n\nexport type ScopeName = keyof typeof scopeMap;\n\nconst tsScopes = new Set<ScopeName>([\n 'typescript',\n 'typescriptDom',\n 'typescriptNode',\n 'vue2Typescript',\n 'vue3Typescript',\n 'vue2TypescriptOptionsApi',\n 'vue3TypescriptOptionsApi',\n 'jsdocBasedTs',\n]);\n\nconst vueScopes = new Set<ScopeName>([\n 'vue2',\n 'vue3',\n 'vue2Typescript',\n 'vue3Typescript',\n 'vue2OptionsApi',\n 'vue3OptionsApi',\n 'vue2TypescriptOptionsApi',\n 'vue3TypescriptOptionsApi',\n]);\n\ntype LanguageOptions = NonNullable<Linter.Config['languageOptions']>;\n\nfunction cloneLanguageOptions (languageOptions: LanguageOptions): LanguageOptions {\n const clone: LanguageOptions = { ...languageOptions };\n\n if (languageOptions.parserOptions) {\n clone.parserOptions = { ...languageOptions.parserOptions };\n }\n\n if (languageOptions.globals) {\n clone.globals = { ...languageOptions.globals };\n }\n\n return clone;\n}\n\n// ESLint's flat-config schema validates every OWN key, so `languageOptions: undefined`\n// fails with `Key \"languageOptions\": Expected an object.` — absent keys must stay absent.\nfunction clonePreset (preset: Linter.Config): Linter.Config {\n const { languageOptions, rules, settings, ...rest } = preset;\n const clone: Linter.Config = { ...rest };\n\n if (languageOptions) {\n clone.languageOptions = cloneLanguageOptions(languageOptions);\n }\n\n if (rules) {\n clone.rules = { ...rules };\n }\n\n if (settings) {\n clone.settings = { ...settings };\n }\n\n return clone;\n}\n\nexport function isScopeName (key: string): key is ScopeName {\n return Object.keys(scopeMap).includes(key);\n}\n\nfunction applyCommonEntryFields (block: Linter.Config, entry: CommonEntryFields): void {\n if (entry.ignores && entry.ignores.length > 0) {\n block.ignores = [...entry.ignores];\n }\n\n if (entry.globals) {\n const languageOptions = block.languageOptions ?? {};\n const existingGlobals = languageOptions.globals ?? {};\n\n languageOptions.globals = { ...existingGlobals, ...entry.globals };\n block.languageOptions = languageOptions;\n }\n}\n\nexport async function buildScopeBlocks (\n scope: ScopeName,\n entries: readonly ScopeEntry[]\n): Promise<Linter.Config[]> {\n const preset = (await scopeMap[scope]()).default;\n\n return entries.map(entry => {\n const block = clonePreset(preset);\n\n block.files = entry.files;\n\n if (tsScopes.has(scope)) {\n const { tsconfigPaths } = entry as TsEntry;\n\n if (tsconfigPaths) {\n applyTypescriptOverrides(block, { tsconfigPaths });\n }\n }\n\n if (vueScopes.has(scope)) {\n const { globalComponents } = entry as VueEntry;\n\n applyVueOverrides(block, { globalComponents });\n }\n\n applyCommonEntryFields(block, entry);\n\n return block;\n });\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n interface ParserOptionsShape {\n project?: string[];\n tsconfigRootDir?: string;\n [key: string]: unknown;\n }\n\n describe('buildScopeBlocks — ignores', () => {\n test('sets block.ignores when entry.ignores is non-empty', async () => {\n const [block] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['scripts/**/*.js'],\n ignores: ['scripts/legacy/**'],\n },\n ]);\n\n expect(block.ignores).toStrictEqual(['scripts/legacy/**']);\n });\n\n test('omits block.ignores when entry.ignores is missing', async () => {\n const [block] = await buildScopeBlocks('javascriptNode', [\n { files: ['scripts/**/*.js'] },\n ]);\n\n expect(block.ignores).toBeUndefined();\n });\n\n test('omits block.ignores when entry.ignores is an empty array', async () => {\n const [block] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['scripts/**/*.js'],\n ignores: [],\n },\n ]);\n\n expect(block.ignores).toBeUndefined();\n });\n\n test('copies the ignores array (caller mutation does not leak into block)', async () => {\n const ignores = ['scripts/legacy/**'];\n const [block] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['scripts/**/*.js'],\n ignores,\n },\n ]);\n\n ignores.push('mutated/**');\n\n expect(block.ignores).toStrictEqual(['scripts/legacy/**']);\n });\n });\n\n describe('buildScopeBlocks — globals', () => {\n test('merges user globals on top of preset globals (user wins on conflict)', async () => {\n const [block] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['**/*.js'],\n globals: { MyCustomGlobal: 'readonly' },\n },\n ]);\n\n const blockGlobals = block.languageOptions?.globals as Record<string, unknown>;\n\n expect(blockGlobals.MyCustomGlobal).toBe('readonly');\n });\n\n test('preserves preset globals when entry.globals is absent', async () => {\n const [withUserGlobals] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['**/*.js'],\n globals: { MyCustomGlobal: 'readonly' },\n },\n ]);\n const [withoutUserGlobals] = await buildScopeBlocks('javascriptNode', [\n { files: ['**/*.js'] },\n ]);\n\n const presetGlobals = withoutUserGlobals.languageOptions?.globals as Record<string, unknown>;\n const mergedGlobals = withUserGlobals.languageOptions?.globals as Record<string, unknown>;\n\n Object.keys(presetGlobals).forEach(key => {\n expect(mergedGlobals[key]).toBe(presetGlobals[key]);\n });\n });\n\n test('user globals override preset globals on key conflict', async () => {\n const [presetBlock] = await buildScopeBlocks('javascriptNode', [\n { files: ['**/*.js'] },\n ]);\n const presetGlobals = presetBlock.languageOptions?.globals as Record<string, unknown>;\n const [sharedKey] = Object.keys(presetGlobals);\n\n const [overrideBlock] = await buildScopeBlocks('javascriptNode', [\n {\n files: ['**/*.js'],\n globals: { [sharedKey]: 'writable' },\n },\n ]);\n const overrideGlobals = overrideBlock.languageOptions?.globals as Record<string, unknown>;\n\n expect(overrideGlobals[sharedKey]).toBe('writable');\n });\n });\n\n describe('buildScopeBlocks — ignores + typescript override interaction', () => {\n test('TS scope: ignores and tsconfigPaths both applied, ignores not overwritten', async () => {\n const [block] = await buildScopeBlocks('typescriptNode', [\n {\n files: ['*.ts', 'scripts/**/*.ts', 'e2e/**/*.ts'],\n tsconfigPaths: ['./tsconfig.node.json'],\n ignores: ['e2e/**/*.test.ts', 'scripts/design-system.d.ts'],\n },\n ]);\n\n const parserOptions = block.languageOptions?.parserOptions as ParserOptionsShape;\n\n expect(parserOptions.project).toStrictEqual(['./tsconfig.node.json']);\n expect(block.ignores).toStrictEqual([\n 'e2e/**/*.test.ts',\n 'scripts/design-system.d.ts',\n ]);\n });\n });\n\n describe('buildScopeBlocks — ignores + vue override interaction', () => {\n test('Vue scope: ignores and globalComponents both applied', async () => {\n const [block] = await buildScopeBlocks('vue3', [\n {\n files: ['src/**/*.vue'],\n globalComponents: ['RouterLink'],\n ignores: ['src/legacy/**/*.vue'],\n },\n ]);\n\n expect(block.ignores).toStrictEqual(['src/legacy/**/*.vue']);\n expect(block.rules?.['vue/no-undef-components']).toStrictEqual([\n 'error',\n { ignorePatterns: ['^RouterLink$'] },\n ]);\n });\n });\n\n describe('buildScopeBlocks — no undefined-valued own keys', () => {\n const scopeNames = Object.keys(scopeMap) as ScopeName[];\n\n test.each(scopeNames)('%s block exposes no own key set to undefined', async scope => {\n const entry = {\n files: ['src/**/*'],\n tsconfigPaths: ['./tsconfig.json'],\n };\n const [block] = await buildScopeBlocks(scope, [entry]);\n\n const undefinedKeys = Object.entries(block)\n .filter(([, value]) => value === undefined)\n .map(([key]) => key);\n\n expect(undefinedKeys).toStrictEqual([]);\n });\n\n test('a preset declaring neither key keeps both absent instead of undefined', async () => {\n const [block] = await buildScopeBlocks('vitest', [{ files: ['**/*.spec.js'] }]);\n\n expect('languageOptions' in block).toBe(false);\n expect('settings' in block).toBe(false);\n });\n\n test('every preset that declares languageOptions leaves no undefined key inside it', async () => {\n const inspected: string[] = [];\n\n await Promise.all(scopeNames.map(async scope => {\n const [block] = await buildScopeBlocks(scope, [{\n files: ['src/**/*'],\n tsconfigPaths: ['./tsconfig.json'],\n }]);\n\n if (!block.languageOptions) {\n return;\n }\n\n inspected.push(scope);\n\n const undefinedKeys = Object.entries(block.languageOptions)\n .filter(([, value]) => value === undefined)\n .map(([key]) => key);\n\n expect(undefinedKeys).toStrictEqual([]);\n }));\n\n expect(inspected.length).toBeGreaterThan(0);\n });\n });\n\n describe('buildScopeBlocks — real-world repro (MB-2937 / mindbehind-fe)', () => {\n test('typescriptNode entry with design-system.d.ts in ignores emits matching block.ignores', async () => {\n const [, secondBlock] = await buildScopeBlocks('typescriptNode', [\n {\n files: ['e2e/**/*.test.ts'],\n tsconfigPaths: ['./tsconfig.node.json'],\n },\n {\n files: ['*.ts', 'scripts/**/*.ts', 'e2e/**/*.ts'],\n tsconfigPaths: ['./tsconfig.node.json'],\n ignores: ['e2e/**/*.test.ts', 'scripts/design-system.d.ts'],\n },\n ]);\n\n expect(secondBlock.files).toStrictEqual(['*.ts', 'scripts/**/*.ts', 'e2e/**/*.ts']);\n expect(secondBlock.ignores).toContain('scripts/design-system.d.ts');\n expect(secondBlock.ignores).toContain('e2e/**/*.test.ts');\n });\n });\n}\n","import type { Linter } from 'eslint';\n\ninterface SpellCheckerInput {\n ignoredPaths?: string[];\n customWordListFile?: string;\n}\n\nexport function buildSpellCheckerBlocks (input: SpellCheckerInput | undefined): Linter.Config[] {\n if (!input) {\n return [];\n }\n\n const blocks: Linter.Config[] = [];\n\n if (input.customWordListFile) {\n blocks.push({\n rules: {\n '@cspell/spellchecker': ['error', { customWordListFile: input.customWordListFile }],\n },\n });\n }\n\n if (input.ignoredPaths && input.ignoredPaths.length > 0) {\n blocks.push({\n files: input.ignoredPaths,\n rules: {\n '@cspell/spellchecker': 'off',\n },\n });\n }\n\n return blocks;\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('buildSpellCheckerBlocks', () => {\n test('returns empty array when input is undefined', () => {\n expect(buildSpellCheckerBlocks(undefined)).toStrictEqual([]);\n });\n\n test('returns empty array when input has neither option', () => {\n expect(buildSpellCheckerBlocks({})).toStrictEqual([]);\n });\n\n test('returns customWordListFile block alone', () => {\n const blocks = buildSpellCheckerBlocks({ customWordListFile: './words.json' });\n\n expect(blocks).toStrictEqual([\n {\n rules: {\n '@cspell/spellchecker': ['error', { customWordListFile: './words.json' }],\n },\n },\n ]);\n });\n\n test('returns ignoredPaths off block alone', () => {\n const blocks = buildSpellCheckerBlocks({ ignoredPaths: ['vendor/**'] });\n\n expect(blocks).toStrictEqual([\n {\n files: ['vendor/**'],\n rules: { '@cspell/spellchecker': 'off' },\n },\n ]);\n });\n\n test('emits customWordListFile first then ignoredPaths so ignore wins on overlap', () => {\n const blocks = buildSpellCheckerBlocks({\n customWordListFile: './cspell.json',\n ignoredPaths: ['vendor/**', 'dist/**'],\n });\n\n expect(blocks).toHaveLength(2);\n expect(blocks[0].rules?.['@cspell/spellchecker']).toStrictEqual([\n 'error',\n { customWordListFile: './cspell.json' },\n ]);\n expect(blocks[1].files).toStrictEqual(['vendor/**', 'dist/**']);\n expect(blocks[1].rules?.['@cspell/spellchecker']).toBe('off');\n });\n\n test('ignores empty ignoredPaths array', () => {\n const blocks = buildSpellCheckerBlocks({ ignoredPaths: [] });\n\n expect(blocks).toStrictEqual([]);\n });\n });\n}\n","import type { Linter } from 'eslint';\n\nexport async function buildStrictBlocks (value: boolean | string[] | undefined): Promise<Linter.Config[]> {\n if (!value) {\n return [];\n }\n\n const isFileScoped = Array.isArray(value);\n\n if (isFileScoped && value.length === 0) {\n return [];\n }\n\n const { default: strictConfig } = await import('../configs/strict');\n const clone: Linter.Config = { ...strictConfig };\n\n if (clone.rules) {\n clone.rules = { ...clone.rules };\n }\n\n if (isFileScoped) {\n clone.files = value;\n } else {\n delete clone.files;\n }\n\n return [clone];\n}\n","import { readFileSync, realpathSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, resolve as resolvePath } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { AST_NODE_TYPES, type TSESTree } from '@typescript-eslint/types';\n\nexport interface ConfigStructureViolation {\n line: number;\n column: number;\n reason: string;\n}\n\nexport interface ValidateProgramStructureOptions {\n isInsiderImportSource?: (source: string, configPath: string) => boolean;\n}\n\nconst CONFIG_FILE_REGEX = /eslint\\.config\\.[mc]?[jt]s(?:$|\\?)/;\nconst OPT_OUT_ENV = 'INSIDER_SKIP_CONFIG_VALIDATION';\nconst FILE_URL_PREFIX = 'file://';\nconst TARGET_CALLEE_NAME = 'useInsider';\nconst INSIDER_IMPORT_SOURCE = '@useinsider/eslint-config';\nlet bypassWarnEmitted = false;\n\nfunction computeSelfModulePath (): string {\n const raw = fileURLToPath(import.meta.url);\n\n try {\n return realpathSync(raw);\n } catch {\n return raw;\n }\n}\n\nconst SELF_MODULE_PATH = computeSelfModulePath();\n\nfunction isPathSpecifier (source: string): boolean {\n return source.startsWith('.') || source.startsWith('/');\n}\n\nexport function defaultIsInsiderImportSource (source: string, configPath: string): boolean {\n if (source === INSIDER_IMPORT_SOURCE) {\n return true;\n }\n\n if (!configPath || !isPathSpecifier(source)) {\n return false;\n }\n\n try {\n const absolute = resolvePath(dirname(configPath), source);\n const canonical = realpathSync(absolute);\n\n return canonical === SELF_MODULE_PATH;\n } catch {\n return false;\n }\n}\n\nfunction locOf (node: TSESTree.Node): { line: number; column: number } {\n return { line: node.loc.start.line, column: node.loc.start.column };\n}\n\nfunction validateCallExpression (\n callExpr: TSESTree.CallExpression\n): ConfigStructureViolation[] {\n const { callee } = callExpr;\n\n if (callee.type !== AST_NODE_TYPES.Identifier || callee.name !== TARGET_CALLEE_NAME) {\n return [{\n ...locOf(callExpr),\n reason: 'Default export must call `useInsider(...)` directly.',\n }];\n }\n\n return [];\n}\n\nfunction validateExportedCall (\n callee: TSESTree.Expression,\n fallbackLoc: { line: number; column: number }\n): ConfigStructureViolation[] {\n let target: TSESTree.Expression = callee;\n\n if (target.type === AST_NODE_TYPES.AwaitExpression) {\n target = target.argument;\n }\n\n if (target.type === AST_NODE_TYPES.CallExpression) {\n return validateCallExpression(target);\n }\n\n if (target.type === AST_NODE_TYPES.ArrayExpression) {\n return [{\n ...locOf(target),\n reason: 'Default export must not wrap `useInsider(...)` in an array. Use the typed options object instead.',\n }];\n }\n\n const isGated = target.type === AST_NODE_TYPES.LogicalExpression\n || target.type === AST_NODE_TYPES.ConditionalExpression;\n\n if (isGated) {\n const reason = [\n 'Default export must not gate `useInsider(...)` behind a logical/conditional expression.',\n 'Use the typed options object instead.',\n ].join(' ');\n\n return [{ ...locOf(target), reason }];\n }\n\n return [{\n ...fallbackLoc,\n reason: 'Default export must be a direct call to `useInsider(...)`.',\n }];\n}\n\ninterface IgnoreClassification {\n type: 'ignore';\n}\n\ninterface ExportClassification {\n type: 'export';\n violations: ConfigStructureViolation[];\n}\n\ninterface DuplicateExportClassification {\n type: 'duplicateExport';\n violation: ConfigStructureViolation;\n}\n\ninterface DisallowedClassification {\n type: 'disallowed';\n violation: ConfigStructureViolation;\n}\n\ntype StatementClassification = DisallowedClassification | DuplicateExportClassification | ExportClassification | IgnoreClassification; // eslint-disable-line @stylistic/max-len -- type alias union; splitting hits operator-linebreak rule.\n\nfunction collectInsiderBindings (\n node: TSESTree.ImportDeclaration,\n bindings: Set<string>,\n isInsiderSource: (source: string) => boolean\n): void {\n if (!isInsiderSource(node.source.value)) {\n return;\n }\n\n node.specifiers.forEach(specifier => {\n bindings.add(specifier.local.name);\n });\n}\n\nfunction classifyExpressionStatement (\n node: TSESTree.ExpressionStatement,\n insiderBindings: ReadonlySet<string>\n): StatementClassification {\n const { expression } = node;\n const isInsiderCall = expression.type === AST_NODE_TYPES.CallExpression\n && expression.callee.type === AST_NODE_TYPES.Identifier\n && insiderBindings.has(expression.callee.name);\n\n if (isInsiderCall) {\n return { type: 'ignore' };\n }\n\n const reason = [\n 'Disallowed top-level statement `ExpressionStatement`.',\n `Only call expressions whose callee was imported from \\`${INSIDER_IMPORT_SOURCE}\\` are allowed at top level.`,\n ].join(' ');\n\n return {\n type: 'disallowed',\n violation: { ...locOf(node), reason },\n };\n}\n\nfunction classifyStatement (\n node: TSESTree.ProgramStatement,\n exportSeen: boolean,\n insiderBindings: ReadonlySet<string>\n): StatementClassification {\n if (node.type === AST_NODE_TYPES.ImportDeclaration || node.type === AST_NODE_TYPES.VariableDeclaration) {\n return { type: 'ignore' };\n }\n\n if (node.type === AST_NODE_TYPES.ExpressionStatement) {\n return classifyExpressionStatement(node, insiderBindings);\n }\n\n if (node.type === AST_NODE_TYPES.ExportDefaultDeclaration) {\n if (exportSeen) {\n return {\n type: 'duplicateExport',\n violation: { ...locOf(node), reason: 'Only one default export is allowed.' },\n };\n }\n\n const declaration = node.declaration as TSESTree.Expression;\n\n return { type: 'export', violations: validateExportedCall(declaration, locOf(node)) };\n }\n\n const reason = [\n `Disallowed top-level statement \\`${node.type}\\`.`,\n 'Only imports, variable declarations, helper calls from `@useinsider/eslint-config`,',\n 'and one `export default useInsider(...)` are allowed.',\n ].join(' ');\n\n return {\n type: 'disallowed',\n violation: { ...locOf(node), reason },\n };\n}\n\nexport function validateProgramStructure (\n program: TSESTree.Program,\n configPath = '',\n options: ValidateProgramStructureOptions = {}\n): ConfigStructureViolation[] {\n const violations: ConfigStructureViolation[] = [];\n const insiderBindings = new Set<string>();\n const resolver = options.isInsiderImportSource ?? defaultIsInsiderImportSource;\n const isInsiderSource = (source: string): boolean => resolver(source, configPath);\n let exportSeen = false;\n\n program.body.forEach(node => {\n if (node.type === AST_NODE_TYPES.ImportDeclaration) {\n collectInsiderBindings(node, insiderBindings, isInsiderSource);\n }\n\n const result = classifyStatement(node, exportSeen, insiderBindings);\n\n if (result.type === 'ignore') {\n return;\n }\n\n if (result.type === 'export') {\n exportSeen = true;\n violations.push(...result.violations);\n\n return;\n }\n\n if (result.type === 'duplicateExport') {\n violations.push(result.violation);\n\n return;\n }\n\n violations.push(result.violation);\n });\n\n if (!exportSeen) {\n violations.push({\n line: 1,\n column: 0,\n reason: 'Missing `export default useInsider(...)`.',\n });\n }\n\n return violations;\n}\n\ninterface CallSiteLike {\n getFileName: (this: void) => string | null;\n}\n\nfunction readStackFrames (): readonly CallSiteLike[] {\n // eslint-disable-next-line @typescript-eslint/unbound-method -- Error.prepareStackTrace is a value slot, not a method call.\n const original = Error.prepareStackTrace;\n\n Error.prepareStackTrace = (_, frames) => frames;\n\n const holder: { stack?: unknown } = {};\n\n Error.captureStackTrace(holder, readStackFrames);\n\n const { stack } = holder;\n\n Error.prepareStackTrace = original;\n\n if (!Array.isArray(stack)) {\n return [];\n }\n\n return stack as readonly CallSiteLike[];\n}\n\nfunction normalizeFrameFilename (raw: string | null): string | null {\n if (!raw) {\n return null;\n }\n\n const withoutPrefix = raw.startsWith(FILE_URL_PREFIX) ? raw.slice(FILE_URL_PREFIX.length) : raw;\n const queryIndex = withoutPrefix.indexOf('?');\n\n return queryIndex === -1 ? withoutPrefix : withoutPrefix.slice(0, queryIndex);\n}\n\nfunction extractMatchesFromStack (frames: readonly CallSiteLike[]): string[] {\n const matches = new Set<string>();\n\n frames.forEach(frame => {\n const name = normalizeFrameFilename(frame.getFileName());\n\n if (name && CONFIG_FILE_REGEX.test(name)) {\n matches.add(name);\n }\n });\n\n return Array.from(matches);\n}\n\nfunction argvCandidateAt (argv: readonly string[], index: number): string | null {\n const arg = argv[index];\n\n if (arg === '--config' || arg === '-c') {\n return argv[index + 1] ?? null;\n }\n\n if (arg.startsWith('--config=')) {\n return arg.slice('--config='.length);\n }\n\n return null;\n}\n\nfunction extractConfigFromArgv (argv: readonly string[]): string | null {\n for (let i = 0; i < argv.length; i += 1) {\n const candidate = argvCandidateAt(argv, i);\n\n if (candidate && CONFIG_FILE_REGEX.test(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\nexport function getConsumerConfigPath (\n frames: readonly CallSiteLike[] = readStackFrames(),\n argv: readonly string[] = process.argv\n): string | null {\n const stackMatches = extractMatchesFromStack(frames);\n\n if (stackMatches.length === 1) {\n return stackMatches[0];\n }\n\n const fromArgv = extractConfigFromArgv(argv);\n\n if (fromArgv) {\n return fromArgv;\n }\n\n return null;\n}\n\nfunction formatReport (configPath: string, violations: readonly ConfigStructureViolation[]): string {\n const header = [\n '@useinsider/eslint-config: protected configuration structure violated in:',\n ` ${configPath}`,\n ].join('\\n');\n const lines = violations.map(violation => ` - [${violation.line}:${violation.column}] ${violation.reason}`);\n const footer = [\n '',\n 'Allowed structure:',\n ' - import statements',\n ' - const/let/var declarations',\n ' - exactly one `export default useInsider({ ... })`',\n '',\n 'Emergency bypass: set INSIDER_SKIP_CONFIG_VALIDATION=true (do not commit).',\n ].join('\\n');\n\n return `${header}\\n${lines.join('\\n')}\\n${footer}`;\n}\n\nfunction emitBypassWarning (): void {\n if (bypassWarnEmitted) {\n return;\n }\n\n bypassWarnEmitted = true;\n\n const message = [\n '@useinsider/eslint-config: structure validation BYPASSED',\n 'via INSIDER_SKIP_CONFIG_VALIDATION — emergency use only.',\n ].join(' ');\n\n // eslint-disable-next-line no-console -- diagnostic surface, mirrors ensureDependencies.ts.\n console.warn(message);\n}\n\nclass ConfigStructureExit extends Error {}\n\nfunction fail (message: string): never {\n // eslint-disable-next-line no-console -- diagnostic surface, mirrors ensureDependencies.ts.\n console.error(message);\n\n const exit: (code: number) => void = process.exit.bind(process);\n\n exit(1);\n\n throw new ConfigStructureExit(message);\n}\n\nfunction readConsumerSource (configPath: string): string {\n try {\n return readFileSync(configPath, 'utf8');\n } catch (readError) {\n const { message } = readError as Error;\n\n return fail(`@useinsider/eslint-config: failed to read consumer config at ${configPath}: ${message}`);\n }\n}\n\nconst lazyRequire = createRequire(import.meta.url);\n\nfunction loadParser (): typeof import('@typescript-eslint/parser').parse {\n const mod = lazyRequire('@typescript-eslint/parser') as typeof import('@typescript-eslint/parser');\n\n return mod.parse;\n}\n\nfunction parseConsumerSource (configPath: string, source: string): TSESTree.Program {\n const parse = loadParser();\n\n try {\n return parse(source, { sourceType: 'module', range: true, loc: true });\n } catch (parseError) {\n const { message } = parseError as Error;\n\n return fail(`@useinsider/eslint-config: failed to parse consumer config at ${configPath}: ${message}`);\n }\n}\n\nfunction runValidation (overridePath?: string | null): void {\n const configPath = overridePath === undefined ? getConsumerConfigPath() : overridePath;\n\n if (!configPath) {\n fail([\n '@useinsider/eslint-config: unable to locate consumer `eslint.config.*` file for structure validation.',\n 'If this is an unexpected environment, set INSIDER_SKIP_CONFIG_VALIDATION=true (do not commit).',\n ].join('\\n'));\n }\n\n const source = readConsumerSource(configPath);\n const program = parseConsumerSource(configPath, source);\n const violations = validateProgramStructure(program, configPath);\n\n if (violations.length === 0) {\n return;\n }\n\n fail(formatReport(configPath, violations));\n}\n\nexport function ensureConfigStructure (overridePath?: string | null): void {\n if (process.env[OPT_OUT_ENV] === 'true') {\n emitBypassWarning();\n\n return;\n }\n\n try {\n runValidation(overridePath);\n } catch (error) {\n if (error instanceof ConfigStructureExit) {\n return;\n }\n\n throw error;\n }\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n function parseSource (source: string): TSESTree.Program {\n return loadParser()(source, { sourceType: 'module', range: true, loc: true });\n }\n\n describe('validateProgramStructure', () => {\n test('accepts `export default await useInsider(...)` (useInsider returns a Promise)', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'export default await useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('rejects await around a non-useInsider call', () => {\n const program = parseSource([\n \"import { other } from 'x';\",\n 'export default await other({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/must call `useInsider/);\n });\n\n test('accepts a valid baseline: import + const + export default useInsider(...)', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n \"const tsconfigPaths = ['./tsconfig.json'];\",\n \"export default useInsider({ typescript: [{ files: ['**/*.ts'], tsconfigPaths }] });\",\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('accepts let / var declarations alongside const', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'let a = 1;',\n 'var b = 2;',\n 'const c = 3;',\n 'export default useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('rejects spread sibling around useInsider() in array', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'export default [...useInsider(), { rules: {} }];',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/must not wrap `useInsider/);\n });\n\n test('rejects array wrapping the useInsider call', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'export default [useInsider()];',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/must not wrap `useInsider/);\n });\n\n test('rejects an extra top-level statement when callee is not from @useinsider/eslint-config', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n \"console.log('boot');\",\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Disallowed top-level statement `ExpressionStatement`/);\n });\n\n test('accepts top-level `silenceDependencyWarning(true);` when imported from @useinsider/eslint-config', () => {\n const program = parseSource([\n \"import { useInsider, silenceDependencyWarning } from '@useinsider/eslint-config';\",\n 'silenceDependencyWarning(true);',\n 'export default await useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('accepts top-level `silenceRules([...]);` when imported from @useinsider/eslint-config', () => {\n const program = parseSource([\n \"import { useInsider, silenceRules } from '@useinsider/eslint-config';\",\n \"silenceRules(['no-unused-vars']);\",\n 'export default await useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('accepts a renamed import binding via `as` alias when calling the alias at top level', () => {\n const program = parseSource([\n \"import { silenceDependencyWarning as silence, useInsider } from '@useinsider/eslint-config';\",\n 'silence(true);',\n 'export default await useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n\n test('rejects top-level call whose callee identifier is imported from a different module', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n \"import { somethingElse } from 'other-package';\",\n 'somethingElse(true);',\n 'export default await useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Disallowed top-level statement `ExpressionStatement`/);\n });\n\n test('rejects multiple exports (named export alongside default)', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'export const foo = 1;',\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations.length).toBeGreaterThanOrEqual(1);\n expect(violations[0].reason).toMatch(/Disallowed top-level statement `ExportNamedDeclaration`/);\n });\n\n test('rejects two default exports — second one flagged as duplicate', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'export default useInsider({});',\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Only one default export/);\n });\n\n test('rejects export default with a non-useInsider callee', () => {\n const program = parseSource([\n \"import { someOther } from 'x';\",\n 'export default someOther({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/must call `useInsider/);\n });\n\n test('rejects export default as a logical expression around the call', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'const flag = true;',\n 'export default flag && useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/logical\\/conditional/);\n });\n\n test('rejects export default as a conditional expression', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'const flag = true;',\n 'export default flag ? useInsider({}) : [];',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/logical\\/conditional/);\n });\n\n test('rejects missing default export', () => {\n const program = parseSource([\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'const a = 1;',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Missing `export default useInsider/);\n });\n\n test('rejects export default of a plain object literal', () => {\n const program = parseSource('export default { rules: {} };');\n const violations = validateProgramStructure(program);\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/direct call to `useInsider/);\n });\n });\n\n describe('validateProgramStructure — self-relative imports', () => {\n const acceptSelfRelative = (source: string, configPath: string): boolean => {\n if (source === INSIDER_IMPORT_SOURCE) {\n return true;\n }\n\n return configPath === '/repo/packages/eslint-config/eslint.config.js'\n && source === './dist/main.js';\n };\n\n test('accepts top-level helper call when relative import resolves to the validator module', () => {\n const program = parseSource([\n \"import { useInsider, silenceDependencyWarning } from './dist/main.js';\",\n 'silenceDependencyWarning(true);',\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(\n program,\n '/repo/packages/eslint-config/eslint.config.js',\n { isInsiderImportSource: acceptSelfRelative }\n );\n\n expect(violations).toStrictEqual([]);\n });\n\n test('rejects top-level call when relative import points to a different module', () => {\n const program = parseSource([\n \"import { silenceDependencyWarning } from './other/path.js';\",\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'silenceDependencyWarning(true);',\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(\n program,\n '/repo/packages/eslint-config/eslint.config.js',\n { isInsiderImportSource: acceptSelfRelative }\n );\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Disallowed top-level statement `ExpressionStatement`/);\n });\n\n test('treats `@useinsider/eslint-config` as Insider regardless of configPath', () => {\n const program = parseSource([\n \"import { useInsider, silenceDependencyWarning } from '@useinsider/eslint-config';\",\n 'silenceDependencyWarning(true);',\n 'export default useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program, '/elsewhere/eslint.config.mjs'))\n .toStrictEqual([]);\n });\n\n test('default resolver: relative path that does not resolve falls back to non-Insider (no crash)', () => {\n const program = parseSource([\n \"import { silenceDependencyWarning } from './does/not/exist.js';\",\n \"import { useInsider } from '@useinsider/eslint-config';\",\n 'silenceDependencyWarning(true);',\n 'export default useInsider({});',\n ].join('\\n'));\n\n const violations = validateProgramStructure(program, '/no/such/eslint.config.js');\n\n expect(violations).toHaveLength(1);\n expect(violations[0].reason).toMatch(/Disallowed top-level statement `ExpressionStatement`/);\n });\n\n test('back-compat: calls without configPath still validate against the canonical source', () => {\n const program = parseSource([\n \"import { useInsider, silenceDependencyWarning } from '@useinsider/eslint-config';\",\n 'silenceDependencyWarning(true);',\n 'export default useInsider({});',\n ].join('\\n'));\n\n expect(validateProgramStructure(program)).toStrictEqual([]);\n });\n });\n\n describe('defaultIsInsiderImportSource', () => {\n test('returns true for the canonical package source', () => {\n expect(defaultIsInsiderImportSource('@useinsider/eslint-config', '')).toBe(true);\n expect(defaultIsInsiderImportSource('@useinsider/eslint-config', '/any/eslint.config.js')).toBe(true);\n });\n\n test('returns false for unrelated package sources', () => {\n expect(defaultIsInsiderImportSource('other-package', '/any/eslint.config.js')).toBe(false);\n });\n\n test('returns false for relative path that cannot be realpath-resolved', () => {\n expect(defaultIsInsiderImportSource('./missing.js', '/does/not/exist/eslint.config.js'))\n .toBe(false);\n });\n\n test('returns false for relative source when configPath is empty', () => {\n expect(defaultIsInsiderImportSource('./dist/main.js', '')).toBe(false);\n });\n });\n\n describe('getConsumerConfigPath', () => {\n test('returns single matching stack frame', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => '/repo/node_modules/@useinsider/eslint-config/dist/main.js' },\n { getFileName: () => '/repo/eslint.config.mjs' },\n { getFileName: () => 'node:internal/process' },\n ];\n\n expect(getConsumerConfigPath(frames, [])).toBe('/repo/eslint.config.mjs');\n });\n\n test('strips ESM cache-buster query string (`?mtime=...`) from stack frame', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => 'file:///repo/eslint.config.mjs?mtime=1779282184336' },\n ];\n\n expect(getConsumerConfigPath(frames, [])).toBe('/repo/eslint.config.mjs');\n });\n\n test('strips file:// prefix from stack frame', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => 'file:///repo/eslint.config.ts' },\n ];\n\n expect(getConsumerConfigPath(frames, [])).toBe('/repo/eslint.config.ts');\n });\n\n test('falls back to argv when stack has 0 matches', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => 'node:internal/process' },\n ];\n const argv = ['node', 'eslint', '--config', '/abs/eslint.config.mjs', 'src'];\n\n expect(getConsumerConfigPath(frames, argv)).toBe('/abs/eslint.config.mjs');\n });\n\n test('falls back to argv when stack has multiple matches', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => '/repo/a/eslint.config.mjs' },\n { getFileName: () => '/repo/b/eslint.config.mjs' },\n ];\n const argv = ['node', 'eslint', '-c', '/cli/eslint.config.cts', 'src'];\n\n expect(getConsumerConfigPath(frames, argv)).toBe('/cli/eslint.config.cts');\n });\n\n test('supports --config=<path> argv form', () => {\n const frames: CallSiteLike[] = [];\n const argv = ['node', 'eslint', '--config=/cli/eslint.config.mts'];\n\n expect(getConsumerConfigPath(frames, argv)).toBe('/cli/eslint.config.mts');\n });\n\n test('returns null when nothing matches in stack or argv', () => {\n const frames: CallSiteLike[] = [\n { getFileName: () => 'node:internal/process' },\n { getFileName: () => null },\n ];\n\n expect(getConsumerConfigPath(frames, ['node', 'eslint', 'src'])).toBeNull();\n });\n\n test('default arguments: stack walk + process.argv probe runs without throwing', () => {\n const result = getConsumerConfigPath();\n\n expect(result === null || typeof result === 'string').toBe(true);\n });\n\n test('detection regex covers all six config extensions', () => {\n const cases = [\n 'eslint.config.js',\n 'eslint.config.mjs',\n 'eslint.config.cjs',\n 'eslint.config.ts',\n 'eslint.config.mts',\n 'eslint.config.cts',\n ];\n\n cases.forEach(name => {\n const frames: CallSiteLike[] = [{ getFileName: () => `/repo/${name}` }];\n\n expect(getConsumerConfigPath(frames, [])).toBe(`/repo/${name}`);\n });\n });\n });\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { installPackage } from '@antfu/install-pkg';\nimport semver from 'semver';\n\nlet turnOff = false;\nlet scanned = false;\nconst silencedDependencies = new Set<string>();\nconst fixedDependencies = [\n 'eslint',\n 'globals',\n];\nconst require = createRequire(import.meta.url);\n\nfunction getPackageJSONPath (currentDir: string) {\n const fileLocation = resolve(currentDir, 'package.json');\n\n if (existsSync(fileLocation)) {\n return fileLocation;\n }\n\n if (currentDir === process.cwd()) {\n throw new Error(`Unable to locate package.json in ${currentDir}`);\n }\n\n const parentDir = resolve(currentDir, '..');\n\n return getPackageJSONPath(parentDir);\n}\n\nfunction getPackageJSON (fileLocation: string) {\n const packageJsonContent = readFileSync(fileLocation, 'utf8');\n\n return JSON.parse(packageJsonContent) as Record<string, Record<string, string>>;\n}\n\nfunction getExpectedDependencies (): Record<string, string> {\n const path = getPackageJSONPath(fileURLToPath(new URL(/* @vite-ignore */ import.meta.url)));\n const packageJson = getPackageJSON(path);\n\n return packageJson.dependencies;\n}\n\nfunction getDependencyVersionFromConsumer (depName: string, baseDir: string = process.cwd()): string | undefined {\n try {\n const pkgJsonPath = require.resolve(`${depName}/package.json`, { paths: [baseDir] });\n const pkgContent = readFileSync(pkgJsonPath, 'utf8');\n const pkg: { version?: string } = JSON.parse(pkgContent) as Record<string, Record<string, string>>;\n\n return pkg.version;\n } catch {\n return undefined;\n }\n}\n\nexport async function ensureDependencies () {\n if (turnOff || scanned) {\n return;\n }\n\n scanned = true;\n\n const expectedDependencies = Object.entries(getExpectedDependencies());\n const missingDependencyEntries = expectedDependencies.filter(\n ([expectedDependency, expectedVersion]) => {\n if (!fixedDependencies.includes(expectedDependency) || silencedDependencies.has(expectedDependency)) {\n return false;\n }\n\n const installedVersion = getDependencyVersionFromConsumer(expectedDependency);\n\n if (!installedVersion) {\n return true;\n }\n\n return semver.lt(installedVersion, expectedVersion);\n }\n );\n\n if (!missingDependencyEntries.length) {\n return;\n }\n\n const missingDependencies = missingDependencyEntries.map(([dependency, version]) => `${dependency}@${version.replace(/^\\W+/, '')}`);\n\n // eslint-disable-next-line no-console\n console.info('Missing dependencies detected:');\n // eslint-disable-next-line no-console\n console.info(missingDependencies.map(dependency => ` - ${dependency}`).join('\\n'));\n // eslint-disable-next-line no-console\n console.info('\\nInstalling dependencies:');\n\n await installPackage(missingDependencies, { dev: true, additionalArgs: ['-E'] });\n\n // eslint-disable-next-line no-process-exit\n process.exit(1);\n}\n\nexport function silenceDependencyWarning (\n /**\n * Pass `true` to silence all dependency warnings.\n */\n all: boolean\n): void;\n\nexport function silenceDependencyWarning (dependencies: string[]): void;\n\nexport function silenceDependencyWarning (input: boolean | string[]) {\n if (typeof input === 'boolean') {\n turnOff = input;\n\n return;\n }\n\n silencedDependencies.clear();\n input.forEach(dependency => silencedDependencies.add(String(dependency)));\n}\n","import { warn } from 'node:console';\nimport type { Linter } from 'eslint';\n\nconst globallySilencedRules = new Set<string>();\n\n/**\n * Silence rules globally.\n * This will affect all configs scopes.\n */\nexport function silenceRules (rules: string[]) {\n rules.forEach(rule => {\n globallySilencedRules.add(rule);\n });\n}\n\nfunction silenceRule (config: Linter.RuleEntry) {\n const [, ...restConfig] = Array.isArray(config) ? config : [];\n\n return ['warn', ...restConfig as Record<string, unknown>[]] as Linter.RuleSeverityAndOptions;\n}\n\nexport function filterSilencedRules (\n rules: Partial<Linter.RulesRecord> | Readonly<Linter.RulesRecord> | undefined,\n silencedRules?: string[] | Set<string>\n) {\n let targetSilencedRules = silencedRules || globallySilencedRules;\n\n if (Array.isArray(targetSilencedRules)) {\n targetSilencedRules = new Set(targetSilencedRules);\n }\n\n if (!rules || !targetSilencedRules.size) {\n return rules;\n }\n\n if (silencedRules) {\n targetSilencedRules.forEach(rule => {\n if (rules[rule]) {\n return;\n }\n\n warn(`Unknown rule \"${rule}\". Please remove it from the silenced rules list.`);\n });\n }\n\n return Object.entries(rules).reduce((newRules, [key, value]) => {\n if (value) {\n newRules[key] = targetSilencedRules.has(key) ? silenceRule(value) : value;\n }\n\n return newRules;\n }, {} as Linter.RulesRecord);\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { vi, describe, test, expect, afterEach } = import.meta.vitest;\n\n const mocks = vi.hoisted(() => ({\n warn: vi.fn(),\n }));\n\n vi.mock('node:console', () => mocks);\n\n afterEach(() => {\n globallySilencedRules.clear();\n });\n\n describe('silenceRules', () => {\n test('should register given rule keys as silenced', () => {\n const rule = '@stylistic/type-generic-spacing';\n\n expect(globallySilencedRules.has(rule)).toBe(false);\n silenceRules([rule]);\n expect(globallySilencedRules.has(rule)).toBe(true);\n });\n });\n\n describe('filterSilencedRules', () => {\n test('should return the same rules object if no rules are silenced', () => {\n const rules = {\n 'no-console': 'warn',\n 'no-debugger': 'warn',\n } as Linter.RulesRecord;\n\n expect(filterSilencedRules(rules)).toBe(rules);\n });\n\n test('should return a new rules object with silenced rules silenced and other rules unchanged', () => {\n silenceRules(['no-console']);\n\n const rules = {\n 'no-console': 'error',\n 'no-debugger': 'warn',\n 'no-alert': 'error',\n } as Linter.RulesRecord;\n\n expect(filterSilencedRules(rules)).toStrictEqual({\n 'no-console': ['warn'],\n 'no-debugger': 'warn',\n 'no-alert': 'error',\n });\n });\n\n test('should return a new rules object with silenced rules silenced and other rules unchanged', () => {\n silenceRules(['no-bitwise', 'new-cap']);\n\n const rules = {\n 'max-len': 'off',\n 'new-cap': ['error'],\n 'no-alert': ['error'],\n 'no-bitwise': ['error', {\n allow: ['~'],\n }],\n } as Linter.RulesRecord;\n\n expect(filterSilencedRules(rules)).toStrictEqual({\n 'max-len': 'off',\n 'new-cap': ['warn'],\n 'no-alert': ['error'],\n 'no-bitwise': ['warn', {\n allow: ['~'],\n }],\n });\n });\n\n test('should not warn about unknown rules', () => {\n silenceRules(['no-console', 'no-bitwise']);\n\n const rules = {\n 'no-console': 'error',\n 'no-debugger': 'warn',\n 'no-alert': 'error',\n } as Linter.RulesRecord;\n\n filterSilencedRules(rules);\n\n expect(mocks.warn).not.toHaveBeenCalled();\n expect(mocks.warn).not.toHaveBeenCalled();\n });\n\n test('should warn about unknown rules', () => {\n const rules = {\n 'no-console': 'error',\n 'no-debugger': 'warn',\n 'no-alert': 'error',\n } as Linter.RulesRecord;\n\n filterSilencedRules(rules, ['no-console', 'no-bitwise']);\n\n expect(mocks.warn).toHaveBeenCalledTimes(1);\n expect(mocks.warn).toHaveBeenCalledWith(\n 'Unknown rule \"no-bitwise\". Please remove it from the silenced rules list.'\n );\n });\n\n test.each([\n [['@typescript-eslint/no-unsafe-call'], ['warn']],\n [new Set(['@typescript-eslint/no-unsafe-call']), ['warn']],\n [[], 'error'],\n ])(\n '#%# should filter scope-related rules and ignore globally silenced rules',\n (silencedRules, expectation) => {\n silenceRules(['@typescript-eslint/no-unsafe-assignment']);\n\n const rules = {\n '@typescript-eslint/no-unsafe-assignment': 'error',\n '@typescript-eslint/no-unsafe-member-access': 'error',\n '@typescript-eslint/no-unsafe-call': 'error',\n } as Linter.RulesRecord;\n\n expect(filterSilencedRules(rules, silencedRules)).toStrictEqual({\n '@typescript-eslint/no-unsafe-assignment': 'error',\n '@typescript-eslint/no-unsafe-member-access': 'error',\n '@typescript-eslint/no-unsafe-call': expectation,\n });\n }\n );\n });\n}\n","import type { Linter } from 'eslint';\nimport { defineConfig } from 'eslint/config';\nimport { buildConfigFilesBlocks } from './builders/configFiles';\nimport { buildJqueryBlocks, type JqueryEntry } from './builders/jquery';\nimport { buildScopeBlocks, isScopeName, type ScopeEntry } from './builders/scopeBlocks';\nimport { buildSpellCheckerBlocks } from './builders/spellChecker';\nimport { buildStrictBlocks } from './builders/strict';\nimport { ensureConfigStructure } from './utils/ensureConfigStructure';\nimport { ensureDependencies } from './utils/ensureDependencies';\nimport { filterSilencedRules } from './utils/silenceRules';\n\ninterface CommonEntryFields {\n ignores?: string[];\n globals?: Linter.Globals;\n}\n\ninterface JsEntry extends CommonEntryFields {\n files: string[];\n}\n\ninterface TsEntry extends CommonEntryFields {\n files: string[];\n tsconfigPaths: string[];\n}\n\ninterface VueEntry extends CommonEntryFields {\n files: string[];\n globalComponents?: string[];\n}\n\ninterface VueTsEntry extends CommonEntryFields {\n files: string[];\n globalComponents?: string[];\n tsconfigPaths: string[];\n}\n\ninterface TestEntry extends CommonEntryFields {\n files: string[];\n}\n\ninterface SpellCheckerOptions {\n ignoredPaths?: string[];\n customWordListFile?: string;\n}\n\nexport interface Config {\n ignores?: string[];\n\n javascript?: JsEntry[];\n javascriptDom?: JsEntry[];\n javascriptNode?: JsEntry[];\n typescript?: TsEntry[];\n typescriptDom?: TsEntry[];\n typescriptNode?: TsEntry[];\n vue2?: VueEntry[];\n vue3?: VueEntry[];\n vue2Typescript?: VueTsEntry[];\n vue3Typescript?: VueTsEntry[];\n vue2OptionsApi?: VueEntry[];\n vue3OptionsApi?: VueEntry[];\n vue2TypescriptOptionsApi?: VueTsEntry[];\n vue3TypescriptOptionsApi?: VueTsEntry[];\n jest?: TestEntry[];\n vitest?: TestEntry[];\n jsdocBasedTs?: TsEntry[];\n\n jquery?: JqueryEntry[];\n strict?: boolean | string[];\n spellChecker?: SpellCheckerOptions;\n silenceRules?: string[];\n configFiles?: false | string[];\n}\n\nconst reservedKeys = new Set([\n 'ignores',\n 'jquery',\n 'strict',\n 'spellChecker',\n 'silenceRules',\n 'configFiles',\n]);\n\nconst removedKeys = new Map<string, string>([\n [\n 'config',\n 'use `configFiles: string[]` instead. Its block is emitted after every scope block, so'\n + ' the overrides actually win; the `config` scope was emitted before them and was silently'\n + ' overridden for any file a language scope also matched.',\n ],\n]);\n\nfunction assertNoRemovedKeys (config: Config): void {\n const used = Object.keys(config).filter(key => removedKeys.has(key));\n\n if (used.length === 0) {\n return;\n }\n\n const details = used.map(key => ` - \\`${key}\\`: ${removedKeys.get(key)}`).join('\\n');\n\n throw new Error(`@useinsider/eslint-config: removed option(s) passed to useInsider():\\n${details}`);\n}\n\nfunction isProcessableScopeKey (key: string): boolean {\n return !reservedKeys.has(key) && isScopeName(key);\n}\n\nasync function collectScopeBlocks (config: Config): Promise<Linter.Config[]> {\n const scopeKeys = Object.keys(config).filter(isProcessableScopeKey);\n\n return scopeKeys.reduce<Promise<Linter.Config[]>>(async (accumulator, key) => {\n const blocks = await accumulator;\n const entries = config[key as keyof Config] as ScopeEntry[] | undefined;\n\n if (!entries || entries.length === 0) {\n return blocks;\n }\n\n const scopeBlocks = await buildScopeBlocks(key as Parameters<typeof buildScopeBlocks>[0], entries);\n\n return [...blocks, ...scopeBlocks];\n }, Promise.resolve([]));\n}\n\nfunction applySilenceRules (blocks: Linter.Config[], perCallSilence: string[] | undefined): void {\n blocks.forEach(block => {\n if (!block.rules) {\n return;\n }\n\n block.rules = perCallSilence\n ? filterSilencedRules(block.rules, perCallSilence)\n : filterSilencedRules(block.rules);\n });\n}\n\n/** Resolved return type of {@link useInsider}: a Promise of an ESLint flat-config array. */\nexport type UseInsiderResult = Promise<Linter.Config[]>;\n\nfunction buildIgnoresBlocks (ignores: string[] | undefined): Linter.Config[] {\n if (!ignores || ignores.length === 0) {\n return [];\n }\n\n return [{ ignores: [...ignores] }];\n}\n\nexport async function useInsider (config: Config): UseInsiderResult {\n assertNoRemovedKeys(config);\n ensureConfigStructure();\n await ensureDependencies();\n\n const ignoresBlocks = buildIgnoresBlocks(config.ignores);\n const scopeBlocks = await collectScopeBlocks(config);\n const jqueryBlocks = buildJqueryBlocks(config.jquery);\n const configFilesBlocks = buildConfigFilesBlocks(config.configFiles);\n const strictBlocks = await buildStrictBlocks(config.strict);\n const spellCheckerBlocks = buildSpellCheckerBlocks(config.spellChecker);\n\n const blocks: Linter.Config[] = [\n ...ignoresBlocks,\n ...scopeBlocks,\n ...jqueryBlocks,\n ...configFilesBlocks,\n ...strictBlocks,\n ...spellCheckerBlocks,\n ];\n\n applySilenceRules(blocks, config.silenceRules);\n\n return defineConfig(blocks);\n}\n\nexport { merge } from './utils/merge';\n\nexport { mergeAll } from './utils/mergeAllConfig';\n\nexport { silenceDependencyWarning } from './utils/ensureDependencies';\n\nexport { ensureConfigStructure } from './utils/ensureConfigStructure';\n\nexport { silenceRules } from './utils/silenceRules';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAWA,IAAa,4BAA+C;CACxD;CACA;CACA;AACH;AAED,IAAM,kBAAsC;CACxC,uCAAuC;CACvC,kCAAkC;AACrC;AAED,SAAS,WAAY,WAA4B;CAC7C,MAAM,uBAAO,IAAI,IAAG;CACpB,MAAM,SAAmB,CAAA;CAEzB,0BAA0B,SAAQ,SAAO;EACrC,IAAI,CAAC,KAAK,IAAI,IAAI,GAAG;GACjB,KAAK,IAAI,IAAI;GACb,OAAO,KAAK,IAAI;EACpB;CACJ,CAAC;CAED,UAAU,SAAQ,SAAO;EACrB,IAAI,CAAC,KAAK,IAAI,IAAI,GAAG;GACjB,KAAK,IAAI,IAAI;GACb,OAAO,KAAK,IAAI;EACpB;CACJ,CAAC;CAED,OAAO;AACX;AAEA,SAAgB,uBAAwB,OAAmC;CACvE,IAAI,UAAU,OACV,OAAO,CAAA;CAKX,OAAO,CAAC;EACJ,OAHU,UAAU,KAAA,IAAY,CAAC,GAAG,yBAAyB,IAAI,WAAW,KAAK;EAIjF,OAAO,EAAE,GAAG,gBAAe;CAC9B,CAAA;AACL;;;AC/CA,SAAgB,kBAAmB,SAA2C;CAC1E,IAAI,CAAC,WAAW,QAAQ,WAAW,GAC/B,OAAO,CAAA;CAGX,OAAO,QAAQ,KAAI,WAAU;EACzB,OAAO,MAAM;EACb,iBAAiB,EACb,SAAS,EAAE,GAAG,QAAQ,OAAM,EAC/B;CACJ,EAAC;AACN;;;ACNA,SAAgB,yBACZ,OACA,OAA8B;CAE9B,MAAM,kBAAkB,MAAM,mBAAmB,CAAA;CACjD,MAAM,gBAAiB,gBAAgB,iBAAiB,CAAA;CAExD,cAAc,UAAU,MAAM;CAC9B,cAAc,kBAAkB,QAAQ,IAAG;CAC3C,gBAAgB,gBAAgB;CAChC,MAAM,kBAAkB;CAExB,OAAO;AACX;;;ACnBA,SAAgB,YAAa,OAAa;CACtC,OAAO,MAAM,QAAQ,uBAAuB,MAAM;AACtD;AAEA,SAAgB,oBAAqB,OAAa;CAC9C,OAAO,IAAI,YAAY,KAAK,EAAC;AACjC;AAEA,SAAgB,kBACZ,OACA,OAAuB;CAEvB,IAAI,CAAC,MAAM,oBAAoB,MAAM,iBAAiB,WAAW,GAC7D,OAAO;CAGX,MAAM,QAAQ,MAAM,SAAS,CAAA;CAE7B,MAAM,6BAA6B,CAAC,SAAS,EACzC,gBAAgB,MAAM,iBAAiB,IAAI,mBAAmB,EACjE,CAAA;CACD,MAAM,QAAQ;CAEd,OAAO;AACX;;;ACKA,IAAM,WAAW;CACb,MAAM,aAAU;EACZ,OAAO,OAAO;CAClB;CACA,MAAM,gBAAa;EACf,OAAO,OAAO;CAClB;CACA,MAAM,iBAAc;EAChB,OAAO,OAAO;CAClB;CACA,MAAM,aAAU;EACZ,OAAO,OAAO;CAClB;CACA,MAAM,gBAAa;EACf,OAAO,OAAO;CAClB;CACA,MAAM,iBAAc;EAChB,OAAO,OAAO;CAClB;CACA,MAAM,OAAI;EACN,OAAO,OAAO;CAClB;CACA,MAAM,OAAI;EACN,OAAO,OAAO;CAClB;CACA,MAAM,iBAAc;EAChB,OAAO,OAAO;CAClB;CACA,MAAM,iBAAc;EAChB,OAAO,OAAO;CAClB;CACA,MAAM,iBAAc;EAChB,OAAO,OAAO;CAClB;CACA,MAAM,iBAAc;EAChB,OAAO,OAAO;CAClB;CACA,MAAM,2BAAwB;EAC1B,OAAO,OAAO;CAClB;CACA,MAAM,2BAAwB;EAC1B,OAAO,OAAO;CAClB;CACA,MAAM,OAAI;EACN,OAAO,OAAO;CAClB;CACA,MAAM,SAAM;EACR,OAAO,OAAO;CAClB;CACA,MAAM,eAAY;EACd,OAAO,OAAO;CAClB;AACiE;AAIrE,IAAM,2BAAW,IAAI,IAAe;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACH,CAAA;AAED,IAAM,4BAAY,IAAI,IAAe;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACH,CAAA;AAID,SAAS,qBAAsB,iBAAgC;CAC3D,MAAM,QAAyB,EAAE,GAAG,gBAAe;CAEnD,IAAI,gBAAgB,eAChB,MAAM,gBAAgB,EAAE,GAAG,gBAAgB,cAAa;CAG5D,IAAI,gBAAgB,SAChB,MAAM,UAAU,EAAE,GAAG,gBAAgB,QAAO;CAGhD,OAAO;AACX;AAIA,SAAS,YAAa,QAAqB;CACvC,MAAM,EAAE,iBAAiB,OAAO,UAAU,GAAG,SAAS;CACtD,MAAM,QAAuB,EAAE,GAAG,KAAI;CAEtC,IAAI,iBACA,MAAM,kBAAkB,qBAAqB,eAAe;CAGhE,IAAI,OACA,MAAM,QAAQ,EAAE,GAAG,MAAK;CAG5B,IAAI,UACA,MAAM,WAAW,EAAE,GAAG,SAAQ;CAGlC,OAAO;AACX;AAEA,SAAgB,YAAa,KAAW;CACpC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,SAAS,GAAG;AAC7C;AAEA,SAAS,uBAAwB,OAAsB,OAAwB;CAC3E,IAAI,MAAM,WAAW,MAAM,QAAQ,SAAS,GACxC,MAAM,UAAU,CAAC,GAAG,MAAM,OAAO;CAGrC,IAAI,MAAM,SAAS;EACf,MAAM,kBAAkB,MAAM,mBAAmB,CAAA;EAGjD,gBAAgB,UAAU;GAAE,GAFJ,gBAAgB,WAAW,CAAA;GAEH,GAAG,MAAM;EAAO;EAChE,MAAM,kBAAkB;CAC5B;AACJ;AAEA,eAAsB,iBAClB,OACA,SAA8B;CAE9B,MAAM,UAAU,MAAM,SAAS,MAAM,CAAA,EAAA,CAAI;CAEzC,OAAO,QAAQ,KAAI,UAAQ;EACvB,MAAM,QAAQ,YAAY,MAAM;EAEhC,MAAM,QAAQ,MAAM;EAEpB,IAAI,SAAS,IAAI,KAAK,GAAG;GACrB,MAAM,EAAE,kBAAkB;GAE1B,IAAI,eACA,yBAAyB,OAAO,EAAE,cAAa,CAAE;EAEzD;EAEA,IAAI,UAAU,IAAI,KAAK,GAAG;GACtB,MAAM,EAAE,qBAAqB;GAE7B,kBAAkB,OAAO,EAAE,iBAAgB,CAAE;EACjD;EAEA,uBAAuB,OAAO,KAAK;EAEnC,OAAO;CACX,CAAC;AACL;;;AC9LA,SAAgB,wBAAyB,OAAoC;CACzE,IAAI,CAAC,OACD,OAAO,CAAA;CAGX,MAAM,SAA0B,CAAA;CAEhC,IAAI,MAAM,oBACN,OAAO,KAAK,EACR,OAAO,EACH,wBAAwB,CAAC,SAAS,EAAE,oBAAoB,MAAM,mBAAkB,CAAE,EACrF,EACJ,CAAA;CAGL,IAAI,MAAM,gBAAgB,MAAM,aAAa,SAAS,GAClD,OAAO,KAAK;EACR,OAAO,MAAM;EACb,OAAO,EACH,wBAAwB,MAC3B;CACJ,CAAA;CAGL,OAAO;AACX;;;AC9BA,eAAsB,kBAAmB,OAAqC;CAC1E,IAAI,CAAC,OACD,OAAO,CAAA;CAGX,MAAM,eAAe,MAAM,QAAQ,KAAK;CAExC,IAAI,gBAAgB,MAAM,WAAW,GACjC,OAAO,CAAA;CAGX,MAAM,EAAE,SAAS,iBAAiB,MAAM,OAAO;CAC/C,MAAM,QAAuB,EAAE,GAAG,aAAY;CAE9C,IAAI,MAAM,OACN,MAAM,QAAQ,EAAE,GAAG,MAAM,MAAK;CAGlC,IAAI,cACA,MAAM,QAAQ;MAEd,OAAO,MAAM;CAGjB,OAAO,CAAC,KAAK;AACjB;;;ACXA,IAAM,oBAAoB;AAC1B,IAAM,cAAc;AACpB,IAAM,kBAAkB;AACxB,IAAM,qBAAqB;AAC3B,IAAM,wBAAwB;AAC9B,IAAI,oBAAoB;AAExB,SAAS,wBAAqB;CAC1B,MAAM,MAAM,cAAc,YAAY,GAAG;CAEzC,IAAI;EACA,OAAO,aAAa,GAAG;CAC3B,QAAQ;EACJ,OAAO;CACX;AACJ;AAEA,IAAM,mBAAmB,sBAAqB;AAE9C,SAAS,gBAAiB,QAAc;CACpC,OAAO,OAAO,WAAW,GAAG,KAAK,OAAO,WAAW,GAAG;AAC1D;AAEA,SAAgB,6BAA8B,QAAgB,YAAkB;CAC5E,IAAI,WAAW,uBACX,OAAO;CAGX,IAAI,CAAC,cAAc,CAAC,gBAAgB,MAAM,GACtC,OAAO;CAGX,IAAI;EACA,MAAM,WAAW,QAAY,QAAQ,UAAU,GAAG,MAAM;EAGxD,OAFkB,aAAa,QAExB,MAAc;CACzB,QAAQ;EACJ,OAAO;CACX;AACJ;AAEA,SAAS,MAAO,MAAmB;CAC/B,OAAO;EAAE,MAAM,KAAK,IAAI,MAAM;EAAM,QAAQ,KAAK,IAAI,MAAM;CAAM;AACrE;AAEA,SAAS,uBACL,UAAiC;CAEjC,MAAM,EAAE,WAAW;CAEnB,IAAI,OAAO,SAAS,eAAe,cAAc,OAAO,SAAS,oBAC7D,OAAO,CAAC;EACJ,GAAG,MAAM,QAAQ;EACjB,QAAQ;CACX,CAAA;CAGL,OAAO,CAAA;AACX;AAEA,SAAS,qBACL,QACA,aAA6C;CAE7C,IAAI,SAA8B;CAElC,IAAI,OAAO,SAAS,eAAe,iBAC/B,SAAS,OAAO;CAGpB,IAAI,OAAO,SAAS,eAAe,gBAC/B,OAAO,uBAAuB,MAAM;CAGxC,IAAI,OAAO,SAAS,eAAe,iBAC/B,OAAO,CAAC;EACJ,GAAG,MAAM,MAAM;EACf,QAAQ;CACX,CAAA;CAML,IAHgB,OAAO,SAAS,eAAe,qBACxC,OAAO,SAAS,eAAe,uBAEzB;EACT,MAAM,SAAS,CACX,2FACA,uCACH,CAAA,CAAC,KAAK,GAAG;EAEV,OAAO,CAAC;GAAE,GAAG,MAAM,MAAM;GAAG;EAAM,CAAE;CACxC;CAEA,OAAO,CAAC;EACJ,GAAG;EACH,QAAQ;CACX,CAAA;AACL;AAuBA,SAAS,uBACL,MACA,UACA,iBAA4C;CAE5C,IAAI,CAAC,gBAAgB,KAAK,OAAO,KAAK,GAClC;CAGJ,KAAK,WAAW,SAAQ,cAAY;EAChC,SAAS,IAAI,UAAU,MAAM,IAAI;CACrC,CAAC;AACL;AAEA,SAAS,4BACL,MACA,iBAAoC;CAEpC,MAAM,EAAE,eAAe;CAKvB,IAJsB,WAAW,SAAS,eAAe,kBAClD,WAAW,OAAO,SAAS,eAAe,cAC1C,gBAAgB,IAAI,WAAW,OAAO,IAAI,GAG7C,OAAO,EAAE,MAAM,SAAQ;CAG3B,MAAM,SAAS,CACX,yDACA,0DAA0D,sBAAqB,6BAClF,CAAA,CAAC,KAAK,GAAG;CAEV,OAAO;EACH,MAAM;EACN,WAAW;GAAE,GAAG,MAAM,IAAI;GAAG;EAAM;CACtC;AACL;AAEA,SAAS,kBACL,MACA,YACA,iBAAoC;CAEpC,IAAI,KAAK,SAAS,eAAe,qBAAqB,KAAK,SAAS,eAAe,qBAC/E,OAAO,EAAE,MAAM,SAAQ;CAG3B,IAAI,KAAK,SAAS,eAAe,qBAC7B,OAAO,4BAA4B,MAAM,eAAe;CAG5D,IAAI,KAAK,SAAS,eAAe,0BAA0B;EACvD,IAAI,YACA,OAAO;GACH,MAAM;GACN,WAAW;IAAE,GAAG,MAAM,IAAI;IAAG,QAAQ;GAAqC;EAC7E;EAGL,MAAM,cAAc,KAAK;EAEzB,OAAO;GAAE,MAAM;GAAU,YAAY,qBAAqB,aAAa,MAAM,IAAI,CAAC;EAAC;CACvF;CAEA,MAAM,SAAS;EACX,oCAAoC,KAAK,KAAI;EAC7C;EACA;CACH,CAAA,CAAC,KAAK,GAAG;CAEV,OAAO;EACH,MAAM;EACN,WAAW;GAAE,GAAG,MAAM,IAAI;GAAG;EAAM;CACtC;AACL;AAEA,SAAgB,yBACZ,SACA,aAAa,IACb,UAA2C,CAAA,GAAE;CAE7C,MAAM,aAAyC,CAAA;CAC/C,MAAM,kCAAkB,IAAI,IAAG;CAC/B,MAAM,WAAW,QAAQ,yBAAyB;CAClD,MAAM,mBAAmB,WAA4B,SAAS,QAAQ,UAAU;CAChF,IAAI,aAAa;CAEjB,QAAQ,KAAK,SAAQ,SAAO;EACxB,IAAI,KAAK,SAAS,eAAe,mBAC7B,uBAAuB,MAAM,iBAAiB,eAAe;EAGjE,MAAM,SAAS,kBAAkB,MAAM,YAAY,eAAe;EAElE,IAAI,OAAO,SAAS,UAChB;EAGJ,IAAI,OAAO,SAAS,UAAU;GAC1B,aAAa;GACb,WAAW,KAAK,GAAG,OAAO,UAAU;GAEpC;EACJ;EAEA,IAAI,OAAO,SAAS,mBAAmB;GACnC,WAAW,KAAK,OAAO,SAAS;GAEhC;EACJ;EAEA,WAAW,KAAK,OAAO,SAAS;CACpC,CAAC;CAED,IAAI,CAAC,YACD,WAAW,KAAK;EACZ,MAAM;EACN,QAAQ;EACR,QAAQ;CACX,CAAA;CAGL,OAAO;AACX;AAMA,SAAS,kBAAe;CAEpB,MAAM,WAAW,MAAM;CAEvB,MAAM,qBAAqB,GAAG,WAAW;CAEzC,MAAM,SAA8B,CAAA;CAEpC,MAAM,kBAAkB,QAAQ,eAAe;CAE/C,MAAM,EAAE,UAAU;CAElB,MAAM,oBAAoB;CAE1B,IAAI,CAAC,MAAM,QAAQ,KAAK,GACpB,OAAO,CAAA;CAGX,OAAO;AACX;AAEA,SAAS,uBAAwB,KAAkB;CAC/C,IAAI,CAAC,KACD,OAAO;CAGX,MAAM,gBAAgB,IAAI,WAAW,eAAe,IAAI,IAAI,MAAM,CAAsB,IAAI;CAC5F,MAAM,aAAa,cAAc,QAAQ,GAAG;CAE5C,OAAO,eAAe,KAAK,gBAAgB,cAAc,MAAM,GAAG,UAAU;AAChF;AAEA,SAAS,wBAAyB,QAA+B;CAC7D,MAAM,0BAAU,IAAI,IAAG;CAEvB,OAAO,SAAQ,UAAQ;EACnB,MAAM,OAAO,uBAAuB,MAAM,YAAW,CAAE;EAEvD,IAAI,QAAQ,kBAAkB,KAAK,IAAI,GACnC,QAAQ,IAAI,IAAI;CAExB,CAAC;CAED,OAAO,MAAM,KAAK,OAAO;AAC7B;AAEA,SAAS,gBAAiB,MAAyB,OAAa;CAC5D,MAAM,MAAM,KAAK;CAEjB,IAAI,QAAQ,cAAc,QAAQ,MAC9B,OAAO,KAAK,QAAQ,MAAM;CAG9B,IAAI,IAAI,WAAW,WAAW,GAC1B,OAAO,IAAI,MAAM,CAAkB;CAGvC,OAAO;AACX;AAEA,SAAS,sBAAuB,MAAuB;CACnD,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;EACrC,MAAM,YAAY,gBAAgB,MAAM,CAAC;EAEzC,IAAI,aAAa,kBAAkB,KAAK,SAAS,GAC7C,OAAO;CAEf;CAEA,OAAO;AACX;AAEA,SAAgB,sBACZ,SAAkC,gBAAe,GACjD,OAA0B,QAAQ,MAAI;CAEtC,MAAM,eAAe,wBAAwB,MAAM;CAEnD,IAAI,aAAa,WAAW,GACxB,OAAO,aAAa;CAGxB,MAAM,WAAW,sBAAsB,IAAI;CAE3C,IAAI,UACA,OAAO;CAGX,OAAO;AACX;AAEA,SAAS,aAAc,YAAoB,YAA+C;CACtF,MAAM,SAAS,CACX,6EACA,KAAK,YACR,CAAA,CAAC,KAAK,IAAI;CACX,MAAM,QAAQ,WAAW,KAAI,cAAa,QAAQ,UAAU,KAAI,GAAI,UAAU,OAAM,IAAK,UAAU,QAAQ;CAC3G,MAAM,SAAS;EACX;EACA;EACA;EACA;EACA;EACA;EACA;CACH,CAAA,CAAC,KAAK,IAAI;CAEX,OAAO,GAAG,OAAM,IAAK,MAAM,KAAK,IAAI,EAAC,IAAK;AAC9C;AAEA,SAAS,oBAAiB;CACtB,IAAI,mBACA;CAGJ,oBAAoB;CAEpB,MAAM,UAAU,CACZ,4DACA,0DACH,CAAA,CAAC,KAAK,GAAG;CAGV,QAAQ,KAAK,OAAO;AACxB;AAEA,IAAM,sBAAN,cAAkC,MAAK,CAAG;AAE1C,SAAS,KAAM,SAAe;CAE1B,QAAQ,MAAM,OAAO;CAIrB,QAF6C,KAAK,KAAK,OAEvD,CAAA,CAAK,CAAC;CAEN,MAAM,IAAI,oBAAoB,OAAO;AACzC;AAEA,SAAS,mBAAoB,YAAkB;CAC3C,IAAI;EACA,OAAO,aAAa,YAAY,MAAM;CAC1C,SAAS,WAAW;EAChB,MAAM,EAAE,YAAY;EAEpB,OAAO,KAAK,gEAAgE,WAAU,IAAK,SAAS;CACxG;AACJ;AAEA,IAAM,cAAc,cAAc,YAAY,GAAG;AAEjD,SAAS,aAAU;CAGf,OAFY,YAAY,2BAEjB,CAAA,CAAI;AACf;AAEA,SAAS,oBAAqB,YAAoB,QAAc;CAC5D,MAAM,QAAQ,WAAU;CAExB,IAAI;EACA,OAAO,MAAM,QAAQ;GAAE,YAAY;GAAU,OAAO;GAAM,KAAK;EAAI,CAAE;CACzE,SAAS,YAAY;EACjB,MAAM,EAAE,YAAY;EAEpB,OAAO,KAAK,iEAAiE,WAAU,IAAK,SAAS;CACzG;AACJ;AAEA,SAAS,cAAe,cAA4B;CAChD,MAAM,aAAa,iBAAiB,KAAA,IAAY,sBAAqB,IAAK;CAE1E,IAAI,CAAC,YACD,KAAK,CACD,yGACA,gGACH,CAAA,CAAC,KAAK,IAAI,CAAC;CAKhB,MAAM,aAAa,yBADH,oBAAoB,YADrB,mBAAmB,UACc,CACJ,GAAS,UAAU;CAE/D,IAAI,WAAW,WAAW,GACtB;CAGJ,KAAK,aAAa,YAAY,UAAU,CAAC;AAC7C;AAEA,SAAgB,sBAAuB,cAA4B;CAC/D,IAAI,QAAQ,IAAI,iBAAiB,QAAQ;EACrC,kBAAiB;EAEjB;CACJ;CAEA,IAAI;EACA,cAAc,YAAY;CAC9B,SAAS,OAAO;EACZ,IAAI,iBAAiB,qBACjB;EAGJ,MAAM;CACV;AACJ;;;ACjdA,IAAI,UAAU;AACd,IAAI,UAAU;AACd,IAAM,uCAAuB,IAAI,IAAG;AACpC,IAAM,oBAAoB,CACtB,UACA,SACH;AACD,IAAM,UAAU,cAAc,YAAY,GAAG;AAE7C,SAAS,mBAAoB,YAAkB;CAC3C,MAAM,eAAe,QAAQ,YAAY,cAAc;CAEvD,IAAI,WAAW,YAAY,GACvB,OAAO;CAGX,IAAI,eAAe,QAAQ,IAAG,GAC1B,MAAM,IAAI,MAAM,oCAAoC,YAAY;CAKpE,OAAO,mBAFW,QAAQ,YAAY,IAEZ,CAAS;AACvC;AAEA,SAAS,eAAgB,cAAoB;CACzC,MAAM,qBAAqB,aAAa,cAAc,MAAM;CAE5D,OAAO,KAAK,MAAM,kBAAkB;AACxC;AAEA,SAAS,0BAAuB;CAI5B,OAFoB,eADP,mBAAmB,cAAc,IAAI;;EAAuB,YAAY;CAAG,CAAC,CACtD,CAE5B,CAAA,CAAY;AACvB;AAEA,SAAS,iCAAkC,SAAiB,UAAkB,QAAQ,IAAG,GAAE;CACvF,IAAI;EACA,MAAM,cAAc,QAAQ,QAAQ,GAAG,QAAO,gBAAiB,EAAE,OAAO,CAAC,OAAO,EAAC,CAAE;EACnF,MAAM,aAAa,aAAa,aAAa,MAAM;EAGnD,OAFkC,KAAK,MAAM,UAEtC,CAAA,CAAI;CACf,QAAQ;EACJ;CACJ;AACJ;AAEA,eAAsB,qBAAkB;CACpC,IAAI,WAAW,SACX;CAGJ,UAAU;CAGV,MAAM,2BADuB,OAAO,QAAQ,wBAAuB,CAClC,CAAA,CAAqB,QACjD,CAAC,oBAAoB,qBAAoB;EACtC,IAAI,CAAC,kBAAkB,SAAS,kBAAkB,KAAK,qBAAqB,IAAI,kBAAkB,GAC9F,OAAO;EAGX,MAAM,mBAAmB,iCAAiC,kBAAkB;EAE5E,IAAI,CAAC,kBACD,OAAO;EAGX,OAAO,OAAO,GAAG,kBAAkB,eAAe;CACtD,CAAC;CAGL,IAAI,CAAC,yBAAyB,QAC1B;CAGJ,MAAM,sBAAsB,yBAAyB,KAAK,CAAC,YAAY,aAAa,GAAG,WAAU,GAAI,QAAQ,QAAQ,QAAQ,EAAE,GAAG;CAGlI,QAAQ,KAAK,gCAAgC;CAE7C,QAAQ,KAAK,oBAAoB,KAAI,eAAc,OAAO,YAAY,CAAC,CAAC,KAAK,IAAI,CAAC;CAElF,QAAQ,KAAK,4BAA4B;CAEzC,MAAM,eAAe,qBAAqB;EAAE,KAAK;EAAM,gBAAgB,CAAC,IAAI;CAAC,CAAE;CAG/E,QAAQ,KAAK,CAAC;AAClB;AAWA,SAAgB,yBAA0B,OAAyB;CAC/D,IAAI,OAAO,UAAU,WAAW;EAC5B,UAAU;EAEV;CACJ;CAEA,qBAAqB,MAAK;CAC1B,MAAM,SAAQ,eAAc,qBAAqB,IAAI,OAAO,UAAU,CAAC,CAAC;AAC5E;;;ACnHA,IAAM,wCAAwB,IAAI,IAAG;;;;;AAMrC,SAAgB,aAAc,OAAe;CACzC,MAAM,SAAQ,SAAO;EACjB,sBAAsB,IAAI,IAAI;CAClC,CAAC;AACL;AAEA,SAAS,YAAa,QAAwB;CAC1C,MAAM,GAAG,GAAG,cAAc,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAA;CAE3D,OAAO,CAAC,QAAQ,GAAG,UAAuC;AAC9D;AAEA,SAAgB,oBACZ,OACA,eAAsC;CAEtC,IAAI,sBAAsB,iBAAiB;CAE3C,IAAI,MAAM,QAAQ,mBAAmB,GACjC,sBAAsB,IAAI,IAAI,mBAAmB;CAGrD,IAAI,CAAC,SAAS,CAAC,oBAAoB,MAC/B,OAAO;CAGX,IAAI,eACA,oBAAoB,SAAQ,SAAO;EAC/B,IAAI,MAAM,OACN;EAGJ,KAAK,iBAAiB,KAAI,kDAAmD;CACjF,CAAC;CAGL,OAAO,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ,UAAU,CAAC,KAAK,WAAU;EAC3D,IAAI,OACA,SAAS,OAAO,oBAAoB,IAAI,GAAG,IAAI,YAAY,KAAK,IAAI;EAGxE,OAAO;CACX,GAAG,CAAA,CAAwB;AAC/B;;;ACqBA,IAAM,+BAAe,IAAI,IAAI;CACzB;CACA;CACA;CACA;CACA;CACA;AACH,CAAA;AAED,IAAM,8BAAc,IAAI,IAAoB,CACxC,CACI,UACA,sOAGH,CACJ,CAAA;AAED,SAAS,oBAAqB,QAAc;CACxC,MAAM,OAAO,OAAO,KAAK,MAAM,CAAC,CAAC,QAAO,QAAO,YAAY,IAAI,GAAG,CAAC;CAEnE,IAAI,KAAK,WAAW,GAChB;CAGJ,MAAM,UAAU,KAAK,KAAI,QAAO,SAAS,IAAG,MAAO,YAAY,IAAI,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI;CAEpF,MAAM,IAAI,MAAM,yEAAyE,SAAS;AACtG;AAEA,SAAS,sBAAuB,KAAW;CACvC,OAAO,CAAC,aAAa,IAAI,GAAG,KAAK,YAAY,GAAG;AACpD;AAEA,eAAe,mBAAoB,QAAc;CAG7C,OAFkB,OAAO,KAAK,MAAM,CAAC,CAAC,OAAO,qBAEtC,CAAA,CAAU,OAAiC,OAAO,aAAa,QAAO;EACzE,MAAM,SAAS,MAAM;EACrB,MAAM,UAAU,OAAO;EAEvB,IAAI,CAAC,WAAW,QAAQ,WAAW,GAC/B,OAAO;EAGX,MAAM,cAAc,MAAM,iBAAiB,KAA+C,OAAO;EAEjG,OAAO,CAAC,GAAG,QAAQ,GAAG,WAAW;CACrC,GAAG,QAAQ,QAAQ,CAAA,CAAE,CAAC;AAC1B;AAEA,SAAS,kBAAmB,QAAyB,gBAAoC;CACrF,OAAO,SAAQ,UAAQ;EACnB,IAAI,CAAC,MAAM,OACP;EAGJ,MAAM,QAAQ,iBACR,oBAAoB,MAAM,OAAO,cAAc,IAC/C,oBAAoB,MAAM,KAAK;CACzC,CAAC;AACL;AAKA,SAAS,mBAAoB,SAA6B;CACtD,IAAI,CAAC,WAAW,QAAQ,WAAW,GAC/B,OAAO,CAAA;CAGX,OAAO,CAAC,EAAE,SAAS,CAAC,GAAG,OAAO,EAAC,CAAE;AACrC;AAEA,eAAsB,WAAY,QAAc;CAC5C,oBAAoB,MAAM;CAC1B,sBAAqB;CACrB,MAAM,mBAAkB;CAExB,MAAM,gBAAgB,mBAAmB,OAAO,OAAO;CACvD,MAAM,cAAc,MAAM,mBAAmB,MAAM;CACnD,MAAM,eAAe,kBAAkB,OAAO,MAAM;CACpD,MAAM,oBAAoB,uBAAuB,OAAO,WAAW;CACnE,MAAM,eAAe,MAAM,kBAAkB,OAAO,MAAM;CAC1D,MAAM,qBAAqB,wBAAwB,OAAO,YAAY;CAEtE,MAAM,SAA0B;EAC5B,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;CACN;CAED,kBAAkB,QAAQ,OAAO,YAAY;CAE7C,OAAO,aAAa,MAAM;AAC9B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region ../utils/dist/index.js
|
|
2
|
-
var UNSAFE_KEYS = new Set([
|
|
2
|
+
var UNSAFE_KEYS = /* @__PURE__ */ new Set([
|
|
3
3
|
"__proto__",
|
|
4
4
|
"constructor",
|
|
5
5
|
"prototype",
|
|
@@ -22,7 +22,7 @@ function isMergeableObject(value) {
|
|
|
22
22
|
}
|
|
23
23
|
function combineArrays(options, previous, next) {
|
|
24
24
|
if (!options.mergeArrays) return next;
|
|
25
|
-
return options.uniqueArrayItems ? [
|
|
25
|
+
return options.uniqueArrayItems ? [.../* @__PURE__ */ new Set([...previous, ...next])] : [...previous, ...next];
|
|
26
26
|
}
|
|
27
27
|
function mergeSources(options, sources) {
|
|
28
28
|
return sources.reduce((result, source) => {
|
|
@@ -64,4 +64,4 @@ Object.defineProperty(exports, "merge", {
|
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
//# sourceMappingURL=merge-
|
|
67
|
+
//# sourceMappingURL=merge-DJQU2y2U.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-DJQU2y2U.cjs","names":[],"sources":["../../utils/dist/index.js","../src/utils/merge.ts"],"sourcesContent":["//#region src/merge.ts\nvar UNSAFE_KEYS = /* @__PURE__ */ new Set([\n\t\"__proto__\",\n\t\"constructor\",\n\t\"prototype\",\n\t\"toString\",\n\t\"valueOf\",\n\t\"hasOwnProperty\",\n\t\"isPrototypeOf\",\n\t\"propertyIsEnumerable\",\n\t\"toLocaleString\"\n]);\nvar defaultOptions = {\n\tallowUndefinedOverrides: true,\n\tmergeArrays: true,\n\tuniqueArrayItems: true\n};\nfunction isMergeableObject(value) {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst prototype = Object.getPrototypeOf(value);\n\treturn prototype === Object.prototype || prototype === null;\n}\nfunction combineArrays(options, previous, next) {\n\tif (!options.mergeArrays) return next;\n\treturn options.uniqueArrayItems ? [.../* @__PURE__ */ new Set([...previous, ...next])] : [...previous, ...next];\n}\nfunction mergeSources(options, sources) {\n\treturn sources.reduce((result, source) => {\n\t\tif (Array.isArray(source)) throw new TypeError(\"Arguments provided to merge must be objects, not arrays.\");\n\t\tObject.keys(source).forEach((key) => {\n\t\t\tif (UNSAFE_KEYS.has(key)) return;\n\t\t\tconst previous = result[key];\n\t\t\tconst next = source[key];\n\t\t\tif (Array.isArray(previous) && Array.isArray(next)) result[key] = combineArrays(options, previous, next);\n\t\t\telse if (isMergeableObject(previous) && isMergeableObject(next)) result[key] = mergeSources(options, [previous, next]);\n\t\t\telse result[key] = next === void 0 && !options.allowUndefinedOverrides ? previous : next;\n\t\t});\n\t\treturn result;\n\t}, {});\n}\n/**\n* Recursively merges `sources` left to right into a fresh object, skipping keys that would\n* poison `Object.prototype`.\n* @example\n* deepMerge({ branches: ['main'] }, { branches: ['beta'] });\n* // => { branches: ['main', 'beta'] }\n*/\nfunction deepMerge(...sources) {\n\treturn mergeSources(defaultOptions, sources);\n}\n/**\n* {@link deepMerge} with the merge strategy tuned per call.\n* @example\n* deepMergeWithOptions({ mergeArrays: false }, { branches: ['main'] }, { branches: ['beta'] });\n* // => { branches: ['beta'] }\n* @param options Only the fields being overridden; every omitted field keeps its default.\n*/\nfunction deepMergeWithOptions(options, ...sources) {\n\treturn mergeSources({\n\t\t...defaultOptions,\n\t\t...options\n\t}, sources);\n}\n//#endregion\nexport { deepMerge, deepMergeWithOptions };\n\n//# sourceMappingURL=index.js.map","// eslint-disable-next-line import-x/no-extraneous-dependencies -- private workspace package, bundled into dist/ at build time; a runtime dependency entry would be unresolvable for consumers\nimport { deepMergeWithOptions } from '@useinsider/utils';\n\nexport function merge<Arguments extends NonNullable<unknown>[]> (...args: Arguments): Arguments[number] {\n return deepMergeWithOptions({ mergeArrays: false }, ...args);\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('merge', () => {\n test('later arguments override earlier ones on overlapping keys', () => {\n const result = merge(\n { rules: { 'no-console': 'error' } },\n { rules: { 'no-console': 'off' } }\n );\n\n expect(result).toStrictEqual({ rules: { 'no-console': 'off' } });\n });\n\n test('precedence holds across three or more arguments', () => {\n const result = merge(\n { rules: { indent: 'off' } },\n { rules: { indent: 'warn' } },\n { rules: { indent: 'error' } }\n );\n\n expect(result).toStrictEqual({ rules: { indent: 'error' } });\n });\n\n test('non-overlapping keys from every argument are preserved', () => {\n const result = merge(\n { rules: { 'no-console': 'error' } },\n { rules: { 'no-debugger': 'warn' } },\n { languageOptions: { ecmaVersion: 2022 } }\n );\n\n expect(result).toStrictEqual({\n rules: {\n 'no-console': 'error',\n 'no-debugger': 'warn',\n },\n languageOptions: { ecmaVersion: 2022 },\n });\n });\n\n test('rule option arrays are replaced wholesale, never concatenated', () => {\n const result = merge(\n { rules: { quotes: ['error', 'double'] } },\n { rules: { quotes: ['error', 'single', { avoidEscape: true }] } }\n );\n\n expect(result).toStrictEqual({\n rules: { quotes: ['error', 'single', { avoidEscape: true }] },\n });\n });\n\n test('files arrays are replaced so a later block fully owns its globs', () => {\n const result = merge(\n { files: ['**/*.js'] },\n { files: ['**/*.ts'] }\n );\n\n expect(result).toStrictEqual({ files: ['**/*.ts'] });\n });\n\n test('deeply nested settings merge key-by-key with later winning', () => {\n const result = merge(\n { settings: { jsdoc: { mode: 'typescript', tagNamePreference: { returns: 'returns' } } } },\n { settings: { jsdoc: { tagNamePreference: { returns: 'return' } } } }\n );\n\n expect(result).toStrictEqual({\n settings: {\n jsdoc: {\n mode: 'typescript',\n tagNamePreference: { returns: 'return' },\n },\n },\n });\n });\n\n test('self-referential plugin objects merge without unbounded recursion', () => {\n const plugin: Record<string, unknown> = { rules: {} };\n\n plugin.configs = { recommended: { plugins: { fake: plugin } } };\n\n const result = merge({}, { plugins: { fake: plugin } }) as { plugins: { fake: unknown } };\n\n expect(result.plugins.fake).toBe(plugin);\n });\n\n test('prototype-polluting keys never reach Object.prototype', () => {\n const payload = JSON.parse('{\"__proto__\":{\"polluted\":true},\"constructor\":{\"polluted\":true}}') as object;\n\n merge({}, payload);\n\n expect(({} as Record<string, unknown>).polluted).toBeUndefined();\n expect(Object.prototype).not.toHaveProperty('polluted');\n });\n\n test('Object.prototype method keys are dropped so the result stays usable as a string', () => {\n const payload = {\n toString: 'not-a-function',\n valueOf: 1,\n hasOwnProperty: null,\n isPrototypeOf: 0,\n propertyIsEnumerable: false,\n toLocaleString: ['nope'],\n };\n\n const result = merge({ rules: { semi: 'error' } }, payload) as Record<string, unknown>;\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- default stringification is exactly what this CVE regression asserts\n const stringify = () => String(result);\n\n expect(Object.keys(result)).toStrictEqual(['rules']);\n expect(typeof result.toString).toBe('function');\n expect(typeof result.valueOf).toBe('function');\n expect(typeof result.hasOwnProperty).toBe('function');\n expect(stringify).not.toThrow();\n expect(stringify()).toBe('[object Object]');\n });\n\n test('nested Object.prototype method keys are dropped too', () => {\n const result = merge(\n { settings: { shared: { name: 'insider' } } },\n { settings: { shared: { toString: 'not-a-function' } } }\n ) as { settings: { shared: Record<string, unknown> } };\n\n expect(result.settings.shared).toStrictEqual({ name: 'insider' });\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- default stringification is exactly what this CVE regression asserts\n expect(String(result.settings.shared)).toBe('[object Object]');\n });\n\n test('null-prototype objects are still merged key-by-key', () => {\n const bare = Object.create(null) as Record<string, unknown>;\n\n bare.semi = 'error';\n\n const result = merge({ rules: bare }, { rules: { quotes: 'error' } }) as { rules: unknown };\n\n expect(result.rules).toStrictEqual({ semi: 'error', quotes: 'error' });\n });\n\n test('array arguments are rejected', () => {\n expect(() => merge({}, ['not-an-object'])).toThrow(TypeError);\n });\n });\n}\n"],"mappings":";AACA,IAAI,8BAA8B,IAAI,IAAI;CACzC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;AACD,IAAI,iBAAiB;CACpB,yBAAyB;CACzB,aAAa;CACb,kBAAkB;AACnB;AACA,SAAS,kBAAkB,OAAO;CACjC,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,MAAM,YAAY,OAAO,eAAe,KAAK;CAC7C,OAAO,cAAc,OAAO,aAAa,cAAc;AACxD;AACA,SAAS,cAAc,SAAS,UAAU,MAAM;CAC/C,IAAI,CAAC,QAAQ,aAAa,OAAO;CACjC,OAAO,QAAQ,mBAAmB,CAAC,mBAAmB,IAAI,IAAI,CAAC,GAAG,UAAU,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,UAAU,GAAG,IAAI;AAC/G;AACA,SAAS,aAAa,SAAS,SAAS;CACvC,OAAO,QAAQ,QAAQ,QAAQ,WAAW;EACzC,IAAI,MAAM,QAAQ,MAAM,GAAG,MAAM,IAAI,UAAU,0DAA0D;EACzG,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,QAAQ;GACpC,IAAI,YAAY,IAAI,GAAG,GAAG;GAC1B,MAAM,WAAW,OAAO;GACxB,MAAM,OAAO,OAAO;GACpB,IAAI,MAAM,QAAQ,QAAQ,KAAK,MAAM,QAAQ,IAAI,GAAG,OAAO,OAAO,cAAc,SAAS,UAAU,IAAI;QAClG,IAAI,kBAAkB,QAAQ,KAAK,kBAAkB,IAAI,GAAG,OAAO,OAAO,aAAa,SAAS,CAAC,UAAU,IAAI,CAAC;QAChH,OAAO,OAAO,SAAS,KAAK,KAAK,CAAC,QAAQ,0BAA0B,WAAW;EACrF,CAAC;EACD,OAAO;CACR,GAAG,CAAC,CAAC;AACN;;;;;;;;AAkBA,SAAS,qBAAqB,SAAS,GAAG,SAAS;CAClD,OAAO,aAAa;EACnB,GAAG;EACH,GAAG;CACJ,GAAG,OAAO;AACX;;;AC3DA,SAAgB,MAAiD,GAAG,MAAe;CAC/E,OAAO,qBAAqB,EAAE,aAAa,MAAK,GAAI,GAAG,IAAI;AAC/D"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region ../utils/dist/index.js
|
|
2
|
-
var UNSAFE_KEYS = new Set([
|
|
2
|
+
var UNSAFE_KEYS = /* @__PURE__ */ new Set([
|
|
3
3
|
"__proto__",
|
|
4
4
|
"constructor",
|
|
5
5
|
"prototype",
|
|
@@ -22,7 +22,7 @@ function isMergeableObject(value) {
|
|
|
22
22
|
}
|
|
23
23
|
function combineArrays(options, previous, next) {
|
|
24
24
|
if (!options.mergeArrays) return next;
|
|
25
|
-
return options.uniqueArrayItems ? [
|
|
25
|
+
return options.uniqueArrayItems ? [.../* @__PURE__ */ new Set([...previous, ...next])] : [...previous, ...next];
|
|
26
26
|
}
|
|
27
27
|
function mergeSources(options, sources) {
|
|
28
28
|
return sources.reduce((result, source) => {
|
|
@@ -59,4 +59,4 @@ function merge(...args) {
|
|
|
59
59
|
//#endregion
|
|
60
60
|
export { merge as t };
|
|
61
61
|
|
|
62
|
-
//# sourceMappingURL=merge-
|
|
62
|
+
//# sourceMappingURL=merge-R5ZHqMsV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-R5ZHqMsV.js","names":[],"sources":["../../utils/dist/index.js","../src/utils/merge.ts"],"sourcesContent":["//#region src/merge.ts\nvar UNSAFE_KEYS = /* @__PURE__ */ new Set([\n\t\"__proto__\",\n\t\"constructor\",\n\t\"prototype\",\n\t\"toString\",\n\t\"valueOf\",\n\t\"hasOwnProperty\",\n\t\"isPrototypeOf\",\n\t\"propertyIsEnumerable\",\n\t\"toLocaleString\"\n]);\nvar defaultOptions = {\n\tallowUndefinedOverrides: true,\n\tmergeArrays: true,\n\tuniqueArrayItems: true\n};\nfunction isMergeableObject(value) {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst prototype = Object.getPrototypeOf(value);\n\treturn prototype === Object.prototype || prototype === null;\n}\nfunction combineArrays(options, previous, next) {\n\tif (!options.mergeArrays) return next;\n\treturn options.uniqueArrayItems ? [.../* @__PURE__ */ new Set([...previous, ...next])] : [...previous, ...next];\n}\nfunction mergeSources(options, sources) {\n\treturn sources.reduce((result, source) => {\n\t\tif (Array.isArray(source)) throw new TypeError(\"Arguments provided to merge must be objects, not arrays.\");\n\t\tObject.keys(source).forEach((key) => {\n\t\t\tif (UNSAFE_KEYS.has(key)) return;\n\t\t\tconst previous = result[key];\n\t\t\tconst next = source[key];\n\t\t\tif (Array.isArray(previous) && Array.isArray(next)) result[key] = combineArrays(options, previous, next);\n\t\t\telse if (isMergeableObject(previous) && isMergeableObject(next)) result[key] = mergeSources(options, [previous, next]);\n\t\t\telse result[key] = next === void 0 && !options.allowUndefinedOverrides ? previous : next;\n\t\t});\n\t\treturn result;\n\t}, {});\n}\n/**\n* Recursively merges `sources` left to right into a fresh object, skipping keys that would\n* poison `Object.prototype`.\n* @example\n* deepMerge({ branches: ['main'] }, { branches: ['beta'] });\n* // => { branches: ['main', 'beta'] }\n*/\nfunction deepMerge(...sources) {\n\treturn mergeSources(defaultOptions, sources);\n}\n/**\n* {@link deepMerge} with the merge strategy tuned per call.\n* @example\n* deepMergeWithOptions({ mergeArrays: false }, { branches: ['main'] }, { branches: ['beta'] });\n* // => { branches: ['beta'] }\n* @param options Only the fields being overridden; every omitted field keeps its default.\n*/\nfunction deepMergeWithOptions(options, ...sources) {\n\treturn mergeSources({\n\t\t...defaultOptions,\n\t\t...options\n\t}, sources);\n}\n//#endregion\nexport { deepMerge, deepMergeWithOptions };\n\n//# sourceMappingURL=index.js.map","// eslint-disable-next-line import-x/no-extraneous-dependencies -- private workspace package, bundled into dist/ at build time; a runtime dependency entry would be unresolvable for consumers\nimport { deepMergeWithOptions } from '@useinsider/utils';\n\nexport function merge<Arguments extends NonNullable<unknown>[]> (...args: Arguments): Arguments[number] {\n return deepMergeWithOptions({ mergeArrays: false }, ...args);\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('merge', () => {\n test('later arguments override earlier ones on overlapping keys', () => {\n const result = merge(\n { rules: { 'no-console': 'error' } },\n { rules: { 'no-console': 'off' } }\n );\n\n expect(result).toStrictEqual({ rules: { 'no-console': 'off' } });\n });\n\n test('precedence holds across three or more arguments', () => {\n const result = merge(\n { rules: { indent: 'off' } },\n { rules: { indent: 'warn' } },\n { rules: { indent: 'error' } }\n );\n\n expect(result).toStrictEqual({ rules: { indent: 'error' } });\n });\n\n test('non-overlapping keys from every argument are preserved', () => {\n const result = merge(\n { rules: { 'no-console': 'error' } },\n { rules: { 'no-debugger': 'warn' } },\n { languageOptions: { ecmaVersion: 2022 } }\n );\n\n expect(result).toStrictEqual({\n rules: {\n 'no-console': 'error',\n 'no-debugger': 'warn',\n },\n languageOptions: { ecmaVersion: 2022 },\n });\n });\n\n test('rule option arrays are replaced wholesale, never concatenated', () => {\n const result = merge(\n { rules: { quotes: ['error', 'double'] } },\n { rules: { quotes: ['error', 'single', { avoidEscape: true }] } }\n );\n\n expect(result).toStrictEqual({\n rules: { quotes: ['error', 'single', { avoidEscape: true }] },\n });\n });\n\n test('files arrays are replaced so a later block fully owns its globs', () => {\n const result = merge(\n { files: ['**/*.js'] },\n { files: ['**/*.ts'] }\n );\n\n expect(result).toStrictEqual({ files: ['**/*.ts'] });\n });\n\n test('deeply nested settings merge key-by-key with later winning', () => {\n const result = merge(\n { settings: { jsdoc: { mode: 'typescript', tagNamePreference: { returns: 'returns' } } } },\n { settings: { jsdoc: { tagNamePreference: { returns: 'return' } } } }\n );\n\n expect(result).toStrictEqual({\n settings: {\n jsdoc: {\n mode: 'typescript',\n tagNamePreference: { returns: 'return' },\n },\n },\n });\n });\n\n test('self-referential plugin objects merge without unbounded recursion', () => {\n const plugin: Record<string, unknown> = { rules: {} };\n\n plugin.configs = { recommended: { plugins: { fake: plugin } } };\n\n const result = merge({}, { plugins: { fake: plugin } }) as { plugins: { fake: unknown } };\n\n expect(result.plugins.fake).toBe(plugin);\n });\n\n test('prototype-polluting keys never reach Object.prototype', () => {\n const payload = JSON.parse('{\"__proto__\":{\"polluted\":true},\"constructor\":{\"polluted\":true}}') as object;\n\n merge({}, payload);\n\n expect(({} as Record<string, unknown>).polluted).toBeUndefined();\n expect(Object.prototype).not.toHaveProperty('polluted');\n });\n\n test('Object.prototype method keys are dropped so the result stays usable as a string', () => {\n const payload = {\n toString: 'not-a-function',\n valueOf: 1,\n hasOwnProperty: null,\n isPrototypeOf: 0,\n propertyIsEnumerable: false,\n toLocaleString: ['nope'],\n };\n\n const result = merge({ rules: { semi: 'error' } }, payload) as Record<string, unknown>;\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- default stringification is exactly what this CVE regression asserts\n const stringify = () => String(result);\n\n expect(Object.keys(result)).toStrictEqual(['rules']);\n expect(typeof result.toString).toBe('function');\n expect(typeof result.valueOf).toBe('function');\n expect(typeof result.hasOwnProperty).toBe('function');\n expect(stringify).not.toThrow();\n expect(stringify()).toBe('[object Object]');\n });\n\n test('nested Object.prototype method keys are dropped too', () => {\n const result = merge(\n { settings: { shared: { name: 'insider' } } },\n { settings: { shared: { toString: 'not-a-function' } } }\n ) as { settings: { shared: Record<string, unknown> } };\n\n expect(result.settings.shared).toStrictEqual({ name: 'insider' });\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- default stringification is exactly what this CVE regression asserts\n expect(String(result.settings.shared)).toBe('[object Object]');\n });\n\n test('null-prototype objects are still merged key-by-key', () => {\n const bare = Object.create(null) as Record<string, unknown>;\n\n bare.semi = 'error';\n\n const result = merge({ rules: bare }, { rules: { quotes: 'error' } }) as { rules: unknown };\n\n expect(result.rules).toStrictEqual({ semi: 'error', quotes: 'error' });\n });\n\n test('array arguments are rejected', () => {\n expect(() => merge({}, ['not-an-object'])).toThrow(TypeError);\n });\n });\n}\n"],"mappings":";AACA,IAAI,8BAA8B,IAAI,IAAI;CACzC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;AACD,IAAI,iBAAiB;CACpB,yBAAyB;CACzB,aAAa;CACb,kBAAkB;AACnB;AACA,SAAS,kBAAkB,OAAO;CACjC,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,MAAM,YAAY,OAAO,eAAe,KAAK;CAC7C,OAAO,cAAc,OAAO,aAAa,cAAc;AACxD;AACA,SAAS,cAAc,SAAS,UAAU,MAAM;CAC/C,IAAI,CAAC,QAAQ,aAAa,OAAO;CACjC,OAAO,QAAQ,mBAAmB,CAAC,mBAAmB,IAAI,IAAI,CAAC,GAAG,UAAU,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,UAAU,GAAG,IAAI;AAC/G;AACA,SAAS,aAAa,SAAS,SAAS;CACvC,OAAO,QAAQ,QAAQ,QAAQ,WAAW;EACzC,IAAI,MAAM,QAAQ,MAAM,GAAG,MAAM,IAAI,UAAU,0DAA0D;EACzG,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,QAAQ;GACpC,IAAI,YAAY,IAAI,GAAG,GAAG;GAC1B,MAAM,WAAW,OAAO;GACxB,MAAM,OAAO,OAAO;GACpB,IAAI,MAAM,QAAQ,QAAQ,KAAK,MAAM,QAAQ,IAAI,GAAG,OAAO,OAAO,cAAc,SAAS,UAAU,IAAI;QAClG,IAAI,kBAAkB,QAAQ,KAAK,kBAAkB,IAAI,GAAG,OAAO,OAAO,aAAa,SAAS,CAAC,UAAU,IAAI,CAAC;QAChH,OAAO,OAAO,SAAS,KAAK,KAAK,CAAC,QAAQ,0BAA0B,WAAW;EACrF,CAAC;EACD,OAAO;CACR,GAAG,CAAC,CAAC;AACN;;;;;;;;AAkBA,SAAS,qBAAqB,SAAS,GAAG,SAAS;CAClD,OAAO,aAAa;EACnB,GAAG;EACH,GAAG;CACJ,GAAG,OAAO;AACX;;;AC3DA,SAAgB,MAAiD,GAAG,MAAe;CAC/E,OAAO,qBAAqB,EAAE,aAAa,MAAK,GAAI,GAAG,IAAI;AAC/D"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as merge } from "./merge-
|
|
1
|
+
import { t as merge } from "./merge-R5ZHqMsV.js";
|
|
2
2
|
//#region src/utils/mergeAllConfig.ts
|
|
3
3
|
function mergeAll(...configs) {
|
|
4
4
|
return configs.flat().reduce((acc, scope) => merge(acc, scope), {});
|
|
@@ -6,4 +6,4 @@ function mergeAll(...configs) {
|
|
|
6
6
|
//#endregion
|
|
7
7
|
export { mergeAll as t };
|
|
8
8
|
|
|
9
|
-
//# sourceMappingURL=mergeAllConfig-
|
|
9
|
+
//# sourceMappingURL=mergeAllConfig-B2rSXCdn.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mergeAllConfig-
|
|
1
|
+
{"version":3,"file":"mergeAllConfig-B2rSXCdn.js","names":[],"sources":["../src/utils/mergeAllConfig.ts"],"sourcesContent":["import type { Linter } from 'eslint';\nimport { merge } from './merge';\n\nexport function mergeAll (...configs: Linter.Config[][]) {\n return configs.flat().reduce((acc, scope) => merge(acc, scope), {} as Linter.Config);\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe('mergeAll', () => {\n test('flattens every config array into a single block', () => {\n const result = mergeAll(\n [{ rules: { 'no-console': 'error' } }],\n [{ rules: { 'no-debugger': 'warn' } }]\n );\n\n expect(result).toStrictEqual({\n rules: {\n 'no-console': 'error',\n 'no-debugger': 'warn',\n },\n });\n });\n\n test('later arrays override earlier ones on overlapping rules', () => {\n const result = mergeAll(\n [{ rules: { semi: 'off' } }],\n [{ rules: { semi: 'error' } }]\n );\n\n expect(result.rules).toStrictEqual({ semi: 'error' });\n });\n\n test('later entries within one array override earlier entries', () => {\n const result = mergeAll([\n { rules: { semi: 'off' } },\n { rules: { semi: 'error' } },\n ]);\n\n expect(result.rules).toStrictEqual({ semi: 'error' });\n });\n\n test('does not mutate the caller-supplied config blocks', () => {\n const first: Linter.Config = { rules: { semi: 'off' } };\n const second: Linter.Config = { rules: { semi: 'error' } };\n\n mergeAll([first], [second]);\n\n expect(first.rules).toStrictEqual({ semi: 'off' });\n expect(second.rules).toStrictEqual({ semi: 'error' });\n });\n\n test('returns an empty config when given no blocks', () => {\n expect(mergeAll([])).toStrictEqual({});\n });\n });\n}\n"],"mappings":";;AAGA,SAAgB,SAAU,GAAG,SAA0B;CACnD,OAAO,QAAQ,KAAI,CAAE,CAAC,QAAQ,KAAK,UAAU,MAAM,KAAK,KAAK,GAAG,CAAA,CAAmB;AACvF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_merge = require("./merge-
|
|
1
|
+
const require_merge = require("./merge-DJQU2y2U.cjs");
|
|
2
2
|
//#region src/utils/mergeAllConfig.ts
|
|
3
3
|
function mergeAll(...configs) {
|
|
4
4
|
return configs.flat().reduce((acc, scope) => require_merge.merge(acc, scope), {});
|
|
@@ -11,4 +11,4 @@ Object.defineProperty(exports, "mergeAll", {
|
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
//# sourceMappingURL=mergeAllConfig-
|
|
14
|
+
//# sourceMappingURL=mergeAllConfig-DjMWjBNg.cjs.map
|