Skip to content

Commit

Permalink
feat: support autoExternal for dts files (#80)
Browse files Browse the repository at this point in the history
  • Loading branch information
9aoy authored Aug 12, 2024
1 parent f533fbb commit 9d40d43
Show file tree
Hide file tree
Showing 18 changed files with 458 additions and 27 deletions.
1 change: 1 addition & 0 deletions e2e/cases/auto-external/default/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "auto-external-default-test",
"dependencies": {
"ora": "8.0.1",
"react": "^18.3.1"
}
}
15 changes: 13 additions & 2 deletions e2e/cases/auto-external/default/rslib.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@ import { generateBundleCjsConfig, generateBundleEsmConfig } from '@e2e/helper';
import { defineConfig } from '@rslib/core';

export default defineConfig({
lib: [generateBundleEsmConfig(__dirname), generateBundleCjsConfig(__dirname)],
lib: [
generateBundleEsmConfig(__dirname, {
dts: {
bundle: true,
},
}),
generateBundleCjsConfig(__dirname, {
dts: {
bundle: true,
},
}),
],
source: {
entry: {
main: '../__fixtures__/src/index.ts',
main: './src/index.ts',
},
},
});
7 changes: 7 additions & 0 deletions e2e/cases/auto-external/default/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { oraPromise } from 'ora';
import React from 'react';

export type { oraPromise };
export const foo = () => {
return React.version;
};
8 changes: 8 additions & 0 deletions e2e/cases/auto-external/default/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}
1 change: 1 addition & 0 deletions e2e/cases/auto-external/false/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "auto-external-false-test",
"dependencies": {
"ora": "8.0.1",
"react": "^18.3.1"
}
}
18 changes: 11 additions & 7 deletions e2e/cases/auto-external/false/rslib.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@ import { defineConfig } from '@rslib/core';

export default defineConfig({
lib: [
{
...generateBundleEsmConfig(__dirname),
generateBundleEsmConfig(__dirname, {
autoExternal: false,
},
{
...generateBundleCjsConfig(__dirname),
dts: {
bundle: true,
},
}),
generateBundleCjsConfig(__dirname, {
autoExternal: false,
},
dts: {
bundle: true,
},
}),
],
source: {
entry: {
main: '../__fixtures__/src/index.ts',
main: './src/index.ts',
},
},
});
7 changes: 7 additions & 0 deletions e2e/cases/auto-external/false/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { oraPromise } from 'ora';
import React from 'react';

export type { oraPromise };
export const foo = () => {
return React.version;
};
8 changes: 8 additions & 0 deletions e2e/cases/auto-external/false/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}
21 changes: 15 additions & 6 deletions e2e/cases/auto-external/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import { expect, test } from 'vitest';

