Skip to content

Commit

Permalink
feat: support js autoExtension (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
Timeless0911 authored Jul 8, 2024
1 parent 2c2743e commit 5f38246
Show file tree
Hide file tree
Showing 19 changed files with 235 additions and 49 deletions.
2 changes: 1 addition & 1 deletion e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Rslib will try to cover the common scenarios in the [integration test cases of M
| --------------- | ------ | ----------------------------------------------------------------------------------------------------------------------- |
| alias | 🟢 | |
| asset | ⚪️ | |
| autoExtension | ⚪️ | |
| autoExtension | 🟡 | Support js extension, dts extension need to be supported in the future |
| autoExternal | ⚪️ | |
| banner-footer | ⚪️ | |
| buildType | ⚪️ | |
Expand Down
12 changes: 6 additions & 6 deletions e2e/cases/alias/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { expect, test } from 'vitest';
import { buildAndGetResults } from '#shared';
import { buildAndGetEntryJsResults } from '#shared';

test('alias should work', async () => {
const fixturePath = __dirname;
const { entries } = await buildAndGetResults(fixturePath);
const { contents } = await buildAndGetEntryJsResults(fixturePath);

expect(entries.esm).toContain('hello world');
expect(entries.cjs).toContain('hello world');
expect(contents.esm).toContain('hello world');
expect(contents.cjs).toContain('hello world');

// simple artifacts check
expect(entries.esm).toMatchSnapshot();
expect(entries.cjs).toMatchSnapshot();
expect(contents.esm).toMatchSnapshot();
expect(contents.cjs).toMatchSnapshot();
});
17 changes: 17 additions & 0 deletions e2e/cases/autoExtension/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { extname, join } from 'node:path';
import { expect, test } from 'vitest';
import { buildAndGetEntryJsResults } from '#shared';

test('autoExtension generate .mjs in build artifacts with esm format when type is commonjs', async () => {
const fixturePath = join(__dirname, 'type-commonjs');
const { files } = await buildAndGetEntryJsResults(fixturePath);
expect(extname(files.esm!)).toEqual('.mjs');
expect(extname(files.cjs!)).toEqual('.js');
});

test('autoExtension generate .cjs in build artifacts with cjs format when type is module', async () => {
const fixturePath = join(__dirname, 'type-module');
const { files } = await buildAndGetEntryJsResults(fixturePath);
expect(extname(files.esm!)).toEqual('.js');
expect(extname(files.cjs!)).toEqual('.cjs');
});
5 changes: 5 additions & 0 deletions e2e/cases/autoExtension/type-commonjs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "auto-extension-commonjs-test",
"version": "1.0.0",
"private": true
}
22 changes: 22 additions & 0 deletions e2e/cases/autoExtension/type-commonjs/rslib.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { join } from 'node:path';
import { defineConfig } from '@rslib/core';
import {
generateBundleCjsConfig,
generateBundleEsmConfig,
} from '../../../scripts/shared';

export default defineConfig({
lib: [
generateBundleEsmConfig(__dirname, {
autoExtension: true,
}),
generateBundleCjsConfig(__dirname, {
autoExtension: true,
}),
],
source: {
entry: {
main: join(__dirname, 'src/index.ts'),
},
},
});
1 change: 1 addition & 0 deletions e2e/cases/autoExtension/type-commonjs/src/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const a = 1;
3 changes: 3 additions & 0 deletions e2e/cases/autoExtension/type-commonjs/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { a } from './common';

console.log(a);
6 changes: 6 additions & 0 deletions e2e/cases/autoExtension/type-module/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "auto-extension-module-test",
"version": "1.0.0",
"private": true,
"type": "module"
}
22 changes: 22 additions & 0 deletions e2e/cases/autoExtension/type-module/rslib.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { join } from 'node:path';
import { defineConfig } from '@rslib/core';
import {
generateBundleCjsConfig,
generateBundleEsmConfig,
} from '../../../scripts/shared';

