From e614c53caa88bc9358f3ec6f9ba9ae7615b28fe1 Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Mon, 4 May 2026 14:35:11 +0900 Subject: [PATCH 1/6] fix: include framework TypeScript scripts in tsconfig Co-authored-by: WillBooster (Codex CLI) --- packages/wbfy/src/generators/tsconfig.ts | 19 +++++++ packages/wbfy/test/tsconfigGenerator.test.ts | 56 ++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/packages/wbfy/src/generators/tsconfig.ts b/packages/wbfy/src/generators/tsconfig.ts index 56f47b8a..cdadd5ec 100644 --- a/packages/wbfy/src/generators/tsconfig.ts +++ b/packages/wbfy/src/generators/tsconfig.ts @@ -2,6 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import merge from 'deepmerge'; +import fg from 'fast-glob'; import type { TsConfigJson } from 'type-fest'; import { logger } from '../logger.js'; @@ -141,6 +142,7 @@ async function cleanupLegacyTsconfigModuleSettings(config: PackageConfig): Promi const settings = JSON.parse(await fs.promises.readFile(filePath, 'utf8')) as TsConfigJson; normalizeNextTsconfigModuleSettings(settings.compilerOptions); normalizeNextTsconfigPathAliases(settings.compilerOptions); + await addScriptsIncludeForFrameworkProject(settings, config); await promisePool.run(() => fsUtil.generateFile(filePath, JSON.stringify(settings, undefined, 2))); } catch { // Next/Blitz own their tsconfig shape, but TypeScript 6 no longer accepts @@ -148,6 +150,23 @@ async function cleanupLegacyTsconfigModuleSettings(config: PackageConfig): Promi } } +async function addScriptsIncludeForFrameworkProject(settings: TsConfigJson, config: PackageConfig): Promise { + if (settings.include?.includes('scripts/**/*') || !(await doesContainScriptsTypeScript(config))) return; + + settings.include ??= []; + settings.include.push('scripts/**/*'); + settings.include.sort(); +} + +async function doesContainScriptsTypeScript(config: PackageConfig): Promise { + const filePaths = await fg.glob('scripts/**/*.{cts,mts,ts,tsx}', { + cwd: config.dirPath, + onlyFiles: true, + }); + + return filePaths.length > 0; +} + function normalizeNextTsconfigModuleSettings(compilerOptions: TsConfigJson.CompilerOptions | undefined): void { if (!compilerOptions) return; if ( diff --git a/packages/wbfy/test/tsconfigGenerator.test.ts b/packages/wbfy/test/tsconfigGenerator.test.ts index 1711be24..7b28993c 100644 --- a/packages/wbfy/test/tsconfigGenerator.test.ts +++ b/packages/wbfy/test/tsconfigGenerator.test.ts @@ -219,6 +219,62 @@ test('normalizes Next.js aliases for TypeScript 6 linting', async () => { expect(tsconfig.compilerOptions.paths).toEqual({ '@/*': ['./src/*'] }); }); +test('adds scripts include for Next.js projects with TypeScript scripts', async () => { + const dirPath = createTempDir(); + await fs.promises.mkdir(path.join(dirPath, 'scripts'), { recursive: true }); + await fs.promises.writeFile( + path.join(dirPath, 'tsconfig.json'), + JSON.stringify({ + compilerOptions: { + moduleResolution: 'bundler', + }, + include: ['src/**/*'], + }) + ); + await fs.promises.writeFile(path.join(dirPath, 'scripts', 'run.ts'), 'console.log(process.cwd());\n'); + + await generateTsconfig( + createConfig({ + dirPath, + depending: { + ...createConfig().depending, + next: true, + }, + }) + ); + await promisePool.promiseAll(); + + const tsconfig = await readTsconfig(dirPath); + expect(tsconfig.include).toEqual(['scripts/**/*', 'src/**/*']); +}); + +test('does not add scripts include for Next.js projects without TypeScript scripts', async () => { + const dirPath = createTempDir(); + await fs.promises.writeFile( + path.join(dirPath, 'tsconfig.json'), + JSON.stringify({ + compilerOptions: { + moduleResolution: 'bundler', + }, + include: ['src/**/*'], + }) + ); + + await generateTsconfig( + createConfig({ + dirPath, + depending: { + ...createConfig().depending, + next: true, + }, + }) + ); + await promisePool.promiseAll(); + + const tsconfig = await readTsconfig(dirPath); + expect(tsconfig.include).toEqual(['src/**/*']); +}); + function createTempDir(): string { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wbfy-tsconfig-')); tempDirs.push(tempDir); From 087782482c1620313fc983345b943e4dd029ccaa Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Mon, 4 May 2026 14:39:10 +0900 Subject: [PATCH 2/6] fix: always include framework scripts in tsconfig Co-authored-by: WillBooster (Codex CLI) --- packages/wbfy/src/generators/tsconfig.ts | 16 +++------------- packages/wbfy/test/tsconfigGenerator.test.ts | 10 ++++------ 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/packages/wbfy/src/generators/tsconfig.ts b/packages/wbfy/src/generators/tsconfig.ts index cdadd5ec..3a141546 100644 --- a/packages/wbfy/src/generators/tsconfig.ts +++ b/packages/wbfy/src/generators/tsconfig.ts @@ -2,7 +2,6 @@ import fs from 'node:fs'; import path from 'node:path'; import merge from 'deepmerge'; -import fg from 'fast-glob'; import type { TsConfigJson } from 'type-fest'; import { logger } from '../logger.js'; @@ -142,7 +141,7 @@ async function cleanupLegacyTsconfigModuleSettings(config: PackageConfig): Promi const settings = JSON.parse(await fs.promises.readFile(filePath, 'utf8')) as TsConfigJson; normalizeNextTsconfigModuleSettings(settings.compilerOptions); normalizeNextTsconfigPathAliases(settings.compilerOptions); - await addScriptsIncludeForFrameworkProject(settings, config); + addScriptsIncludeForFrameworkProject(settings); await promisePool.run(() => fsUtil.generateFile(filePath, JSON.stringify(settings, undefined, 2))); } catch { // Next/Blitz own their tsconfig shape, but TypeScript 6 no longer accepts @@ -150,23 +149,14 @@ async function cleanupLegacyTsconfigModuleSettings(config: PackageConfig): Promi } } -async function addScriptsIncludeForFrameworkProject(settings: TsConfigJson, config: PackageConfig): Promise { - if (settings.include?.includes('scripts/**/*') || !(await doesContainScriptsTypeScript(config))) return; +function addScriptsIncludeForFrameworkProject(settings: TsConfigJson): void { + if (settings.include?.includes('scripts/**/*')) return; settings.include ??= []; settings.include.push('scripts/**/*'); settings.include.sort(); } -async function doesContainScriptsTypeScript(config: PackageConfig): Promise { - const filePaths = await fg.glob('scripts/**/*.{cts,mts,ts,tsx}', { - cwd: config.dirPath, - onlyFiles: true, - }); - - return filePaths.length > 0; -} - function normalizeNextTsconfigModuleSettings(compilerOptions: TsConfigJson.CompilerOptions | undefined): void { if (!compilerOptions) return; if ( diff --git a/packages/wbfy/test/tsconfigGenerator.test.ts b/packages/wbfy/test/tsconfigGenerator.test.ts index 7b28993c..55aaf050 100644 --- a/packages/wbfy/test/tsconfigGenerator.test.ts +++ b/packages/wbfy/test/tsconfigGenerator.test.ts @@ -219,9 +219,8 @@ test('normalizes Next.js aliases for TypeScript 6 linting', async () => { expect(tsconfig.compilerOptions.paths).toEqual({ '@/*': ['./src/*'] }); }); -test('adds scripts include for Next.js projects with TypeScript scripts', async () => { +test('adds scripts include for Next.js projects', async () => { const dirPath = createTempDir(); - await fs.promises.mkdir(path.join(dirPath, 'scripts'), { recursive: true }); await fs.promises.writeFile( path.join(dirPath, 'tsconfig.json'), JSON.stringify({ @@ -231,7 +230,6 @@ test('adds scripts include for Next.js projects with TypeScript scripts', async include: ['src/**/*'], }) ); - await fs.promises.writeFile(path.join(dirPath, 'scripts', 'run.ts'), 'console.log(process.cwd());\n'); await generateTsconfig( createConfig({ @@ -248,7 +246,7 @@ test('adds scripts include for Next.js projects with TypeScript scripts', async expect(tsconfig.include).toEqual(['scripts/**/*', 'src/**/*']); }); -test('does not add scripts include for Next.js projects without TypeScript scripts', async () => { +test('does not duplicate scripts include for Next.js projects', async () => { const dirPath = createTempDir(); await fs.promises.writeFile( path.join(dirPath, 'tsconfig.json'), @@ -256,7 +254,7 @@ test('does not add scripts include for Next.js projects without TypeScript scrip compilerOptions: { moduleResolution: 'bundler', }, - include: ['src/**/*'], + include: ['scripts/**/*', 'src/**/*'], }) ); @@ -272,7 +270,7 @@ test('does not add scripts include for Next.js projects without TypeScript scrip await promisePool.promiseAll(); const tsconfig = await readTsconfig(dirPath); - expect(tsconfig.include).toEqual(['src/**/*']); + expect(tsconfig.include).toEqual(['scripts/**/*', 'src/**/*']); }); function createTempDir(): string { From ab4768efa6ffbdab12b96c1ce322225b97243399 Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Mon, 4 May 2026 14:48:22 +0900 Subject: [PATCH 3/6] fix: avoid global oxfmt config collision Co-authored-by: WillBooster (Codex CLI) --- packages/wbfy/src/generators/oxfmtConfig.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wbfy/src/generators/oxfmtConfig.ts b/packages/wbfy/src/generators/oxfmtConfig.ts index 12bb1a44..32d93f73 100644 --- a/packages/wbfy/src/generators/oxfmtConfig.ts +++ b/packages/wbfy/src/generators/oxfmtConfig.ts @@ -42,10 +42,10 @@ function getConfigContent(config: PackageConfig): string { `// oxlint-disable unicorn/prefer-module -- Oxfmt config files are only auto-discovered as .ts, and CommonJS avoids Node typeless ESM warnings. const oxfmtConfig = require('@willbooster/oxfmt-config'); -const config = oxfmtConfig.default ?? oxfmtConfig;` +const oxfmtResolvedConfig = oxfmtConfig.default ?? oxfmtConfig;` )} -${managedConfigBlocks.getBlock('export', 'module.exports = config;')} +${managedConfigBlocks.getBlock('export', 'module.exports = oxfmtResolvedConfig;')} `; } From dacebcfe71c04160092e992e59cdc28b31acea5e Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Mon, 4 May 2026 15:01:54 +0900 Subject: [PATCH 4/6] fix: preserve framework tsconfig default includes Co-authored-by: WillBooster (Codex CLI) --- packages/wbfy/src/generators/tsconfig.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/wbfy/src/generators/tsconfig.ts b/packages/wbfy/src/generators/tsconfig.ts index 3a141546..c16da806 100644 --- a/packages/wbfy/src/generators/tsconfig.ts +++ b/packages/wbfy/src/generators/tsconfig.ts @@ -150,9 +150,11 @@ async function cleanupLegacyTsconfigModuleSettings(config: PackageConfig): Promi } function addScriptsIncludeForFrameworkProject(settings: TsConfigJson): void { - if (settings.include?.includes('scripts/**/*')) return; + // Omitting include lets framework tsconfigs keep TypeScript's default + // "all TS/TSX files" behavior, which already covers scripts. + if (!settings.include) return; + if (settings.include.includes('scripts/**/*')) return; - settings.include ??= []; settings.include.push('scripts/**/*'); settings.include.sort(); } From 8d9eb557503f905f560cb398dafeb1368da1a114 Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Mon, 4 May 2026 15:10:54 +0900 Subject: [PATCH 5/6] fix: avoid generic oxlint config variable name Co-authored-by: WillBooster (Codex CLI) --- packages/wbfy/src/generators/oxlintConfig.ts | 8 ++++---- packages/wbfy/test/oxlintConfigGenerator.test.ts | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/wbfy/src/generators/oxlintConfig.ts b/packages/wbfy/src/generators/oxlintConfig.ts index 3dffba99..45e0f8dc 100644 --- a/packages/wbfy/src/generators/oxlintConfig.ts +++ b/packages/wbfy/src/generators/oxlintConfig.ts @@ -68,15 +68,15 @@ function getConfigContent(config: PackageConfig): string { `// oxlint-disable unicorn/prefer-module -- Oxlint only auto-discovers .ts config files, and CommonJS avoids Node typeless ESM warnings. const oxlintBaseConfig = require('@willbooster/oxlint-config'); -const config = oxlintBaseConfig.default ?? oxlintBaseConfig;` +const oxlintResolvedConfig = oxlintBaseConfig.default ?? oxlintBaseConfig;` )} -${managedConfigBlocks.getBlock('export', 'module.exports = config;')} +${managedConfigBlocks.getBlock('export', 'module.exports = oxlintResolvedConfig;')} `; } - return `${managedConfigBlocks.getBlock('base', "import config from '@willbooster/oxlint-config';")} + return `${managedConfigBlocks.getBlock('base', "import oxlintResolvedConfig from '@willbooster/oxlint-config';")} -${managedConfigBlocks.getBlock('export', 'export default config;')} +${managedConfigBlocks.getBlock('export', 'export default oxlintResolvedConfig;')} `; } diff --git a/packages/wbfy/test/oxlintConfigGenerator.test.ts b/packages/wbfy/test/oxlintConfigGenerator.test.ts index f108406a..8a97730a 100644 --- a/packages/wbfy/test/oxlintConfigGenerator.test.ts +++ b/packages/wbfy/test/oxlintConfigGenerator.test.ts @@ -35,8 +35,8 @@ export default config; expect(content).toContain('// wbfy:start oxlint-base'); expect(content).toContain('// wbfy:start oxlint-export'); expect(content).toContain("const oxlintBaseConfig = require('@willbooster/oxlint-config');"); - expect(content).toContain('const config = oxlintBaseConfig.default ?? oxlintBaseConfig;'); - expect(content).toContain('module.exports = config;'); + expect(content).toContain('const oxlintResolvedConfig = oxlintBaseConfig.default ?? oxlintBaseConfig;'); + expect(content).toContain('module.exports = oxlintResolvedConfig;'); expect(content).not.toContain("config.ignorePatterns?.push('generated/**');"); }); @@ -48,7 +48,7 @@ test('updates only managed blocks in marked oxlint config', async () => { const staleConfig = require('@willbooster/oxlint-config'); // wbfy:end oxlint-base -config.ignorePatterns?.push('generated/**'); +oxlintResolvedConfig.ignorePatterns?.push('generated/**'); // wbfy:start oxlint-export module.exports = staleConfig; @@ -61,8 +61,8 @@ module.exports = staleConfig; const content = await readOxlintConfig(dirPath); expect(content).toContain("const oxlintBaseConfig = require('@willbooster/oxlint-config');"); - expect(content).toContain("config.ignorePatterns?.push('generated/**');"); - expect(content).toContain('module.exports = config;'); + expect(content).toContain("oxlintResolvedConfig.ignorePatterns?.push('generated/**');"); + expect(content).toContain('module.exports = oxlintResolvedConfig;'); expect(content).not.toContain('staleConfig'); }); @@ -73,8 +73,8 @@ test('generates esm oxlint config for esm packages', async () => { await promisePool.promiseAll(); const content = await readOxlintConfig(dirPath); - expect(content).toContain("import config from '@willbooster/oxlint-config';"); - expect(content).toContain('export default config;'); + expect(content).toContain("import oxlintResolvedConfig from '@willbooster/oxlint-config';"); + expect(content).toContain('export default oxlintResolvedConfig;'); }); function createTempDir(): string { From 6be336234db2cf1ceaf15713baa74bc2a8aa9786 Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Mon, 4 May 2026 15:14:01 +0900 Subject: [PATCH 6/6] fix: migrate oxlint config references Co-authored-by: WillBooster (Codex CLI) --- packages/wbfy/src/generators/oxlintConfig.ts | 16 +++++++++++----- packages/wbfy/test/oxlintConfigGenerator.test.ts | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/wbfy/src/generators/oxlintConfig.ts b/packages/wbfy/src/generators/oxlintConfig.ts index 45e0f8dc..60c7cf58 100644 --- a/packages/wbfy/src/generators/oxlintConfig.ts +++ b/packages/wbfy/src/generators/oxlintConfig.ts @@ -26,11 +26,13 @@ export async function generateOxlintConfig(config: PackageConfig, _rootConfig: P const desiredContent = shouldPreservePublishedLinterConfig && existingContent ? existingContent - : managedConfigBlocks.getConfigContent({ - desiredContent: getConfigContent(config), - existingContent, - filePath, - }); + : replaceLegacyConfigReferences( + managedConfigBlocks.getConfigContent({ + desiredContent: getConfigContent(config), + existingContent, + filePath, + }) + ); const promises: Promise[] = []; if (!shouldPreservePublishedLinterConfig) { @@ -57,6 +59,10 @@ export async function generateOxlintConfig(config: PackageConfig, _rootConfig: P }); } +function replaceLegacyConfigReferences(content: string): string { + return content.replaceAll('config.', 'oxlintResolvedConfig.'); +} + function getConfigContent(config: PackageConfig): string { // Do not collapse this to a static import for every package. CommonJS packages // type-check auto-discovered oxlint.config.ts as CommonJS, so importing the ESM diff --git a/packages/wbfy/test/oxlintConfigGenerator.test.ts b/packages/wbfy/test/oxlintConfigGenerator.test.ts index 8a97730a..af7fd25f 100644 --- a/packages/wbfy/test/oxlintConfigGenerator.test.ts +++ b/packages/wbfy/test/oxlintConfigGenerator.test.ts @@ -48,7 +48,7 @@ test('updates only managed blocks in marked oxlint config', async () => { const staleConfig = require('@willbooster/oxlint-config'); // wbfy:end oxlint-base -oxlintResolvedConfig.ignorePatterns?.push('generated/**'); +config.ignorePatterns?.push('generated/**'); // wbfy:start oxlint-export module.exports = staleConfig;