Migration from lianhanlin-modular
This guide describes how to upgrade projects using the legacy lianhanlin-modular prototype to the official @path-ioc/* ecosystem.
1. Architecture Evolution & Package Decoupling
lianhanlin-modular was the initial monolithic prototype. To achieve microsecond throughput, minimize bundle size, and decouple bundler compiler dependencies, the architecture was refactored into dedicated @path-ioc/* scoped packages:
| Legacy Feature | Modern Scoped Package | Description |
|---|---|---|
| Dependency lookup & topological runtime | @path-ioc/core | Runtime Required. Microsecond lock-free engine |
| Vite / Webpack / Rollup compiler plugins | @path-ioc/unplugin | Dev Dependency. Universal bundler adapter |
| Synchronous getter lazy loading POC | @path-ioc/container | Experimental. Not recommended for production, used for benchmark comparison |
| Mesh module standalone npm packaging | @path-ioc/pack | Optional. Packages module directories into distributable npm packages |
2. Dependency Changes
Run the following in your project root:
# 1. Remove legacy monolithic package
pnpm remove lianhanlin-modular
# 2. Install core runtime and compiler adapter
pnpm add @path-ioc/core
pnpm add -D @path-ioc/unplugin(If you are using npm or yarn, simply substitute with your package manager).
3. Bundler Configuration Updates
Vite (vite.config.ts)
Replace the legacy plugin with @path-ioc/unplugin:
import { defineConfig } from "vite";
- import modularType from "lianhanlin-modular/plugin-vite";
+ import pathIoc from "@path-ioc/unplugin";
export default defineConfig({
plugins: [
- modularType({ typeFileOutput: "types" }),
+ pathIoc.vite({
+ modulesPath: "src/modules", // Module directory, default: "src/modules"
+ typeFileOutput: "types", // Output directory, default: "types"
+ }),
],
});Webpack (webpack.config.js)
- const { ModularWebpackPlugin } = require("lianhanlin-modular/webpack");
+ const { webpackPlugin: pathIoc } = require("@path-ioc/unplugin");
module.exports = {
plugins: [
- new ModularWebpackPlugin(),
+ pathIoc({
+ modulesPath: "src/modules",
+ typeFileOutput: "types",
+ }),
],
};4. Container Bootstrap Adaptation
Import the initialize bootstrap function from @path-ioc/core:
import { container } from "./container";
import { modules } from "virtual:modular-container";
- import { initialize } from "lianhanlin-modular";
+ import { initialize } from "@path-ioc/core";
await initialize(modules, container);5. Dynamic Dependency Filtering: Standard Array APIs
Legacy helper utilities like getModuleNameByPrefix or getModuleDeclarations are deprecated. Because moduleDeclarationNames is a standard string[] array, use native JavaScript array methods directly:
- import { getModuleNameByPrefix } from "lianhanlin-modular";
export const dependencies = (moduleNames: string[]) => {
return [
- ...getModuleNameByPrefix(moduleNames, "/plugins/"),
+ ...moduleNames.filter((name) => name.startsWith("/plugins/")),
"configService",
];
};6. Physical Registry Bundling Migration (@path-ioc/pack)
If your project utilizes Mesh Registry Bundling (Modular Pack)—generating a physical entry file by scanning all Mesh modules (common in micro-frontend sub-app distribution, cross-monorepo component libraries, static pre-compilation, or AST obfuscation workflows)—this capability is now provided by the dedicated official plugin @path-ioc/pack.
1. Install Pack Plugin
pnpm add -D @path-ioc/pack2. Configure modularPackPlugin (Dedicated Config Recommended)
WARNING
Because @path-ioc/pack overrides the build pipeline (enforces build.lib, sets outDir to dist-plugin, and triggers npm pack on closeBundle), do not attach it unconditionally to your standard application vite.config.ts. Projects should control activation based on their build pipeline needs.
Recommended Pattern: Create a dedicated vite.config.pack.ts:
// vite.config.pack.ts
import { defineConfig } from "vite";
import { modularPackPlugin } from "@path-ioc/pack";
export default defineConfig({
plugins: [
modularPackPlugin({
modulesPath: "src/modules", // Module scan directory, default: "src/modules"
// entryFile: ".modular-plugin-entry.ts", // Physical entry path, automatically cleaned up after build
}),
],
});Configure a dedicated script in package.json:
{
"scripts": {
"build": "vite build",
"build:pack": "vite build --config vite.config.pack.ts"
}
}(Alternatively, conditionally load it in vite.config.ts using custom environment variables, e.g., process.env.BUILD_TARGET === 'pack')
3. Generated Physical Entry & Declaration Specifications
- Default physical entry path is
.modular-plugin-entry.tsat the project root, automatically cleaned up upon build completion (recommended to add this pattern to.gitignoreto prevent stray files if builds are forcefully terminated); - Built-in
dtscompilation automatically outputs declaration files for all exported modules underdist-plugin/src/anddist-plugin/index.d.ts, delivering full TypeScript definitions; - The entry file collates all discovered modules and exports a runtime registry array:typescript
export const modules = [ { key: "/auth/userService", module: module_0 }, // ... ];
7. Business Code Conventions (100% Preserved)
Existing business logic and patterns remain completely backward compatible:
mainExport: Modules continue exportingexport const main = () => ...;;- Zero Cross-Module Imports: Strict avoidance of relative imports preserves AOP interception contracts;
- In-Body Destructuring:tsx
export const UserService = () => { const { apiClient, cacheService } = modularContainer; // ... };
8. Migration Checklist
- [ ] Remove
lianhanlin-modularand install@path-ioc/core&@path-ioc/unplugin; - [ ] Update bundler configuration to import from
@path-ioc/unplugin; - [ ] Update container bootstrap
initializeimport to@path-ioc/core; - [ ] Replace any legacy helper functions with native
.filter(...)/.startsWith(...); - [ ] (If using registry bundling) Install
@path-ioc/packand configuremodularPackPlugin; - [ ] Start dev server and verify
ignore.modular.d.tsgenerates cleanly.