export default defineConfig({
lib: [
generateBundleEsmConfig(__dirname, {
autoExtension: true,
}),
generateBundleCjsConfig(__dirname, {
autoExtension: true,
}),
],
source: {
entry: {
main: join(__dirname, 'src/index.ts'),
},
},
});
1 change: 1 addition & 0 deletions e2e/cases/autoExtension/type-module/src/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const a = 1;
3 changes: 3 additions & 0 deletions e2e/cases/autoExtension/type-module/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { a } from './common';

console.log(a);
12 changes: 6 additions & 6 deletions e2e/cases/define/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { expect, test } from 'vitest';
import { buildAndGetResults } from '#shared';
import { buildAndGetEntryJsResults } from '#shared';

test('define should work', async () => {
const fixturePath = __dirname;
const { entries } = await buildAndGetResults(fixturePath);
const { contents } = await buildAndGetEntryJsResults(fixturePath);

expect(entries.esm).not.toContain('console.info(VERSION)');
expect(entries.esm).toContain('1.0.0');
expect(contents.esm).not.toContain('console.info(VERSION)');
expect(contents.esm).toContain('1.0.0');

expect(entries.cjs).not.toContain('console.info(VERSION)');
expect(entries.cjs).toContain('1.0.0');
expect(contents.cjs).not.toContain('console.info(VERSION)');
expect(contents.cjs).toContain('1.0.0');
});
4 changes: 2 additions & 2 deletions e2e/cases/externals/browser/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { join } from 'node:path';
import { expect, test } from 'vitest';
import { buildAndGetResults } from '#shared';
import { buildAndGetEntryJsResults } from '#shared';

test('should fail when platform is not "node"', async () => {
const fixturePath = join(__dirname);
const build = buildAndGetResults(fixturePath);
const build = buildAndGetEntryJsResults(fixturePath);
await expect(build).rejects.toThrowError('Rspack build failed!');
});
8 changes: 4 additions & 4 deletions e2e/cases/externals/node/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { join } from 'node:path';
import { expect, test } from 'vitest';
import { buildAndGetResults } from '#shared';
import { buildAndGetEntryJsResults } from '#shared';

test('auto externalize Node.js built-in modules when platform is "node"', async () => {
const fixturePath = join(__dirname);
const { entries } = await buildAndGetResults(fixturePath);
const { contents } = await buildAndGetEntryJsResults(fixturePath);

for (const external of [
'import * as __WEBPACK_EXTERNAL_MODULE_fs__ from "fs"',
'import * as __WEBPACK_EXTERNAL_MODULE_node_assert__ from "node:assert"',
'import * as __WEBPACK_EXTERNAL_MODULE_react__ from "react"',
]) {
expect(entries.esm).toContain(external);
expect(contents.esm).toContain(external);
}

for (const external of [
'var external_fs_namespaceObject = require("fs");',
'var external_node_assert_namespaceObject = require("node:assert");',
'var external_react_namespaceObject = require("react");',
]) {
expect(entries.cjs).toContain(external);
expect(contents.cjs).toContain(external);
}
});
24 changes: 17 additions & 7 deletions e2e/scripts/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,37 @@ export function generateBundleCjsConfig(
}

export async function getEntryJsResults(rslibConfig: RslibConfig) {
const results: Record<string, string> = {};
const files: Record<string, string> = {};
const contents: Record<string, string> = {};

for (const libConfig of rslibConfig.lib) {
const result = await globContentJSON(libConfig?.output?.distPath?.root!, {
absolute: true,
ignore: ['/**/*.map'],
});

const entryJs = Object.keys(result).find((file) => file.endsWith('.js'));
const entryJs = Object.keys(result).find((file) =>
/\.(js|cjs|mjs)$/.test(file),
);

if (entryJs) {
results[libConfig.format!] = result[entryJs]!;
files[libConfig.format!] = entryJs;
contents[libConfig.format!] = result[entryJs]!;
}
}

return results;
return {
files,
contents,
};
}

