Skip to main content
The compose command assembles a mini-program from multiple independently developed modules — a host app, main packages, subpackages, and plugins — into a single output directory.
mor compose [options]

How compose works

Compose runs through up to eight sequential stages for each module:
  1. Initial — Load module metadata and compare hashes.
  2. Download — Fetch the module from git, npm, tar, file, or symlink.
  3. Before scripts — Run pre-processing shell commands.
  4. Config loaded — Read subpackage.json, plugin.json, or app.json.
  5. Copied or compiled — Copy the module output into the host directory.
  6. After scripts — Run post-processing shell commands.
  7. Composed — Merge module routing into the host app.json.
  8. Retry — Automatically retry stages 2–6 on failure.
MorJS caches module downloads by hashing each module’s source configuration. A module is re-downloaded only when its git, npm, tar, dist, or mode settings change.

Flags

-t, --target
string
The platform target (for example, wechat, alipay). Used when modules need to be compiled before integration.
-o, --output-path
string
The directory where the composed output is written. Defaults to the target’s default output path.
--compile-type
string
The compile type of the host. Accepted values: miniprogram, plugin, subpackage, component.
--with-modules
string
Include only matching modules in the integration. Accepts glob patterns. Pass the flag multiple times to include several patterns.
--without-modules
string
Exclude matching modules from the integration. Accepts glob patterns.
--from-state
number
Force modules that have passed this state to restart from it. Useful for recovering from a partial integration.State values: 0 initial, 1 downloaded, 2 before-scripts executed, 3 config loaded, 4 copied/compiled, 5 after-scripts executed, 6 composed.
--to-state
number
Stop integration at this state. Useful for running only the download phase or only the copy phase.
--concurrency
number
Maximum number of modules to download and process in parallel. Defaults to the system queue concurrency.
--combine-modules
boolean
Flatten all subpackage pages into the host app.json pages array instead of keeping them in subPackages. Useful for platforms that do not support subpackages.

Configuration in mor.config.ts

Compose is configured under the same mor.config.ts config object as the compiler. Set compose: true to enable it.
mor.config.ts
import { defineConfig } from '@morjs/cli'

export default defineConfig({
  name: 'my-app',
  sourceType: 'wechat',
  target: 'wechat',

  // Enable compose
  compose: true,

  // Host application
  host: {
    git: {
      url: 'https://github.com/my-org/host-app.git',
      branch: 'main'
    },
    dist: 'dist/wechat'
  },

  // Modules to integrate
  modules: [
    {
      name: 'shop',
      type: 'subpackage',
      npm: {
        name: '@my-org/shop-subpackage',
        version: 'latest'
      },
      dist: 'dist/wechat'
    },
    {
      name: 'user',
      type: 'subpackage',
      git: {
        url: 'https://github.com/my-org/user-subpackage.git',
        branch: 'release'
      },
      dist: 'dist/wechat',
      scripts: {
        before: ['npm install', 'npm run build'],
        after: []
      }
    }
  ]
})

Module downloader options

Each module entry must include exactly one downloader configuration:
git: {
  url: 'https://github.com/org/repo.git', // required
  branch: 'main',  // default: 'master'
  commit: 'abc123',  // optional: pin to a specific commit
  tag: 'v1.2.0'   // optional: pin to a tag
}
// or shorthand:
git: 'https://github.com/org/repo.git'
npm: {
  name: '@my-org/my-subpackage',  // required
  version: '1.0.0',               // default: 'latest'
  registry: 'https://registry.npmjs.org'  // optional
}
// or shorthand:
npm: '@my-org/my-subpackage'
file: {
  path: './packages/my-subpackage'
}
// or shorthand:
file: './packages/my-subpackage'
tar: {
  url: 'https://example.com/release.tar.gz'
}
// or shorthand:
tar: 'https://example.com/release.tar.gz'

Module type

modules[].type
string
default:"subpackage"
Determines how the module’s pages are merged into the host app.json.
  • subpackage — Pages are added to subPackages.
  • main — Pages are added directly to the top-level pages array.
  • plugin — Plugin configuration is pulled and routing support is added.

Module integration mode

modules[].mode
string
default:"compose"
  • compose — The module’s compiled output is copied directly into the host output directory.
  • compile — The module is compiled by MorJS as part of the current build, then composed.

Module scripts

modules[].scripts
object
Shell commands to run at different stages of the integration lifecycle.
scripts: {
  before: ['npm install', 'npm run build'],  // run before copy/compile
  after: ['echo done'],                       // run after copy/compile
  composed: ['./post-compose.sh'],            // run after all modules are composed
  env: { NODE_ENV: 'production' },            // environment variables for all scripts
  options: {}                                 // execa options
}
The following environment variables are injected automatically into each script:
VariableDescription
MOR_COMPOSER_MODULE_CWDAbsolute path of the project working directory
MOR_COMPOSER_MODULE_TYPEModule type (host, main, subpackage, plugin)
MOR_COMPOSER_MODULE_HASHModule hash
MOR_COMPOSER_MODULE_ROOTModule root directory (relative to cwd)
MOR_COMPOSER_MODULE_SOURCEModule source directory (relative to cwd)
MOR_COMPOSER_MODULE_OUTPUT_FROMCompiled output source path
MOR_COMPOSER_MODULE_OUTPUT_TOComposed output destination path

Combine modules

combineModules
boolean
default:"false"
When true, all subpackage pages are merged into the host’s top-level pages array, removing the subPackages section from app.json. Use this for platforms that do not support the subpackage model.

Usage examples

mor compose --target wechat

Configuration reference →

Full mor.config.ts documentation including all compose configuration fields.

Build docs developers (and LLMs) love