id | title |
---|---|
configuration |
Configuration |
When running haul init
the default configuration file - haul.config.js
- will be created:
import {makeConfig, withPolyfills} from '@haul-bundler/preset-0.59';
export default makeConfig({
bundles: {
index: {
entry: withPolyfills('./index.js'),
},
},
});
You can use the default configuration as it is or tweak it for your needs.
This function takes your configuration object, fills in the default values and turns it into a normalized config that Haul understands.
Usually, the return value of makeConfig
should be returned as default export with export default
.
Name | Type | Description |
---|---|---|
projectConfig |
ProjectConfig |
User-provided configuration object. |
returns | NormalizedProjectConfig |
Normalized configuration object with defaults filled-in. |
Decorate the entry file(s) with React Native polyfills. You can specify custom root
directory and path to InitializeCore.js
file.
Name | Type | Description |
---|---|---|
entry |
string / string[] |
Entry file(s) path(s) |
options.initializeCoreLocation |
`string | undefined` |
options.additionalSetupFiles |
`string[] | undefined ` |
returns | string[] |
Entry file(s) with polyfill paths |
Project configuration object (the one accepted by makeConfig
function) consist of the following properties:
-
server?: ServerConfig
(optional) - Specify custom port and host for the packager server:host?: string
(optional) - Host to use for the packager server, defaults tolocalhost
.- Note: if you are having trouble connecting to the development server with
localhost
try'0.0.0.0'
- Note: if you are having trouble connecting to the development server with
port?: number
(optional) - Port to use for the packager server, defaults to8081
.
-
platforms?: string[]
(optional) - List of supported platforms - useful for defining out-of-tree platforms. Defaults to['ios', 'android']
.When providing the value, you need to specify all of the available platforms, for example to add
windows
your need to pass['windows', 'ios', 'android']
. -
templates?: TemplateConfig
(optional) - Customize templates for given platform:-
filename?: { [platform: string]: string }
(optional) - Customize bundle filename for given platform. If--bundle-output
is provided, the filename will be inferred from it usingpath.basename()
, otherwise it will defaults to:{ ios: '[bundleName].jsbundle', android: '[bundleName].[platform].bundle', }
You can add your own platform with a template, for example:
{ 'my-platform': '[bundleName].resource' }
.You can use
[bundleName]
,[platform]
,[mode]
(prod
ordev
) and[type]
(default
,app
ordll
) placeholders in a template.
-
-
features: { multiBundle?: 1 | 2 }
- Enable specific feature's version (Advanced use only). -
bundles: { [bundleName: string]: BundleConfigBuilder | BundleConfig }
(required) - An object where the property name (a key) will become bundle name and a bundle config or a bundle config builder. See below for bundle config reference.
Each bundle config can be a plan object or a function that given env: EnvOptions
and runtime: Runtime
returns an object.
The bundle configuration consist of the following properties:
-
name?: string
(optional) - Overwrite bundle name. Be default bundle name will be inferred from the property inbundles
object. -
entry: string | string[]
(required) - Path to entry file(s). -
type?: 'basic-bundle' | 'indexed-ram-bundle' | 'file-ram-bundle'
(optional) - Specify bundle type.Defaults to
basic-bundle
when usingbundle
command or:indexed-ram-bundle
forram-bundle
command for iOSfile-ram-bundle
forram-bundle
command for Android
When serving files from packager server (
start
command) thetype
will be always set tobasic-bundle
. -
platform?: string
(optional) - Overwrite the platform for which the bundle is built for. By default the platform will be taken form CLI argument--platform
or provided by packager server. -
root?: string
(optional) - Overwrite the root directory of the project, defaults toprocess.cwd()
. -
dev?: boolean
(optional) - Overwrite whether to bundle for production -false
or development -true
. By default will use CLI argument--dev
. -
assetsDest?: string
(optional) - Overwrite the asset output directory. By default will use CLI argument--assets-dest
. -
minify?: boolean
(optional) - Overwrite whether to minify the code. By default will use CLI argument--minify
or infer the value from--dev
argument - when building for production, the minification will be enabled. -
minifyOptions?: Pick<MinifyOptions, Exclude<keyof MinifyOptions, 'sourceMap'>>
(optional) - Provide custom minication options for Terser except forsourceMap
which will be provided automatically by Haul. -
sourceMap?: boolean | 'inline'
(optional) - Overwrite whether to create source maps or not. The default value istrue
and the source maps will be emitted to a separate file with.map
extension. Useinline
, if you want to have source maps within the bundle (uses Webpack'sEvalSourceMapDevToolPlugin
). -
dll?: boolean
(optional, multi-bundle mode only) - Specify if the bundle is a Dynamically Linked Library. Usually bundles that contain common/shared dependencies should be a DLL. -
app?: boolean
(optional, multi-bundle mode only) - Specify if the bundle is an Application bundle. Every application bundle should export a root component for rendering as default export. -
dependsOn?: string[]
(optional, multi-bundle mode only) - Specify on which DLLs the bundle depends on, which will be automatically linked when building the bundle. -
providesModuleNodeModules?: Array<string | { name: string; directory: string }>
- Provide custom modules for Haste. -
hasteOptions?: any
- Provide Haste options. -
looseMode?: true | Array<string | RegExp> | ((filename: string) => boolean)
- Removes'use strict';
from:- the whole bundle if
true
- modules matched by absolute filename if array of string is passed (can be mixed with regexes)
- modules matched by the the regexes if array of regexes is passed (can be mixed with strings)
- modules for which function
(filename: string) => boolean
returnstrue
- the whole bundle if
-
transform?: WebpackConfigTransform
- Customize the Webpack config after it's been created.The
transform
function will receive an object withbundleName
,env
,runtime
and Webpack'sconfig
:transform: ({bundleName, env, runtime, config}) => { runtime.logger.info( `Altering Webpack config for bundle ${bundleName} for ${env.platform}` ); // alter the config ... };
More information can be found in Customize Webpack config recipe.
-
bundlePath?: string
- Absolute path to an external (pre-built) bundle. -
manifestPath?: string
- Absolute path to manifest for an external (pre-built) bundle, ifdll
istrue
. -
assetsPath?: string
- Absolute or relative path pointing to where assets for an external (pre-built) bundle are stored. If relative, it will be joined withpath.dirname(bundlePath)
. -
copyBundle?: boolean
- Whether to copy bundle, source map and assets of an external (pre-built) bundle to output directory. -
maxWorkers?: number
- Number of workers used to transpile modules by our Babel loader. Forram-bundle
workers are used also for the minification. Defaults to the number of the CPUs - 1 on local machine, but is limited to7
on CI, as the number of CPUs can happen to be incorrect.
If you want to provide the bundle config based on received CLI arguments, you can do so, by using BundleConfigBuilder
:
import {makeConfig, withPolyfills} from '@haul-bundler/preset-0.59';
export default makeConfig({
bundles: {
index: (env, runtime) => ({
entry: withPolyfills(env.platform === 'ios' ? './ios.js' : 'android.js'),
}),
},
});
To add support for out-of-tree platform, there are 2 properties that's need to be defined: platforms
and templates.filename
.
For the platform custom
, here's how the config would look like:
import {makeConfig, withPolyfills} from '@haul-bundler/preset-0.59';
export default makeConfig({
platforms: ['custom'],
templates: {
filename: {
custom: '[bundleName].[platform].bundle', // adjust the template for your needs
},
},
bundles: {
index: {
entry: withPolyfills('./index.js'),
},
},
});
This config will allow you to run bundle
, ram-bundle
and multi-bundle
(experimental) with --platform custom
.
Remember that, if the --bundle-output
is provided, the templates.filename.custom
won't be used, instead the filename will be inferred from the --bundle-output
argument.
Please note that by providing
platforms
array, no defaults will be added, meaningios
andandroid
are no loger supported. To add support forios
andandroid
alongsidecustom
use['custom', 'ios', 'android']
:
import {makeConfig, withPolyfills} from '@haul-bundler/preset-0.59';
export default makeConfig({
platforms: ['custom', 'ios', 'android'],
templates: {
filename: {
custom: '[bundleName].[platform].bundle', // adjust the template for your needs
},
},
bundles: {
index: {
entry: withPolyfills('./index.js'),
},
},
});
For ios
and android
the default filename templates will be used:
{
ios: '[bundleName].jsbundle',
android: '[bundleName].[platform].bundle',
}
By default Webpack config is not exposed directly. The only way to customize it, is to use transform
function, which runs after all other configuration options were normalized and applied to Webpack config - the transform
function runs at the very end.
import path from 'path';
import { makeConfig, withPolyfills } from "@haul-bundler/preset-0.59";
export default makeConfig({
bundles: {
index: {
entry: withPolyfills('./index.js'),
transform({ bundleName, env, runtime, config }) {
runtime.logger.info(`Altering Webpack config for bundle ${bundleName}`);
config.resolve.alias = {
...config.resolve.alias,
'my-alias': path.join(__dirname, 'src/myAlias.js'),
},
},
},
},
});
In transform
function apart from bundleName
and Webpack config
, you have access to:
runtime
- Runtime instance, useful for logging viaruntime.logger
(check here).env
- Options received from the CLI arguments/options (check here).
For multi bundle support using v2 behavior, please check the example app here: https://github.com/react-native-community/react-native-multibundle
Multi-bundle mode is a experimental and not-yet-available in React Native's public releases. Please check this RFC and a PR to React Native for more info.
In order to create multiple bundles, you need to provide bundle configs for them in bundle
property:
import {makeConfig, withPolyfills} from '@haul-bundler/preset-0.59';
export default makeConfig({
bundles: {
index: {
entry: withPolyfills('./src/host.js'),
dependsOn: ['base_dll'],
},
base_dll: {
entry: withPolyfills(['react', 'react-native', 'react-navigation']),
dll: true,
type: 'indexed-ram-bundle',
},
app0: {
entry: './src/app0',
dependsOn: ['base_dll'],
app: true,
type: 'indexed-ram-bundle',
},
app1: {
entry: './src/app1',
dependsOn: ['base_dll'],
app: true,
type: 'indexed-ram-bundle',
},
},
});
The config from the example above will create 4 bundles:
host
bundle with bootstrapping logic for request and load app bundles.base_dll
bundle with common/shared dependencies betweenhost
,app0
andapp1
(as Indexed RAM bundle).app0
bundle with application logic (as Indexed RAM bundle).app1
bundle with application logic (as Indexed RAM bundle).
To build static bundles run multi-bundle
command instead of bundle
or ram-bundle
command. When running start
command the packager server will automatically detect the multi-bundle mode, so no actions needs to be done here.
The host logic is up to the user to provide, based on the use case, but here's a example implementation:
import React from 'react';
import {AppRegistry, BundleRegistry, View, Text, Linking} from 'react-native';
import {
createAppContainer,
createBottomTabNavigator,
NavigationActions,
} from 'react-navigation';
function makeScreenForAppBundle(bundleName) {
const screen = props => {
if (!BundleRegistry.isBundleLoaded(bundleName)) {
throw new Error(`App bundle ${bundleName} was not loaded`);
}
const Component = BundleRegistry.getBundleExport(bundleName);
return <Component {...props} />;
};
return {
screen,
navigationOptions: {
tabBarOnPress: ({navigation, defaultHandler}) => {
if (BundleRegistry.isBundleLoaded(bundleName)) {
defaultHandler();
} else {
const listener = ({
bundleName: currentlyLoadedBundle,
loadStartTimestamp,
}) => {
if (currentlyLoadedBundle === bundleName) {
BundleRegistry.removeListener('bundleLoaded', listener);
navigation.setParams({loadStartTimestamp});
defaultHandler();
}
};
BundleRegistry.addListener('bundleLoaded', listener);
BundleRegistry.loadBundle(bundleName);
}
},
},
};
}
const AppContainer = createAppContainer(
createBottomTabNavigator(
{
Initial: () => <Text>Initial</Text>,
app0: makeScreenForAppBundle('app0'),
app1: makeScreenForAppBundle('app1'),
},
{
initialRouteName: 'Initial',
}
)
);
class RootComponent extends React.Component {
navigatorRef = React.createRef();
handleURL = event => {
const [, bundleName] = event.url.match(/.+\/\/(.+)/);
BundleRegistry.loadBundle(bundleName);
};
onBundleLoaded = ({bundleName, loadStartTimestamp}) => {
if (this.navigatorRef.current) {
this.navigatorRef.current.dispatch(
NavigationActions.navigate({
routeName: bundleName,
params: {loadStartTimestamp},
})
);
}
};
async componentDidMount() {
BundleRegistry.addListener('bundleLoaded', this.onBundleLoaded);
Linking.addEventListener('url', this.handleURL);
const initialUrl = await Linking.getInitialURL();
if (initialUrl) {
this.handleURL({url: initialUrl});
}
}
componentWillUnmount() {
Linking.removeListener('url', this.handleURL);
BundleRegistry.removeListener('bundleLoaded', this.onBundleLoaded);
}
render() {
return (
<View style={{flex: 1, width: '100%'}}>
<AppContainer
ref={this.navigatorRef}
// we handle deep linking manually
enableURLHandling={false}
/>
</View>
);
}
}
AppRegistry.registerComponent('MyApp', () => RootComponent);
Check out monorepo fixture 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:
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:
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:
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.