Skip to content
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
5 changes: 5 additions & 0 deletions .changeset/reject-webpack-asset-requests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@callstack/repack": patch
---

Reject pending webpack asset requests when compilation fails instead of leaving requests hanging.
6 changes: 6 additions & 0 deletions .changeset/unify-bundler-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@callstack/repack": minor
"@callstack/repack-init": patch
---

Add the unified `@callstack/repack/commands` entry point with automatic bundler detection and a `--bundler` override. Re.Pack Init now uses it, while bundler-specific entry points remain available with deprecation warnings.
7 changes: 2 additions & 5 deletions apps/tester-app/__tests__/bundle.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
import rspackCommands from '@callstack/repack/commands/rspack';
import webpackCommands from '@callstack/repack/commands/webpack';
import commands from '@callstack/repack/commands';
import { globby } from 'globby';
import {
afterEach,
Expand All @@ -17,15 +16,13 @@ describe('bundle command', () => {
describe.each([
{
bundler: 'webpack',
commands: webpackCommands,
configFile: './webpack.config.mjs',
},
{
bundler: 'rspack',
commands: rspackCommands,
configFile: './rspack.config.mjs',
},
])('using $bundler', ({ bundler, commands, configFile }) => {
])('using $bundler', ({ bundler, configFile }) => {
const bundleCommand = commands.find((command) => command.name === 'bundle');
if (!bundleCommand) throw new Error('bundle command not found');

Expand Down
7 changes: 2 additions & 5 deletions apps/tester-app/__tests__/start.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
import rspackCommands from '@callstack/repack/commands/rspack';
import webpackCommands from '@callstack/repack/commands/webpack';
import commands from '@callstack/repack/commands';
import getPort from 'get-port';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

Expand All @@ -12,15 +11,13 @@ describe('start command', () => {
describe.each([
{
bundler: 'webpack',
commands: webpackCommands,
configFile: './webpack.config.mjs',
},
{
bundler: 'rspack',
commands: rspackCommands,
configFile: './rspack.config.mjs',
},
])('using $bundler', ({ bundler, commands, configFile }) => {
])('using $bundler', ({ bundler, configFile }) => {
const startCommand = commands.find((command) => command.name === 'start');
if (!startCommand) throw new Error('start command not found');

Expand Down
6 changes: 1 addition & 5 deletions apps/tester-app/react-native.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
const { configureProjects } = require('react-native-test-app');

const useWebpack = Boolean(process.env.USE_WEBPACK);

module.exports = {
project: configureProjects({
android: {
Expand All @@ -11,7 +9,5 @@ module.exports = {
sourceDir: 'ios',
},
}),
commands: useWebpack
? require('@callstack/repack/commands/webpack')
: require('@callstack/repack/commands/rspack'),
commands: require('@callstack/repack/commands'),
};
6 changes: 1 addition & 5 deletions apps/tester-federation-v2/react-native.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
const { configureProjects } = require('react-native-test-app');

const useWebpack = Boolean(process.env.USE_WEBPACK);

module.exports = {
project: configureProjects({
android: {
Expand All @@ -11,7 +9,5 @@ module.exports = {
sourceDir: 'ios',
},
}),
commands: useWebpack
? require('@callstack/repack/commands/webpack')
: require('@callstack/repack/commands/rspack'),
commands: require('@callstack/repack/commands'),
};
6 changes: 1 addition & 5 deletions apps/tester-federation/react-native.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
const { configureProjects } = require('react-native-test-app');

const useWebpack = Boolean(process.env.USE_WEBPACK);

module.exports = {
project: configureProjects({
android: {
Expand All @@ -11,7 +9,5 @@ module.exports = {
sourceDir: 'ios',
},
}),
commands: useWebpack
? require('@callstack/repack/commands/webpack')
: require('@callstack/repack/commands/rspack'),
commands: require('@callstack/repack/commands'),
};
2 changes: 1 addition & 1 deletion packages/init/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default async function run(options: Options) {
options.entry
);

modifyReactNativeConfig(bundler, projectRootDir);
modifyReactNativeConfig(projectRootDir);

modifyIOS(projectRootDir);

Expand Down
17 changes: 8 additions & 9 deletions packages/init/src/tasks/modifyReactNativeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@ import path from 'node:path';
import dedent from 'dedent';
import logger from '../utils/logger.js';

const createDefaultConfig = (bundler: 'rspack' | 'webpack') => dedent`
const COMMANDS_REQUIRE = `require('@callstack/repack/commands')`;

const createDefaultConfig = () => dedent`
module.exports = {
commands: require('@callstack/repack/commands/${bundler}'),
commands: ${COMMANDS_REQUIRE},
};`;

/**
* Checks whether react-native.config.js exists and adds the commands to it
*
* @param cwd current working directory
*/
export default function modifyReactNativeConfig(
bundler: 'rspack' | 'webpack',
cwd: string
): void {
export default function modifyReactNativeConfig(cwd: string): void {
const configPath = path.join(cwd, 'react-native.config.js');

if (!fs.existsSync(configPath)) {
fs.writeFileSync(configPath, createDefaultConfig(bundler));
fs.writeFileSync(configPath, createDefaultConfig());
logger.info('Created react-native.config.js');
return;
}
Expand All @@ -31,14 +30,14 @@ export default function modifyReactNativeConfig(
if (!configContent.includes('commands:')) {
updatedConfigContent = configContent.replace(
'module.exports = {',
`module.exports = {\n commands: require('@callstack/repack/commands/${bundler}'),`
`module.exports = {\n commands: ${COMMANDS_REQUIRE},`
);
} else {
const commandsIndex = configContent.indexOf('commands:');
const commandsEndIndex = configContent.indexOf(',', commandsIndex);
const commandsString = configContent.slice(commandsIndex, commandsEndIndex);

const newCommandsString = `commands: require('@callstack/repack/commands/${bundler}')`;
const newCommandsString = `commands: ${COMMANDS_REQUIRE}`;
if (commandsString === newCommandsString) {
logger.info('File react-native.config.js is already up to date');
return;
Expand Down
2 changes: 2 additions & 0 deletions packages/repack/commands/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import commands from '../dist/commands/index.js';
export = commands;
2 changes: 2 additions & 0 deletions packages/repack/commands/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const commands = require('../dist/commands/index.js');
module.exports = commands.default;
2 changes: 1 addition & 1 deletion packages/repack/commands/rspack.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
import commands from '../dist/commands/rspack/index.js';
import commands from '../dist/commands/index.js';
export = commands;
7 changes: 5 additions & 2 deletions packages/repack/commands/rspack.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
const commands = require('../dist/commands/rspack/index.js');
module.exports = commands.default;
console.warn(
'Importing "@callstack/repack/commands/rspack" is deprecated. Use "@callstack/repack/commands" instead.'
);
const { createBoundCommands } = require('../dist/commands/index.js');
module.exports = createBoundCommands('rspack');
2 changes: 1 addition & 1 deletion packages/repack/commands/webpack.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
import commands from '../dist/commands/webpack/index.js';
import commands from '../dist/commands/index.js';
export = commands;
7 changes: 5 additions & 2 deletions packages/repack/commands/webpack.js
Comment thread
jbroma marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
const commands = require('../dist/commands/webpack/index.js');
module.exports = commands.default;
console.warn(
'Importing "@callstack/repack/commands/webpack" is deprecated. Use "@callstack/repack/commands" instead.'
);
const { createBoundCommands } = require('../dist/commands/index.js');
module.exports = createBoundCommands('webpack');
1 change: 1 addition & 0 deletions packages/repack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"exports": {
".": "./dist/index.js",
"./client": "./client/index.js",
"./commands": "./commands/index.js",
"./commands/*": "./commands/*.js",
"./assets-loader": "./dist/loaders/assetsLoader/index.js",
"./babel-loader": "./dist/loaders/babelLoader/index.js",
Expand Down
49 changes: 49 additions & 0 deletions packages/repack/src/commands/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { bundle } from '../bundle.js';
import { createBoundCommands } from '../index.js';
import type { BundleArguments, CliConfig, StartArguments } from '../types.js';

jest.mock('../bundle.js');
jest.mock('../start.js');

const cliConfig: CliConfig = {
root: '/project',
platforms: ['ios'],
reactNativePath: '/project/node_modules/react-native',
};

const args = {
dev: true,
host: '',
platform: 'ios',
} satisfies BundleArguments & StartArguments;

describe('createBoundCommands', () => {
const consoleWarn = jest.spyOn(console, 'warn').mockImplementation();

test('warns when the bound command overrides --bundler', async () => {
const [command] = createBoundCommands('webpack');

await command.func([], cliConfig, { ...args, bundler: 'rspack' });

expect(consoleWarn).toHaveBeenCalledWith(
'Ignoring "--bundler rspack" because the deprecated "@callstack/repack/commands/webpack" entry point explicitly selects webpack. Use "@callstack/repack/commands" to select a bundler with --bundler.'
);
expect(bundle).toHaveBeenCalledWith(
[],
cliConfig,
{ ...args, bundler: 'rspack' },
'webpack'
);
});

test.each([undefined, 'webpack' as const])(
'does not warn when --bundler is %s',
async (bundler) => {
const [command] = createBoundCommands('webpack');

await command.func([], cliConfig, { ...args, bundler });

expect(consoleWarn).not.toHaveBeenCalled();
}
);
});
20 changes: 20 additions & 0 deletions packages/repack/src/commands/__tests__/options.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { bundleCommandOptions, startCommandOptions } from '../options.js';

describe.each([
['start', startCommandOptions],
['bundle', bundleCommandOptions],
])('%s command options', (_, options) => {
const bundlerOption = options.find(
(option) => option.name === '--bundler <string>'
);

test.each(['rspack', 'webpack'])('accepts the %s bundler', (bundler) => {
expect(bundlerOption?.parse?.(bundler)).toBe(bundler);
});

test('rejects an unsupported bundler', () => {
expect(() => bundlerOption?.parse?.('metro')).toThrow(
'Invalid bundler "metro". Expected "rspack" or "webpack".'
);
});
});
Loading
Loading