From b7aaede4d6359866e540ff9cd75f2a1f048cc09d Mon Sep 17 00:00:00 2001 From: Kevin Deng Date: Tue, 7 Apr 2026 19:32:36 +0900 Subject: [PATCH] feat(target): add support for `baseline-widely-available` target --- dts.snapshot.json | 52 ++++++++++--------- package.json | 1 + packages/css/src/options.ts | 3 +- pnpm-lock.yaml | 14 +++-- pnpm-workspace.yaml | 1 + scripts/generate-target.ts | 35 +++++++++++++ src/config/types.ts | 2 +- src/features/target.test.ts | 18 ++++++- src/features/target.ts | 17 +++++- src/internal.ts | 1 + .../baseline-widely-available-target.snap.md | 9 ++++ tests/target.test.ts | 9 ++++ 12 files changed, 129 insertions(+), 33 deletions(-) create mode 100644 scripts/generate-target.ts create mode 100644 tests/__snapshots__/target/baseline-widely-available-target.snap.md diff --git a/dts.snapshot.json b/dts.snapshot.json index 1de74acbd..3f72b9e53 100644 --- a/dts.snapshot.json +++ b/dts.snapshot.json @@ -1,5 +1,5 @@ { - "config-!~{00c}~.d.mts": { + "config-!~{00d}~.d.mts": { "defineConfig": "declare function defineConfig(_: UserConfigExport): UserConfigExport", "mergeConfig": "declare function mergeConfig(_: InlineConfig, ...overrides: InlineConfig[]): InlineConfig", "resolveUserConfig": "declare function resolveUserConfig(_: UserConfig, _: InlineConfig): Promise" @@ -13,6 +13,29 @@ "mergeConfig" ] }, + "index-!~{00b}~.d.mts": { + "Arrayable": "type Arrayable = T | T[]", + "Awaitable": "type Awaitable = T | Promise", + "globalLogger": "Logger", + "Logger": "interface Logger {\n level: LogLevel\n options?: LoggerOptions\n info: (...args: any[]) => void\n warn: (...args: any[]) => void\n warnOnce: (...args: any[]) => void\n error: (...args: any[]) => void\n success: (...args: any[]) => void\n clearScreen: (_: LogType) => void\n}", + "LoggerOptions": "interface LoggerOptions {\n allowClearScreen?: boolean\n customLogger?: Logger\n console?: Console\n failOnWarn?: boolean\n}", + "LogLevel": "type LogLevel = LogType | 'silent'", + "LogType": "type LogType = 'error' | 'warn' | 'info'", + "MarkPartial": "type MarkPartial = Omit, K> & Partial>", + "Overwrite": "type Overwrite = Omit & U", + "PackageJson": "interface PackageJson {\n name?: string\n version?: string\n description?: string\n keywords?: string[]\n homepage?: string\n bugs?: string | { url?: string; email?: string }\n license?: string\n repository?: string | { type: string; url: string; directory?: string }\n scripts?: PackageJsonScripts\n private?: boolean\n author?: PackageJsonPerson\n contributors?: PackageJsonPerson[]\n funding?: PackageJsonFunding | PackageJsonFunding[]\n files?: string[]\n main?: string\n browser?: string | Record\n unpkg?: string\n bin?: string | Record\n man?: string | string[]\n dependencies?: Record\n devDependencies?: Record\n optionalDependencies?: Record\n peerDependencies?: Record\n types?: string\n typings?: string\n module?: string\n type?: 'module' | 'commonjs'\n exports?: PackageJsonExports\n imports?: Record>\n workspaces?: string[] | { packages?: string[]; nohoist?: string[] }\n typesVersions?: Record>\n os?: string[]\n cpu?: string[]\n publishConfig?: { registry?: string; tag?: string; access?: 'public' | 'restricted'; executableFiles?: string[]; directory?: string; linkDirectory?: boolean } & Pick\n packageManager?: string\n [key: string]: any\n}", + "PackageJsonCommonScripts": "type PackageJsonCommonScripts = 'build' | 'coverage' | 'deploy' | 'dev' | 'format' | 'lint' | 'preview' | 'release' | 'typecheck' | 'watch'", + "PackageJsonExportKey": "type PackageJsonExportKey = '.' | 'import' | 'require' | 'types' | 'node' | 'browser' | 'default' | (string & {})", + "PackageJsonExports": "type PackageJsonExports = string | PackageJsonExportsObject | Array", + "PackageJsonExportsObject": "type PackageJsonExportsObject = { [P in PackageJsonExportKey]?: string | PackageJsonExportsObject | Array }", + "PackageJsonFunding": "type PackageJsonFunding = string | { url: string; type?: string }", + "PackageJsonNpmLifeCycleScripts": "type PackageJsonNpmLifeCycleScripts = 'dependencies' | 'prepublishOnly' | PackageJsonScriptWithPreAndPost<'install' | 'pack' | 'prepare' | 'publish' | 'restart' | 'start' | 'stop' | 'test' | 'version'>", + "PackageJsonPerson": "type PackageJsonPerson = string | { name: string; email?: string; url?: string }", + "PackageJsonPnpmLifeCycleScripts": "type PackageJsonPnpmLifeCycleScripts = 'pnpm:devPreinstall'", + "PackageJsonScriptName": "type PackageJsonScriptName = PackageJsonCommonScripts | PackageJsonNpmLifeCycleScripts | PackageJsonPnpmLifeCycleScripts | (string & {})", + "PackageJsonScripts": "type PackageJsonScripts = { [P in PackageJsonScriptName]?: string }", + "PackageJsonScriptWithPreAndPost": "type PackageJsonScriptWithPreAndPost = S | `${'pre' | 'post'}${S}`" + }, "index.d.mts": { "build": "declare function build(_: InlineConfig): Promise", "buildWithConfigs": "declare function buildWithConfigs(_: ResolvedConfig[], _: string[], _: () => void): Promise", @@ -71,6 +94,7 @@ ] }, "internal.d.mts": { + "expandBaselineTarget": "declare function expandBaselineTarget(_: string[]): string[]", "fsExists": "declare function fsExists(_: string): Promise", "fsRemove": "declare function fsRemove(_: string): Promise", "importWithError": "declare function importWithError(_: string): Promise", @@ -80,6 +104,7 @@ "Logger", "MarkPartial", "Overwrite", + "expandBaselineTarget", "fsExists", "fsRemove", "importWithError", @@ -87,17 +112,6 @@ "toArray" ] }, - "logger-!~{00a}~.d.mts": { - "Arrayable": "type Arrayable = T | T[]", - "Awaitable": "type Awaitable = T | Promise", - "globalLogger": "Logger", - "Logger": "interface Logger {\n level: LogLevel\n options?: LoggerOptions\n info: (...args: any[]) => void\n warn: (...args: any[]) => void\n warnOnce: (...args: any[]) => void\n error: (...args: any[]) => void\n success: (...args: any[]) => void\n clearScreen: (_: LogType) => void\n}", - "LoggerOptions": "interface LoggerOptions {\n allowClearScreen?: boolean\n customLogger?: Logger\n console?: Console\n failOnWarn?: boolean\n}", - "LogLevel": "type LogLevel = LogType | 'silent'", - "LogType": "type LogType = 'error' | 'warn' | 'info'", - "MarkPartial": "type MarkPartial = Omit, K> & Partial>", - "Overwrite": "type Overwrite = Omit & U" - }, "plugins.d.mts": { "NodeProtocolPlugin": "declare function NodeProtocolPlugin(_: 'strip' | true): Plugin", "ShebangPlugin": "declare function ShebangPlugin(_: Logger, _: string, _: string, _: boolean): Plugin", @@ -113,7 +127,7 @@ "run.d.mts": { "#exports": [] }, - "types-!~{00b}~.d.mts": { + "types-!~{00c}~.d.mts": { "AttwOptions": "interface AttwOptions extends CheckPackageOptions {\n module?: typeof _$_arethetypeswrong_core0\n profile?: 'strict' | 'node16' | 'esm-only'\n level?: 'error' | 'warn'\n ignoreRules?: ('no-resolution' | 'untyped-resolution' | 'false-cjs' | 'false-esm' | 'cjs-resolves-to-esm' | 'fallback-condition' | 'cjs-only-exports-default' | 'named-exports' | 'false-export-default' | 'missing-export-equals' | 'unexpected-module-syntax' | 'internal-resolution-error' | (string & {}))[]\n}", "BuildContext": "interface BuildContext {\n options: ResolvedConfig\n hooks: Hookable\n}", "ChunkAddon": "type ChunkAddon = ChunkAddonObject | ChunkAddonFunction | string", @@ -137,18 +151,6 @@ "OutExtensionContext": "interface OutExtensionContext {\n options: InputOptions\n format: NormalizedFormat\n pkgType?: PackageType\n}", "OutExtensionFactory": "type OutExtensionFactory = (_: OutExtensionContext) => OutExtensionObject | undefined", "OutExtensionObject": "interface OutExtensionObject {\n js?: string\n dts?: string\n}", - "PackageJson": "interface PackageJson {\n name?: string\n version?: string\n description?: string\n keywords?: string[]\n homepage?: string\n bugs?: string | { url?: string; email?: string }\n license?: string\n repository?: string | { type: string; url: string; directory?: string }\n scripts?: PackageJsonScripts\n private?: boolean\n author?: PackageJsonPerson\n contributors?: PackageJsonPerson[]\n funding?: PackageJsonFunding | PackageJsonFunding[]\n files?: string[]\n main?: string\n browser?: string | Record\n unpkg?: string\n bin?: string | Record\n man?: string | string[]\n dependencies?: Record\n devDependencies?: Record\n optionalDependencies?: Record\n peerDependencies?: Record\n types?: string\n typings?: string\n module?: string\n type?: 'module' | 'commonjs'\n exports?: PackageJsonExports\n imports?: Record>\n workspaces?: string[] | { packages?: string[]; nohoist?: string[] }\n typesVersions?: Record>\n os?: string[]\n cpu?: string[]\n publishConfig?: { registry?: string; tag?: string; access?: 'public' | 'restricted'; executableFiles?: string[]; directory?: string; linkDirectory?: boolean } & Pick\n packageManager?: string\n [key: string]: any\n}", - "PackageJsonCommonScripts": "type PackageJsonCommonScripts = 'build' | 'coverage' | 'deploy' | 'dev' | 'format' | 'lint' | 'preview' | 'release' | 'typecheck' | 'watch'", - "PackageJsonExportKey": "type PackageJsonExportKey = '.' | 'import' | 'require' | 'types' | 'node' | 'browser' | 'default' | (string & {})", - "PackageJsonExports": "type PackageJsonExports = string | PackageJsonExportsObject | Array", - "PackageJsonExportsObject": "type PackageJsonExportsObject = { [P in PackageJsonExportKey]?: string | PackageJsonExportsObject | Array }", - "PackageJsonFunding": "type PackageJsonFunding = string | { url: string; type?: string }", - "PackageJsonNpmLifeCycleScripts": "type PackageJsonNpmLifeCycleScripts = 'dependencies' | 'prepublishOnly' | PackageJsonScriptWithPreAndPost<'install' | 'pack' | 'prepare' | 'publish' | 'restart' | 'start' | 'stop' | 'test' | 'version'>", - "PackageJsonPerson": "type PackageJsonPerson = string | { name: string; email?: string; url?: string }", - "PackageJsonPnpmLifeCycleScripts": "type PackageJsonPnpmLifeCycleScripts = 'pnpm:devPreinstall'", - "PackageJsonScriptName": "type PackageJsonScriptName = PackageJsonCommonScripts | PackageJsonNpmLifeCycleScripts | PackageJsonPnpmLifeCycleScripts | (string & {})", - "PackageJsonScripts": "type PackageJsonScripts = { [P in PackageJsonScriptName]?: string }", - "PackageJsonScriptWithPreAndPost": "type PackageJsonScriptWithPreAndPost = S | `${'pre' | 'post'}${S}`", "PackageJsonWithPath": "interface PackageJsonWithPath extends PackageJson {\n packageJsonPath: string\n}", "PackageType": "type PackageType = 'module' | 'commonjs' | undefined", "PublintOptions": "interface PublintOptions extends Omit {\n module?: [typeof _$publint, typeof _$publint_utils0]\n}", diff --git a/package.json b/package.json index eee63647b..37e597fc2 100644 --- a/package.json +++ b/package.json @@ -156,6 +156,7 @@ "@vitest/coverage-v8": "catalog:dev", "@vitest/ui": "catalog:dev", "@vueuse/core": "catalog:docs", + "baseline-browser-mapping": "catalog:dev", "bumpp": "catalog:dev", "dedent": "catalog:dev", "eslint": "catalog:dev", diff --git a/packages/css/src/options.ts b/packages/css/src/options.ts index c6aae2a08..93f094b47 100644 --- a/packages/css/src/options.ts +++ b/packages/css/src/options.ts @@ -1,4 +1,5 @@ import { + expandBaselineTarget, resolveComma, toArray, type MarkPartial, @@ -213,7 +214,7 @@ export function resolveCssOptions( } else if (options.target == null) { cssTarget = topLevelTarget } else { - cssTarget = resolveComma(toArray(options.target)) + cssTarget = expandBaselineTarget(resolveComma(toArray(options.target))) } return { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a7e47a4f..2d3761da7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,6 +40,9 @@ catalogs: '@vitest/ui': specifier: ^4.1.2 version: 4.1.2 + baseline-browser-mapping: + specifier: ^2.10.16 + version: 2.10.16 bumpp: specifier: ^11.0.1 version: 11.0.1 @@ -319,6 +322,9 @@ importers: '@vueuse/core': specifier: catalog:docs version: 14.2.1(vue@3.5.32(typescript@6.0.2)) + baseline-browser-mapping: + specifier: catalog:dev + version: 2.10.16 bumpp: specifier: catalog:dev version: 11.0.1 @@ -2171,8 +2177,8 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - baseline-browser-mapping@2.10.14: - resolution: {integrity: sha512-fOVLPAsFTsQfuCkvahZkzq6nf8KvGWanlYoTh0SVA0A/PIUxQGU2AOZAoD95n2gFLVDW/jP6sbGLny95nmEuHA==} + baseline-browser-mapping@2.10.16: + resolution: {integrity: sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==} engines: {node: '>=6.0.0'} hasBin: true @@ -6253,7 +6259,7 @@ snapshots: balanced-match@4.0.4: {} - baseline-browser-mapping@2.10.14: {} + baseline-browser-mapping@2.10.16: {} birpc@2.9.0: {} @@ -6272,7 +6278,7 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.14 + baseline-browser-mapping: 2.10.16 caniuse-lite: 1.0.30001785 electron-to-chromium: 1.5.331 node-releases: 2.0.37 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1b98094d8..5490ff939 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -21,6 +21,7 @@ catalogs: '@typescript/native-preview': 7.0.0-dev.20260405.1 '@vitest/coverage-v8': ^4.1.2 '@vitest/ui': ^4.1.2 + baseline-browser-mapping: ^2.10.16 bumpp: ^11.0.1 dedent: ^1.7.2 eslint: ^10.2.0 diff --git a/scripts/generate-target.ts b/scripts/generate-target.ts new file mode 100644 index 000000000..13d692d22 --- /dev/null +++ b/scripts/generate-target.ts @@ -0,0 +1,35 @@ +import { getCompatibleVersions } from 'baseline-browser-mapping' + +// Update on each major release +const targetDate = '2026-01-01' + +// https://oxc.rs/docs/guide/usage/transformer/lowering#target +const baselineToOxcTargetMap: Record = { + chrome: 'chrome', + edge: 'edge', + firefox: 'firefox', + safari: 'safari', + safari_ios: 'ios', +} + +const oxcSupportedBrowsers = new Set([ + 'chrome', + 'edge', + 'firefox', + 'safari', + 'ios', +]) + +const results = getCompatibleVersions({ + widelyAvailableOnDate: targetDate, +}) + +const oxcTargets = results + .map((target) => ({ + browser: baselineToOxcTargetMap[target.browser], + version: target.version, + })) + .filter((target) => oxcSupportedBrowsers.has(target.browser)) + .map((target) => `${target.browser}${target.version}`) + +console.log('Baseline Widely Available Targets:', oxcTargets) diff --git a/src/config/types.ts b/src/config/types.ts index 67c2e3484..5c4d27fce 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -215,7 +215,7 @@ export interface UserConfig { * If not set, defaults to the value of `engines.node` in your project's `package.json`. * If no `engines.node` field exists, no syntax transformations are applied. * - * Accepts a single target (e.g., `'es2020'`, `'node18'`), an array of targets, or `false` to disable all transformations. + * Accepts a single target (e.g., `'es2020'`, `'node18'`, `'baseline-widely-available'`), an array of targets, or `false` to disable all transformations. * * @see {@link https://tsdown.dev/options/target#supported-targets} for a list of valid targets and more details. * diff --git a/src/features/target.test.ts b/src/features/target.test.ts index 3c2cca047..9896829e0 100644 --- a/src/features/target.test.ts +++ b/src/features/target.test.ts @@ -1,5 +1,21 @@ import { expect, test } from 'vitest' -import { resolvePackageTarget } from './target.ts' +import { expandBaselineTarget, resolvePackageTarget } from './target.ts' + +test('expandBaselineTarget', () => { + expect(expandBaselineTarget(['baseline-widely-available'])).toEqual([ + 'chrome111', + 'edge111', + 'firefox114', + 'safari16.4', + 'ios16.4', + ]) + + expect(expandBaselineTarget(['es2020'])).toEqual(['es2020']) + + expect(expandBaselineTarget(['node18', 'baseline-widely-available'])).toEqual( + ['node18', 'chrome111', 'edge111', 'firefox114', 'safari16.4', 'ios16.4'], + ) +}) test('resolvePackageTarget', () => { expect(testVersion('>= 14')).toMatchInlineSnapshot(`"node14.0.0"`) diff --git a/src/features/target.ts b/src/features/target.ts index 6c95b6aff..b266e7f9b 100644 --- a/src/features/target.ts +++ b/src/features/target.ts @@ -4,6 +4,21 @@ import type { Logger } from '../utils/logger.ts' import type { Ansis } from 'ansis' import type { PackageJson } from 'pkg-types' +// Generated by `scripts/generate-target.ts` +const BASELINE_WIDELY_AVAILABLE_TARGET: string[] = [ + 'chrome111', + 'edge111', + 'firefox114', + 'safari16.4', + 'ios16.4', +] + +export function expandBaselineTarget(targets: string[]): string[] { + return targets.flatMap((t) => + t === 'baseline-widely-available' ? BASELINE_WIDELY_AVAILABLE_TARGET : t, + ) +} + export function resolveTarget( logger: Logger, target: string | string[] | false | undefined, @@ -24,7 +39,7 @@ export function resolveTarget( if (typeof target === 'number') { throw new TypeError(`Invalid target: ${target}`) } - const targets = resolveComma(toArray(target)) + const targets = expandBaselineTarget(resolveComma(toArray(target))) if (targets.length) logger.info( nameLabel, diff --git a/src/internal.ts b/src/internal.ts index 21509763e..9d26f71db 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -1,3 +1,4 @@ +export { expandBaselineTarget } from './features/target.ts' export { fsExists, fsRemove } from './utils/fs.ts' export { importWithError, resolveComma, toArray } from './utils/general.ts' export type { Logger } from './utils/logger.ts' diff --git a/tests/__snapshots__/target/baseline-widely-available-target.snap.md b/tests/__snapshots__/target/baseline-widely-available-target.snap.md new file mode 100644 index 000000000..65bd86b7a --- /dev/null +++ b/tests/__snapshots__/target/baseline-widely-available-target.snap.md @@ -0,0 +1,9 @@ +## index.mjs + +```mjs +//#region index.ts +const foo = a?.b?.(); +//#endregion +export { foo }; + +``` diff --git a/tests/target.test.ts b/tests/target.test.ts index 4bed24b97..1b7676121 100644 --- a/tests/target.test.ts +++ b/tests/target.test.ts @@ -20,6 +20,15 @@ describe('target', () => { expect(snapshot).contain('?.') }) + test('baseline-widely-available target', async (context) => { + const { snapshot } = await testBuild({ + context, + files: { 'index.ts': 'export const foo: number = a?.b?.()' }, + options: { target: 'baseline-widely-available' }, + }) + expect(snapshot).contain('?.') + }) + test('target: false disables all syntax transformations', async (context) => { const { snapshot } = await testBuild({ context,