Skip to content

Commit

Permalink
[BREAKING] feat: override start & bundle commands (#537)
Browse files Browse the repository at this point in the history
* feat: override metro commands by default

* feat: update types for commands

* test: add basic tests to TesterApp suite

* chore: add changeset

* chore: adjust changeset
  • Loading branch information
jbroma committed Apr 5, 2024
1 parent a74930b commit 6fa32cb
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 130 deletions.
22 changes: 22 additions & 0 deletions .changeset/honest-wolves-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"@callstack/repack": major
---

BREAKING CHANGE:

Re.Pack commands now override the default `start` and `bundle` CLI commands for enhanced functionality and compatibility with `react-native` versions >= 0.74. For earlier versions of `react-native` (< 0.74), the traditional commands `webpack-start` and `webpack-bundle` remain available and recommended.

This change primarily impacts setups where both Metro and Re.Pack are used simultaneously.

To maintain your current workflow without adopting these overrides, especially to avoid conflicts in projects using both Metro and Re.Pack, you can opt out by filtering out the new command names and reverting to the legacy `webpack` prefixed commands:

```js
// react-native.config.js
const commands = require("@callstack/repack/commands");

module.exports = {
commands: commands.filter((command) => command.name.startsWith("webpack")),
};
```

Additionally, this update ensures that running `react-native run-ios` or `react-native run-android` will launch the Re.Pack dev server by default instead of the Metro dev server.
148 changes: 79 additions & 69 deletions packages/TesterApp/__tests__/bundle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,75 +21,85 @@ const REACT_NATIVE_ANDROID_ASSET_PATH = RELATIVE_REACT_NATIVE_PATH.replaceAll(
'_'
).replaceAll(/[-.@+]/g, '');

beforeAll(async () => {
await fs.promises.rm(TMP_DIR, {
recursive: true,
force: true,
describe('bundle command', () => {
it("should be also available under 'webpack-bundle' alias", () => {
const bundleCommand = commands.find((command) => command.name === 'bundle');
const webpackBundleCommand = commands.find(
(command) => command.name === 'webpack-bundle'
);

expect(bundleCommand).toBeDefined();
expect(webpackBundleCommand).toBeDefined();
expect(bundleCommand!.func).toEqual(webpackBundleCommand!.func);
});
});

describe.each([
{
platform: 'ios',
assets: [
'index.bundle',
'index.bundle.map',
'miniapp.chunk.bundle',
'miniapp.chunk.bundle.map',
'remote.chunk.bundle',
'remote.chunk.bundle.map',
'src_asyncChunks_Async_local_tsx.chunk.bundle',
'src_asyncChunks_Async_local_tsx.chunk.bundle.map',
'react-native-bundle-output/main.jsbundle',
'react-native-bundle-output/main.jsbundle.map',
'react-native-bundle-output/src_asyncChunks_Async_local_tsx.chunk.bundle',
'react-native-bundle-output/src_asyncChunks_Async_local_tsx.chunk.bundle.map',
'assets/src/miniapp/callstack-dark.png',
`assets/${RELATIVE_REACT_NATIVE_PATH}/Libraries/NewAppScreen/components/logo.png`,
'assets/src/assetsTest/localAssets/webpack.png',
'assets/src/assetsTest/localAssets/[email protected]',
'assets/src/assetsTest/localAssets/[email protected]',
'remote-assets/assets/src/assetsTest/remoteAssets/webpack.png',
'remote-assets/assets/src/assetsTest/remoteAssets/[email protected]',
'remote-assets/assets/src/assetsTest/remoteAssets/[email protected]',
'react-native-bundle-output/assets/src/assetsTest/localAssets/webpack.png',
'react-native-bundle-output/assets/src/assetsTest/localAssets/[email protected]',
'react-native-bundle-output/assets/src/assetsTest/localAssets/[email protected]',
`react-native-bundle-output/assets/${RELATIVE_REACT_NATIVE_PATH}/Libraries/NewAppScreen/components/logo.png`,
],
},
{
platform: 'android',
assets: [
'index.bundle',
'index.bundle.map',
'miniapp.chunk.bundle',
'miniapp.chunk.bundle.map',
'remote.chunk.bundle',
'remote.chunk.bundle.map',
'src_asyncChunks_Async_local_tsx.chunk.bundle',
'src_asyncChunks_Async_local_tsx.chunk.bundle.map',
`drawable-mdpi/${REACT_NATIVE_ANDROID_ASSET_PATH}_libraries_newappscreen_components_logo.png`,
'drawable-mdpi/src_assetstest_localassets_webpack.png',
'drawable-xxhdpi/src_assetstest_localassets_webpack.png',
'drawable-xhdpi/src_assetstest_localassets_webpack.png',
'drawable-mdpi/src_miniapp_callstackdark.png',
'react-native-bundle-output/index.android.bundle',
'react-native-bundle-output/index.android.bundle.map',
'react-native-bundle-output/src_asyncChunks_Async_local_tsx.chunk.bundle',
'react-native-bundle-output/src_asyncChunks_Async_local_tsx.chunk.bundle.map',
`react-native-bundle-output/drawable-mdpi/${REACT_NATIVE_ANDROID_ASSET_PATH}_libraries_newappscreen_components_logo.png`,
'react-native-bundle-output/drawable-mdpi/src_assetstest_localassets_webpack.png',
'react-native-bundle-output/drawable-xxhdpi/src_assetstest_localassets_webpack.png',
'react-native-bundle-output/drawable-xhdpi/src_assetstest_localassets_webpack.png',
'remote-assets/assets/src/assetsTest/remoteAssets/webpack.png',
'remote-assets/assets/src/assetsTest/remoteAssets/[email protected]',
'remote-assets/assets/src/assetsTest/remoteAssets/[email protected]',
],
},
])(
'bundle command should successfully produce bundle assets',
({ platform, assets }) => {
describe.each([
{
platform: 'ios',
assets: [
'index.bundle',
'index.bundle.map',
'miniapp.chunk.bundle',
'miniapp.chunk.bundle.map',
'remote.chunk.bundle',
'remote.chunk.bundle.map',
'src_asyncChunks_Async_local_tsx.chunk.bundle',
'src_asyncChunks_Async_local_tsx.chunk.bundle.map',
'react-native-bundle-output/main.jsbundle',
'react-native-bundle-output/main.jsbundle.map',
'react-native-bundle-output/src_asyncChunks_Async_local_tsx.chunk.bundle',
'react-native-bundle-output/src_asyncChunks_Async_local_tsx.chunk.bundle.map',
'assets/src/miniapp/callstack-dark.png',
`assets/${RELATIVE_REACT_NATIVE_PATH}/Libraries/NewAppScreen/components/logo.png`,
'assets/src/assetsTest/localAssets/webpack.png',
'assets/src/assetsTest/localAssets/[email protected]',
'assets/src/assetsTest/localAssets/[email protected]',
'remote-assets/assets/src/assetsTest/remoteAssets/webpack.png',
'remote-assets/assets/src/assetsTest/remoteAssets/[email protected]',
'remote-assets/assets/src/assetsTest/remoteAssets/[email protected]',
'react-native-bundle-output/assets/src/assetsTest/localAssets/webpack.png',
'react-native-bundle-output/assets/src/assetsTest/localAssets/[email protected]',
'react-native-bundle-output/assets/src/assetsTest/localAssets/[email protected]',
`react-native-bundle-output/assets/${RELATIVE_REACT_NATIVE_PATH}/Libraries/NewAppScreen/components/logo.png`,
],
},
{
platform: 'android',
assets: [
'index.bundle',
'index.bundle.map',
'miniapp.chunk.bundle',
'miniapp.chunk.bundle.map',
'remote.chunk.bundle',
'remote.chunk.bundle.map',
'src_asyncChunks_Async_local_tsx.chunk.bundle',
'src_asyncChunks_Async_local_tsx.chunk.bundle.map',
`drawable-mdpi/${REACT_NATIVE_ANDROID_ASSET_PATH}_libraries_newappscreen_components_logo.png`,
'drawable-mdpi/src_assetstest_localassets_webpack.png',
'drawable-xxhdpi/src_assetstest_localassets_webpack.png',
'drawable-xhdpi/src_assetstest_localassets_webpack.png',
'drawable-mdpi/src_miniapp_callstackdark.png',
'react-native-bundle-output/index.android.bundle',
'react-native-bundle-output/index.android.bundle.map',
'react-native-bundle-output/src_asyncChunks_Async_local_tsx.chunk.bundle',
'react-native-bundle-output/src_asyncChunks_Async_local_tsx.chunk.bundle.map',
`react-native-bundle-output/drawable-mdpi/${REACT_NATIVE_ANDROID_ASSET_PATH}_libraries_newappscreen_components_logo.png`,
'react-native-bundle-output/drawable-mdpi/src_assetstest_localassets_webpack.png',
'react-native-bundle-output/drawable-xxhdpi/src_assetstest_localassets_webpack.png',
'react-native-bundle-output/drawable-xhdpi/src_assetstest_localassets_webpack.png',
'remote-assets/assets/src/assetsTest/remoteAssets/webpack.png',
'remote-assets/assets/src/assetsTest/remoteAssets/[email protected]',
'remote-assets/assets/src/assetsTest/remoteAssets/[email protected]',
],
},
])('should successfully produce bundle assets', ({ platform, assets }) => {
beforeAll(async () => {
await fs.promises.rm(TMP_DIR, {
recursive: true,
force: true,
});
});

afterEach(() => {
delete process.env.TEST_WEBPACK_OUTPUT_PATH;
});
Expand Down Expand Up @@ -122,5 +132,5 @@ describe.each([
},
60 * 1000
);
}
);
});
});
124 changes: 67 additions & 57 deletions packages/TesterApp/__tests__/start.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,66 +21,76 @@ const RELATIVE_REACT_NATIVE_PATH = path.relative(
path.dirname(REACT_NATIVE_PATH)
);

beforeAll(async () => {
await fs.promises.rm(TMP_DIR, {
recursive: true,
force: true,
describe('start command', () => {
it("should be also available under 'webpack-start' alias", () => {
const startCommand = commands.find((command) => command.name === 'start');
const webpackStartCommand = commands.find(
(command) => command.name === 'webpack-start'
);

expect(startCommand).toBeDefined();
expect(webpackStartCommand).toBeDefined();
expect(startCommand!.func).toEqual(webpackStartCommand!.func);
});

port = await getPort();
const config = {
root: path.join(__dirname, '..'),
reactNativePath: path.join(__dirname, '../node_modules/react-native'),
};
const args = {
port,
silent: true,
logFile: path.join(TMP_DIR, 'server.log'),
webpackConfig: path.join(__dirname, './webpack.config.mjs'),
};
describe.each([
{
platform: 'ios',
requests: [
'index.bundle.map?platform=ios',
'index.bundle.map?platform=ios',
'ios/miniapp.chunk.bundle',
'ios/miniapp.chunk.bundle.map',
'ios/remote.chunk.bundle',
'ios/remote.chunk.bundle.map',
'ios/src_asyncChunks_Async_local_tsx.chunk.bundle',
'ios/src_asyncChunks_Async_local_tsx.chunk.bundle.map',
'assets/src/miniapp/callstack-dark.png?platform=ios',
`assets/${RELATIVE_REACT_NATIVE_PATH}/Libraries/NewAppScreen/components/logo.png?platform=ios`,
],
},
{
platform: 'android',
requests: [
'index.bundle.map?platform=android',
'index.bundle.map?platform=android',
'android/miniapp.chunk.bundle',
'android/miniapp.chunk.bundle.map',
'android/remote.chunk.bundle',
'android/remote.chunk.bundle.map',
'android/src_asyncChunks_Async_local_tsx.chunk.bundle',
'android/src_asyncChunks_Async_local_tsx.chunk.bundle.map',
'assets/src/miniapp/callstack-dark.png?platform=android',
`assets/${RELATIVE_REACT_NATIVE_PATH}/Libraries/NewAppScreen/components/logo.png?platform=android`,
],
},
])('should successfully produce bundle assets', ({ platform, requests }) => {
beforeAll(async () => {
await fs.promises.rm(TMP_DIR, {
recursive: true,
force: true,
});

const { stop } = await start.func([], config as Config, args as Args);
stopServer = stop;
});
port = await getPort();
const config = {
root: path.join(__dirname, '..'),
reactNativePath: path.join(__dirname, '../node_modules/react-native'),
};
const args = {
port,
silent: true,
logFile: path.join(TMP_DIR, 'server.log'),
webpackConfig: path.join(__dirname, './webpack.config.mjs'),
};

afterAll(async () => {
await stopServer();
});
const { stop } = await start.func([], config as Config, args as Args);
stopServer = stop;
});

afterAll(async () => {
await stopServer();
});

describe.each([
{
platform: 'ios',
requests: [
'index.bundle.map?platform=ios',
'index.bundle.map?platform=ios',
'ios/miniapp.chunk.bundle',
'ios/miniapp.chunk.bundle.map',
'ios/remote.chunk.bundle',
'ios/remote.chunk.bundle.map',
'ios/src_asyncChunks_Async_local_tsx.chunk.bundle',
'ios/src_asyncChunks_Async_local_tsx.chunk.bundle.map',
'assets/src/miniapp/callstack-dark.png?platform=ios',
`assets/${RELATIVE_REACT_NATIVE_PATH}/Libraries/NewAppScreen/components/logo.png?platform=ios`,
],
},
{
platform: 'android',
requests: [
'index.bundle.map?platform=android',
'index.bundle.map?platform=android',
'android/miniapp.chunk.bundle',
'android/miniapp.chunk.bundle.map',
'android/remote.chunk.bundle',
'android/remote.chunk.bundle.map',
'android/src_asyncChunks_Async_local_tsx.chunk.bundle',
'android/src_asyncChunks_Async_local_tsx.chunk.bundle.map',
'assets/src/miniapp/callstack-dark.png?platform=android',
`assets/${RELATIVE_REACT_NATIVE_PATH}/Libraries/NewAppScreen/components/logo.png?platform=android`,
],
},
])(
'start command should successfully produce bundle assets',
({ platform, requests }) => {
it(
`for ${platform}`,
async () => {
Expand Down Expand Up @@ -115,5 +125,5 @@ describe.each([
},
60 * 1000
);
}
);
});
});
14 changes: 13 additions & 1 deletion packages/repack/commands.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
const commands: [
{
name: 'bundle';
description: string;
options: Array<any>;
func: typeof import('./dist/commands/bundle').bundle;
},
{
name: 'start';
description: string;
options: Array<any>;
func: typeof import('./dist/commands/start').start;
},
{
name: 'webpack-bundle';
description: string;
Expand All @@ -10,6 +22,6 @@ const commands: [
description: string;
options: Array<any>;
func: typeof import('./dist/commands/start').start;
}
},
];
export default commands;
13 changes: 10 additions & 3 deletions packages/repack/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ const webpackConfigOption = {
},
};

module.exports = [
const commands = [
{
name: 'webpack-bundle',
name: 'bundle',
description: bundleCommand.description,
options: bundleCommand.options.concat(
{
Expand All @@ -96,7 +96,7 @@ module.exports = [
func: require('./dist/commands/bundle').bundle,
},
{
name: 'webpack-start',
name: 'start',
options: startCommand.options.concat(
{
name: '--verbose',
Expand Down Expand Up @@ -124,3 +124,10 @@ module.exports = [
func: require('./dist/commands/start').start,
},
];

const webpackCommands = commands.map((command) => ({
...command,
name: `webpack-${command.name}`,
}));

module.exports = [...commands, ...webpackCommands];

0 comments on commit 6fa32cb

Please sign in to comment.