# Runtime API

MF runtime APIs are built around the `ModuleFederation` instance. The default instance is usually created automatically by the build plugin, so you do not need to pass an instance explicitly when calling runtime APIs — they automatically apply to the default instance in the runtime.

## createInstance

Used to create a new `ModuleFederation` instance. Unlike other runtime APIs, `createInstance` does not act on the default instance; it returns a brand-new instance that is isolated from the default one.

**When to use**

The default instance pattern already covers most scenarios. You only need `createInstance` in the following cases:

- No build plugin is used (pure runtime scenario)
- You need to create multiple `ModuleFederation` instances with different configurations in the same application
- You want to leverage Module Federation's partitioning feature to encapsulate a set of APIs for use by other projects

**Behavior**

- Every call creates a brand-new instance and adds it to the global instance list
- It does not look up existing instances by `name` / `version`, nor does it merge the new options into an existing instance
- The new instance does not replace the default instance; if you need to retrieve it elsewhere, use [getInstance](#getinstance)

**Example**

```ts
import { createInstance } from '@module-federation/enhanced/runtime';

const mf = createInstance({
  name: 'host',
  remotes: [
    {
      name: 'sub1',
      entry: 'http://localhost:8080/mf-manifest.json'
    }
  ]
});

mf.loadRemote('sub1/util').then((m) => m.add(1, 2, 3));
```

## init Use with caution

Used to initialize or reuse a `ModuleFederation` runtime instance.

`init` does not always create a new instance: when called, the runtime first looks for an existing instance by `name` and `version`. If a matching one is found, it is reused and the new options are merged via `instance.initOptions(options)`. Only when no match is found will a new instance be created.

Therefore, `init` is better suited for scenarios where the same host is initialized multiple times and you want to reuse and extend the same runtime instance.

:::warning

- If you need to create a completely independent new instance, use [createInstance](#createinstance).
- If you only want to access the instance created by the build plugin, use [getInstance](#getinstance).

:::

- Type: `init(options: InitOptions): ModuleFederation`


  InitOptions Type

```ts
type InitOptions {
  // Name of the current host
  name: string;
  // List of dependent remote modules
  // tip: The remotes configured at runtime are not completely consistent in type and data with those passed in by the build plugin.
  remotes: Array<RemoteInfo>;
  // List of dependencies that the current host needs to share
  // When using the build plugin, users can configure the dependencies to be shared in the build plugin, and the build plugin will inject the dependencies to be shared into the shared configuration at runtime.
  // When shared is passed in at runtime, the version instance reference must be passed in manually, because it cannot be directly obtained at runtime.
  shared?: {
    [pkgName: string]: ShareArgs | ShareArgs[];
  };
};

type ShareArgs =
  | (SharedBaseArgs & { get: SharedGetter })
  | (SharedBaseArgs & { lib: () => Module });

type SharedBaseArgs = {
  version: string;
  shareConfig?: SharedConfig;
  scope?: string | Array<string>;
  deps?: Array<string>;
  strategy?: 'version-first' | 'loaded-first';
};

type SharedGetter = (() => () => Module) | (() => Promise<() => Module>);

type RemoteInfo = {
  alias?: string;
};

interface RemotesWithEntry {
  name: string;
  entry: string;
}

type ShareInfos = {
  // Package name and basic information of the dependency, sharing strategy
  [pkgName: string]: Share;
};

type Share = {
  // Version of the shared dependency
  version: string;
  // Which modules consume the current shared dependency
  useIn?: Array<string>;
  // Which module the shared dependency comes from
  from?: string;
  // Factory function to get the instance of the shared dependency. When the cached shared instance cannot be loaded, it will load its own shared dependency.
  lib: () => Module;
  // Sharing strategy, which strategy will be used to determine the reuse of shared dependencies
  shareConfig?: SharedConfig;
  // Dependencies between shares
  deps?: Array<string>;
  // Under which scope the current shared dependency is placed, the default is default
  scope?: string | Array<string>;
};
```


  Example

```ts
import { init, loadRemote } from '@module-federation/enhanced/runtime';

init({
  name: "mf_host",
  remotes: [
    {
      name: "remote",
      // After configuring an alias, it can be loaded directly through the alias
      alias: "app1",
      // Decide which module to load by specifying the address of the module's manifest.json file
      entry: "http://localhost:2001/mf-manifest.json"
    }
  ],
});
```

:::tip Recommended migration

If your code currently uses `init`, you can migrate to the recommended approach based on your scenario.

:::

### Migration option - using the build plugin

Remove the `init` call, use [getInstance](#getinstance) to retrieve the instance, and use [registerRemotes](#registerremotes) / [registerShared](#registershared) / [registerPlugins](#registerplugins) to register the options that were previously passed to `init`.

```diff
- import { init } from '@module-federation/enhanced/runtime';
+ import { registerShared, registerRemotes, registerPlugins, getInstance } from '@module-federation/enhanced/runtime';
  import React from 'react';
  import mfRuntimePlugin from 'mf-runtime-plugin';

- const instance = init({
-   name: 'mf_host',
-   remotes: [
-     {
-       name: 'remote',
-       entry: 'http://localhost:2001/mf-manifest.json',
-     },
-   ],
-   shared: {
-     react: {
-       version: '18.0.0',
-       scope: 'default',
-       lib: () => React,
-       shareConfig: {
-         singleton: true,
-         requiredVersion: '^18.0.0',
-       },
-     },
-   },
-   plugins: [mfRuntimePlugin()],
- });
+ const instance = getInstance();
+ registerRemotes([
+   {
+     name: 'remote',
+     entry: 'http://localhost:2001/mf-manifest.json',
+   },
+ ]);
+ registerShared({
+   react: {
+     version: '18.0.0',
+     scope: 'default',
+     lib: () => React,
+     shareConfig: {
+       singleton: true,
+       requiredVersion: '^18.0.0',
+     },
+   },
+ });
+ registerPlugins([mfRuntimePlugin()]);
```

### Migration option - pure runtime

Switch directly to [createInstance](#createinstance); the option shape stays the same. But note the different semantics: `createInstance` always creates a new instance, while `init` may reuse an existing instance with the same `name` / `version` and merge the new options into it.

```diff
- import { init } from '@module-federation/enhanced/runtime';
+ import { createInstance } from '@module-federation/enhanced/runtime';

- const instance = init({
+ const instance = createInstance({
    name: 'mf_host',
    remotes: [
      {
        name: 'remote',
        entry: 'http://localhost:2001/mf-manifest.json',
      },
    ],
    shared: {
      react: {
        version: '18.0.0',
        scope: 'default',
        lib: () => React,
        shareConfig: {
          singleton: true,
          requiredVersion: '^18.0.0',
        },
      },
    },
    plugins: [mfRuntimePlugin()],
  });
```

## getInstance

- Type: `getInstance(): ModuleFederation | null`
- Type: `getInstance(finder: (instance: ModuleFederation) => boolean): ModuleFederation | null`
- Retrieves the default `ModuleFederation` instance, or the first registered instance that matches a finder callback

Once the default instance has been created by the build plugin or by [init](#init), you can call `getInstance()` to retrieve it.

```ts
import { getInstance } from '@module-federation/enhanced/runtime';

const mfInstance = getInstance();
if (!mfInstance) {
  throw new Error('Module Federation instance is not initialized');
}

mfInstance.loadRemote('remote/util');
```

If the build plugin is not used, calling `getInstance` will throw an exception. In that case you need to use [createInstance](#createinstance) to create a new instance.

Instances created via [createInstance](#createinstance) do not replace the default instance, but they are still registered in the global instance list. So even if you did not keep the returned reference, you can still find them later by passing a finder callback to `getInstance`.

The finder callback behaves like `Array.prototype.find`: the runtime iterates over the currently registered instances and returns the first match. If no matching instance is found, `getInstance` returns `null`.

```ts
const targetInstance = getInstance(
  (instance) => instance.name === 'remote-host',
);

if (targetInstance) {
  targetInstance.loadRemote('remote/util');
}
```

## registerRemotes


  Type declaration

```typescript
function registerRemotes(remotes: Remote[], options?: { force?: boolean }) {}

type Remote = (RemoteWithEntry | RemoteWithVersion) & RemoteInfoCommon;

interface RemoteInfoCommon {
  alias?: string;
  shareScope?: string;
  type?: RemoteEntryType;
  entryGlobalName?: string;
}

interface RemoteWithEntry {
  name: string;
  entry: string;
}

interface RemoteWithVersion {
  name: string;
  version: string;
}
```

:::warning

When `force: true` is set, the newly registered modules will overwrite already-registered and loaded modules, and the cache of the loaded modules will be automatically deleted. A warning will also be printed to the console to inform you that this operation is risky.

:::


**Build Plugin (Use build plugin)**

```tsx
import { registerRemotes } from '@module-federation/enhanced/runtime';

// register new remote sub2
registerRemotes([
  {
    name: 'sub2',
    entry: 'http://localhost:2002/mf-manifest.json',
  }
]);

// override remote sub1
registerRemotes([
  {
    name: 'sub1',
    entry: 'http://localhost:2003/mf-manifest.json',
  }
], { force: true });
```


**Pure Runtime (Not use build plugin)**

```ts
import { createInstance } from '@module-federation/enhanced/runtime';

const mf = createInstance({
  name: 'mf_host',
  remotes: [
    {
      name: 'sub1',
      entry: 'http://localhost:2001/mf-manifest.json',
    }
  ]
});

// register new remote sub2
mf.registerRemotes([
  {
    name: 'sub2',
    entry: 'http://localhost:2002/mf-manifest.json',
  }
]);

// override remote sub1
mf.registerRemotes([
  {
    name: 'sub1',
    entry: 'http://localhost:2003/mf-manifest.json',
  }
], { force: true });
```


## registerPlugins

```typescript
function registerPlugins(plugins: ModuleFederationRuntimePlugin[]) {}
```


**Build Plugin (Use build plugin)**

```tsx
import { registerPlugins } from '@module-federation/enhanced/runtime';
import runtimePlugin from './custom-runtime-plugin';

// add new runtime plugin
registerPlugins([runtimePlugin()]);

registerPlugins([
  {
    name: 'custom-plugin-runtime',
    beforeInit(args) {
      const { userOptions, origin } = args;
      if (origin.options.name && origin.options.name !== userOptions.name) {
        userOptions.name = origin.options.name;
      }
      console.log('[build time inject] beforeInit: ', args);
      return args;
    },
    beforeLoadShare(args) {
      console.log('[build time inject] beforeLoadShare: ', args);
      return args;
    },
    createLink({ url }) {
      const link = document.createElement('link');
      link.setAttribute('href', url);
      link.setAttribute('rel', 'preload');
      link.setAttribute('as', 'script');
      link.setAttribute('crossorigin', 'anonymous');
      return link;
    },
  }
]);
```


**Pure Runtime (Not use build plugin)**

```ts
import { createInstance } from '@module-federation/enhanced/runtime';
import runtimePlugin from './custom-runtime-plugin';

const mf = createInstance({
  name: 'mf_host',
  remotes: [
    {
      name: 'sub1',
      entry: 'http://localhost:2001/mf-manifest.json',
    }
  ]
});

// add new runtime plugin
mf.registerPlugins([runtimePlugin()]);

mf.registerPlugins([
  {
    name: 'custom-plugin-runtime',
    beforeInit(args) {
      const { userOptions, origin } = args;
      if (origin.options.name && origin.options.name !== userOptions.name) {
        userOptions.name = origin.options.name;
      }
      console.log('[build time inject] beforeInit: ', args);
      return args;
    },
    beforeLoadShare(args) {
      console.log('[build time inject] beforeLoadShare: ', args);
      return args;
    },
    createLink({ url }) {
      const link = document.createElement('link');
      link.setAttribute('href', url);
      link.setAttribute('rel', 'preload');
      link.setAttribute('as', 'script');
      link.setAttribute('crossorigin', 'anonymous');
      return link;
    },
  }
]);
```


## registerGlobalPlugins

```typescript
function registerGlobalPlugins(plugins: ModuleFederationRuntimePlugin[]): void {}
```

Registers plugins on the global federation state instead of on a single current instance. This is suitable for scenarios such as:

- shared instrumentation
- environment-wide policy
- host-wide default plugins

Global plugins are deduplicated by `plugin.name`.

```ts
import { registerGlobalPlugins, createInstance } from '@module-federation/enhanced/runtime';

import runtimePlugin from './runtime-plugin';

registerGlobalPlugins([runtimePlugin()]);

const mf = createInstance({
  name: 'mf_host',
  remotes: [
    {
      name: 'sub1',
      entry: 'http://localhost:2001/mf-manifest.json',
    },
  ],
});
```

For predictable behavior, register global plugins before creating or using runtime instances.

## registerShared

Registers shared dependencies for the host. The runtime will prefer to reuse a shared dependency that already exists globally and satisfies the conditions; otherwise it falls back to the dependency registered here.

```typescript
function registerShared(shared: Shared): void;

type Shared = {
  // Mapping of shared dependency package names; the same package can be registered with multiple versions (array form)
  [pkgName: string]: ShareArgs | ShareArgs[];
};

/**
 * ShareArgs must provide either lib or get
 *
 * lib: synchronous factory function that returns the module immediately when called; should not return a Promise.
 * Suitable when the module is already available at the time `registerShared` is called, e.g. `import React from 'react'`
 *
 * get: used for async / lazy loading scenarios
 * */
type ShareArgs =
  | (SharedBaseArgs & { lib: () => Module })
  | (SharedBaseArgs & { get: SharedGetter })
  | SharedBaseArgs;

type SharedBaseArgs = {
  // Version number being registered; recommended for version differentiation
  version?: string;
  // Sharing strategy, controlling singleton, requiredVersion, etc.
  shareConfig?: SharedConfig;
  // Scope it belongs to, defaults to 'default'; can be placed under multiple scopes simultaneously
  scope?: string | Array<string>;
  // Names of other shared dependencies that this one depends on
  deps?: Array<string>;
  // Version selection strategy: prefer by version number / prefer already loaded instance
  strategy?: 'version-first' | 'loaded-first';
  loaded?: boolean;
};

interface SharedConfig {
  singleton?: boolean;
  requiredVersion: false | string;
  eager?: boolean;
  strictVersion?: boolean;
  layer?: string | null;
}

type SharedGetter = (() => () => Module) | (() => Promise<() => Module>);
```


**Build Plugin (Use build plugin)**

```tsx
import { registerShared } from '@module-federation/enhanced/runtime';
import React from 'react';
import ReactDom from 'react-dom';

registerShared({
  react: {
    version: '18.0.0',
    scope: 'default',
    lib: () => React,
    shareConfig: {
      singleton: true,
      requiredVersion: '^18.0.0',
    },
  },
  'react-dom': {
    version: '18.0.0',
    scope: 'default',
    lib: () => ReactDom,
    shareConfig: {
      singleton: true,
      requiredVersion: '^18.0.0',
    },
  },
  antd: {
    version: '1.0.0',
    scope: 'default',
    get: () => import('antd').then((m) => () => m),
  },
});
```


**Pure Runtime (Not use build plugin)**

```ts
import { createInstance } from '@module-federation/enhanced/runtime';
import React from 'react';
import ReactDom from 'react-dom';

const mf = createInstance({
  name: 'mf_host',
  remotes: [
    {
      name: 'sub1',
      entry: 'http://localhost:2001/mf-manifest.json',
    }
  ]
});

mf.registerShared({
  react: {
    version: '18.0.0',
    scope: 'default',
    lib: () => React,
    shareConfig: {
      singleton: true,
      requiredVersion: '^18.0.0',
    },
  },
  'react-dom': {
    version: '18.0.0',
    scope: 'default',
    lib: () => ReactDom,
    shareConfig: {
      singleton: true,
      requiredVersion: '^18.0.0',
    },
  },
  antd: {
    version: '1.0.0',
    scope: 'default',
    get: () => import('antd').then((m) => () => m),
  },
});
```


## loadShare

```ts
type loadShare = (
  pkgName: string,
  extraOptions?: {
    customShareInfo?: Partial<Shared>;
    resolver?: (sharedOptions: ShareInfos[string]) => Shared;
  }
) => Promise<() => ShareModule>;
```

Gets a `share` dependency. When there is a `share` dependency in the global environment that meets the requirements of the current `host`, the existing dependency that meets the `share` conditions will be reused first. Otherwise, its own dependency will be loaded and stored in the global cache for later reuse.

This `API` is **generally not called directly by the user, but is used by the build plugin when transforming its own dependencies**.


**Build Plugin (Use build plugin)**

```tsx
import { registerShared, loadShare } from '@module-federation/enhanced/runtime';
import React from 'react';
import ReactDom from 'react-dom';

registerShared({
  react: {
    version: '17.0.0',
    scope: 'default',
    lib: () => React,
    shareConfig: {
      singleton: true,
      requiredVersion: '^17.0.0',
    },
  },
  'react-dom': {
    version: '17.0.0',
    scope: 'default',
    lib: () => ReactDom,
    shareConfig: {
      singleton: true,
      requiredVersion: '^17.0.0',
    },
  },
});

loadShare('react').then((reactFactory) => {
  console.log(reactFactory());
});
```


**Pure Runtime (Not use build plugin)**

```ts
import { createInstance } from '@module-federation/enhanced/runtime';
import React from 'react';
import ReactDom from 'react-dom';

const mf = createInstance({
  name: 'mf_host',
  remotes: [
    {
      name: 'remote',
      entry: 'http://localhost:2001/mf-manifest.json',
      alias: 'app1',
    }
  ]
});

mf.registerShared({
  react: {
    version: '17.0.0',
    scope: 'default',
    lib: () => React,
    shareConfig: {
      singleton: true,
      requiredVersion: '^17.0.0',
    },
  },
  'react-dom': {
    version: '17.0.0',
    scope: 'default',
    lib: () => ReactDom,
    shareConfig: {
      singleton: true,
      requiredVersion: '^17.0.0',
    },
  },
});

mf.loadShare('react').then((reactFactory) => {
  console.log(reactFactory());
});
```


If multiple versions of shared are set, by default the loaded one with the highest version is returned. This behavior can be changed by setting `extraOptions.resolver`:

```ts
loadShare('react', {
  resolver: (sharedOptions) => {
    return (
      sharedOptions.find((i) => i.version === '17.0.0') ?? sharedOptions[0]
    );
  },
}).then((reactFactory) => {
  console.log(reactFactory()); // { version: '17.0.0' }
});
```

## loadRemote

This API is used to load a remote module at runtime.

```ts
type loadRemote = (remoteNameOrAlias: string) => Promise<RemoteModule>;
```


**Build Plugin (Use build plugin)**

```tsx
import { loadRemote } from '@module-federation/enhanced/runtime';

// remoteName + expose
loadRemote('remote/util').then((m) => m.add(1, 2, 3));

// alias + expose
loadRemote('app1/util').then((m) => m.add(1, 2, 3));
```


**Pure Runtime (Not use build plugin)**

```ts
import { createInstance } from '@module-federation/enhanced/runtime';

const mf = createInstance({
  name: 'mf_host',
  remotes: [
    {
      name: 'remote',
      entry: 'http://localhost:2001/mf-manifest.json',
      alias: 'app1',
    }
  ]
});

// remoteName + expose
mf.loadRemote('remote/util').then((m) => m.add(1, 2, 3));

// alias + expose
mf.loadRemote('app1/util').then((m) => m.add(1, 2, 3));
```


## preloadRemote


  Type declaration

```typescript
async function preloadRemote(preloadOptions: Array<PreloadRemoteArgs>){}

type depsPreloadArg = Omit<PreloadRemoteArgs, 'depsRemote'>;
type PreloadRemoteArgs = {
  // Name or alias of the remote to be preloaded
  nameOrAlias: string;
  // The exposes to be preloaded
  // By default, all exposes are preloaded
  // When exposes are provided, only the required exposes will be loaded
  exposes?: Array<string>; // Default request
  // The default is sync, which only loads the synchronous code referenced in expose
  // When set to all, both synchronous and asynchronous references will be loaded
  resourceCategory?: 'all' | 'sync';
  // When no value is configured, all dependencies are loaded by default
  // After configuring dependencies, only the configuration options will be loaded
  depsRemote?: boolean | Array<depsPreloadArg>;
  // When not configured, resources are not filtered
  // After configuration, unnecessary resources will be filtered
  filter?: (assetUrl: string) => boolean;
  // Whether to record preloaded JS and CSS URLs and skip duplicate loading
  // Defaults to true
  recordPreloadedAssets?: boolean;
};
```

With `preloadRemote`, you can start preloading module resources at an earlier stage to avoid waterfall requests. `preloadRemote` can preload:

- the `remote`'s `remoteEntry`
- the `remote`'s `expose`
- the `remote`'s synchronous or asynchronous resources
- the `remote`'s dependent `remote` resources

`preloadRemote` waits for all resources involved in the current preload call to finish. If every resource is loaded successfully or hits the cache, the Promise resolves; if any resource fails or times out, the Promise rejects, and the error object carries the resource results of this preload call.

If you only care about the final result, you can use `await` or `.then/.catch` directly:

```ts
import { preloadRemote } from '@module-federation/enhanced/runtime';

try {
  await preloadRemote([
    {
      nameOrAlias: 'sub1',
      exposes: ['add'],
      resourceCategory: 'all',
    },
  ]);

  console.log('sub1/add preload success');
} catch (error) {
  console.error('sub1/add preload failed', error);
}
```

If you need to track which specific resources succeeded, failed, timed out, or came from cache, read `results` from the error object:

```ts
type PreloadRemoteError = Error & {
  results?: Array<{
    id: string;
    results: Array<{
      url: string;
      status: 'success' | 'error' | 'timeout' | 'cached';
      resourceType: 'manifest' | 'remoteEntry' | 'js' | 'css';
      error?: unknown;
    }>;
  }>;
};

preloadRemote([
  {
    nameOrAlias: 'sub1',
    exposes: ['add'],
    resourceCategory: 'all',
  },
]).catch((error: PreloadRemoteError) => {
  const failedResources =
    error.results
      ?.flatMap((remoteResult) =>
        remoteResult.results.map((resource) => ({
          id: remoteResult.id,
          ...resource,
        })),
      )
      .filter(
        (resource) =>
          resource.status === 'error' || resource.status === 'timeout',
      ) ?? [];

  failedResources.forEach((resource) => {
    console.error(
      `[preloadRemote] ${resource.id} ${resource.resourceType} failed`,
      resource.url,
      resource.error,
    );
  });
});
```

When `exposes` is not specified, the resource id for this preload is `remoteName/*`. When `exposes` is specified, the runtime generates resources per expose, and the resource id is `remoteName/expose`.

By default, the runtime records each JS and CSS URL before starting its preload. Later attempts to preload the same loading or loaded URL are skipped and reported as `cached`. This URL tracking does not change the existing lifecycle of the generated link or script element. Up to 2,000 URLs are retained; when the limit is exceeded, the oldest URL is removed. Set `recordPreloadedAssets: false` to disable both URL lookup and recording for a preload operation.


**Build Plugin (Use build plugin)**

```tsx
import { registerRemotes, preloadRemote } from '@module-federation/enhanced/runtime';

registerRemotes([
  {
    name: 'sub1',
    entry: 'http://localhost:2001/mf-manifest.json',
  },
  {
    name: 'sub2',
    entry: 'http://localhost:2002/mf-manifest.json',
  },
  {
    name: 'sub3',
    entry: 'http://localhost:2003/mf-manifest.json',
  },
]);

// preload sub1 module
// filter the resource information that carries ignore in the resource name
// only preload sub-dependent sub1-button module
preloadRemote([
  {
    nameOrAlias: 'sub1',
    filter(assetUrl) {
      return assetUrl.indexOf('ignore') === -1;
    },
    depsRemote: [{ nameOrAlias: 'sub1-button' }],
  },
]);

// preload sub2 module
// preload all exposes under sub2
// preload synchronous and asynchronous resources of sub2
preloadRemote([
  {
    nameOrAlias: 'sub2',
    resourceCategory: 'all',
  },
]);

// preload add expose of sub3 module
preloadRemote([
  {
    nameOrAlias: 'sub3',
    resourceCategory: 'all',
    exposes: ['add'],
  },
]);
```


**Pure Runtime (Not use build plugin)**

```ts
import { createInstance } from '@module-federation/enhanced/runtime';

const mf = createInstance({
  name: 'mf_host',
  remotes: [],
});

mf.registerRemotes([
  {
    name: 'sub1',
    entry: 'http://localhost:2001/mf-manifest.json',
  },
  {
    name: 'sub2',
    entry: 'http://localhost:2002/mf-manifest.json',
  },
  {
    name: 'sub3',
    entry: 'http://localhost:2003/mf-manifest.json',
  },
]);

// preload sub1 module
// filter the resource information that carries ignore in the resource name
// only preload sub-dependent sub1-button module
mf.preloadRemote([
  {
    nameOrAlias: 'sub1',
    filter(assetUrl) {
      return assetUrl.indexOf('ignore') === -1;
    },
    depsRemote: [{ nameOrAlias: 'sub1-button' }],
  },
]);

// preload sub2 module
// preload all exposes under sub2
// preload synchronous and asynchronous resources of sub2
mf.preloadRemote([
  {
    nameOrAlias: 'sub2',
    resourceCategory: 'all',
  },
]);

// preload add expose of sub3 module
mf.preloadRemote([
  {
    nameOrAlias: 'sub3',
    resourceCategory: 'all',
    exposes: ['add'],
  },
]);
```

