diff --git a/.gitignore b/.gitignore index b2264058..e882ea49 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ .playwright-cli/ .serena/ .tmp/ +.wb/ __generated__/ @willbooster/ dist/ diff --git a/package.json b/package.json index 4493e71a..f2f9316b 100644 --- a/package.json +++ b/package.json @@ -24,9 +24,7 @@ "prepare": "lefthook install || true", "release": "yarn multi-semantic-release --debug", "test": "CI=1 FORCE_COLOR=3 yarn workspaces foreach --all --verbose run test", - "typecheck": "yarn workspaces foreach --all --parallel --verbose run typecheck", - "verify-code": "yarn workspace @willbooster/wb start --working-dir \"$INIT_CWD\" verify-code", - "verify-code-with-tests": "yarn verify-code && yarn test" + "typecheck": "yarn workspaces foreach --all --parallel --verbose run typecheck" }, "devDependencies": { "@anolilab/multi-semantic-release": "4.4.1", diff --git a/packages/shared-lib-blitz-next/.gitignore b/packages/shared-lib-blitz-next/.gitignore index b2264058..e882ea49 100644 --- a/packages/shared-lib-blitz-next/.gitignore +++ b/packages/shared-lib-blitz-next/.gitignore @@ -14,6 +14,7 @@ .playwright-cli/ .serena/ .tmp/ +.wb/ __generated__/ @willbooster/ dist/ diff --git a/packages/shared-lib-blitz-next/package.json b/packages/shared-lib-blitz-next/package.json index 4804b6b1..1f83152c 100644 --- a/packages/shared-lib-blitz-next/package.json +++ b/packages/shared-lib-blitz-next/package.json @@ -40,9 +40,7 @@ "lint": "oxlint --no-error-on-unmatched-pattern .", "lint-fix": "yarn lint --fix", "test": "vitest", - "typecheck": "tsgo --noEmit", - "verify-code": "yarn workspace @willbooster/wb start --working-dir \"$INIT_CWD\" verify-code", - "verify-code-with-tests": "yarn verify-code && yarn test" + "typecheck": "tsgo --noEmit" }, "devDependencies": { "@tsconfig/node-lts": "24.0.0", diff --git a/packages/shared-lib-next/.gitignore b/packages/shared-lib-next/.gitignore index b2264058..e882ea49 100644 --- a/packages/shared-lib-next/.gitignore +++ b/packages/shared-lib-next/.gitignore @@ -14,6 +14,7 @@ .playwright-cli/ .serena/ .tmp/ +.wb/ __generated__/ @willbooster/ dist/ diff --git a/packages/shared-lib-next/package.json b/packages/shared-lib-next/package.json index 2acdbc2b..417af420 100644 --- a/packages/shared-lib-next/package.json +++ b/packages/shared-lib-next/package.json @@ -40,9 +40,7 @@ "lint": "oxlint --no-error-on-unmatched-pattern .", "lint-fix": "yarn lint --fix", "test": "vitest", - "typecheck": "tsgo --noEmit", - "verify-code": "yarn workspace @willbooster/wb start --working-dir \"$INIT_CWD\" verify-code", - "verify-code-with-tests": "yarn verify-code && yarn test" + "typecheck": "tsgo --noEmit" }, "devDependencies": { "@tsconfig/node-lts": "24.0.0", diff --git a/packages/shared-lib-node/.gitignore b/packages/shared-lib-node/.gitignore index b2264058..e882ea49 100644 --- a/packages/shared-lib-node/.gitignore +++ b/packages/shared-lib-node/.gitignore @@ -14,6 +14,7 @@ .playwright-cli/ .serena/ .tmp/ +.wb/ __generated__/ @willbooster/ dist/ diff --git a/packages/shared-lib-node/package.json b/packages/shared-lib-node/package.json index c2961064..817bf245 100644 --- a/packages/shared-lib-node/package.json +++ b/packages/shared-lib-node/package.json @@ -40,9 +40,7 @@ "lint": "oxlint --no-error-on-unmatched-pattern .", "lint-fix": "yarn lint --fix", "test": "vitest test/", - "typecheck": "tsgo --noEmit", - "verify-code": "yarn workspace @willbooster/wb start --working-dir \"$INIT_CWD\" verify-code", - "verify-code-with-tests": "yarn verify-code && yarn test" + "typecheck": "tsgo --noEmit" }, "dependencies": { "dotenv": "17.4.2", diff --git a/packages/shared-lib-react/.gitignore b/packages/shared-lib-react/.gitignore index f672b214..90952912 100644 --- a/packages/shared-lib-react/.gitignore +++ b/packages/shared-lib-react/.gitignore @@ -14,6 +14,7 @@ .playwright-cli/ .serena/ .tmp/ +.wb/ __generated__/ @willbooster/ dist/ diff --git a/packages/shared-lib-react/package.json b/packages/shared-lib-react/package.json index 3d98dd10..57284912 100644 --- a/packages/shared-lib-react/package.json +++ b/packages/shared-lib-react/package.json @@ -37,9 +37,7 @@ "lint-fix": "yarn lint --fix", "storybook": "start-storybook -p 6006", "test/ci": "yarn build-storybook", - "typecheck": "tsgo --noEmit", - "verify-code": "yarn workspace @willbooster/wb start --working-dir \"$INIT_CWD\" verify-code", - "verify-code-with-tests": "yarn verify-code && yarn test" + "typecheck": "tsgo --noEmit" }, "devDependencies": { "@babel/core": "7.29.0", diff --git a/packages/shared-lib/.gitignore b/packages/shared-lib/.gitignore index b2264058..e882ea49 100644 --- a/packages/shared-lib/.gitignore +++ b/packages/shared-lib/.gitignore @@ -14,6 +14,7 @@ .playwright-cli/ .serena/ .tmp/ +.wb/ __generated__/ @willbooster/ dist/ diff --git a/packages/shared-lib/package.json b/packages/shared-lib/package.json index ac4f3590..2fc313d5 100644 --- a/packages/shared-lib/package.json +++ b/packages/shared-lib/package.json @@ -40,9 +40,7 @@ "lint": "oxlint --no-error-on-unmatched-pattern .", "lint-fix": "yarn lint --fix", "test": "vitest", - "typecheck": "tsgo --noEmit", - "verify-code": "yarn workspace @willbooster/wb start --working-dir \"$INIT_CWD\" verify-code", - "verify-code-with-tests": "yarn verify-code && yarn test" + "typecheck": "tsgo --noEmit" }, "devDependencies": { "@tsconfig/node-lts": "24.0.0", diff --git a/packages/wb/.gitignore b/packages/wb/.gitignore index b2264058..e882ea49 100644 --- a/packages/wb/.gitignore +++ b/packages/wb/.gitignore @@ -14,6 +14,7 @@ .playwright-cli/ .serena/ .tmp/ +.wb/ __generated__/ @willbooster/ dist/ diff --git a/packages/wb/src/commands/optimizeForDockerBuild.ts b/packages/wb/src/commands/optimizeForDockerBuild.ts index 7e86d0f3..d710858c 100644 --- a/packages/wb/src/commands/optimizeForDockerBuild.ts +++ b/packages/wb/src/commands/optimizeForDockerBuild.ts @@ -6,11 +6,13 @@ import chalk from 'chalk'; import type { PackageJson } from 'type-fest'; import type { CommandModule, InferredOptionTypes } from 'yargs'; -import { findDescendantProjects, type Project } from '../project.js'; +import { findDescendantProjects } from '../project.js'; import { packageManager } from '../utils/runtime.js'; import { prepareForRunningCommand } from './commandUtils.js'; +// These tools are declared as devDependencies in source repos, but optimized Docker +// package.json files still need them for in-image codegen/build steps. const runtimeDevDependencies = ['@willbooster/wb', 'build-ts']; const builder = { @@ -39,6 +41,8 @@ export const optimizeForDockerBuildCommand: CommandModule, packageJson: PackageJson): void { promoteRuntimeDevDependencies(packageJson); if (argv.outside) { + // Outside optimization writes dist/package.json before Docker builds the app. + // Keep build-time packages for that later in-image build and remove only known non-build tooling. removeUnnecessaryDevDependenciesForOutsideDockerBuild(packageJson); return; } + // Inside Docker, codegen/build has already finished, so production install should not see dev tooling. delete packageJson.devDependencies; console.info('Removed all devDependencies.'); } @@ -105,6 +110,7 @@ function removeUnnecessaryDevDependenciesForOutsideDockerBuild(packageJson: Pack for (const name of Object.keys(devDeps)) { if ( nameWordsToBeRemoved.some((word) => name.includes(word)) || + // Shared config packages are needed only for lint/format/test commands, not Docker builds. (name.includes('willbooster') && name.includes('config')) ) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete @@ -153,26 +159,6 @@ function optimizeScripts(packageJson: PackageJson): void { console.info('Removed scripts:', removedScripts.join(', ') || 'none'); } -function optimizeDockerInstallPrepareScript( - argv: InferredOptionTypes, - project: Project, - packageJson: PackageJson -): void { - // The published wb binary may run under Node even when invoked by a Bun project script. - // Use the target project instead of the current CLI runtime when deciding Docker install steps. - if (!argv.outside || !project.isBunAvailable) return; - - const devDependencyNames = Object.keys(packageJson.devDependencies ?? {}); - if (devDependencyNames.length === 0) return; - - // Bun validates the lockfile even with --production. This script lets Docker rewrite - // the image-local lockfile after --outside pruning, without hard-coding packages in app Dockerfiles. - const scripts = packageJson.scripts ?? {}; - scripts['docker/install/prepare'] = `bun remove ${devDependencyNames.join(' ')}`; - packageJson.scripts = scripts; - console.info('Added docker/install/prepare script.'); -} - function optimizeRootProps(packageJson: PackageJson): void { delete packageJson.private; delete packageJson.publishConfig; diff --git a/packages/wbfy/.gitignore b/packages/wbfy/.gitignore index 552f5730..32d01910 100644 --- a/packages/wbfy/.gitignore +++ b/packages/wbfy/.gitignore @@ -14,6 +14,7 @@ .playwright-cli/ .serena/ .tmp/ +.wb/ __generated__/ @willbooster/ dist/ diff --git a/packages/wbfy/src/generators/oxfmtConfig.ts b/packages/wbfy/src/generators/oxfmtConfig.ts index 5ec87418..4bf26dd7 100644 --- a/packages/wbfy/src/generators/oxfmtConfig.ts +++ b/packages/wbfy/src/generators/oxfmtConfig.ts @@ -6,17 +6,26 @@ import type { PackageConfig } from '../packageConfig.js'; import { fsUtil } from '../utils/fsUtil.js'; import { promisePool } from '../utils/promisePool.js'; +import { generateToolConfigContent, normalizeToolConfigContent } from './toolConfigContent.js'; + export async function generateOxfmtConfig(config: PackageConfig): Promise { return logger.functionIgnoringException('generateOxfmtConfig', async () => { - const legacyConfigPath = path.resolve(config.dirPath, '.oxfmtrc.json'); + const legacyJsonConfigPath = path.resolve(config.dirPath, '.oxfmtrc.json'); const filePath = path.resolve(config.dirPath, 'oxfmt.config.ts'); - await promisePool.run(() => fs.promises.rm(legacyConfigPath, { force: true })); - if (fs.existsSync(filePath)) return; - await promisePool.run(() => fsUtil.generateFile(filePath, configContent)); + const existingContent = await fsUtil.readFileIgnoringError(filePath); + const desiredContent = getConfigContent(config); + const promises = [promisePool.run(() => fs.promises.rm(legacyJsonConfigPath, { force: true }))]; + if (normalizeToolConfigContent(existingContent) !== normalizeToolConfigContent(desiredContent)) { + promises.push(promisePool.run(() => fsUtil.generateFile(filePath, desiredContent))); + } + await Promise.all(promises); }); } -const configContent = `import config from '@willbooster/oxfmt-config'; - -export default config; -`; +function getConfigContent(config: PackageConfig): string { + return generateToolConfigContent(config, { + commonJsVariableName: 'oxfmtConfig', + packageName: '@willbooster/oxfmt-config', + toolName: 'Oxfmt', + }); +} diff --git a/packages/wbfy/src/generators/oxlintConfig.ts b/packages/wbfy/src/generators/oxlintConfig.ts index b7ad00f5..f8e9fac3 100644 --- a/packages/wbfy/src/generators/oxlintConfig.ts +++ b/packages/wbfy/src/generators/oxlintConfig.ts @@ -7,13 +7,17 @@ import { fsUtil } from '../utils/fsUtil.js'; import { promisePool } from '../utils/promisePool.js'; import { isPublishedWillboosterConfigsPackage } from '../utils/willboosterConfigsUtil.js'; +import { generateToolConfigContent, normalizeToolConfigContent } from './toolConfigContent.js'; + export async function generateOxlintConfig(config: PackageConfig, _rootConfig: PackageConfig): Promise { return logger.functionIgnoringException('generateOxlintConfig', async () => { // willbooster-configs publishes config files as product code, so migration // must not remove package-provided linter settings. const shouldPreservePublishedLinterConfig = isPublishedWillboosterConfigsPackage(config); const filePath = path.resolve(config.dirPath, 'oxlint.config.ts'); - const existingContent = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : undefined; + const existingContent = await fsUtil.readFileIgnoringError(filePath); + const desiredContent = + shouldPreservePublishedLinterConfig && existingContent ? existingContent : getConfigContent(config); const promises: Promise[] = []; if (!shouldPreservePublishedLinterConfig) { @@ -33,14 +37,17 @@ export async function generateOxlintConfig(config: PackageConfig, _rootConfig: P promisePool.run(() => fs.promises.rm(path.resolve(config.dirPath, 'eslint.config.ts'), { force: true })) ); } - if (!existingContent) { - promises.push(promisePool.run(() => fsUtil.generateFile(filePath, configContent))); + if (normalizeToolConfigContent(existingContent) !== normalizeToolConfigContent(desiredContent)) { + promises.push(promisePool.run(() => fsUtil.generateFile(filePath, desiredContent))); } await Promise.all(promises); }); } -const configContent = `import config from '@willbooster/oxlint-config'; - -export default config; -`; +function getConfigContent(config: PackageConfig): string { + return generateToolConfigContent(config, { + commonJsVariableName: 'oxlintConfig', + packageName: '@willbooster/oxlint-config', + toolName: 'Oxlint', + }); +} diff --git a/packages/wbfy/src/generators/toolConfigContent.ts b/packages/wbfy/src/generators/toolConfigContent.ts new file mode 100644 index 00000000..39c640f6 --- /dev/null +++ b/packages/wbfy/src/generators/toolConfigContent.ts @@ -0,0 +1,26 @@ +import type { PackageConfig } from '../packageConfig.js'; + +interface ToolConfigContentOptions { + commonJsVariableName: string; + packageName: string; + toolName: string; +} + +export function generateToolConfigContent(config: PackageConfig, options: ToolConfigContentOptions): string { + if (config.isEsmPackage) { + return `import config from '${options.packageName}'; + +export default config; +`; + } + + return `// oxlint-disable unicorn/prefer-module -- ${options.toolName} only auto-discovers .ts config files, and CommonJS avoids Node typeless ESM warnings. +const ${options.commonJsVariableName} = require('${options.packageName}'); + +module.exports = ${options.commonJsVariableName}.default ?? ${options.commonJsVariableName}; +`; +} + +export function normalizeToolConfigContent(content: string | undefined): string | undefined { + return content?.trim(); +}