From 5f7878723be9198aac7d41765d0c70883ec13794 Mon Sep 17 00:00:00 2001 From: Shan Date: Tue, 24 Mar 2026 10:33:55 +0800 Subject: [PATCH] TINY-14141: Add `skipTypecheck` CLI option --- CHANGELOG.md | 3 + modules/server/src/main/ts/BedrockAuto.ts | 19 +++++- modules/server/src/main/ts/BedrockManual.ts | 20 +++++- .../src/main/ts/bedrock/cli/ClOptions.ts | 8 +++ .../server/src/main/ts/bedrock/cli/Clis.ts | 3 +- .../src/main/ts/bedrock/compiler/Compiler.ts | 21 +----- .../src/main/ts/bedrock/compiler/Rspack.ts | 29 +++++--- .../src/main/ts/bedrock/compiler/Types.ts | 26 ++++--- .../src/main/ts/bedrock/compiler/Webpack.ts | 30 ++++++--- .../src/main/ts/bedrock/core/Settings.ts | 1 + .../main/ts/bedrock/server/RunnerRoutes.ts | 44 ++++++++++-- modules/server/src/test/ts/ClisTest.ts | 67 +++++++++++++++++++ 12 files changed, 214 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ff7d17..4c8c6b76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added +- Added a `--skipTypecheck` CLI option to skip typechecking before tests. + ## 16.1.2 - 2026-03-17 ### Fixed diff --git a/modules/server/src/main/ts/BedrockAuto.ts b/modules/server/src/main/ts/BedrockAuto.ts index 1d02f814..c6d6d746 100644 --- a/modules/server/src/main/ts/BedrockAuto.ts +++ b/modules/server/src/main/ts/BedrockAuto.ts @@ -77,12 +77,27 @@ export const go = async (bedrockAutoSettings: BedrockAutoSettings): Promise Promise)[]) => (immediate?: boolean) => Promise.allSettled(services.map((fn) => fn(immediate))); try { - const driverDeferred = defer>(); const scratchDir = settings.name ? `scratch/${settings.name}` : `scratch/bedrock`; - const routesPromise = RunnerRoutes.generate('auto', settings.projectdir, settings.basedir, scratchDir, settings.config, settings.bundler, settings.testfiles, settings.chunk, settings.retries, settings.singleTimeout, settings.stopOnFailure, basePage, settings.coverage, settings.polyfills); + const routesPromise = RunnerRoutes.generate({ + mode: 'auto', + projectdir: settings.projectdir, + basedir: settings.basedir, + scratchdir: scratchDir, + configFile: settings.config, + bundler: settings.bundler, + testfiles: settings.testfiles, + chunk: settings.chunk, + retries: settings.retries, + singleTimeout: settings.singleTimeout, + stopOnFailure: settings.stopOnFailure, + basePage, + coverage: settings.coverage, + polyfills: settings.polyfills, + skipTypecheck: settings.skipTypecheck + }); const service = await Serve.start({ ...settings, diff --git a/modules/server/src/main/ts/BedrockManual.ts b/modules/server/src/main/ts/BedrockManual.ts index ca906be3..489c8453 100644 --- a/modules/server/src/main/ts/BedrockManual.ts +++ b/modules/server/src/main/ts/BedrockManual.ts @@ -14,8 +14,23 @@ export const go = async (bedrockManualSettings: BedrockManualSettings): Promise< const settings = SettingsResolver.resolveAndLog(bedrockManualSettings); const basePage = 'src/resources/html/bedrock.html'; - const routes = RunnerRoutes.generate('manual', settings.projectdir, settings.basedir, 'scratch', settings.config, settings.bundler, settings.testfiles, settings.chunk, 0, settings.singleTimeout, true, basePage, settings.coverage, settings.polyfills); - + const routes = RunnerRoutes.generate({ + mode: 'manual', + projectdir: settings.projectdir, + basedir: settings.basedir, + scratchdir: 'scratch', + configFile: settings.config, + bundler: settings.bundler, + testfiles: settings.testfiles, + chunk: settings.chunk, + retries: 0, + singleTimeout: settings.singleTimeout, + stopOnFailure: true, + basePage, + coverage: settings.coverage, + polyfills: settings.polyfills, + skipTypecheck: settings.skipTypecheck + }); const port = await portfinder.getPortPromise({ port: 8000, stopPort: 20000 @@ -46,4 +61,3 @@ export const go = async (bedrockManualSettings: BedrockManualSettings): Promise< }; export const mode = 'forManual'; - diff --git a/modules/server/src/main/ts/bedrock/cli/ClOptions.ts b/modules/server/src/main/ts/bedrock/cli/ClOptions.ts index 3374dc84..30f6477f 100644 --- a/modules/server/src/main/ts/bedrock/cli/ClOptions.ts +++ b/modules/server/src/main/ts/bedrock/cli/ClOptions.ts @@ -52,6 +52,14 @@ export const browser: ClOption = { ]) }; +export const skipTypecheck: ClOption = { + name: 'skipTypecheck', + type: Boolean, + defaultValue: false, + description: 'Skip typechecking before tests', + validate: Extraction.any +}; + export const bundler: ClOption = { name: 'bundler', type: String, diff --git a/modules/server/src/main/ts/bedrock/cli/Clis.ts b/modules/server/src/main/ts/bedrock/cli/Clis.ts index 9b30e858..70cd8a53 100644 --- a/modules/server/src/main/ts/bedrock/cli/Clis.ts +++ b/modules/server/src/main/ts/bedrock/cli/Clis.ts @@ -33,7 +33,8 @@ const commonOptions = (directories: Directories) => { ClOptions.bucket, ClOptions.buckets, ClOptions.stopOnFailure, - ClOptions.verbose + ClOptions.verbose, + ClOptions.skipTypecheck ]; }; diff --git a/modules/server/src/main/ts/bedrock/compiler/Compiler.ts b/modules/server/src/main/ts/bedrock/compiler/Compiler.ts index 837453ac..8805767f 100644 --- a/modules/server/src/main/ts/bedrock/compiler/Compiler.ts +++ b/modules/server/src/main/ts/bedrock/compiler/Compiler.ts @@ -7,17 +7,10 @@ export interface Compiler { readonly generate: () => Promise; } -export const compile = (args: { bundler: Types.Bundler; tsConfigFile: string; scratchDir: string; basedir: string; exitOnCompileError: boolean; files: string[]; coverage: string[]; polyfills: string[]; -}): Compiler => { +export const compile = (args: Types.CompilerArgs): Compiler => { const { bundler, - tsConfigFile, - scratchDir, - basedir, - exitOnCompileError, - files, - coverage, - polyfills + ...compileArgs } = args; const getCompileFunc = (): Types.CompileFn => { @@ -31,15 +24,7 @@ export const compile = (args: { bundler: Types.Bundler; tsConfigFile: string; sc const generate = async (): Promise => { const compile = getCompileFunc(); - const compiledJsFilePath = await compile( - tsConfigFile, - scratchDir, - basedir, - exitOnCompileError, - files, - coverage, - polyfills - ); + const compiledJsFilePath = await compile(compileArgs); return fs.readFileSync(compiledJsFilePath); }; diff --git a/modules/server/src/main/ts/bedrock/compiler/Rspack.ts b/modules/server/src/main/ts/bedrock/compiler/Rspack.ts index c0bb92d3..5a5c95cc 100644 --- a/modules/server/src/main/ts/bedrock/compiler/Rspack.ts +++ b/modules/server/src/main/ts/bedrock/compiler/Rspack.ts @@ -8,7 +8,7 @@ import { rspack, RspackOptions } from '@rspack/core'; import { RspackDevServer } from '@rspack/dev-server'; import { RspackCompileInfo, DevServerServeSettings, CompileFn } from './Types'; -const getWebPackConfigTs = (tsConfigFile: string, scratchFile: string, dest: string, manualMode: boolean, basedir: string): RspackOptions => { +const getWebPackConfigTs = (tsConfigFile: string, scratchFile: string, dest: string, manualMode: boolean, basedir: string, skipTypecheck: boolean): RspackOptions => { // eslint-disable-next-line @typescript-eslint/no-var-requires const { TsCheckerRspackPlugin } = require('ts-checker-rspack-plugin'); @@ -105,14 +105,14 @@ const getWebPackConfigTs = (tsConfigFile: string, scratchFile: string, dest: str new rspack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('development') }), - new TsCheckerRspackPlugin({ + ...(!skipTypecheck ? [ new TsCheckerRspackPlugin({ async: manualMode, typescript: { memoryLimit: manualMode ? 4096 : 2048, configFile: getTsConfigFile(), build: true } - }) + }) ] : []) ], output: { @@ -150,7 +150,7 @@ const compileTests = (compileInfo: RspackCompileInfo, exitOnCompileError: boolea }); }; -const getTsCompileInfo = (tsConfigFile: string, scratchDir: string, basedir: string, manualMode: boolean): Promise => { +const getTsCompileInfo = (tsConfigFile: string, scratchDir: string, basedir: string, manualMode: boolean, skipTypecheck: boolean): Promise => { return new Promise((resolve, reject) => { const scratchFile = path.join(scratchDir, 'compiled/tests-imports.ts'); const dest = path.join(scratchDir, 'compiled/tests.js'); @@ -158,18 +158,26 @@ const getTsCompileInfo = (tsConfigFile: string, scratchDir: string, basedir: str if (!fs.existsSync(tsConfigFile)) { reject(`Could not find the required tsconfig file: ${tsConfigFile}`); } else { - const config = getWebPackConfigTs(tsConfigFile, scratchFile, dest, manualMode, basedir); + const config = getWebPackConfigTs(tsConfigFile, scratchFile, dest, manualMode, basedir, skipTypecheck); resolve({ scratchFile, dest, config }); } }); }; -const getCompileInfo = (tsConfigFile: string, scratchDir: string, basedir: string, manualMode: boolean): Promise => { - return getTsCompileInfo(tsConfigFile, scratchDir, basedir, manualMode); +const getCompileInfo = (tsConfigFile: string, scratchDir: string, basedir: string, manualMode: boolean, skipTypecheck: boolean): Promise => { + return getTsCompileInfo(tsConfigFile, scratchDir, basedir, manualMode, skipTypecheck); }; -export const compile: CompileFn = async (tsConfigFile: string, scratchDir: string, basedir: string, exitOnCompileError: boolean, srcFiles: string[], _coverage: string[], polyfills: string[]): Promise => { - const compileInfo = await getCompileInfo(tsConfigFile, scratchDir, basedir, false); +export const compile: CompileFn = async ({ + tsConfigFile, + scratchDir, + basedir, + exitOnCompileError, + srcFiles, + polyfills, + skipTypecheck +}): Promise => { + const compileInfo = await getCompileInfo(tsConfigFile, scratchDir, basedir, false, skipTypecheck); return compileTests(compileInfo, exitOnCompileError, srcFiles, polyfills); }; @@ -178,8 +186,9 @@ const isCompiledRequest = (url?: string) => url && url.startsWith('/compiled/'); export const devserver = async (settings: DevServerServeSettings): Promise => { const scratchDir = path.resolve('scratch'); const tsConfigFile = settings.config; + const skipTypecheck = settings.skipTypecheck; - const compileInfo = await getCompileInfo(tsConfigFile, scratchDir, settings.basedir, true); + const compileInfo = await getCompileInfo(tsConfigFile, scratchDir, settings.basedir, true, skipTypecheck); return Serve.startCustom(settings, (port, handler) => { const scratchFile = compileInfo.scratchFile; console.log(`Loading ${settings.testfiles.length} test files...`); diff --git a/modules/server/src/main/ts/bedrock/compiler/Types.ts b/modules/server/src/main/ts/bedrock/compiler/Types.ts index 6352af6d..c10246b4 100644 --- a/modules/server/src/main/ts/bedrock/compiler/Types.ts +++ b/modules/server/src/main/ts/bedrock/compiler/Types.ts @@ -6,6 +6,22 @@ export interface DevServerServeSettings extends Serve.ServeSettings { readonly config: string; readonly coverage: string[]; readonly polyfills: string[]; + readonly skipTypecheck: boolean; +} + +export interface CompileArgs { + readonly tsConfigFile: string; + readonly scratchDir: string; + readonly basedir: string; + readonly exitOnCompileError: boolean; + readonly srcFiles: string[]; + readonly coverage: string[]; + readonly polyfills: string[]; + readonly skipTypecheck: boolean; +} + +export interface CompilerArgs extends CompileArgs { + readonly bundler: Bundler; } export type WebpackCompileInfo = CompileInfo; @@ -17,14 +33,6 @@ export interface CompileInfo { readonly config: T; } -export type CompileFn = ( - tsConfigFile: string, - scratchDir: string, - basedir: string, - exitOnCompileError: boolean, - srcFiles: string[], - coverage: string[], - polyfills: string[] -) => Promise; +export type CompileFn = (args: CompileArgs) => Promise; export type Bundler = 'rspack' | 'webpack'; diff --git a/modules/server/src/main/ts/bedrock/compiler/Webpack.ts b/modules/server/src/main/ts/bedrock/compiler/Webpack.ts index dd2e98bf..dc166860 100644 --- a/modules/server/src/main/ts/bedrock/compiler/Webpack.ts +++ b/modules/server/src/main/ts/bedrock/compiler/Webpack.ts @@ -40,7 +40,7 @@ const webpackSharedRules = ([] as any[]).concat([ } ]); -const getWebPackConfigTs = (tsConfigFile: string, scratchFile: string, dest: string, coverage: string[], manualMode: boolean, basedir: string): webpack.Configuration => { +const getWebPackConfigTs = (tsConfigFile: string, scratchFile: string, dest: string, coverage: string[], manualMode: boolean, basedir: string, skipTypecheck: boolean): webpack.Configuration => { // eslint-disable-next-line @typescript-eslint/no-var-requires const TsConfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -116,14 +116,14 @@ const getWebPackConfigTs = (tsConfigFile: string, scratchFile: string, dest: str }, plugins: [ - new ForkTsCheckerWebpackPlugin({ + ...(!skipTypecheck ? [ new ForkTsCheckerWebpackPlugin({ async: manualMode, typescript: { memoryLimit: manualMode ? 4096 : 2048, configFile: tsConfigFile, build: true } - }), + }) ] : []), new webpack.WatchIgnorePlugin({ paths: [ // Ignore generated files. See https://github.com/TypeStrong/ts-loader#usage-with-webpack-watch @@ -228,7 +228,7 @@ const compileTests = (compileInfo: WebpackCompileInfo, exitOnCompileError: boole }); }; -const getTsCompileInfo = (tsConfigFile: string, scratchDir: string, basedir: string, manualMode: boolean, coverage: string[]): Promise => { +const getTsCompileInfo = (tsConfigFile: string, scratchDir: string, basedir: string, manualMode: boolean, coverage: string[], skipTypecheck: boolean): Promise => { return new Promise((resolve, reject) => { const scratchFile = path.join(scratchDir, 'compiled/tests-imports.ts'); const dest = path.join(scratchDir, 'compiled/tests.js'); @@ -236,7 +236,7 @@ const getTsCompileInfo = (tsConfigFile: string, scratchDir: string, basedir: str if (!fs.existsSync(tsConfigFile)) { reject(`Could not find the required tsconfig file: ${tsConfigFile}`); } else { - const config = getWebPackConfigTs(tsConfigFile, scratchFile, dest, coverage, manualMode, basedir); + const config = getWebPackConfigTs(tsConfigFile, scratchFile, dest, coverage, manualMode, basedir, skipTypecheck); resolve({ scratchFile, dest, config }); } }); @@ -250,16 +250,25 @@ const getJsCompileInfo = (scratchDir: string, basedir: string, manualMode: boole return Promise.resolve({ scratchFile, dest, config }); }; -const getCompileInfo = (tsConfigFile: string, scratchDir: string, basedir: string, manualMode: boolean, srcFiles: string[], coverage: string[]): Promise => { +const getCompileInfo = (tsConfigFile: string, scratchDir: string, basedir: string, manualMode: boolean, srcFiles: string[], coverage: string[], skipTypecheck: boolean): Promise => { if (hasTs(srcFiles)) { - return getTsCompileInfo(tsConfigFile, scratchDir, basedir, manualMode, coverage); + return getTsCompileInfo(tsConfigFile, scratchDir, basedir, manualMode, coverage, skipTypecheck); } else { return getJsCompileInfo(scratchDir, basedir, manualMode, coverage); } }; -export const compile: CompileFn = async (tsConfigFile: string, scratchDir: string, basedir: string, exitOnCompileError: boolean, srcFiles: string[], coverage: string[], polyfills: string[]): Promise => { - const compileInfo = await getCompileInfo(tsConfigFile, scratchDir, basedir, false, srcFiles, coverage); +export const compile: CompileFn = async ({ + tsConfigFile, + scratchDir, + basedir, + exitOnCompileError, + srcFiles, + coverage, + polyfills, + skipTypecheck +}): Promise => { + const compileInfo = await getCompileInfo(tsConfigFile, scratchDir, basedir, false, srcFiles, coverage, skipTypecheck); return compileTests(compileInfo, exitOnCompileError, srcFiles, polyfills); }; @@ -268,8 +277,9 @@ const isCompiledRequest = (request: { url: string }) => request.url.startsWith(' export const devserver = async (settings: DevServerServeSettings): Promise => { const scratchDir = path.resolve('scratch'); const tsConfigFile = settings.config; + const skipTypecheck = settings.skipTypecheck; - const compileInfo = await getCompileInfo(tsConfigFile, scratchDir, settings.basedir, true, settings.testfiles, settings.coverage); + const compileInfo = await getCompileInfo(tsConfigFile, scratchDir, settings.basedir, true, settings.testfiles, settings.coverage, skipTypecheck); return Serve.startCustom(settings, (port, handler) => { const scratchFile = compileInfo.scratchFile; console.log(`Loading ${settings.testfiles.length} test files...`); diff --git a/modules/server/src/main/ts/bedrock/core/Settings.ts b/modules/server/src/main/ts/bedrock/core/Settings.ts index 25882b43..9daa4e18 100644 --- a/modules/server/src/main/ts/bedrock/core/Settings.ts +++ b/modules/server/src/main/ts/bedrock/core/Settings.ts @@ -19,6 +19,7 @@ export interface BedrockSettings { readonly verbose: boolean; readonly webdriverPort: number; readonly useSelenium: boolean; + readonly skipTypecheck: boolean; } export type BedrockManualSettings = BedrockSettings; diff --git a/modules/server/src/main/ts/bedrock/server/RunnerRoutes.ts b/modules/server/src/main/ts/bedrock/server/RunnerRoutes.ts index 99f6b20f..18efe8e8 100644 --- a/modules/server/src/main/ts/bedrock/server/RunnerRoutes.ts +++ b/modules/server/src/main/ts/bedrock/server/RunnerRoutes.ts @@ -18,8 +18,43 @@ interface WorkspaceRoot { folder: string; } -export const generate = async (mode: string, projectdir: string, basedir: string, scratchdir: string, configFile: string, bundler: Types.Bundler, testfiles: string[], chunk: number, - retries: number, singleTimeout: number, stopOnFailure: boolean, basePage: string, coverage: string[], polyfills: string[]): Promise => { +interface GenerateArgs { + readonly mode: string; + readonly projectdir: string; + readonly basedir: string; + readonly scratchdir: string; + readonly configFile: string; + readonly bundler: Types.Bundler; + readonly testfiles: string[]; + readonly chunk: number; + readonly retries: number; + readonly singleTimeout: number; + readonly stopOnFailure: boolean; + readonly basePage: string; + readonly coverage: string[]; + readonly polyfills: string[]; + readonly skipTypecheck: boolean; +} + +export const generate = async (args: GenerateArgs): Promise => { + const { + mode, + projectdir, + basedir, + scratchdir, + configFile, + bundler, + testfiles, + chunk, + retries, + singleTimeout, + stopOnFailure, + basePage, + coverage, + polyfills, + skipTypecheck + } = args; + const files = testfiles.map((filePath) => { return path.relative(projectdir, filePath); }); @@ -30,9 +65,10 @@ export const generate = async (mode: string, projectdir: string, basedir: string scratchDir: path.join(projectdir, scratchdir), basedir, exitOnCompileError: mode === 'auto', - files, + srcFiles: files, coverage, - polyfills + polyfills, + skipTypecheck }); // read the project json file to determine the project name to expose resources as `/project/${name}` diff --git a/modules/server/src/test/ts/ClisTest.ts b/modules/server/src/test/ts/ClisTest.ts index c0ee791d..93100479 100644 --- a/modules/server/src/test/ts/ClisTest.ts +++ b/modules/server/src/test/ts/ClisTest.ts @@ -57,6 +57,7 @@ describe('Clis.forAuto', () => { verbose: false, bucket: 1, buckets: 1, + skipTypecheck: false, useSandboxForHeadless: false, useSelenium: false, extraBrowserCapabilities: '', @@ -98,6 +99,7 @@ describe('Clis.forAuto', () => { verbose: true, bucket: 1, buckets: 1, + skipTypecheck: false, useSandboxForHeadless: false, useSelenium: false, extraBrowserCapabilities: '', @@ -140,6 +142,7 @@ describe('Clis.forAuto', () => { verbose: false, bucket: 3, buckets: 7, + skipTypecheck: false, useSandboxForHeadless: false, useSelenium: false, extraBrowserCapabilities: '', @@ -181,6 +184,7 @@ describe('Clis.forAuto', () => { verbose: false, bucket: 1, buckets: 1, + skipTypecheck: false, useSandboxForHeadless: false, useSelenium: false, extraBrowserCapabilities: ' --some-browser-flag', @@ -225,6 +229,7 @@ describe('Clis.forAuto', () => { verbose: false, bucket: 1, buckets: 1, + skipTypecheck: false, useSandboxForHeadless: false, useSelenium: false, extraBrowserCapabilities: '', @@ -271,6 +276,7 @@ describe('Clis.forAuto', () => { verbose: false, bucket: 1, buckets: 1, + skipTypecheck: false, useSandboxForHeadless: false, useSelenium: false, extraBrowserCapabilities: '', @@ -303,6 +309,48 @@ describe('Clis.forAuto', () => { ], cleanError(actual)); }); + it('TINY-14141: skips typechecking when requested', () => { + const args = [ + '--browser', 'MicrosoftEdge', + '--files', 'src/test/resources/test.file1', + '--config', 'src/test/resources/tsconfig.sample.json', + '--skipTypecheck' + ]; + const actual = Clis.forAuto(directories, args); + AttemptUtils.assertResult({ + browser: 'MicrosoftEdge', + browserVersion: 'latest', + bundler: 'webpack', + config: 'src/test/resources/tsconfig.sample.json', + name: 'bedrock-run', + output: 'scratch', + help: false, + testfiles: [ + 'src/test/resources/test.file1' + ], + delayExit: false, + singleTimeout: 30000, + stopOnFailure: false, + overallTimeout: 600000, + loglevel: 'advanced', + version: false, + chunk: 2000, + retries: 0, + polyfills: [ 'Symbol' ], + verbose: false, + bucket: 1, + buckets: 1, + skipTypecheck: true, + useSandboxForHeadless: false, + useSelenium: false, + extraBrowserCapabilities: '', + skipResetMousePosition: false, + wipeBrowserCache: false, + remote: '', + webdriverPort: 4444 + }, cleanResult(actual)); + }); + it('fails when browser argument missing', () => { const args = [ '--files', 'src/test/resources/test.file1', @@ -328,6 +376,7 @@ describe('Clis.forManual', () => { version: false, chunk: 2000, polyfills: [ 'Symbol' ], + skipTypecheck: false, bucket: 1, buckets: 1 }; @@ -404,4 +453,22 @@ describe('Clis.forManual', () => { buckets: 15 }, Attempt.map(actual, exclude(['projectdir', 'basedir']))); }); + + it('TINY-14141: skips typechecking when requested', () => { + const args = [ + '--files', 'src/test/resources/test.file1', + '--config', 'src/test/resources/tsconfig.sample.json', + '--skipTypecheck' + ]; + const actual = Clis.forManual(directories, args); + AttemptUtils.assertResult({ + ...defaultCliOptions, + config: 'src/test/resources/tsconfig.sample.json', + skipTypecheck: true, + verbose: false, + testfiles: [ + 'src/test/resources/test.file1' + ] + }, Attempt.map(actual, exclude(['projectdir', 'basedir']))); + }); });