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: external bundles #633

Merged
merged 21 commits into from
Sep 9, 2019
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
10 changes: 10 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ jobs:
- attach_workspace:
at: ~/haul_node_10
- run: yarn test -i integration_tests/react_native_0_60x_multibundle

monorepo_multibundle_integration_tests:
executor: node_10
steps:
- attach_workspace:
at: ~/haul_node_10
- run: yarn test -i integration_tests/monorepo_multibundle

setup_node_latest:
executor: node_latest
Expand Down Expand Up @@ -137,6 +144,9 @@ workflows:
- rn_0_60x_multibundle_integration_tests:
requires:
- setup_node_10
- monorepo_multibundle_integration_tests:
requires:
- setup_node_10
- setup_node_latest
- lint_and_typecheck_node_latest:
requires:
Expand Down
87 changes: 86 additions & 1 deletion docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ The bundle configuration consist of the following properties:
- `providesModuleNodeModules?: Array<string | { name: string; directory: string }>` - Provide custom modules for Haste.
- `hasteOptions?: any` - Provide Haste options.
- `transform?: WebpackConfigTransform` - Customize the Webpack config after it's been created.

The `transform` function will receive an object with `bundleName`, `env`, `runtime` and Webpack's `config`:

```js
Expand All @@ -120,6 +120,11 @@ The bundle configuration consist of the following properties:
```

