Skip to main content
MorJS’s compiler (@morjs/plugin-compiler) is itself a MorJS plugin that runs inside the compile command’s Runner. It registers additional hooks — such as webpackWrapper, scriptParser, templateParser, and styleParser — that let you plug into specific stages of the compilation pipeline.

Compiler plugin interface (CompilerPlugin)

A CompilerPlugin defines target-specific behavior for one mini-program platform. The interface is declared in packages/plugin-compiler/src/compilerPlugins.ts:
export interface CompilerPlugin {
  target: string                        // e.g. 'alipay', 'wechat'
  targetDescription: string
  fileType: {
    template: string                    // e.g. '.axml', '.wxml'
    style: string                       // e.g. '.acss', '.wxss'
    script: string                      // e.g. '.js'
    config: string                      // e.g. '.json'
    sjs: string                         // e.g. '.sjs', '.wxs'
  }
  compileModuleKind: CompileModuleKindType
  compileScriptTarget: CompileScriptTargetType
  globalObject: string                  // e.g. 'my', 'wx'
  resolveMainFields: string[]
  defaultConditionalFileExt: string
  defaultOutputDir: string
  projectConfigFiles: string[]
  supportGlobalComponents: boolean
  templateSingleTagNames: string[]
  templateDirectives: CompilerTemplateDirectives
  templateProcessor: CompilerTemplateProcessor
  getRuntimeFiles?: (sourceType: string, target: string) => CompilerRuntimeConfig
  Plugin?: PluginForCompiler
  // ...additional fields
}

getComposedCompilerPlugins()

Call getComposedCompilerPlugins() to retrieve a map of all registered compiler plugins, keyed first by property name and then by target:
import { getComposedCompilerPlugins } from '@morjs/plugin-compiler'

const composed = getComposedCompilerPlugins()

// Access the global object for the 'alipay' target:
const globalObject = composed.globalObject['alipay']  // 'my'

// Access the file types for the 'wechat' target:
const wechatFileType = composed.fileType['wechat']  // { template: '.wxml', style: '.wxss', ... }
To replace or add targets, call setCompilerPlugins() with a new list:
import {
  getAllCompilerPlugins,
  setCompilerPlugins
} from '@morjs/plugin-compiler'
import type { CompilerPlugin } from '@morjs/plugin-compiler'

const myTarget: CompilerPlugin = {
  target: 'my-target',
  targetDescription: 'My custom target',
  fileType: {
    template: '.html',
    style: '.css',
    script: '.js',
    config: '.json',
    sjs: '.js'
  },
  // ...other required fields
}

setCompilerPlugins([...getAllCompilerPlugins(), myTarget])

CustomLoaderOptions

When writing a custom webpack loader for MorJS files, use CustomLoaderOptions (defined in packages/plugin-compiler/src/constants.ts) to access compiler state:
export interface CustomLoaderOptions {
  userConfig: CompilerUserConfig
  entryBuilder: EntryBuilderHelpers
  runner: Runner
  // Fallback for node_modules processing when userConfig.processNodeModules is not set
  processNodeModules?: boolean
}
A loader receives this object as its options parameter:
// loaders/my-loader.ts
import type { CustomLoaderOptions } from '@morjs/plugin-compiler'

export default function myLoader(this: any, source: string): string {
  const options: CustomLoaderOptions = this.getOptions()
  const { userConfig, runner } = options

  runner.logger.debug(`Processing file for target: ${userConfig.target}`)
  // transform source...
  return source
}

Adding custom webpack configuration

Use the webpackWrapper hook to modify the webpack chain config. This hook is fired by @morjs/plugin-compiler after the base webpack config is built:
import type { Plugin, Runner } from '@morjs/utils'

export class MyWebpackPlugin implements Plugin {
  name = 'MyWebpackPlugin'

  apply(runner: Runner) {
    runner.hooks.webpackWrapper.tap(this.name, (wrapper) => {
      const chain = wrapper.chain

      // Add a resolve alias
      chain.resolve.alias.set('@utils', '/src/utils')

      // Add a custom rule
      chain
        .module
        .rule('my-rule')
        .test(/\.custom$/)
        .use('my-loader')
        .loader(require.resolve('./loaders/my-loader'))
        .options({ myOption: true })

      // Add a webpack plugin
      chain
        .plugin('my-webpack-plugin')
        .use(require('some-webpack-plugin'), [{ option: true }])
    })
  }
}
The webpackWrapper hook is only available in compiler plugins (i.e., plugins running inside a compile command runner). It is not present in the base Runner.

Parser plugins

The compiler exposes per-file-type parser hooks. Each hook receives the current parser chain and file options, and must return the (possibly modified) chain.

scriptParser

Tap into scriptParser to add TypeScript custom transformers:
runner.hooks.scriptParser.tap(this.name, (transformers, options) => {
  const { fileInfo } = options
  runner.logger.debug(`Parsing script: ${fileInfo.path}`)

  transformers.before.push(
    myTypescriptTransformer()
  )

  return transformers
})

templateParser

Tap into templateParser to walk and modify the PostHTML AST for template files (.axml, .wxml, etc.):
runner.hooks.templateParser.tap(this.name, (tree, options) => {
  const { fileInfo } = options

  tree.walk((node) => {
    if (node.tag === 'view' && node.attrs?.class) {
      // Modify class attributes
      node.attrs.class = node.attrs.class + ' injected'
    }
    return node
  })

  return tree
})

styleParser

Tap into styleParser to add PostCSS plugins:
import { cssProcessorFactory } from '@morjs/utils'

runner.hooks.styleParser.tap(this.name, (plugins, options) => {
  return plugins.concat(
    cssProcessorFactory(this.name, (root) => {
      root.walkDecls(/color/, (decl) => {
        runner.logger.debug(`Found color declaration in ${options.fileInfo.path}`)
      })
    })
  )
})

configParserPlugin

Tap into configParser to transform JSON config files:
runner.hooks.configParser.tap(this.name, (config, options) => {
  // config is the parsed JSON object for the current .json file
  if (options.fileInfo.path.endsWith('app.json')) {
    config.customField = 'injected'
  }
  return config
})

Template processor

For platform-specific template processing, implement CompilerTemplateProcessor:
import type { CompilerTemplateProcessor } from '@morjs/plugin-compiler'

const processor: CompilerTemplateProcessor = {
  onNode(node, options, context) {
    // Called when entering a template node
  },
  onNodeExit(node, options, context) {
    // Called when leaving a template node
  },
  onNodeAttr(attrName, node, options, context) {
    // Called when entering a node attribute
  },
  onNodeAttrExit(attrName, node, options, context) {
    // Called when leaving a node attribute
  }
}

Build docs developers (and LLMs) love