Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support js autoExtension #28

Merged
merged 4 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading