Design and Implementation of a Plugin Management Platform for Vivo Activity Components
The article describes Vivo’s plugin management platform for activity components, built with a Midway Node.js backend, Vue.js frontend, and MySQL, which automates component extraction, Markdown documentation, GitLab hook‑driven NPM package retrieval, and AST‑based export parsing to provide a reusable library that has already saved over 20 person‑days of development effort.
Background
During the development of the Vivo Wukong activity platform, the number of activity components grew rapidly. The team realized the need for a reusable component library to avoid duplicated effort and to make common capabilities visible to product, operations, and testing teams. This motivated the creation of activity‑components and the design of a plugin management platform.
Platform Architecture
The platform is built with a Midway (Node.js) backend, Vue.js frontend, and MySQL database. Midway was chosen because it:
Is compatible with the Egg.js plugin ecosystem.
Provides built‑in Inversion of Control (IoC) for loose coupling.
Offers excellent TypeScript support, improving type safety and developer efficiency.
Key Technical Details
1. Component Extraction
The package.json of the component library defines a map script that traverses the src directory, finds each index.vue file, and generates an export file.
{ "name": "@wukong/activity-components", "version": "1.0.6", "scripts": { "map": "node ./tool/map-components.js", "doc": "node ./tool/create-doc.js", "prepublish": "npm run map && npm run doc" } }The traversal logic (simplified) is:
const deepMapDir = (rootPath, name, cb) => { const list = fse.readdirSync(rootPath); list.forEach(targetPath => { const fullPath = path.join(rootPath, targetPath); const stat = fse.lstatSync(fullPath); if (stat.isDirectory()) { deepMapDir(fullPath, targetPath, cb); } else if (targetPath === 'index.vue') { if (typeof cb === 'function') { cb(rootPath, path.relative('./src', fullPath), name); } } }); };2. Markdown Documentation Generation
Using the internal vue-doc tool, each component’s index.vue is converted into a Markdown file. The generated MD files are stored under a doc folder mirroring the component hierarchy.
const { singleVueDocSync } = require('@vivo/vue-doc'); singleVueDocSync(vuePath, { outputType: 'md', outputPath: mdPath });The resulting JSON data (simplified) looks like:
{ "timestamp": 1628846618611, "list": [ { "name": "CommonDialog", "md": "
CommonDialog
...", "fullPath": "/.../doc/base-components/CommonDialog/index.md", "path": "doc/base-components/CommonDialog/index.md", "cname": "通用基础弹框" } // ... other components ] }3. GitLab Hook Integration
A GitLab push event triggers the backend to fetch the latest component package. The controller is defined with Midway decorators:
@provide() @controller('/api/gitlab') export class GitlabController { @inject() ctx: Context; @post('/push') async push(): Promise
{ try { const event = this.ctx.headers['x-gitlab-event']; const token = this.ctx.headers['x-gitlab-token']; if (token === this.ctx.app.config.gitlab.token) { if (event === 'Push Hook') { const name = 'activity-components'; const npmInfo = await this.ctx.service.activity.getNpmInfo(`@wukong/${name}`); await this.ctx.service.activity.getPkg(name, npmInfo.data.latest.version); } } this.ctx.body = { code: ErrorCode.success, success: true, msg: Message.success } as IRes; } catch (e) { this.ctx.body = { code: ErrorCode.fail, success: false, msg: e.toString() } as IRes; } } }4. NPM Package Retrieval and Extraction
The service fetches the latest version via the Verdaccio sidebar API, downloads the .tgz file, and extracts it using targz :
npm view @wukong/activity-components { host: 'wk-site-npm-test.vivo.xyz', dist: { integrity: 'sha512-...', shasum: 'ff09a0554d66e8...', tarball: 'http://wk-.../activity-components-1.0.0.tgz' } } targz.decompress({ src: tgzPath, dest: extractPath }, err => { if (err) console.log(err); else console.log('Done!'); });After extraction, the source code resides under temp/activity-components and the generated documentation under temp/activity-components/doc .
5. AST Parsing for Export Extraction
To reliably discover component entry points, the platform parses the library’s main file with @babel/parser and traverses the AST to collect ExportSpecifier nodes:
const ast = parse(exportData, { sourceType: 'module' }); const pathList = []; traverse(ast, { ExportSpecifier: { enter(path) { pathList.push({ path: path.parent.source.value, name: path.node.exported.name }); } } });The resulting list is then fed back into vue-doc to generate Markdown for each component.
Conclusion
The platform now provides a complete pipeline: component extraction → Markdown documentation → JSON aggregation → frontend rendering. It has already extracted 26 public components, covering more than 12 building‑block components and serving two business lines, saving over 20 person‑days of effort. Future work includes automated testing, expanding the component set, and adding animation capabilities.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.