@santi020k/eslint-config-basic 3.1.0 → 3.3.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/CHANGELOG.md +54 -0
- package/dist/agent-skill-generator.d.ts +7 -18
- package/dist/agent-skill-generator.js +54 -18
- package/dist/agent-skill-generator.js.map +1 -0
- package/dist/cli-advanced.d.ts +7 -0
- package/dist/cli-advanced.js +74 -15
- package/dist/cli-advanced.js.map +1 -0
- package/dist/cli-migration.js +388 -34
- package/dist/cli-migration.js.map +1 -0
- package/dist/cli-package-manager.d.ts +10 -0
- package/dist/cli-package-manager.js +135 -0
- package/dist/cli-package-manager.js.map +1 -0
- package/dist/cli-preset.d.ts +103 -0
- package/dist/cli-preset.js +562 -0
- package/dist/cli-preset.js.map +1 -0
- package/dist/cli-workflows.js +1 -0
- package/dist/cli-workflows.js.map +1 -0
- package/dist/cli.d.ts +14 -2
- package/dist/cli.js +689 -94
- package/dist/cli.js.map +1 -0
- package/dist/define-config-metadata.d.ts +10 -0
- package/dist/define-config-metadata.js +16 -0
- package/dist/define-config-metadata.js.map +1 -0
- package/dist/frameworks.d.ts +1 -0
- package/dist/frameworks.js +6 -2
- package/dist/frameworks.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +145 -27
- package/dist/index.js.map +1 -0
- package/dist/integrations.js +4 -1
- package/dist/integrations.js.map +1 -0
- package/dist/optional-package-errors.d.ts +3 -0
- package/dist/optional-package-errors.js +15 -0
- package/dist/optional-package-errors.js.map +1 -0
- package/dist/recommended.js +1 -0
- package/dist/recommended.js.map +1 -0
- package/dist/resolvers.js +1 -0
- package/dist/resolvers.js.map +1 -0
- package/dist/tailwind.d.ts +3 -1
- package/dist/tailwind.js +115 -13
- package/dist/tailwind.js.map +1 -0
- package/package.json +20 -19
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli-advanced.ts"],"sourcesContent":["/* eslint-disable no-console -- CLI handlers own user-facing terminal output */\n/* eslint-disable complexity -- report builders intentionally cover compatibility and config-source branches */\nimport { existsSync, readdirSync, readFileSync } from 'node:fs'\nimport { createRequire, findPackageJSON } from 'node:module'\nimport { basename, dirname, join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\ninterface SemverApi {\n satisfies: (version: string, range: string) => boolean\n subset: (subRange: string, superRange: string) => boolean\n validRange: (range: string) => null | string\n}\n\ninterface PackageManifest {\n engines?: { node?: string }\n name?: string\n peerDependencies?: Record<string, string>\n version?: string\n}\n\ninterface ResolvedPackageManifest {\n manifest: PackageManifest\n path: string\n}\n\ninterface FlatConfigEntry {\n files?: unknown\n name?: string\n rules?: Record<string, unknown>\n}\n\ninterface ProjectEslint {\n calculateConfigForFile: (filePath: string) => Promise<unknown>\n lintFiles: (patterns: string[]) => Promise<LintResult[]>\n}\n\ninterface LintResult {\n filePath: string\n messages: {\n column?: number\n endColumn?: number\n endLine?: number\n line?: number\n message: string\n ruleId: null | string\n }[]\n}\n\ninterface RuleDiagnostic {\n column: null | number\n endColumn: null | number\n endLine: null | number\n file: string\n line: null | number\n message: string\n}\n\ntype ProjectEslintConstructor = new(options: { cwd: string }) => ProjectEslint\n\nexport interface ExplainRuleOptions {\n file?: string\n json?: boolean\n rule: string\n}\n\nconst CONFIG_FILENAMES = [\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\nconst BASIC_PACKAGE_NAME = '@santi020k/eslint-config-basic'\nconst FULL_PACKAGE_NAME = '@santi020k/eslint-config-full'\n\nconst RULE_PACKAGE_HINTS: Partial<Record<string, string>> = {\n '@angular-eslint': '@santi020k/eslint-config-angular',\n '@eslint-react': '@santi020k/eslint-config-react',\n '@next/next': '@santi020k/eslint-config-next',\n '@typescript-eslint': '@santi020k/eslint-config-typescript',\n astro: '@santi020k/eslint-config-astro',\n cypress: '@santi020k/eslint-config-testing',\n jest: '@santi020k/eslint-config-testing',\n jsonc: '@santi020k/eslint-config-formats',\n playwright: '@santi020k/eslint-config-testing',\n react: '@santi020k/eslint-config-react',\n 'testing-library': '@santi020k/eslint-config-testing',\n unicorn: '@santi020k/eslint-config-extensions',\n vue: '@santi020k/eslint-config-vue'\n}\n\nconst semver = createRequire(import.meta.url)('semver') as SemverApi\n\nconst readJson = (filePath: string): null | Record<string, unknown> => {\n if (!existsSync(filePath)) return null\n\n try {\n return JSON.parse(readFileSync(filePath, 'utf8')) as Record<string, unknown>\n } catch {\n return null\n }\n}\n\nconst findConfigPath = (cwd: string): null | string => (\n CONFIG_FILENAMES.map(file => join(cwd, file)).find(file => existsSync(file)) ?? null\n)\n\nconst flattenConfig = async (value: unknown): Promise<FlatConfigEntry[]> => {\n const resolved = await value\n\n if (Array.isArray(resolved)) {\n const nested = await Promise.all(resolved.map(entry => flattenConfig(entry)))\n\n return nested.flat()\n }\n\n if (resolved && typeof resolved === 'object') return [resolved]\n\n return []\n}\n\nconst loadConfigEntries = async (configPath: null | string): Promise<FlatConfigEntry[]> => {\n if (!configPath) return []\n\n try {\n const loaded: unknown = await import(`${pathToFileURL(configPath).href}?basic-eslint-explain=${Date.now()}`)\n\n if (!loaded || typeof loaded !== 'object' || !('default' in loaded)) return []\n\n return await flattenConfig((loaded).default)\n } catch {\n return []\n }\n}\n\nconst loadProjectEslint = (cwd: string): ProjectEslint => {\n const projectRequire = createRequire(join(cwd, 'package.json'))\n const eslintModule = projectRequire('eslint') as { ESLint?: ProjectEslintConstructor }\n\n if (!eslintModule.ESLint) throw new Error('The installed eslint package does not expose the ESLint API.')\n\n return new eslintModule.ESLint({ cwd })\n}\n\nconst getRuleSeverity = (ruleValue: unknown): unknown => (\n Array.isArray(ruleValue) ? ruleValue[0] : ruleValue\n)\n\nconst isRuleEnabled = (ruleValue: unknown): boolean => {\n const severity = getRuleSeverity(ruleValue)\n\n return severity !== 0 && severity !== 'off'\n}\n\nconst getPackageHint = (rule: string): null | string => {\n const prefix = rule.includes('/') ? rule.slice(0, rule.indexOf('/')) : rule\n const scopedPrefix = rule.startsWith('@') ? rule.split('/').slice(0, 2).join('/') : prefix\n\n return RULE_PACKAGE_HINTS[scopedPrefix] ?? RULE_PACKAGE_HINTS[prefix] ?? null\n}\n\nconst getRuleDiagnostics = async (\n eslint: ProjectEslint,\n file: string,\n rule: string\n): Promise<RuleDiagnostic[]> => {\n if (rule !== 'complexity') return []\n\n try {\n const results = await eslint.lintFiles([file])\n\n return results.flatMap(result => result.messages\n .filter(message => message.ruleId === rule)\n .map(message => ({\n column: message.column ?? null,\n endColumn: message.endColumn ?? null,\n endLine: message.endLine ?? null,\n file: result.filePath,\n line: message.line ?? null,\n message: message.message\n })))\n } catch {\n return []\n }\n}\n\nexport const handleExplainRule = async (\n cwd: string = process.cwd(),\n options: ExplainRuleOptions\n): Promise<void> => {\n const configPath = findConfigPath(cwd)\n const file = options.file ?? 'eslint.config.js'\n const eslint = loadProjectEslint(cwd)\n const effective = await eslint.calculateConfigForFile(file) as null | { rules?: Record<string, unknown> }\n const effectiveValue = effective?.rules?.[options.rule]\n const entries = await loadConfigEntries(configPath)\n const diagnostics = await getRuleDiagnostics(eslint, file, options.rule)\n\n const sources = entries.flatMap((entry, index) => {\n if (!entry.rules || !Object.hasOwn(entry.rules, options.rule)) return []\n\n return [{\n files: entry.files ?? null,\n index,\n name: entry.name ?? `anonymous config #${index + 1}`,\n value: entry.rules[options.rule]\n }]\n })\n\n const payload = {\n configFile: configPath ? basename(configPath) : null,\n diagnostics,\n effective: effectiveValue ?? null,\n enabled: effectiveValue !== undefined && isRuleEnabled(effectiveValue),\n file,\n packageHint: getPackageHint(options.rule),\n rule: options.rule,\n severity: effectiveValue === undefined ? null : getRuleSeverity(effectiveValue),\n sources\n }\n\n if (options.json) {\n console.log(JSON.stringify(payload, null, 2))\n } else {\n console.log([\n `ESLint rule explanation: ${payload.rule}`,\n `- File: ${payload.file}`,\n `- Config: ${payload.configFile ?? 'none'}`,\n `- Effective value: ${JSON.stringify(payload.effective)}`,\n `- Enabled: ${payload.enabled ? 'yes' : 'no'}`,\n `- Likely package: ${payload.packageHint ?? 'core or custom config'}`,\n `- Defining entries: ${payload.sources.length}`,\n ...(payload.diagnostics.length > 0 ?\n [`- Current findings: ${payload.diagnostics.length}`] :\n [])\n ].join('\\n'))\n\n for (const source of payload.sources) {\n console.log(` - ${source.name}: ${JSON.stringify(source.value)}`)\n }\n\n for (const diagnostic of payload.diagnostics) {\n console.log(\n ` - ${diagnostic.file}:${diagnostic.line ?? '?'}:${diagnostic.column ?? '?'} ` +\n diagnostic.message\n )\n }\n }\n\n if (effectiveValue === undefined) process.exitCode = 1\n}\n\nconst getDependencyVersions = (manifest: null | Record<string, unknown>): Record<string, string> => {\n const fields = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']\n const dependencies: Record<string, string> = {}\n\n for (const field of fields) {\n const values = manifest?.[field]\n\n if (!values || typeof values !== 'object' || Array.isArray(values)) continue\n\n for (const [name, version] of Object.entries(values)) {\n if (typeof version === 'string') dependencies[name] = version\n }\n }\n\n return dependencies\n}\n\nconst readResolvedManifest = (\n basePath: string,\n packageName: string\n): null | ResolvedPackageManifest => {\n try {\n const manifestPath = findPackageJSON(packageName, pathToFileURL(basePath))\n\n const manifest = manifestPath ?\n readJson(manifestPath) as null | PackageManifest :\n null\n\n if (manifestPath && manifest?.name === packageName) return { manifest, path: manifestPath }\n } catch {\n // Fall through for runtimes where package-JSON resolution is unavailable.\n }\n\n try {\n let directory = dirname(createRequire(basePath).resolve(packageName))\n\n for (let depth = 0; depth < 6; depth++) {\n const manifestPath = join(directory, 'package.json')\n const manifest = readJson(manifestPath) as null | PackageManifest\n\n if (manifest?.name === packageName) return { manifest, path: manifestPath }\n\n directory = dirname(directory)\n }\n } catch {\n return null\n }\n\n return null\n}\n\nconst readWorkspaceManifest = (cwd: string, packageName: string): null | ResolvedPackageManifest => {\n const packagesPath = join(cwd, 'packages')\n\n if (!existsSync(packagesPath)) return null\n\n for (const entry of readdirSync(packagesPath, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue\n\n const manifestPath = join(packagesPath, entry.name, 'package.json')\n const manifest = readJson(manifestPath) as null | PackageManifest\n\n if (manifest?.name === packageName) return { manifest, path: manifestPath }\n }\n\n return null\n}\n\nexport const createCompatibilityReport = (cwd: string = process.cwd()) => {\n const rootManifestPath = join(cwd, 'package.json')\n const rootManifest = readJson(rootManifestPath)\n const declared = getDependencyVersions(rootManifest)\n const consumerNodeRange = (rootManifest as null | PackageManifest)?.engines?.node ?? null\n\n const configPackages = Object.keys(declared)\n .filter(name => name.startsWith('@santi020k/eslint-config-'))\n .sort()\n\n const runtimeVersions = {\n consumerNodeRange,\n eslint: readResolvedManifest(rootManifestPath, 'eslint')?.manifest.version ?? null,\n node: process.versions.node,\n typescript: readResolvedManifest(rootManifestPath, 'typescript')?.manifest.version ?? null\n }\n\n const packages = configPackages.map(name => {\n const resolvedPackage = readResolvedManifest(rootManifestPath, name) ?? readWorkspaceManifest(cwd, name)\n const manifest = resolvedPackage?.manifest\n const issues: string[] = []\n\n const aggregatedBasic = name === FULL_PACKAGE_NAME && resolvedPackage ?\n readResolvedManifest(resolvedPackage.path, BASIC_PACKAGE_NAME) :\n null\n\n if (!manifest) {\n issues.push('declared but not installed')\n } else {\n const nodeRange = manifest.engines?.node\n\n if (nodeRange && !semver.satisfies(runtimeVersions.node, nodeRange)) {\n issues.push(`requires Node ${nodeRange}`)\n }\n\n if (nodeRange && consumerNodeRange) {\n if (!semver.validRange(consumerNodeRange)) {\n issues.push(`consumer engines.node ${consumerNodeRange} is not a valid semver range`)\n } else if (!semver.subset(consumerNodeRange, nodeRange)) {\n issues.push(\n `consumer engines.node ${consumerNodeRange} permits unsupported runtimes; requires ${nodeRange}`\n )\n }\n }\n\n for (const peer of ['eslint', 'typescript']) {\n const range = manifest.peerDependencies?.[peer]\n const version = runtimeVersions[peer as 'eslint' | 'typescript']\n\n if (range && version && !semver.satisfies(version, range)) {\n issues.push(`requires ${peer} ${range}, resolved ${version}`)\n }\n }\n }\n\n return {\n aggregatedBasic: name === FULL_PACKAGE_NAME ?\n {\n name: BASIC_PACKAGE_NAME,\n resolved: aggregatedBasic?.manifest.version ?? null,\n resolvedPath: aggregatedBasic?.path ?? null\n } :\n null,\n declared: declared[name],\n issues,\n name,\n resolved: manifest?.version ?? null,\n resolvedPath: resolvedPackage?.path ?? null\n }\n })\n\n return {\n compatible: packages.every(item => item.issues.length === 0),\n packages,\n runtime: runtimeVersions\n }\n}\n\nexport const handleCompatibility = (\n cwd: string = process.cwd(),\n json = false\n): void => {\n const report = createCompatibilityReport(cwd)\n\n if (json) {\n console.log(JSON.stringify(report, null, 2))\n } else {\n console.log([\n `ESLint compatibility: ${report.compatible ? 'compatible' : 'issues found'}`,\n `- Node: ${report.runtime.node}`,\n `- Consumer Node range: ${report.runtime.consumerNodeRange ?? 'not declared'}`,\n `- ESLint: ${report.runtime.eslint ?? 'not installed'}`,\n `- TypeScript: ${report.runtime.typescript ?? 'not installed'}`\n ].join('\\n'))\n\n for (const item of report.packages) {\n console.log(\n `- ${item.name}: ${item.resolved ?? 'not installed'}` +\n (item.resolvedPath ? ` at ${item.resolvedPath}` : '') +\n (item.issues.length > 0 ? ` (${item.issues.join('; ')})` : '')\n )\n\n if (item.aggregatedBasic) {\n console.log(\n ` - Composer ${item.aggregatedBasic.name}: ` +\n (item.aggregatedBasic.resolved ?? 'not resolved') +\n (item.aggregatedBasic.resolvedPath ? ` at ${item.aggregatedBasic.resolvedPath}` : '')\n )\n }\n }\n }\n\n if (!report.compatible) process.exitCode = 1\n}\n"],"mappings":"AAEA,SAAS,YAAY,aAAa,oBAAoB;AACtD,SAAS,eAAe,uBAAuB;AAC/C,SAAS,UAAU,SAAS,YAAY;AACxC,SAAS,qBAAqB;AA4D9B,MAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,qBAAqB;AAC3B,MAAM,oBAAoB;AAE1B,MAAM,qBAAsD;AAAA,EAC1D,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,sBAAsB;AAAA,EACtB,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,mBAAmB;AAAA,EACnB,SAAS;AAAA,EACT,KAAK;AACP;AAEA,MAAM,SAAS,cAAc,YAAY,GAAG,EAAE,QAAQ;AAEtD,MAAM,WAAW,CAAC,aAAqD;AACrE,MAAI,CAAC,WAAW,QAAQ,EAAG,QAAO;AAElC,MAAI;AACF,WAAO,KAAK,MAAM,aAAa,UAAU,MAAM,CAAC;AAAA,EAClD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,MAAM,iBAAiB,CAAC,QACtB,iBAAiB,IAAI,UAAQ,KAAK,KAAK,IAAI,CAAC,EAAE,KAAK,UAAQ,WAAW,IAAI,CAAC,KAAK;AAGlF,MAAM,gBAAgB,OAAO,UAA+C;AAC1E,QAAM,WAAW,MAAM;AAEvB,MAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,UAAM,SAAS,MAAM,QAAQ,IAAI,SAAS,IAAI,WAAS,cAAc,KAAK,CAAC,CAAC;AAE5E,WAAO,OAAO,KAAK;AAAA,EACrB;AAEA,MAAI,YAAY,OAAO,aAAa,SAAU,QAAO,CAAC,QAAQ;AAE9D,SAAO,CAAC;AACV;AAEA,MAAM,oBAAoB,OAAO,eAA0D;AACzF,MAAI,CAAC,WAAY,QAAO,CAAC;AAEzB,MAAI;AACF,UAAM,SAAkB,MAAM,OAAO,GAAG,cAAc,UAAU,EAAE,IAAI,yBAAyB,KAAK,IAAI,CAAC;AAEzG,QAAI,CAAC,UAAU,OAAO,WAAW,YAAY,EAAE,aAAa,QAAS,QAAO,CAAC;AAE7E,WAAO,MAAM,cAAe,OAAQ,OAAO;AAAA,EAC7C,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,MAAM,oBAAoB,CAAC,QAA+B;AACxD,QAAM,iBAAiB,cAAc,KAAK,KAAK,cAAc,CAAC;AAC9D,QAAM,eAAe,eAAe,QAAQ;AAE5C,MAAI,CAAC,aAAa,OAAQ,OAAM,IAAI,MAAM,8DAA8D;AAExG,SAAO,IAAI,aAAa,OAAO,EAAE,IAAI,CAAC;AACxC;AAEA,MAAM,kBAAkB,CAAC,cACvB,MAAM,QAAQ,SAAS,IAAI,UAAU,CAAC,IAAI;AAG5C,MAAM,gBAAgB,CAAC,cAAgC;AACrD,QAAM,WAAW,gBAAgB,SAAS;AAE1C,SAAO,aAAa,KAAK,aAAa;AACxC;AAEA,MAAM,iBAAiB,CAAC,SAAgC;AACtD,QAAM,SAAS,KAAK,SAAS,GAAG,IAAI,KAAK,MAAM,GAAG,KAAK,QAAQ,GAAG,CAAC,IAAI;AACvE,QAAM,eAAe,KAAK,WAAW,GAAG,IAAI,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI;AAEpF,SAAO,mBAAmB,YAAY,KAAK,mBAAmB,MAAM,KAAK;AAC3E;AAEA,MAAM,qBAAqB,OACzB,QACA,MACA,SAC8B;AAC9B,MAAI,SAAS,aAAc,QAAO,CAAC;AAEnC,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,UAAU,CAAC,IAAI,CAAC;AAE7C,WAAO,QAAQ,QAAQ,YAAU,OAAO,SACrC,OAAO,aAAW,QAAQ,WAAW,IAAI,EACzC,IAAI,cAAY;AAAA,MACf,QAAQ,QAAQ,UAAU;AAAA,MAC1B,WAAW,QAAQ,aAAa;AAAA,MAChC,SAAS,QAAQ,WAAW;AAAA,MAC5B,MAAM,OAAO;AAAA,MACb,MAAM,QAAQ,QAAQ;AAAA,MACtB,SAAS,QAAQ;AAAA,IACnB,EAAE,CAAC;AAAA,EACP,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEO,MAAM,oBAAoB,OAC/B,MAAc,QAAQ,IAAI,GAC1B,YACkB;AAClB,QAAM,aAAa,eAAe,GAAG;AACrC,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,SAAS,kBAAkB,GAAG;AACpC,QAAM,YAAY,MAAM,OAAO,uBAAuB,IAAI;AAC1D,QAAM,iBAAiB,WAAW,QAAQ,QAAQ,IAAI;AACtD,QAAM,UAAU,MAAM,kBAAkB,UAAU;AAClD,QAAM,cAAc,MAAM,mBAAmB,QAAQ,MAAM,QAAQ,IAAI;AAEvE,QAAM,UAAU,QAAQ,QAAQ,CAAC,OAAO,UAAU;AAChD,QAAI,CAAC,MAAM,SAAS,CAAC,OAAO,OAAO,MAAM,OAAO,QAAQ,IAAI,EAAG,QAAO,CAAC;AAEvE,WAAO,CAAC;AAAA,MACN,OAAO,MAAM,SAAS;AAAA,MACtB;AAAA,MACA,MAAM,MAAM,QAAQ,qBAAqB,QAAQ,CAAC;AAAA,MAClD,OAAO,MAAM,MAAM,QAAQ,IAAI;AAAA,IACjC,CAAC;AAAA,EACH,CAAC;AAED,QAAM,UAAU;AAAA,IACd,YAAY,aAAa,SAAS,UAAU,IAAI;AAAA,IAChD;AAAA,IACA,WAAW,kBAAkB;AAAA,IAC7B,SAAS,mBAAmB,UAAa,cAAc,cAAc;AAAA,IACrE;AAAA,IACA,aAAa,eAAe,QAAQ,IAAI;AAAA,IACxC,MAAM,QAAQ;AAAA,IACd,UAAU,mBAAmB,SAAY,OAAO,gBAAgB,cAAc;AAAA,IAC9E;AAAA,EACF;AAEA,MAAI,QAAQ,MAAM;AAChB,YAAQ,IAAI,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,EAC9C,OAAO;AACL,YAAQ,IAAI;AAAA,MACV,4BAA4B,QAAQ,IAAI;AAAA,MACxC,WAAW,QAAQ,IAAI;AAAA,MACvB,aAAa,QAAQ,cAAc,MAAM;AAAA,MACzC,sBAAsB,KAAK,UAAU,QAAQ,SAAS,CAAC;AAAA,MACvD,cAAc,QAAQ,UAAU,QAAQ,IAAI;AAAA,MAC5C,qBAAqB,QAAQ,eAAe,uBAAuB;AAAA,MACnE,uBAAuB,QAAQ,QAAQ,MAAM;AAAA,MAC7C,GAAI,QAAQ,YAAY,SAAS,IAC/B,CAAC,uBAAuB,QAAQ,YAAY,MAAM,EAAE,IACpD,CAAC;AAAA,IACL,EAAE,KAAK,IAAI,CAAC;AAEZ,eAAW,UAAU,QAAQ,SAAS;AACpC,cAAQ,IAAI,OAAO,OAAO,IAAI,KAAK,KAAK,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,IACnE;AAEA,eAAW,cAAc,QAAQ,aAAa;AAC5C,cAAQ;AAAA,QACN,OAAO,WAAW,IAAI,IAAI,WAAW,QAAQ,GAAG,IAAI,WAAW,UAAU,GAAG,MAC5E,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAEA,MAAI,mBAAmB,OAAW,SAAQ,WAAW;AACvD;AAEA,MAAM,wBAAwB,CAAC,aAAqE;AAClG,QAAM,SAAS,CAAC,gBAAgB,mBAAmB,wBAAwB,kBAAkB;AAC7F,QAAM,eAAuC,CAAC;AAE9C,aAAW,SAAS,QAAQ;AAC1B,UAAM,SAAS,WAAW,KAAK;AAE/B,QAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG;AAEpE,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,MAAM,GAAG;AACpD,UAAI,OAAO,YAAY,SAAU,cAAa,IAAI,IAAI;AAAA,IACxD;AAAA,EACF;AAEA,SAAO;AACT;AAEA,MAAM,uBAAuB,CAC3B,UACA,gBACmC;AACnC,MAAI;AACF,UAAM,eAAe,gBAAgB,aAAa,cAAc,QAAQ,CAAC;AAEzE,UAAM,WAAW,eACf,SAAS,YAAY,IACrB;AAEF,QAAI,gBAAgB,UAAU,SAAS,YAAa,QAAO,EAAE,UAAU,MAAM,aAAa;AAAA,EAC5F,QAAQ;AAAA,EAER;AAEA,MAAI;AACF,QAAI,YAAY,QAAQ,cAAc,QAAQ,EAAE,QAAQ,WAAW,CAAC;AAEpE,aAAS,QAAQ,GAAG,QAAQ,GAAG,SAAS;AACtC,YAAM,eAAe,KAAK,WAAW,cAAc;AACnD,YAAM,WAAW,SAAS,YAAY;AAEtC,UAAI,UAAU,SAAS,YAAa,QAAO,EAAE,UAAU,MAAM,aAAa;AAE1E,kBAAY,QAAQ,SAAS;AAAA,IAC/B;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,MAAM,wBAAwB,CAAC,KAAa,gBAAwD;AAClG,QAAM,eAAe,KAAK,KAAK,UAAU;AAEzC,MAAI,CAAC,WAAW,YAAY,EAAG,QAAO;AAEtC,aAAW,SAAS,YAAY,cAAc,EAAE,eAAe,KAAK,CAAC,GAAG;AACtE,QAAI,CAAC,MAAM,YAAY,EAAG;AAE1B,UAAM,eAAe,KAAK,cAAc,MAAM,MAAM,cAAc;AAClE,UAAM,WAAW,SAAS,YAAY;AAEtC,QAAI,UAAU,SAAS,YAAa,QAAO,EAAE,UAAU,MAAM,aAAa;AAAA,EAC5E;AAEA,SAAO;AACT;AAEO,MAAM,4BAA4B,CAAC,MAAc,QAAQ,IAAI,MAAM;AACxE,QAAM,mBAAmB,KAAK,KAAK,cAAc;AACjD,QAAM,eAAe,SAAS,gBAAgB;AAC9C,QAAM,WAAW,sBAAsB,YAAY;AACnD,QAAM,oBAAqB,cAAyC,SAAS,QAAQ;AAErF,QAAM,iBAAiB,OAAO,KAAK,QAAQ,EACxC,OAAO,UAAQ,KAAK,WAAW,2BAA2B,CAAC,EAC3D,KAAK;AAER,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA,QAAQ,qBAAqB,kBAAkB,QAAQ,GAAG,SAAS,WAAW;AAAA,IAC9E,MAAM,QAAQ,SAAS;AAAA,IACvB,YAAY,qBAAqB,kBAAkB,YAAY,GAAG,SAAS,WAAW;AAAA,EACxF;AAEA,QAAM,WAAW,eAAe,IAAI,UAAQ;AAC1C,UAAM,kBAAkB,qBAAqB,kBAAkB,IAAI,KAAK,sBAAsB,KAAK,IAAI;AACvG,UAAM,WAAW,iBAAiB;AAClC,UAAM,SAAmB,CAAC;AAE1B,UAAM,kBAAkB,SAAS,qBAAqB,kBACpD,qBAAqB,gBAAgB,MAAM,kBAAkB,IAC7D;AAEF,QAAI,CAAC,UAAU;AACb,aAAO,KAAK,4BAA4B;AAAA,IAC1C,OAAO;AACL,YAAM,YAAY,SAAS,SAAS;AAEpC,UAAI,aAAa,CAAC,OAAO,UAAU,gBAAgB,MAAM,SAAS,GAAG;AACnE,eAAO,KAAK,iBAAiB,SAAS,EAAE;AAAA,MAC1C;AAEA,UAAI,aAAa,mBAAmB;AAClC,YAAI,CAAC,OAAO,WAAW,iBAAiB,GAAG;AACzC,iBAAO,KAAK,yBAAyB,iBAAiB,8BAA8B;AAAA,QACtF,WAAW,CAAC,OAAO,OAAO,mBAAmB,SAAS,GAAG;AACvD,iBAAO;AAAA,YACL,yBAAyB,iBAAiB,2CAA2C,SAAS;AAAA,UAChG;AAAA,QACF;AAAA,MACF;AAEA,iBAAW,QAAQ,CAAC,UAAU,YAAY,GAAG;AAC3C,cAAM,QAAQ,SAAS,mBAAmB,IAAI;AAC9C,cAAM,UAAU,gBAAgB,IAA+B;AAE/D,YAAI,SAAS,WAAW,CAAC,OAAO,UAAU,SAAS,KAAK,GAAG;AACzD,iBAAO,KAAK,YAAY,IAAI,IAAI,KAAK,cAAc,OAAO,EAAE;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,iBAAiB,SAAS,oBACxB;AAAA,QACE,MAAM;AAAA,QACN,UAAU,iBAAiB,SAAS,WAAW;AAAA,QAC/C,cAAc,iBAAiB,QAAQ;AAAA,MACzC,IACA;AAAA,MACF,UAAU,SAAS,IAAI;AAAA,MACvB;AAAA,MACA;AAAA,MACA,UAAU,UAAU,WAAW;AAAA,MAC/B,cAAc,iBAAiB,QAAQ;AAAA,IACzC;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,YAAY,SAAS,MAAM,UAAQ,KAAK,OAAO,WAAW,CAAC;AAAA,IAC3D;AAAA,IACA,SAAS;AAAA,EACX;AACF;AAEO,MAAM,sBAAsB,CACjC,MAAc,QAAQ,IAAI,GAC1B,OAAO,UACE;AACT,QAAM,SAAS,0BAA0B,GAAG;AAE5C,MAAI,MAAM;AACR,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,OAAO;AACL,YAAQ,IAAI;AAAA,MACV,yBAAyB,OAAO,aAAa,eAAe,cAAc;AAAA,MAC1E,WAAW,OAAO,QAAQ,IAAI;AAAA,MAC9B,0BAA0B,OAAO,QAAQ,qBAAqB,cAAc;AAAA,MAC5E,aAAa,OAAO,QAAQ,UAAU,eAAe;AAAA,MACrD,iBAAiB,OAAO,QAAQ,cAAc,eAAe;AAAA,IAC/D,EAAE,KAAK,IAAI,CAAC;AAEZ,eAAW,QAAQ,OAAO,UAAU;AAClC,cAAQ;AAAA,QACN,KAAK,KAAK,IAAI,KAAK,KAAK,YAAY,eAAe,MAClD,KAAK,eAAe,OAAO,KAAK,YAAY,KAAK,OACjD,KAAK,OAAO,SAAS,IAAI,KAAK,KAAK,OAAO,KAAK,IAAI,CAAC,MAAM;AAAA,MAC7D;AAEA,UAAI,KAAK,iBAAiB;AACxB,gBAAQ;AAAA,UACN,gBAAgB,KAAK,gBAAgB,IAAI,QACxC,KAAK,gBAAgB,YAAY,mBACjC,KAAK,gBAAgB,eAAe,OAAO,KAAK,gBAAgB,YAAY,KAAK;AAAA,QACpF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,WAAY,SAAQ,WAAW;AAC7C;","names":[]}
|
package/dist/cli-migration.js
CHANGED
|
@@ -175,6 +175,13 @@ const FACTORY_ALIAS_REPLACEMENTS = /* @__PURE__ */ new Set([
|
|
|
175
175
|
"astroConfig",
|
|
176
176
|
"gitignore"
|
|
177
177
|
]);
|
|
178
|
+
const PARAMETER_PROPERTY_MODIFIERS = /* @__PURE__ */ new Set([
|
|
179
|
+
"override",
|
|
180
|
+
"private",
|
|
181
|
+
"protected",
|
|
182
|
+
"public",
|
|
183
|
+
"readonly"
|
|
184
|
+
]);
|
|
178
185
|
const dependencyFields = [
|
|
179
186
|
"dependencies",
|
|
180
187
|
"devDependencies",
|
|
@@ -323,16 +330,121 @@ const getEnclosingDelimiter = (content, offset) => {
|
|
|
323
330
|
}
|
|
324
331
|
return null;
|
|
325
332
|
};
|
|
333
|
+
const findClosingDelimiter = (content, start, opener, closer) => {
|
|
334
|
+
let depth = 0;
|
|
335
|
+
for (let index = start; index < content.length; index++) {
|
|
336
|
+
if (content[index] === opener) depth++;
|
|
337
|
+
else if (content[index] === closer && --depth === 0) return index + 1;
|
|
338
|
+
}
|
|
339
|
+
return content.length;
|
|
340
|
+
};
|
|
341
|
+
const getShadowedRanges = (content, name) => {
|
|
342
|
+
const masked = maskNonCode(content);
|
|
343
|
+
const importRanges = getImportRanges(content);
|
|
344
|
+
let searchable = masked;
|
|
345
|
+
for (const range of importRanges.toReversed()) {
|
|
346
|
+
searchable = `${searchable.slice(0, range.start)}${" ".repeat(range.end - range.start)}${searchable.slice(range.end)}`;
|
|
347
|
+
}
|
|
348
|
+
const getArrowRange = (start, arrowIndex) => {
|
|
349
|
+
const relativeBodyStart = searchable.slice(arrowIndex + 2).search(/\S/);
|
|
350
|
+
const bodyStart = relativeBodyStart === -1 ? searchable.length : arrowIndex + 2 + relativeBodyStart;
|
|
351
|
+
const bodyDelimiter = searchable[bodyStart];
|
|
352
|
+
let bodyCloser;
|
|
353
|
+
if (bodyDelimiter === "(") bodyCloser = ")";
|
|
354
|
+
else if (bodyDelimiter === "[") bodyCloser = "]";
|
|
355
|
+
if (bodyDelimiter === "{") {
|
|
356
|
+
return { end: findClosingDelimiter(searchable, bodyStart, "{", "}"), start };
|
|
357
|
+
}
|
|
358
|
+
if (bodyCloser) {
|
|
359
|
+
return { end: findClosingDelimiter(searchable, bodyStart, bodyDelimiter, bodyCloser), start };
|
|
360
|
+
}
|
|
361
|
+
const lineEnd = searchable.indexOf("\n", arrowIndex + 2);
|
|
362
|
+
return { end: lineEnd === -1 ? searchable.length : lineEnd, start };
|
|
363
|
+
};
|
|
364
|
+
const unparenthesizedArrowPattern = new RegExp(`\\b${name}\\b\\s*=>`, "gm");
|
|
365
|
+
const ranges = [...searchable.matchAll(unparenthesizedArrowPattern)].map((match) => getArrowRange(match.index, searchable.indexOf("=>", match.index)));
|
|
366
|
+
const splitAtTopLevel = (value, separator) => {
|
|
367
|
+
const parts = [];
|
|
368
|
+
const closers = { "(": ")", "[": "]", "{": "}" };
|
|
369
|
+
const expectedClosers = [];
|
|
370
|
+
let partStart = 0;
|
|
371
|
+
for (let index = 0; index < value.length; index++) {
|
|
372
|
+
const character = value[index];
|
|
373
|
+
const closer = closers[character];
|
|
374
|
+
if (closer) expectedClosers.push(closer);
|
|
375
|
+
else if (character === expectedClosers.at(-1)) expectedClosers.pop();
|
|
376
|
+
else if (character === separator && expectedClosers.length === 0) {
|
|
377
|
+
parts.push(value.slice(partStart, index));
|
|
378
|
+
partStart = index + 1;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
parts.push(value.slice(partStart));
|
|
382
|
+
return parts;
|
|
383
|
+
};
|
|
384
|
+
const hasBindingName = (value) => {
|
|
385
|
+
let binding = splitAtTopLevel(value, "=")[0].trim();
|
|
386
|
+
if (binding.startsWith("...")) binding = binding.slice(3).trim();
|
|
387
|
+
binding = splitAtTopLevel(binding, ":")[0].trim();
|
|
388
|
+
const bindingParts = binding.split(/\s+/);
|
|
389
|
+
while (PARAMETER_PROPERTY_MODIFIERS.has(bindingParts[0])) bindingParts.shift();
|
|
390
|
+
binding = bindingParts.join(" ");
|
|
391
|
+
if (binding.startsWith("{") && binding.endsWith("}")) {
|
|
392
|
+
return splitAtTopLevel(binding.slice(1, -1), ",").some((property) => {
|
|
393
|
+
const propertyParts = splitAtTopLevel(property, ":");
|
|
394
|
+
return hasBindingName(propertyParts.length > 1 ? propertyParts.slice(1).join(":") : property);
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
if (binding.startsWith("[") && binding.endsWith("]")) {
|
|
398
|
+
return splitAtTopLevel(binding.slice(1, -1), ",").some(hasBindingName);
|
|
399
|
+
}
|
|
400
|
+
return binding.replace(/\?$/, "") === name;
|
|
401
|
+
};
|
|
402
|
+
const hasParameterBinding = (parameters) => splitAtTopLevel(parameters, ",").some(hasBindingName);
|
|
403
|
+
const parameterOwnerPattern = /(?:\bfunction\b[^()]*|\bcatch|\b(?:constructor|[a-zA-Z_$][\w$]*))\s*$/;
|
|
404
|
+
for (let parameterStart = 0; parameterStart < searchable.length; parameterStart++) {
|
|
405
|
+
if (searchable[parameterStart] !== "(") continue;
|
|
406
|
+
const parameterEnd = findClosingDelimiter(searchable, parameterStart, "(", ")");
|
|
407
|
+
if (searchable[parameterEnd - 1] !== ")") continue;
|
|
408
|
+
const parameters = searchable.slice(parameterStart + 1, parameterEnd - 1);
|
|
409
|
+
if (!hasParameterBinding(parameters)) continue;
|
|
410
|
+
const relativeBodyStart = searchable.slice(parameterEnd).search(/\S/);
|
|
411
|
+
const bodyStart = relativeBodyStart === -1 ? searchable.length : parameterEnd + relativeBodyStart;
|
|
412
|
+
if (searchable.startsWith("=>", bodyStart)) {
|
|
413
|
+
ranges.push(getArrowRange(parameterStart, bodyStart));
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (searchable[bodyStart] !== "{") continue;
|
|
417
|
+
const ownerMatch = parameterOwnerPattern.exec(searchable.slice(0, parameterStart));
|
|
418
|
+
if (!ownerMatch) continue;
|
|
419
|
+
ranges.push({
|
|
420
|
+
end: findClosingDelimiter(searchable, bodyStart, "{", "}"),
|
|
421
|
+
start: ownerMatch.index
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
const blockBindingPattern = new RegExp(`\\b(?:const|let|var|function|class)\\s+${name}\\b`, "g");
|
|
425
|
+
for (const match of searchable.matchAll(blockBindingPattern)) {
|
|
426
|
+
const blockStart = searchable.lastIndexOf("{", match.index);
|
|
427
|
+
if (blockStart !== -1) {
|
|
428
|
+
ranges.push({
|
|
429
|
+
end: findClosingDelimiter(searchable, blockStart, "{", "}"),
|
|
430
|
+
start: blockStart
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return ranges;
|
|
435
|
+
};
|
|
326
436
|
const replaceBindingReferences = (content, from, to, invokeReferences = false) => {
|
|
327
437
|
const importRanges = getImportRanges(content);
|
|
328
438
|
const masked = maskNonCode(content);
|
|
329
439
|
const identifierPattern = new RegExp(`\\b${from}\\b`, "g");
|
|
330
440
|
const matches = [...masked.matchAll(identifierPattern)];
|
|
441
|
+
const shadowedRanges = getShadowedRanges(content, from);
|
|
331
442
|
let updated = content;
|
|
332
443
|
for (const match of matches.reverse()) {
|
|
333
444
|
const offset = match.index;
|
|
334
445
|
const isImportSpecifier = importRanges.some((range) => offset >= range.start && offset < range.end);
|
|
335
|
-
|
|
446
|
+
const isShadowed = shadowedRanges.some((range) => offset >= range.start && offset < range.end);
|
|
447
|
+
if (isImportSpecifier || isShadowed) continue;
|
|
336
448
|
const precedingContent = masked.slice(0, offset).trimEnd();
|
|
337
449
|
const preceding = precedingContent.at(-1);
|
|
338
450
|
const following = masked.slice(offset + from.length);
|
|
@@ -356,24 +468,27 @@ const replaceImportedAlias = (content, from, to, invokeReferences = false) => {
|
|
|
356
468
|
const owner = REMOVED_ALIAS_OWNERS[from];
|
|
357
469
|
const acceptedPackages = /* @__PURE__ */ new Set([BASIC_PACKAGE, FULL_PACKAGE, ...owner ? [owner] : []]);
|
|
358
470
|
const importPattern = /import\s*\{([^}]*)\}\s*from\s*(['"])(@santi020k\/eslint-config-[^'"]+)\2/g;
|
|
359
|
-
let updated = content.replace(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
471
|
+
let updated = content.replace(
|
|
472
|
+
importPattern,
|
|
473
|
+
(statement, specifierText, _quote, packageName) => {
|
|
474
|
+
if (!acceptedPackages.has(packageName)) return statement;
|
|
475
|
+
const specifiers = specifierText.split(",");
|
|
476
|
+
const rewritten = specifiers.map((specifier) => {
|
|
477
|
+
const aliasMatch = /^(\s*)([a-zA-Z_$][\w$]*)\s+as\s+([a-zA-Z_$][\w$]*)(\s*)$/.exec(specifier);
|
|
478
|
+
if (aliasMatch?.[2] === from) {
|
|
479
|
+
const localName = aliasMatch[3];
|
|
480
|
+
bindings.push({ from: localName, to: localName });
|
|
481
|
+
return `${aliasMatch[1]}${to} as ${localName}${aliasMatch[4]}`;
|
|
482
|
+
}
|
|
483
|
+
const directMatch = /^(\s*)([a-zA-Z_$][\w$]*)(\s*)$/.exec(specifier);
|
|
484
|
+
if (directMatch?.[2] !== from) return specifier;
|
|
485
|
+
bindings.push({ from, to });
|
|
486
|
+
return `${directMatch[1]}${to}${directMatch[3]}`;
|
|
487
|
+
});
|
|
488
|
+
const rewrittenText = rewritten.join(",");
|
|
489
|
+
return rewrittenText === specifierText ? statement : statement.replace(specifierText, rewrittenText);
|
|
490
|
+
}
|
|
491
|
+
);
|
|
377
492
|
for (const binding of bindings) {
|
|
378
493
|
updated = replaceBindingReferences(updated, binding.from, binding.to, invokeReferences);
|
|
379
494
|
}
|
|
@@ -420,17 +535,234 @@ const hasCodePropertySeparator = (codeContent, match) => {
|
|
|
420
535
|
const matchIndex = match.index ?? -1;
|
|
421
536
|
return matchIndex >= 0 && separatorOffset >= 0 && codeContent[matchIndex + separatorOffset] === ":";
|
|
422
537
|
};
|
|
538
|
+
const replaceCodeProperty = (content, property, replacement, matches) => {
|
|
539
|
+
const masked = maskNonCode(content, true);
|
|
540
|
+
const pattern = new RegExp(`\\b${property}\\b(?=\\s*:)`, "g");
|
|
541
|
+
const propertyMatches = matches ?? [...masked.matchAll(pattern)];
|
|
542
|
+
let updated = content;
|
|
543
|
+
for (const match of propertyMatches.toReversed()) {
|
|
544
|
+
const { index } = match;
|
|
545
|
+
if (index === void 0) continue;
|
|
546
|
+
updated = `${updated.slice(0, index)}${replacement}${updated.slice(index + property.length)}`;
|
|
547
|
+
}
|
|
548
|
+
return updated;
|
|
549
|
+
};
|
|
550
|
+
const toFeatureName = (value) => {
|
|
551
|
+
const trimmed = value.trim();
|
|
552
|
+
const enumMatch = /^(?:Extension|Format|Library|Testing|Tool)\.([a-zA-Z0-9_$]+)$/.exec(trimmed);
|
|
553
|
+
const stringMatch = /^(['"])([^'"]+)\1$/.exec(trimmed);
|
|
554
|
+
const raw = enumMatch?.[1] ?? stringMatch?.[2];
|
|
555
|
+
return raw ? toKebabCase(raw) : null;
|
|
556
|
+
};
|
|
557
|
+
const getOwningObjectStart = (structure, position) => {
|
|
558
|
+
const objectStarts = [];
|
|
559
|
+
for (let index = 0; index < position; index++) {
|
|
560
|
+
if (structure[index] === "{") objectStarts.push(index);
|
|
561
|
+
if (structure[index] === "}") objectStarts.pop();
|
|
562
|
+
}
|
|
563
|
+
return objectStarts.at(-1);
|
|
564
|
+
};
|
|
565
|
+
const getDefineConfigOptionOwners = (searchable) => {
|
|
566
|
+
const owners = new Set(
|
|
567
|
+
[...searchable.matchAll(/\bdefineConfig\s*\(\s*\{/g)].map((match) => match.index + match[0].lastIndexOf("{"))
|
|
568
|
+
);
|
|
569
|
+
const optionNames = new Set(
|
|
570
|
+
[...searchable.matchAll(/\bdefineConfig\s*\(\s*([a-zA-Z_$][\w$]*)\b/g)].map((match) => match[1])
|
|
571
|
+
);
|
|
572
|
+
for (const optionName of optionNames) {
|
|
573
|
+
const bindingPattern = new RegExp(`\\b(?:const|let|var)\\s+${optionName}\\s*=\\s*\\{`, "g");
|
|
574
|
+
for (const match of searchable.matchAll(bindingPattern)) {
|
|
575
|
+
owners.add(match.index + match[0].lastIndexOf("{"));
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return owners;
|
|
579
|
+
};
|
|
580
|
+
const getDirectChildObjectStarts = (structure, containerStart) => {
|
|
581
|
+
const children = [];
|
|
582
|
+
const objectStarts = [containerStart];
|
|
583
|
+
for (let index = containerStart + 1; index < structure.length && objectStarts.length > 0; index++) {
|
|
584
|
+
if (structure[index] === "{") {
|
|
585
|
+
if (objectStarts.at(-1) === containerStart) children.push(index);
|
|
586
|
+
objectStarts.push(index);
|
|
587
|
+
} else if (structure[index] === "}") {
|
|
588
|
+
objectStarts.pop();
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return children;
|
|
592
|
+
};
|
|
593
|
+
const getOwnedObjectPropertyStarts = (searchable, structure, property, owners) => {
|
|
594
|
+
const pattern = new RegExp(`\\b${property}\\b\\s*:\\s*\\{`, "g");
|
|
595
|
+
return [...searchable.matchAll(pattern)].flatMap((match) => {
|
|
596
|
+
const owner = getOwningObjectStart(structure, match.index);
|
|
597
|
+
const objectStart = match.index + match[0].lastIndexOf("{");
|
|
598
|
+
return owner !== void 0 && owners.has(owner) && hasCodePropertySeparator(structure, match) && structure[objectStart] === "{" ? [objectStart] : [];
|
|
599
|
+
});
|
|
600
|
+
};
|
|
601
|
+
const getBasicOptionOwners = (searchable, structure) => {
|
|
602
|
+
const owners = getDefineConfigOptionOwners(searchable);
|
|
603
|
+
for (const projectDefaultsOwner of getOwnedObjectPropertyStarts(
|
|
604
|
+
searchable,
|
|
605
|
+
structure,
|
|
606
|
+
"projectDefaults",
|
|
607
|
+
owners
|
|
608
|
+
)) {
|
|
609
|
+
owners.add(projectDefaultsOwner);
|
|
610
|
+
}
|
|
611
|
+
const visitedProjectContainers = /* @__PURE__ */ new Set();
|
|
612
|
+
let projectContainers = getOwnedObjectPropertyStarts(searchable, structure, "projects", owners);
|
|
613
|
+
while (projectContainers.length > 0) {
|
|
614
|
+
for (const container of projectContainers) {
|
|
615
|
+
visitedProjectContainers.add(container);
|
|
616
|
+
for (const projectOwner of getDirectChildObjectStarts(structure, container)) {
|
|
617
|
+
owners.add(projectOwner);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
projectContainers = getOwnedObjectPropertyStarts(searchable, structure, "projects", owners).filter((container) => !visitedProjectContainers.has(container));
|
|
621
|
+
}
|
|
622
|
+
return owners;
|
|
623
|
+
};
|
|
624
|
+
const getOwnedPropertyMatches = (searchable, structure, property, owners) => {
|
|
625
|
+
const pattern = new RegExp(`\\b${property}\\b\\s*:`, "g");
|
|
626
|
+
return [...searchable.matchAll(pattern)].flatMap((match) => {
|
|
627
|
+
const owner = getOwningObjectStart(structure, match.index);
|
|
628
|
+
return owner !== void 0 && owners.has(owner) && hasCodePropertySeparator(structure, match) ? [match] : [];
|
|
629
|
+
});
|
|
630
|
+
};
|
|
631
|
+
const modernizeLiteralFeatureArrays = (content) => {
|
|
632
|
+
const searchable = maskNonCode(content, true);
|
|
633
|
+
const structure = maskNonCode(content);
|
|
634
|
+
const optionOwners = getBasicOptionOwners(searchable, structure);
|
|
635
|
+
const selections = [...searchable.matchAll(
|
|
636
|
+
/\b(?:extensions|formats|libraries|testing|tools)\b\s*:\s*\[([\s\S]*?)\]\s*,?/g
|
|
637
|
+
)].flatMap((match) => {
|
|
638
|
+
if (/\/[/*]/.test(content.slice(match.index, match.index + match[0].length))) return [];
|
|
639
|
+
const features2 = match[1].split(",").filter(Boolean).map(toFeatureName);
|
|
640
|
+
const owner = getOwningObjectStart(structure, match.index);
|
|
641
|
+
if (owner === void 0 || !optionOwners.has(owner) || !hasCodePropertySeparator(structure, match) || features2.length === 0 || features2.some((feature) => feature === null)) return [];
|
|
642
|
+
return [{
|
|
643
|
+
end: match.index + match[0].length,
|
|
644
|
+
features: features2,
|
|
645
|
+
owner,
|
|
646
|
+
start: match.index
|
|
647
|
+
}];
|
|
648
|
+
});
|
|
649
|
+
if (selections.length === 0) return { changed: false, content, features: [] };
|
|
650
|
+
const features = [...new Set(selections.flatMap((selection) => selection.features))];
|
|
651
|
+
const selectionsByOwner = /* @__PURE__ */ new Map();
|
|
652
|
+
for (const selection of selections) {
|
|
653
|
+
const ownedSelections = selectionsByOwner.get(selection.owner);
|
|
654
|
+
if (ownedSelections) ownedSelections.push(selection);
|
|
655
|
+
else selectionsByOwner.set(selection.owner, [selection]);
|
|
656
|
+
}
|
|
657
|
+
const existingFeaturesByOwner = /* @__PURE__ */ new Map();
|
|
658
|
+
for (const match of searchable.matchAll(/\bfeatures\b\s*:\s*\{/g)) {
|
|
659
|
+
const owner = getOwningObjectStart(structure, match.index);
|
|
660
|
+
if (owner !== void 0 && !existingFeaturesByOwner.has(owner)) {
|
|
661
|
+
existingFeaturesByOwner.set(owner, match);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
const edits = [];
|
|
665
|
+
for (const [owner, ownedSelections] of selectionsByOwner) {
|
|
666
|
+
const ownedFeatures = [...new Set(ownedSelections.flatMap((selection) => selection.features))];
|
|
667
|
+
const first = ownedSelections[0];
|
|
668
|
+
const lineStart = content.lastIndexOf("\n", first.start) + 1;
|
|
669
|
+
const indent = /^\s*/.exec(content.slice(lineStart, first.start))?.[0] ?? "";
|
|
670
|
+
const existingFeatures = existingFeaturesByOwner.get(owner);
|
|
671
|
+
const featureBlock = existingFeatures ? `
|
|
672
|
+
${ownedFeatures.map((feature) => `${indent} ${JSON.stringify(feature)}: true,`).join("\n")}` : [
|
|
673
|
+
"features: {",
|
|
674
|
+
...ownedFeatures.map((feature) => `${indent} ${JSON.stringify(feature)}: true,`),
|
|
675
|
+
`${indent}},`
|
|
676
|
+
].join("\n");
|
|
677
|
+
for (const selection of ownedSelections) {
|
|
678
|
+
edits.push({
|
|
679
|
+
end: selection.end,
|
|
680
|
+
replacement: !existingFeatures && selection === first ? featureBlock : "",
|
|
681
|
+
start: selection.start
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
if (existingFeatures) {
|
|
685
|
+
const insertionIndex = (existingFeatures.index ?? 0) + existingFeatures[0].length;
|
|
686
|
+
edits.push({
|
|
687
|
+
end: insertionIndex,
|
|
688
|
+
replacement: featureBlock,
|
|
689
|
+
start: insertionIndex
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
let updated = content;
|
|
694
|
+
for (const edit of edits.sort((a, b) => b.start - a.start)) {
|
|
695
|
+
updated = `${updated.slice(0, edit.start)}${edit.replacement}${updated.slice(edit.end)}`;
|
|
696
|
+
}
|
|
697
|
+
return { changed: updated !== content, content: updated, features };
|
|
698
|
+
};
|
|
699
|
+
const modernizeConfigOptions = (content) => {
|
|
700
|
+
const changes = [];
|
|
701
|
+
let updated = content;
|
|
702
|
+
const searchable = maskNonCode(updated, true);
|
|
703
|
+
const structure = maskNonCode(updated);
|
|
704
|
+
const basicOptionOwners = getBasicOptionOwners(searchable, structure);
|
|
705
|
+
const featureOwners = new Set(
|
|
706
|
+
getOwnedPropertyMatches(searchable, structure, "features", basicOptionOwners).flatMap((match) => {
|
|
707
|
+
if (match.index === void 0) return [];
|
|
708
|
+
const owner = getOwningObjectStart(structure, match.index);
|
|
709
|
+
return owner === void 0 ? [] : [owner];
|
|
710
|
+
})
|
|
711
|
+
);
|
|
712
|
+
const integrationMatches = getOwnedPropertyMatches(searchable, structure, "integrations", basicOptionOwners).filter((match) => {
|
|
713
|
+
if (match.index === void 0) return false;
|
|
714
|
+
const owner = getOwningObjectStart(structure, match.index);
|
|
715
|
+
return owner !== void 0 && !featureOwners.has(owner);
|
|
716
|
+
});
|
|
717
|
+
if (integrationMatches.length > 0) {
|
|
718
|
+
updated = replaceCodeProperty(updated, "integrations", "features", integrationMatches);
|
|
719
|
+
changes.push("Replaced the deprecated integrations map with features.");
|
|
720
|
+
}
|
|
721
|
+
const updatedSearchable = maskNonCode(updated, true);
|
|
722
|
+
const updatedStructure = maskNonCode(updated);
|
|
723
|
+
const optionOwners = getDefineConfigOptionOwners(updatedSearchable);
|
|
724
|
+
const rootMatches = getOwnedPropertyMatches(updatedSearchable, updatedStructure, "root", optionOwners);
|
|
725
|
+
const rootAliasMatches = /* @__PURE__ */ new Map([
|
|
726
|
+
["detectRootDir", getOwnedPropertyMatches(updatedSearchable, updatedStructure, "detectRootDir", optionOwners)],
|
|
727
|
+
["tsconfigRootDir", getOwnedPropertyMatches(updatedSearchable, updatedStructure, "tsconfigRootDir", optionOwners)]
|
|
728
|
+
]);
|
|
729
|
+
const rootAliases = [...rootAliasMatches].filter(([, matches]) => matches.length > 0).map(([alias]) => alias);
|
|
730
|
+
if (rootMatches.length === 0 && rootAliases.length === 1) {
|
|
731
|
+
const alias = rootAliases[0];
|
|
732
|
+
updated = replaceCodeProperty(updated, alias, "root", rootAliasMatches.get(alias));
|
|
733
|
+
changes.push(`Replaced ${alias} with the v3 root option.`);
|
|
734
|
+
} else if (rootAliases.length > 0) {
|
|
735
|
+
changes.push(
|
|
736
|
+
`Manual action required: consolidate ${rootAliases.join(" and ")} into root after confirming they resolve to the same directory.`
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
const featureMigration = modernizeLiteralFeatureArrays(updated);
|
|
740
|
+
if (featureMigration.changed) {
|
|
741
|
+
updated = featureMigration.content;
|
|
742
|
+
changes.push(`Replaced literal category arrays with the v3 features map (${featureMigration.features.join(", ")}).`);
|
|
743
|
+
}
|
|
744
|
+
if (maskNonCode(updated, true).includes("better-tailwindcss/no-unknown-classes")) {
|
|
745
|
+
changes.push(
|
|
746
|
+
'Manual action required: replace the raw better-tailwindcss/no-unknown-classes override with projects["<scope>"].tailwind.noUnknownClasses.'
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
return { changes, content: updated };
|
|
750
|
+
};
|
|
423
751
|
const getExplicitConfigFeaturePackages = (content) => {
|
|
424
752
|
const packages = /* @__PURE__ */ new Set();
|
|
425
753
|
const codeContent = maskNonCode(content);
|
|
426
754
|
const searchableContent = maskNonCode(content, true);
|
|
755
|
+
const optionOwners = getBasicOptionOwners(searchableContent, codeContent);
|
|
756
|
+
const isBasicOption = (match) => {
|
|
757
|
+
const owner = getOwningObjectStart(codeContent, match.index ?? -1);
|
|
758
|
+
return owner !== void 0 && optionOwners.has(owner) && hasCodePropertySeparator(codeContent, match);
|
|
759
|
+
};
|
|
427
760
|
for (const category of Object.keys(FEATURE_PACKAGES)) {
|
|
428
761
|
const categorySelections = searchableContent.matchAll(
|
|
429
|
-
// eslint-disable-next-line security/detect-non-literal-regexp -- category names come from the internal package registry
|
|
430
762
|
new RegExp(`(?:\\b${category}\\b|['"]${category}['"])\\s*:\\s*\\[([\\s\\S]*?)\\]`, "g")
|
|
431
763
|
);
|
|
432
764
|
for (const selection of categorySelections) {
|
|
433
|
-
if (
|
|
765
|
+
if (isBasicOption(selection) && selection[1].trim()) {
|
|
434
766
|
packages.add(FEATURE_PACKAGES[category]);
|
|
435
767
|
break;
|
|
436
768
|
}
|
|
@@ -438,7 +770,7 @@ const getExplicitConfigFeaturePackages = (content) => {
|
|
|
438
770
|
}
|
|
439
771
|
const featureSelections = /(?:\b(?:features|integrations)\b|['"](?:features|integrations)['"])\s*:\s*\{([\s\S]*?)\}/g;
|
|
440
772
|
for (const selection of searchableContent.matchAll(featureSelections)) {
|
|
441
|
-
if (!
|
|
773
|
+
if (!isBasicOption(selection)) continue;
|
|
442
774
|
const enabledFeaturePattern = /(?:['"]([^'"]+)['"]|([a-zA-Z_$][\w$-]*))\s*:\s*true\b/g;
|
|
443
775
|
for (const match of selection[1].matchAll(enabledFeaturePattern)) {
|
|
444
776
|
const featureName = (match[1] || match[2]).toLowerCase();
|
|
@@ -448,7 +780,7 @@ const getExplicitConfigFeaturePackages = (content) => {
|
|
|
448
780
|
}
|
|
449
781
|
const enumPresetPattern = /(?:\bpreset\b|['"]preset['"])\s*:\s*Preset\.(All|App|CI|Library|Monorepo)/g;
|
|
450
782
|
for (const match of searchableContent.matchAll(enumPresetPattern)) {
|
|
451
|
-
if (!
|
|
783
|
+
if (!isBasicOption(match)) continue;
|
|
452
784
|
const presetName = match[1].toLowerCase();
|
|
453
785
|
for (const packageName of PRESET_FEATURE_PACKAGES[presetName] ?? []) {
|
|
454
786
|
packages.add(packageName);
|
|
@@ -456,31 +788,39 @@ const getExplicitConfigFeaturePackages = (content) => {
|
|
|
456
788
|
}
|
|
457
789
|
const stringPresetPattern = /(?:\bpreset\b|['"]preset['"])\s*:\s*(['"`])(all|app|ci|library|monorepo)\1/g;
|
|
458
790
|
for (const match of searchableContent.matchAll(stringPresetPattern)) {
|
|
459
|
-
if (!
|
|
791
|
+
if (!isBasicOption(match)) continue;
|
|
460
792
|
for (const packageName of PRESET_FEATURE_PACKAGES[match[2]] ?? []) {
|
|
461
793
|
packages.add(packageName);
|
|
462
794
|
}
|
|
463
795
|
}
|
|
464
796
|
const strictPattern = /(?:\bstrict\b|['"]strict['"])\s*:\s*(['"`])pedantic\1/g;
|
|
465
797
|
for (const match of searchableContent.matchAll(strictPattern)) {
|
|
466
|
-
if (
|
|
798
|
+
if (isBasicOption(match)) {
|
|
467
799
|
packages.add(FEATURE_PACKAGES.extensions);
|
|
468
800
|
break;
|
|
469
801
|
}
|
|
470
802
|
}
|
|
471
803
|
return [...packages];
|
|
472
804
|
};
|
|
805
|
+
const replacePackageSpecifiers = (content, from, to) => content.replace(
|
|
806
|
+
/(\b(?:from|import|export)\s*(?:\(\s*)?|\brequire\s*\(\s*)(['"])([^'"]+)\2/g,
|
|
807
|
+
(statement, prefix, quote, specifier) => specifier === from ? `${prefix}${quote}${to}${quote}` : statement
|
|
808
|
+
);
|
|
473
809
|
const migrateConfigToV3 = (content, mode) => {
|
|
474
810
|
const changes = [];
|
|
475
811
|
const featurePackages = new Set(getExplicitConfigFeaturePackages(content));
|
|
476
812
|
let migrated = content;
|
|
477
|
-
|
|
478
|
-
|
|
813
|
+
const liteMigrated = replacePackageSpecifiers(migrated, LITE_PACKAGE, BASIC_PACKAGE);
|
|
814
|
+
if (liteMigrated !== migrated) {
|
|
815
|
+
migrated = liteMigrated;
|
|
479
816
|
changes.push("Replaced the Lite compatibility import with Basic.");
|
|
480
817
|
}
|
|
481
|
-
if (mode === "full"
|
|
482
|
-
|
|
483
|
-
|
|
818
|
+
if (mode === "full") {
|
|
819
|
+
const fullMigrated = replacePackageSpecifiers(migrated, BASIC_PACKAGE, FULL_PACKAGE);
|
|
820
|
+
if (fullMigrated !== migrated) {
|
|
821
|
+
migrated = fullMigrated;
|
|
822
|
+
changes.push("Switched the root config import to the full bundle.");
|
|
823
|
+
}
|
|
484
824
|
}
|
|
485
825
|
if (mode === "lean") {
|
|
486
826
|
const split = splitBasicIntegrationImports(migrated);
|
|
@@ -506,13 +846,21 @@ const migrateConfigToV3 = (content, mode) => {
|
|
|
506
846
|
migrated = remixMigrated;
|
|
507
847
|
changes.push('Moved frameworks.remix to frameworks["react-router"].');
|
|
508
848
|
}
|
|
509
|
-
|
|
510
|
-
migrated
|
|
849
|
+
const reactRouterMigrated = replacePackageSpecifiers(
|
|
850
|
+
migrated,
|
|
851
|
+
REMIX_PACKAGE,
|
|
852
|
+
FRAMEWORK_PACKAGES["react-router"]
|
|
853
|
+
);
|
|
854
|
+
if (reactRouterMigrated !== migrated) {
|
|
855
|
+
migrated = reactRouterMigrated;
|
|
511
856
|
changes.push("Replaced the Remix package import with React Router.");
|
|
512
857
|
}
|
|
513
858
|
if (/\bloadModule\b/.test(migrated)) {
|
|
514
859
|
changes.push("Manual action required: replace loadModule usage with createModuleLoader(resolver).");
|
|
515
860
|
}
|
|
861
|
+
const modernized = modernizeConfigOptions(migrated);
|
|
862
|
+
migrated = modernized.content;
|
|
863
|
+
changes.push(...modernized.changes);
|
|
516
864
|
return {
|
|
517
865
|
changes,
|
|
518
866
|
content: migrated,
|
|
@@ -525,13 +873,18 @@ const shouldUseFullBundle = (configContent, packageJson, forceFull) => {
|
|
|
525
873
|
if (configContent) {
|
|
526
874
|
const codeContent = maskNonCode(configContent);
|
|
527
875
|
const searchableContent = maskNonCode(configContent, true);
|
|
876
|
+
const optionOwners = getDefineConfigOptionOwners(searchableContent);
|
|
877
|
+
const isRootOption = (match) => {
|
|
878
|
+
const owner = getOwningObjectStart(codeContent, match.index ?? -1);
|
|
879
|
+
return owner !== void 0 && optionOwners.has(owner) && hasCodePropertySeparator(codeContent, match);
|
|
880
|
+
};
|
|
528
881
|
const enumPresetPattern = /(?:\bpreset\b|['"]preset['"])\s*:\s*Preset\.All/g;
|
|
529
882
|
for (const match of searchableContent.matchAll(enumPresetPattern)) {
|
|
530
|
-
if (
|
|
883
|
+
if (isRootOption(match)) return true;
|
|
531
884
|
}
|
|
532
885
|
const stringPresetPattern = /(?:\bpreset\b|['"]preset['"])\s*:\s*(['"`])all\1/g;
|
|
533
886
|
for (const match of searchableContent.matchAll(stringPresetPattern)) {
|
|
534
|
-
if (
|
|
887
|
+
if (isRootOption(match)) return true;
|
|
535
888
|
}
|
|
536
889
|
}
|
|
537
890
|
return getDeclaredConfigPackages(packageJson).has(FULL_PACKAGE);
|
|
@@ -780,3 +1133,4 @@ export {
|
|
|
780
1133
|
handleMigrateV3,
|
|
781
1134
|
migrateConfigToV3
|
|
782
1135
|
};
|
|
1136
|
+
//# sourceMappingURL=cli-migration.js.map
|