Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 5 additions & 2 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ const composeFormatConfig = ({
bundle?: boolean;
umdName?: Rspack.LibraryName;
}): EnvironmentConfig => {
const jsParserOptions = {
const jsParserOptions: Record<string, Rspack.JavascriptParserOptions> = {
cjs: {
requireResolve: false,
requireDynamic: false,
Expand All @@ -611,11 +611,14 @@ const composeFormatConfig = ({
esm: {
importMeta: false,
importDynamic: false,
commonjs: {
exports: 'skipInEsm',
},
},
others: {
worker: false,
},
} as const;
};

// The built-in Rslib plugin will apply to all formats except the `mf` format.
// The `mf` format functions more like an application than a library and requires additional webpack runtime.
Expand Down
12 changes: 12 additions & 0 deletions packages/core/tests/__snapshots__/config.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,9 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
typeReexportsPresence: 'tolerant',
importMeta: false,
importDynamic: false,
commonjs: {
exports: 'skipInEsm'
},
requireResolve: false,
requireDynamic: false,
requireAsExpression: false,
Expand Down Expand Up @@ -1158,6 +1161,9 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
typeReexportsPresence: 'tolerant',
importMeta: false,
importDynamic: false,
commonjs: {
exports: 'skipInEsm'
},
requireResolve: false,
requireDynamic: false,
requireAsExpression: false,
Expand Down Expand Up @@ -3658,6 +3664,9 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
"module": {
"parser": {
"javascript": {
"commonjs": {
"exports": "skipInEsm",
},
"importDynamic": false,
"importMeta": false,
"requireAsExpression": false,
Expand Down Expand Up @@ -3944,6 +3953,9 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
"module": {
"parser": {
"javascript": {
"commonjs": {
"exports": "skipInEsm",
},
"importDynamic": false,
"importMeta": false,
"requireAsExpression": false,
Expand Down
22 changes: 12 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 7 additions & 21 deletions tests/integration/format/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,32 +114,18 @@ test('throw error when using mf with `bundle: false`', async () => {
);
});

test("API plugin's api should be skipped in parser", async () => {
const fixturePath = path.resolve(__dirname, 'api-plugin');
const { entries } = await buildAndGetResults({
test('ESM interop should be correct', async () => {
const fixturePath = path.resolve(__dirname, 'esm-interop');
const { entryFiles } = await buildAndGetResults({
fixturePath,
});

expect(entries.esm).toMatchInlineSnapshot(`
"const a = require.cache;
const b = require.extensions;
const c = require.config;
const d = require.version;
const e = require.include;
const f = require.onError;
export { a, b, c, d, e, f };
"
`);

expect(entries.cjs).toContain('const a = require.cache;');
expect(entries.cjs).toContain('const b = require.extensions;');
expect(entries.cjs).toContain('const c = require.config;');
expect(entries.cjs).toContain('const d = require.version;');
expect(entries.cjs).toContain('const e = require.include;');
expect(entries.cjs).toContain('const f = require.onError;');
const cjsOutput = await import(entryFiles.cjs);
expect(typeof cjsOutput.default.path1.basename).toBe('function');
expect(cjsOutput.default.path1).toBe(cjsOutput.default.path2);
});

test('ESM interop should be correct', async () => {
test('`module` should be correctly handled by `parserOptions.commonjs.exports = "skipInEsm"`', async () => {
const fixturePath = path.resolve(__dirname, 'esm-interop');
const { entryFiles } = await buildAndGetResults({
fixturePath,
Expand Down
28 changes: 28 additions & 0 deletions tests/integration/parser-javascript/api-plugin/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import path from 'node:path';
import { expect, test } from '@rstest/core';
import { buildAndGetResults } from 'test-helper';

test("API plugin's api should be skipped in parser", async () => {
const fixturePath = path.resolve(__dirname);
const { entries } = await buildAndGetResults({
fixturePath,
});

expect(entries.esm).toMatchInlineSnapshot(`
"const a = require.cache;
const b = require.extensions;
const c = require.config;
const d = require.version;
const e = require.include;
const f = require.onError;
export { a, b, c, d, e, f };
"
`);

expect(entries.cjs).toContain('const a = require.cache;');
expect(entries.cjs).toContain('const b = require.extensions;');
expect(entries.cjs).toContain('const c = require.config;');
expect(entries.cjs).toContain('const d = require.version;');
expect(entries.cjs).toContain('const e = require.include;');
expect(entries.cjs).toContain('const f = require.onError;');
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "api-plugin-test",
"name": "parser-javascript-api-plugin-test",
"version": "1.0.0",
"private": true,
"type": "module"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const value = () => 42;

// Make the export immutable
Object.defineProperty(module, 'exports', {
enumerable: true,
get: value,
});
57 changes: 57 additions & 0 deletions tests/integration/parser-javascript/module/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import path from 'node:path';
import { expect, test } from '@rstest/core';
import { buildAndGetResults, queryContent } from 'test-helper';

test('`module` variable should be preserved as-is by `javascript.commonjs.exports = "false"`', async () => {
const fixturePath = path.resolve(__dirname);
const { contents } = await buildAndGetResults({
fixturePath,
});

const esm1 = queryContent(contents.esm, 'm1.mjs', { basename: true });
const esm2 = queryContent(contents.esm, 'm2.mjs', { basename: true });
const cjs1 = queryContent(contents.cjs, 'm1.js', { basename: true });
const cjs2 = queryContent(contents.cjs, 'm2.js', { basename: true });

expect(
(
await Promise.all([
import(esm1.path),
import(esm2.path),
import(cjs1.path),
import(cjs2.path),
])
).map((m) => m.value),
).toEqual([42, 42, 42, 42]);

const checksM1 = [
'if (module.children) module.children = module.children.filter((item)=>item.filename !== path);',
'module.exports = original',
'if (module.exports && module.exports.test) return module.exports.test()',
];

const checksEsm2 = [
'if (node_module.children) node_module.children = node_module.children.filter((item)=>item.filename !== path);',
'node_module.exports = original',
'if (node_module.exports && node_module.exports.test) return node_module.exports.test()',
];

const checksCjs2 = [
'if (external_node_module_default().children) external_node_module_default().children = external_node_module_default().children.filter((item)=>item.filename !== path);',
'external_node_module_default().exports = original',
'if (external_node_module_default().exports && external_node_module_default().exports.test) return external_node_module_default().exports.test()',
];

for (const check of checksM1) {
expect(esm1.content).toContain(check);
expect(cjs1.content).toContain(check);
}

for (const check of checksEsm2) {
expect(esm2.content).toContain(check);
}

for (const check of checksCjs2) {
expect(cjs2.content).toContain(check);
}
});
5 changes: 5 additions & 0 deletions tests/integration/parser-javascript/module/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "parser-javascript-module-test",
"version": "1.0.0",
"private": true
}
25 changes: 25 additions & 0 deletions tests/integration/parser-javascript/module/rslib.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { defineConfig } from '@rslib/core';
import { generateBundleCjsConfig, generateBundleEsmConfig } from 'test-helper';

export default defineConfig({
lib: [
// ESM
generateBundleEsmConfig({
source: {
entry: {
m1: './src/module1.js',
m2: './src/module2.js',
},
},
}),
// CJS
generateBundleCjsConfig({
source: {
entry: {
m1: './src/module1.js',
m2: './src/module2.js',
},
},
}),
],
});
7 changes: 7 additions & 0 deletions tests/integration/parser-javascript/module/src/cjsFile.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const value = () => 42;

// Make the export immutable
Object.defineProperty(module, 'exports', {
enumerable: true,
get: value,
});
36 changes: 36 additions & 0 deletions tests/integration/parser-javascript/module/src/module1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import value from './cjsFile.cjs';
export { value };

export function foo() {
console.log(module);
if (module.children) {
module.children = module.children.filter((item) => item.filename !== path);
}
}

export function bar() {
const original = module.exports;

try {
module.exports = {
...module.exports,
test: () => 'ok',
};

return module.exports.test();
} finally {
module.exports = original;
}
}

export function baz() {
if (typeof module === 'undefined') {
return undefined;
}

if (module.exports && module.exports.test) {
return module.exports.test();
}

return undefined;
}
37 changes: 37 additions & 0 deletions tests/integration/parser-javascript/module/src/module2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import module from 'node:module';
import value from './cjsFile.cjs';
export { value };

export function foo() {
console.log(module);
if (module.children) {
module.children = module.children.filter((item) => item.filename !== path);
}
}

export function bar() {
const original = module.exports;

try {
module.exports = {
...module.exports,
test: () => 'ok',
};

return module.exports.test();
} finally {
module.exports = original;
}
}

export function baz() {
if (typeof module === 'undefined') {
return undefined;
}

if (module.exports && module.exports.test) {
return module.exports.test();
}

return undefined;
}
Loading