Skip to content

Commit be08388

Browse files
committed
feat: add oxc-transoform as experimental declaration compiler
1 parent 36222da commit be08388

File tree

11 files changed

+430
-287
lines changed

11 files changed

+430
-287
lines changed

.changeset/friendly-radios-walk.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@ice/pkg': minor
3+
---
4+
5+
feat: add oxc-transoform as experimental declaration compiler

examples/react-component/build.config.mts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ export default defineConfig({
1818
// formats: [],
1919
},
2020
// jsxRuntime: 'classic',
21-
// declaration: false,
21+
declaration: {
22+
generator: 'oxc'
23+
},
2224
sourceMaps: false,
2325
bundle: {
2426
formats: ['esm', 'es2017'],

packages/pkg/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"gzip-size": "^7.0.0",
6262
"lodash.merge": "^4.6.2",
6363
"magic-string": "^0.25.7",
64+
"oxc-transform": "^0.35.0",
6465
"picocolors": "^1.0.0",
6566
"postcss": "^8.4.31",
6667
"postcss-plugin-rpx2vw": "^1.0.0",
@@ -69,7 +70,7 @@
6970
"rollup-plugin-visualizer": "^5.12.0",
7071
"semver": "^7.5.2",
7172
"tsc-alias": "^1.8.2",
72-
"typescript": "^4.9.5"
73+
"typescript": "^5.6.0"
7374
},
7475
"devDependencies": {
7576
"@types/babel__core": "^7.1.20",

packages/pkg/src/config/userConfig.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,15 @@ function getUserConfig() {
7575
},
7676
{
7777
name: 'declaration',
78-
validation: 'boolean',
78+
validation: 'boolean|object',
7979
defaultValue: true,
80+
setConfig: (config: TaskConfig, declarationConfig: UserConfig['declaration']) => {
81+
if (config.type === 'declaration') {
82+
if (typeof declarationConfig === 'object') {
83+
config.generator = declarationConfig.generator
84+
}
85+
}
86+
},
8087
},
8188
// TODO: validate values recursively
8289
{

packages/pkg/src/helpers/dts.ts

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { prepareSingleFileReplaceTscAliasPaths } from 'tsc-alias';
77
import fse from 'fs-extra';
88
import * as path from 'path';
99
import merge from 'lodash.merge';
10+
import oxc from 'oxc-transform';
1011

1112
export type FileExt = 'js' | 'ts' | 'tsx' | 'jsx' | 'cjs' | 'mjs' | 'mts' | 'cts';
1213

@@ -44,6 +45,7 @@ interface DtsCompileOptions {
4445
alias: TaskConfig['alias'];
4546
rootDir: string;
4647
outputDir: string;
48+
usingOxc: boolean;
4749
}
4850

4951
async function getTSConfig(
@@ -92,7 +94,7 @@ async function getProjectTSConfig(rootDir: string): Promise<ts.ParsedCommandLine
9294
};
9395
}
9496

95-
export async function dtsCompile({ files, rootDir, outputDir, alias }: DtsCompileOptions): Promise<DtsInputFile[]> {
97+
export async function dtsCompile({ files, rootDir, outputDir, alias, usingOxc }: DtsCompileOptions): Promise<DtsInputFile[]> {
9698
if (!files.length) {
9799
return [];
98100
}
@@ -108,18 +110,50 @@ export async function dtsCompile({ files, rootDir, outputDir, alias }: DtsCompil
108110
dtsPath: normalizePath(dtsPath),
109111
}));
110112

113+
const compileFunction = usingOxc ? compileFromOxc : compileFromTsc;
114+
const dtsFiles = compileFunction(_files.map(file => file.filePath), tsConfig)
115+
116+
if (!Object.keys(alias).length) {
117+
// no alias config
118+
return _files;
119+
}
120+
121+
// We use tsc-alias to resolve d.ts alias.
122+
// Reason: https://github.com/microsoft/TypeScript/issues/30952#issuecomment-1114225407
123+
const tsConfigLocalPath = path.join(rootDir, 'node_modules/.cache/ice-pkg/tsconfig.json');
124+
await fse.ensureFile(tsConfigLocalPath);
125+
await fse.writeJSON(tsConfigLocalPath, {
126+
...tsConfig,
127+
compilerOptions: tsConfig.options,
128+
}, { spaces: 2 });
129+
130+
const runFile = await prepareSingleFileReplaceTscAliasPaths({
131+
configFile: tsConfigLocalPath,
132+
outDir: outputDir,
133+
});
134+
135+
const result = _files.map((file) => ({
136+
...file,
137+
dtsContent: dtsFiles[file.dtsPath] ? runFile({ fileContents: dtsFiles[file.dtsPath], filePath: file.dtsPath }) : '',
138+
}));
139+
140+
return result;
141+
}
142+
143+
async function compileFromTsc(absFiles: string[], tsConfig: ts.ParsedCommandLine): Promise<Record<string, string>> {
111144
// In order to only include the update files instead of all the files in the watch mode.
112145
function getProgramRootNames(originalFilenames: string[]) {
113146
// Should include all the resolved .d.ts file to avoid dts generate error:
114147
// TS4025: Exported variable '<name>' has or is using private name '<name>'.
115148
const dtsFilenames = originalFilenames.filter((filename) => filename.endsWith('.d.ts'));
116-
const needCompileFileNames = _files.map(({ filePath }) => filePath);
149+
const needCompileFileNames = absFiles;
117150
return [...needCompileFileNames, ...dtsFilenames];
118151
}
119152

120-
const dtsFiles = {};
121153
const host = ts.createCompilerHost(tsConfig.options);
122154

155+
const dtsFiles: Record<string, string> = {};
156+
123157
host.writeFile = (fileName, contents) => {
124158
dtsFiles[fileName] = contents;
125159
};
@@ -147,29 +181,22 @@ export async function dtsCompile({ files, rootDir, outputDir, alias }: DtsCompil
147181
});
148182
}
149183