test('auto external default should works', async () => {
const fixturePath = join(__dirname, 'default');
const { entries } = await buildAndGetResults(fixturePath);
const { js, dts } = await buildAndGetResults(fixturePath, 'all');

expect(entries.esm).toContain(
expect(js.entries.esm).toContain(
'import * as __WEBPACK_EXTERNAL_MODULE_react__ from "react"',
);

expect(entries.cjs).toContain(
expect(js!.entries.cjs).toContain(
'var external_react_namespaceObject = require("react");',
);

// dts should externalized
expect(dts.entries.esm).toContain("import type { oraPromise } from 'ora';");
expect(dts.entries.cjs).toContain("import type { oraPromise } from 'ora';");
});

test('auto external sub path should works', async () => {
Expand All @@ -36,15 +40,20 @@ test('auto external sub path should works', async () => {

test('auto external false should works', async () => {
const fixturePath = join(__dirname, 'false');
const { entries } = await buildAndGetResults(fixturePath);
const { js, dts } = await buildAndGetResults(fixturePath, 'all');

expect(entries.esm).not.toContain(
expect(js.entries.esm).not.toContain(
'import * as __WEBPACK_EXTERNAL_MODULE_react__ from "react"',
);

expect(entries.cjs).not.toContain(
expect(js.entries.cjs).not.toContain(
'var external_react_namespaceObject = require("react");',
);

// dts should bundled
expect(dts.entries.esm).toContain('export declare function oraPromise');

expect(dts.entries.cjs).toContain('export declare function oraPromise');
});

test('externals should overrides auto external', async () => {
Expand Down
49 changes: 43 additions & 6 deletions e2e/scripts/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,24 +102,61 @@ export async function getResults(
};
}

export const buildAndGetResults = async (
fixturePath: string,
type: 'js' | 'dts' = 'js',
): Promise<{
type BuildResult = {
contents: Record<string, Record<string, string>>;
files: Record<string, string[]>;
entries: Record<string, string>;
entryFiles: Record<string, string>;
rspackConfig: InspectConfigResult['origin']['bundlerConfigs'];
rsbuildConfig: InspectConfigResult['origin']['rsbuildConfig'];
isSuccess: boolean;
}> => {
};

export async function buildAndGetResults(
fixturePath: string,
type: 'all',
): Promise<{
js: BuildResult;
dts: BuildResult;
}>;
export async function buildAndGetResults(
fixturePath: string,
type?: 'js' | 'dts',
): Promise<BuildResult>;
export async function buildAndGetResults(
fixturePath: string,
type: 'js' | 'dts' | 'all' = 'js',
) {
const rslibConfig = await loadConfig(join(fixturePath, 'rslib.config.ts'));
process.chdir(fixturePath);
const rsbuildInstance = await build(rslibConfig);
const {
origin: { bundlerConfigs, rsbuildConfig },
} = await rsbuildInstance.inspectConfig({ verbose: true });
if (type === 'all') {
const jsResults = await getResults(rslibConfig, fixturePath, 'js');
const dtsResults = await getResults(rslibConfig, fixturePath, 'dts');
return {
js: {
contents: jsResults.contents,
files: jsResults.files,
entries: jsResults.entries,
entryFiles: jsResults.entryFiles,
rspackConfig: bundlerConfigs,
rsbuildConfig: rsbuildConfig,
isSuccess: Boolean(rsbuildInstance),
},
dts: {
contents: dtsResults.contents,
files: dtsResults.files,
entries: dtsResults.entries,
entryFiles: dtsResults.entryFiles,
rspackConfig: bundlerConfigs,
rsbuildConfig: rsbuildConfig,
isSuccess: Boolean(rsbuildInstance),
},
};
}

const results = await getResults(rslibConfig, fixturePath, type);
return {
Expand All @@ -131,4 +168,4 @@ export const buildAndGetResults = async (
rsbuildConfig: rsbuildConfig,
isSuccess: Boolean(rsbuildInstance),
};
};
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"test:artifact": "vitest run --project artifact",
"test:artifact:watch": "vitest --project artifact",
"test:e2e": "cd e2e && pnpm run test",
"test:unit": "vitest run --project unit",
"test:unit:watch": "vitest --project unit",
"test:unit": "vitest run --project unit*",
"test:unit:watch": "vitest --project unit*",
"watch": "pnpm build --watch"
},
"simple-git-hooks": {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ const composeDtsConfig = async (
libConfig: LibConfig,
dtsExtension: string,
): Promise<RsbuildConfig> => {
const { dts, bundle, output } = libConfig;
const { dts, bundle, output, autoExternal } = libConfig;

if (dts === false || dts === undefined) return {};

Expand All @@ -409,6 +409,7 @@ const composeDtsConfig = async (
distPath: dts?.distPath ?? output?.distPath?.root ?? './dist',
abortOnError: dts?.abortOnError ?? true,
dtsExtension,
autoExternal,
}),
],
};
Expand Down
5 changes: 3 additions & 2 deletions packages/plugin-dts/src/apiExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type BundleOptions = {
dtsExtension: string;
dtsEntry: DtsEntry;
tsconfigPath?: string;
bundledPackages?: string[];
};

export async function bundleDts(options: BundleOptions): Promise<void> {
Expand All @@ -29,6 +30,7 @@ export async function bundleDts(options: BundleOptions): Promise<void> {
path: 'index.d.ts',
},
tsconfigPath = 'tsconfig.json',
bundledPackages = [],
} = options;
try {
const start = Date.now();
Expand All @@ -40,8 +42,7 @@ export async function bundleDts(options: BundleOptions): Promise<void> {
const mainEntryPointFilePath = dtsEntry.path!;
const internalConfig = {
mainEntryPointFilePath,
// TODO: use !externals
// bundledPackages: [],
bundledPackages,
dtsRollup: {
enabled: true,
untrimmedFilePath,
Expand Down
97 changes: 97 additions & 0 deletions packages/plugin-dts/src/dts.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import fs from 'node:fs';
import { basename, dirname, join, relative } from 'node:path';
import { logger } from '@rsbuild/core';
import color from 'picocolors';
Expand All @@ -6,6 +7,95 @@ import * as ts from 'typescript';
import { emitDts } from './tsc';
import { ensureTempDeclarationDir, loadTsconfig } from './utils';

const isObject = (obj: unknown): obj is Record<string, any> =>
Object.prototype.toString.call(obj) === '[object Object]';

// use !externals
export const calcBundledPackages = (options: {
autoExternal: DtsGenOptions['autoExternal'];
cwd: string;
userExternals?: DtsGenOptions['userExternals'];
}): string[] => {
const { autoExternal, cwd, userExternals } = options;

let pkgJson: {
dependencies?: Record<string, string>;
peerDependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
};

try {
const content = fs.readFileSync(join(cwd, 'package.json'), 'utf-8');
pkgJson = JSON.parse(content);
} catch (err) {
logger.warn(
'The type of third-party packages will not be bundled due to read package.json failed',
);
return [];
}

const externalOptions = autoExternal
? {
dependencies: true,
peerDependencies: true,
devDependencies: false,
...(autoExternal === true ? {} : autoExternal),
}
: {
dependencies: false,
peerDependencies: false,
devDependencies: false,
};

// User externals should not bundled
// Only handle the case where the externals type is string / (string | RegExp)[] / plain object, function type is too complex.
const getUserExternalsKeys = (
value: typeof userExternals,
): (string | RegExp)[] => {
if (!value) {
return [];
}

if (typeof value === 'string' || value instanceof RegExp) {
return [value];
}

if (Array.isArray(value)) {
return value.flatMap((v) => getUserExternalsKeys(v));
}

if (isObject(userExternals)) {
return Object.keys(userExternals);
}
return [];
};

const externals: (string | RegExp)[] = getUserExternalsKeys(userExternals);

const allDeps: string[] = [];

for (const type of [
'dependencies',
'peerDependencies',
'devDependencies',
] as const) {
const deps = pkgJson[type] && Object.keys(pkgJson[type]);
if (deps) {
if (externalOptions[type]) {
externals.push(...deps);
}
allDeps.push(...deps);
}
}

const bundledPackages = allDeps.filter(
(d) =>
!externals.some((e) => (typeof e === 'string' ? d === e : e.test(d))),
);

return Array.from(new Set(bundledPackages));
};

export async function generateDts(data: DtsGenOptions): Promise<void> {
const {
bundle,
Expand All @@ -16,6 +106,8 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
cwd,
isWatch,
dtsExtension = '.d.ts',
autoExternal = true,
userExternals,
} = data;
logger.start(`Generating DTS... ${color.gray(`(${name})`)}`);
const configPath = ts.findConfigFile(cwd, ts.sys.fileExists, tsconfigPath);
Expand Down Expand Up @@ -66,6 +158,11 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
},
tsconfigPath,
dtsExtension,
bundledPackages: calcBundledPackages({
autoExternal,
cwd,
userExternals,
}),
});
}
};
Expand Down
Loading

0 comments on commit 9d40d43

Please sign in to comment.