More information can be found in [Customize Webpack config recipe](#customize-webpack-config).
- `bundlePath?: string` - Absolute path to an external (pre-built) bundle.
- `manifestPath?: string` - Absolute path to manifest for an external (pre-built) bundle, if `dll` is `true`.
- `assetsPath?: string` - Absolute or relative path pointing to where assets for an external (pre-built) bundle are stored. If relative, it will be joined with `path.dirname(bundlePath)`.
- `copyBundle?: boolean` - Whether to copy bundle, source map and assets of an external (pre-built) bundle to output directory.


If you want to provide the bundle config based on received CLI arguments, you can do so, by using `BundleConfigBuilder`:

Expand Down Expand Up @@ -376,3 +381,83 @@ class RootComponent extends React.Component {

AppRegistry.registerComponent('MyApp', () => RootComponent);
```

### External (pre-built) bundles

Check out [monorepo fixture](../fixtures/monorepo) for a complete an example.

To use external bundles, first build the bundles that will be considered pre-built. You only need a single bundle config:
```js
import { withPolyfills, makeConfig } from '@haul-bundler/preset-0.60';
import { join } from 'path';

const entry = withPolyfills([
require.resolve('react'),
require.resolve('react-native'),
require.resolve('react-navigation'),
]);

export default makeConfig({
bundles: {
base_dll: ({ dev }) => ({
entry,
dll: true,
// Packager server will use development bundle, so it should be built as a plain JS bundle.
type: dev ? 'basic-bundle' : 'indexed-ram-bundle',
}),
},
});
```

Use `--skip-host-check` option when building pre-built bundles:
thymikee marked this conversation as resolved.
Show resolved Hide resolved
```sh
yarn haul multi-bundle --skip-host-check <...>
```

Now in a project, where you want to use pre-built bundles, you need to specify, which bundle is external one:
```js
import {makeConfig, withPolyfills} from '@haul-bundler/preset-0.59';

export default makeConfig({
bundles: {
index: {
entry: withPolyfills('./src/host.js'),
dependsOn: ['base_dll'],
},
// Let's assume base-dll bundle is a Node module.
base_dll: ({ platform, bundleTarget, dev }) => {
// For a pre-built bundle, both development and production bundle have to be present.
const basePath = join(
__dirname,
`node_modules/base-dll/dist/${platform}/${dev ? 'dev' : 'prod'}`
);
const filename = `base_dll${
platform === 'ios' ? '.jsbundle' : '.android.bundle'
}`;
return {
dll: true,
copyBundle,
bundlePath: join(basePath, filename),
manifestPath: join(basePath, 'base_dll.manifest.json'),
}
},
// Let's assume app bundle is a Node module.
app: ({ platform, dev }) => {
const basePath = join(
__dirname,
`node_modules/app/dist/${platform}/${dev ? 'dev' : 'prod'}`
);
const filename = `app0${
platform === 'ios' ? '.jsbundle' : '.android.bundle'
}`;

return {
bundlePath: join(basePath, filename),
copyBundle: true,
};
},
},
});
```

Now when building a static bundle, the other external bundles alongside it's assets and source maps will be copied to next to `index` bundle.
53 changes: 53 additions & 0 deletions fixtures/monorepo/app0/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View, TouchableOpacity } from 'react-native';

const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});

export default class App extends Component {
state = {
counter: 0,
}

render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>App 0</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Text style={styles.instructions}>{instructions}</Text>
<Text style={styles.instructions}>Counter: {this.state.counter}</Text>
<TouchableOpacity onPress={() => {
this.setState(state => ({ counter: state.counter + 1 }))
}}><Text style={styles.instructions}>Increment</Text></TouchableOpacity>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 36,
textAlign: 'center',
color: '#282828',
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
body: {
fontSize: 24,
textAlign: 'center',
color: '#282828',
},
});
1 change: 1 addition & 0 deletions fixtures/monorepo/app0/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('../babel.config');
13 changes: 13 additions & 0 deletions fixtures/monorepo/app0/haul.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { withPolyfills, makeConfig } from '../../../packages/haul-preset-0.60';
import makeBaseDllConfig from 'base-dll/makeConfig';

export default makeConfig({
bundles: {
base_dll: makeBaseDllConfig(),
app0: {
entry: './App',
dependsOn: ['base_dll'],
app: true,
}
},
});
23 changes: 23 additions & 0 deletions fixtures/monorepo/app0/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "app0",
"version": "0.1.0",
"files": [
"dist/"
],
"scripts": {
"haul": "node ../../../packages/haul-cli/bin/haul.js",
"build:base": "yarn haul multi-bundle --skip-host-check",
"build:ios:dev": "yarn build:base --platform ios --dev true --bundle-output dist/ios/dev --assets-dest dist/ios/dev",
"build:ios:prod": "yarn build:base --platform ios --dev false --bundle-output dist/ios/prod --assets-dest dist/ios/prod",
"build:android:dev": "yarn build:base --platform android --dev true --bundle-output dist/android/dev --assets-dest dist/android/dev",
"build:android:prod": "yarn build:base --platform android --dev false --bundle-output dist/android/prod --assets-dest dist/android/prod",
"build:all": "yarn build:ios:dev && yarn build:ios:prod && yarn build:android:dev && yarn build:android:prod"
},
"dependencies": {
"react": "^16.8.6",
"react-native": "^0.60.5",
"react-native-gesture-handler": "^1.3.0",
"react-navigation": "^3.11.0",
"base-dll": "*"
}
}
3 changes: 3 additions & 0 deletions fixtures/monorepo/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: [`module:${require.resolve('../../packages/haul-babel-preset-react-native')}`],
};
1 change: 1 addition & 0 deletions fixtures/monorepo/base-dll/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('../babel.config');
18 changes: 18 additions & 0 deletions fixtures/monorepo/base-dll/haul.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { withPolyfills, makeConfig } from '../../../packages/haul-preset-0.60';
import { join } from 'path';

const entry = withPolyfills([
require.resolve('react'),
require.resolve('react-native'),
require.resolve('react-navigation'),
]);

export default makeConfig({
bundles: {
base_dll: ({ dev }) => ({
entry,
dll: true,
type: dev ? 'basic-bundle' : 'indexed-ram-bundle',
}),
},
});
19 changes: 19 additions & 0 deletions fixtures/monorepo/base-dll/makeConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { join } from 'path';

export default function makeConfig(copyBundle = false) {
return ({ platform, bundleTarget, dev }) => {
const basePath = join(
__dirname,
`./dist/${platform}/${dev || bundleTarget === 'server' ? 'dev' : 'prod'}`
);
const filename = `base_dll${
platform === 'ios' ? '.jsbundle' : '.android.bundle'
}`;
return {
dll: true,
copyBundle,
bundlePath: join(basePath, filename),
manifestPath: join(basePath, 'base_dll.manifest.json'),
}
};
}
23 changes: 23 additions & 0 deletions fixtures/monorepo/base-dll/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "base-dll",
"version": "0.1.0",
"files": [
"makeConfig.js",
"dist/"
],
"scripts": {
"haul": "node ../../../packages/haul-cli/bin/haul.js",
"build:base": "yarn haul multi-bundle --skip-host-check",
"build:ios:dev": "yarn build:base --platform ios --dev true --bundle-output dist/ios/dev --assets-dest dist/ios/dev",
"build:ios:prod": "yarn build:base --platform ios --dev false --bundle-output dist/ios/prod --assets-dest dist/ios/prod",
"build:android:dev": "yarn build:base --platform android --dev true --bundle-output dist/android/dev --assets-dest dist/android/dev",
"build:android:prod": "yarn build:base --platform android --dev false --bundle-output dist/android/prod --assets-dest dist/android/prod",
"build:all": "yarn build:ios:dev && yarn build:ios:prod && yarn build:android:dev && yarn build:android:prod"
},
"dependencies": {
"react": "^16.8.6",
"react-native": "^0.60.5",
"react-native-gesture-handler": "^1.3.0",
"react-navigation": "^3.11.0"
}
}
Loading