150-
if (!Object.keys(alias).length) {
151-
// no alias config
152-
return _files;
153-
}
154-
155-
// We use tsc-alias to resolve d.ts alias.
156-
// Reason: https://github.com/microsoft/TypeScript/issues/30952#issuecomment-1114225407
157-
const tsConfigLocalPath = path.join(rootDir, 'node_modules/.cache/ice-pkg/tsconfig.json');
158-
await fse.ensureFile(tsConfigLocalPath);
159-
await fse.writeJSON(tsConfigLocalPath, {
160-
...tsConfig,
161-
compilerOptions: tsConfig.options,
162-
}, { spaces: 2 });
184+
return dtsFiles
185+
}
163186

164-
const runFile = await prepareSingleFileReplaceTscAliasPaths({
165-
configFile: tsConfigLocalPath,
166-
outDir: outputDir,
167-
});
187+
async function compileFromOxc(absFiles: string[], tsConfig: ts.ParsedCommandLine): Promise<Record<string, string>> {
188+
if (!tsConfig.options.isolatedDeclarations) {
189+
consola.warn(`Please enable isolatedDeclarations in tsconfig.json when using oxc generator`)
190+
}
191+
const dtsFiles: Record<string, string> = {};
192+
for (const file of absFiles) {
193+
const fileContent = fse.readFileSync(file, 'utf-8');
194+
const { code } = oxc.isolatedDeclaration(file, fileContent, {
195+
sourcemap: false,
196+
});
168197

169-
const result = _files.map((file) => ({
170-
...file,
171-
dtsContent: dtsFiles[file.dtsPath] ? runFile({ fileContents: dtsFiles[file.dtsPath], filePath: file.dtsPath }) : '',
172-
}));
198+
dtsFiles[file] = code;
199+
}
173200

174-
return result;
201+
return dtsFiles
175202
}

packages/pkg/src/tasks/declaration.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import path from 'node:path';
22
import { Worker, MessagePort } from 'node:worker_threads';
33
import { fileURLToPath } from 'node:url';
4-
import { OutputResult, TaskRunnerContext, WatchChangedFile } from '../types.js';
4+
import { DeclarationTaskConfig, OutputResult, TaskRunnerContext, WatchChangedFile } from '../types.js';
55
import globby from 'globby';
66
import { Runner } from '../helpers/runner.js';
77
import { Rpc } from '../helpers/rpc.js';
@@ -38,9 +38,10 @@ class DeclarationRunner extends Runner<OutputResult> {
3838
rootDir: context.buildContext.rootDir,
3939
outputDir: context.buildTask.config.outputDir,
4040
alias: context.buildTask.config.alias,
41+
usingOxc: (context.buildTask.config as DeclarationTaskConfig).generator === 'oxc',
4142
}]);
4243

43-
worker.terminate();
44+
await worker.terminate();
4445

4546
return {
4647
taskName: context.buildTask.name,

packages/pkg/src/types.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ export interface BundleUserConfig {
103103
browser?: boolean;
104104
}
105105

106+
107+
export interface DeclarationUserConfig {
108+
/**
109+
* The generator to generate .d.ts file
110+
* - `'tsc'` use typescript
111+
* - `'oxc'` use oxc-transform to generate isolated declaration
112+
* @default 'tsc'
113+
*/
114+
generator?: 'tsc' | 'oxc';
115+
}
116+
106117
export interface UserConfig {
107118
/**
108119
* Entry for a task
@@ -134,7 +145,7 @@ export interface UserConfig {
134145
* Generate .d.ts files from TypeScript files in your project.
135146
* @default true
136147
*/
137-
declaration?: boolean;
148+
declaration?: boolean | DeclarationUserConfig;
138149

139150
/**
140151
* Configure JSX transform type.
@@ -156,6 +167,19 @@ export interface UserConfig {
156167
* "bundle mode" means bundle everything up by using Rollup
157168
*/
158169
bundle?: BundleUserConfig;
170+
171+
/**
172+
* Experimental features
173+
* Is not ready for procution. If you found any bugs, please report to https://github.com/ice-lab/icepkg/issues
174+
*/
175+
experimental?: ExperimentalUserConfig;
176+
}
177+
178+
export interface ExperimentalUserConfig {
179+
/**
180+
* Using oxc-transform to genreation [isolated declaration](https://www.typescriptlang.org/tsconfig/#isolatedDeclarations).
181+
*/
182+
enableOxcIsolatedDeclaration?: boolean;
159183
}
160184

161185
export type PluginUserConfig = string | [string, any?] | Plugin;
@@ -248,7 +272,7 @@ export interface TransformTaskConfig extends _TaskConfig, TransformUserConfig {
248272
define?: Record<string, string>;
249273
}
250274

251-
export interface DeclarationTaskConfig extends _TaskConfig {
275+
export interface DeclarationTaskConfig extends _TaskConfig, DeclarationUserConfig {
252276
type: 'declaration';
253277
}
254278

packages/pkg/tests/projects/default.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,5 @@ runProjectTest('default', [
6060
config: {
6161
sourceMaps: true
6262
}
63-
}
63+
},
6464
])

0 commit comments

Comments
 (0)