export const buildAndGetResults = async (fixturePath: string) => {
export const buildAndGetEntryJsResults = async (fixturePath: string) => {
const rslibConfig = await loadConfig(join(fixturePath, 'rslib.config.ts'));
await build(rslibConfig);
const entries = await getEntryJsResults(rslibConfig);
return { entries };
const results = await getEntryJsResults(rslibConfig);
return {
contents: results.contents,
files: results.files,
};
};
76 changes: 54 additions & 22 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import { DEFAULT_CONFIG_NAME, DEFAULT_EXTENSIONS } from './constant';
import type {
Format,
LibConfig,
Platform,
RslibConfig,
RslibConfigAsyncFn,
RslibConfigExport,
RslibConfigSyncFn,
} from './types/config';
import { getDefaultExtension } from './utils/extension';
import { color } from './utils/helper';
import { nodeBuiltInModules } from './utils/helper';
import { logger } from './utils/logger';
Expand Down Expand Up @@ -90,15 +92,10 @@ export async function createInternalRsbuildConfig(): Promise<RsbuildConfig> {
});
}

export function convertLibConfigToRsbuildConfig(
libConfig: LibConfig,
rsbuildConfig: RsbuildConfig,
): RsbuildConfig {
const { format, platform = 'browser' } = libConfig;
let formatConfig: RsbuildConfig = {};
const getDefaultFormatConfig = (format: Format): RsbuildConfig => {
switch (format) {
case 'esm':
formatConfig = {
return {
tools: {
rspack: {
externalsType: 'module',
Expand All @@ -118,9 +115,8 @@ export function convertLibConfigToRsbuildConfig(
},
},
};
break;
case 'cjs':
formatConfig = {
return {
tools: {
rspack: {
externalsType: 'commonjs',
Expand All @@ -132,9 +128,8 @@ export function convertLibConfigToRsbuildConfig(
},
},
};
break;
case 'umd':
formatConfig = {
return {
tools: {
rspack: {
externalsType: 'umd',
Expand All @@ -146,18 +141,17 @@ export function convertLibConfigToRsbuildConfig(
},
},
};
break;
default:
throw new Error(`Unsupported format: ${libConfig.format}`);
throw new Error(`Unsupported format: ${format}`);
}
};

let platformConfig: RsbuildConfig = {};
const getDefaultPlatformConfig = (platform: Platform): RsbuildConfig => {
switch (platform) {
case 'browser':
platformConfig = {};
break;
return {};
case 'node':
platformConfig = {
return {
output: {
// When output.target is 'node', Node.js's built-in will be treated as externals of type `node-commonjs`.
// Simply override the built-in modules to make them external.
Expand All @@ -166,15 +160,53 @@ export function convertLibConfigToRsbuildConfig(
target: 'node',
},
};
break;
case 'neutral':
platformConfig = {};
break;
return {};
default:
throw new Error(`Unsupported platform: ${libConfig.platform}`);
throw new Error(`Unsupported platform: ${platform}`);
}
};

const getDefaultAutoExtensionConfig = (
format: Format,
root: string,
autoExtension: boolean,
): RsbuildConfig => {
const { jsExtension } = getDefaultExtension({
format,
root,
autoExtension,
});

return mergeRsbuildConfig(rsbuildConfig, formatConfig, platformConfig);
return {
output: {
filename: {
js: `[name]${jsExtension}`,
},
},
};
};

export function convertLibConfigToRsbuildConfig(
libConfig: LibConfig,
rsbuildConfig: RsbuildConfig,
): RsbuildConfig {
const { format, platform = 'browser', autoExtension = false } = libConfig;

const formatConfig = getDefaultFormatConfig(format!);
const platformConfig = getDefaultPlatformConfig(platform);
const autoExtensionConfig = getDefaultAutoExtensionConfig(
format!,
dirname(rsbuildConfig._privateMeta?.configFilePath ?? process.cwd()),
autoExtension,
);

return mergeRsbuildConfig(
rsbuildConfig,
formatConfig,
platformConfig,
autoExtensionConfig,
);
}

export async function composeCreateRsbuildConfig(
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/types/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { RsbuildConfig } from '@rsbuild/core';

export type Format = 'esm' | 'cjs' | 'umd';
export type Platform = 'node' | 'browser' | 'neutral';

export interface LibConfig extends RsbuildConfig {
format?: Format;
platform?: 'node' | 'browser' | 'neutral';
platform?: Platform;
autoExtension?: boolean;
}

export interface RslibConfig extends RsbuildConfig {
Expand Down
Loading

0 comments on commit 5f38246

Please sign in to comment.