UNPKG

@lcap/builder

Version:
260 lines (255 loc) 10.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.executeCreateForSchema = exports.setImportStyle = exports.createForSchema = void 0; const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const vite_1 = require("vite"); const prompts_1 = __importDefault(require("prompts")); const lodash_1 = require("lodash"); const component_1 = require("./component"); const lcap_1 = require("../utils/lcap"); const schema_utils_1 = require("../utils/schema-utils"); const EMPTY_API_TS = ({ pkgName, compName, sourceName, title, description, type, }) => { return `/// <reference types="@nasl/types" /> namespace extensions.${pkgName}.viewComponents { const { Component, Prop, ViewComponent, Slot, Method, Event, ViewComponentOptions } = nasl.ui; @ExtensionComponent({ type: '${type}', sourceName: '${sourceName}', ideusage: { idetype: 'element', } }) @Component({ title: '${title}', description: '${description}', }) export class ${compName} extends ViewComponent { constructor(options?: Partial<${compName}Options>) { super(); } } export class ${compName}Options extends ViewComponentOptions { } }`; }; function generateVue2Component(meta, component, { tagName, componentFolder }) { const VueCode = `<template> <${component.name} v-bind="$attrs" v-on="$listeners"> <template v-for="(_, name) in $scopedSlots" v-slot:[name]="data"> <slot :name="name" v-bind="data"/> </template> </${component.name}> </template> <script> import { ${component.exportName} as ${component.name} } from '${meta.name}'; export default { name: '${tagName}', components: { ${component.name}, }, }; </script>`; fs_extra_1.default.writeFileSync(path_1.default.resolve(componentFolder, 'index.vue'), VueCode, 'utf-8'); } function generateVue3Component(meta, component, { tagName, componentFolder }) { const VueCode = `<template> <${component.name} v-bind="$attrs" v-on="$listeners"> <template v-for="(_, name) in $slots" v-slot:[name]="data"> <slot :name="name" v-bind="data"/> </template> </${component.name}> </template> <script> import { ${component.exportName} as ${component.name} } from '${meta.name}'; export default { name: '${tagName}', components: { ${component.name}, }, }; </script>`; fs_extra_1.default.writeFileSync(path_1.default.resolve(componentFolder, 'index.vue'), VueCode, 'utf-8'); } function generateReactComponent(meta, component, { tagName, componentFolder }) { const code = [ 'import React, { forwardRef } from \'react\'', `import { ${component.exportName} as BaseComponent } from '${meta.name}'`, '', `const ${tagName} = forwardRef<any, any>((props, ref) => {`, ' const {', ' ...rest', ' } = props;', '', ' return (', ' <BaseComponent', ' ref={ref}', ' {...rest}', ' />', ' );', '});', '', `export default ${tagName}`, '', ].join('\n'); fs_extra_1.default.writeFileSync(path_1.default.resolve(componentFolder, 'index.tsx'), code, 'utf-8'); } function createForSchema(rootPath, metaInfo, schema) { return __awaiter(this, void 0, void 0, function* () { const { meta, write: options, component } = schema; const compName = (0, lodash_1.upperFirst)(options.prefix || '') + component.name; const tagName = (0, component_1.getTagName)(metaInfo.framework, compName); const title = (0, lodash_1.kebabCase)(component.name).split('-').map((s) => (0, lodash_1.upperFirst)(s)).join(' '); const type = options.type || 'pc'; const componentFolder = path_1.default.resolve(rootPath, component_1.COMPONENTS_FOLDER, tagName); (0, component_1.createComponent)(rootPath, metaInfo, { name: compName, title, overload: false, type, }); fs_extra_1.default.writeFileSync(path_1.default.resolve(componentFolder, 'api.ts'), EMPTY_API_TS({ pkgName: metaInfo.name, compName, title, sourceName: component.name, description: (0, schema_utils_1.normalizeString)(component.description || title), type, }), 'utf-8'); switch (metaInfo.framework) { case 'vue3': generateVue3Component(meta, component, { tagName, componentFolder }); break; case 'vue2': generateVue2Component(meta, component, { tagName, componentFolder }); break; case 'react': generateReactComponent(meta, component, { tagName, componentFolder }); break; default: break; } }); } exports.createForSchema = createForSchema; function setImportStyle(rootPath, schema) { if (!schema.style) { return; } const entry = path_1.default.resolve(rootPath, 'src/index.ts'); if (!fs_extra_1.default.existsSync(entry)) { return; } const styleCode = `import '${(0, vite_1.normalizePath)(path_1.default.join(schema.name, schema.style))}'`; const content = fs_extra_1.default.readFileSync(entry, 'utf-8').toString().split('\n'); if (content.some((line) => line.includes(styleCode))) { return; } content.unshift(styleCode); fs_extra_1.default.writeFileSync(entry, content.join('\n'), 'utf-8'); } exports.setImportStyle = setImportStyle; function executeCreateForSchema(rootPath, metaInfo, schema, name) { return __awaiter(this, void 0, void 0, function* () { if (!schema || !fs_extra_1.default.existsSync(path_1.default.resolve(rootPath, schema))) { throw new Error(`schema 文件 ${schema} 不存在`); } const material = yield fs_extra_1.default.readJSON(path_1.default.resolve(rootPath, schema), 'utf-8'); if (!material.components || material.components.length === 0) { throw new Error(`schema 文件 ${schema} 中没有组件`); } const apiComponentList = (0, lcap_1.getComponentMetaInfos)(rootPath, true); const components = material.components.filter((c) => (name ? c.name === name : (!apiComponentList.find((l) => l.sourceName === c.name)))); if (components.length === 0) { throw new Error(`schema 文件 ${schema} 中没有可添加的组件`); } let createComponentNames = []; if (name) { // 如果 schema 中没有 write 配置,则默认添加组件名称前缀为 cwx,端类型为 pc if (!material.write) { material.write = { prefix: 'cwx', type: 'pc', }; fs_extra_1.default.writeJSONSync(schema, material, { spaces: 2 }); } createComponentNames = [name]; } else { const answers = yield (0, prompts_1.default)([ { type: 'multiselect', name: 'components', message: '请选择要添加的组件:', choices: components.map((c) => ({ value: c.name, title: c.name })), }, { type: metaInfo.framework === 'react' || material.write ? null : 'select', name: 'type', message: '请选择端', initial: 0, choices: [ { value: 'pc', title: 'PC端' }, { value: 'h5', title: 'H5端' }, { value: 'both', title: '全部' }, ], }, { type: !material.write ? 'text' : null, name: 'prefix', message: '请输入添加组件名称前缀(例如 cwx):', initial: 'cwx', format: (val) => { if (!val) { return ''; } return val.trim().toLowerCase(); }, }, ]); if (!material.write) { material.write = { prefix: answers.prefix, type: answers.type, }; fs_extra_1.default.writeJSONSync(schema, material, { spaces: 2 }); } createComponentNames = answers.components; } if (createComponentNames.length === 0) { throw new Error('请选择要添加的组件'); } const writeOptions = material.write; createComponentNames.forEach((name) => { const component = components.find((c) => c.name === name); if (!component) { throw new Error(`schema 文件 ${schema} 中没有找到组件 ${name}`); } createForSchema(rootPath, metaInfo, { meta: { name: material.name, version: material.version, description: material.description, framework: material.framework, frameworkVersion: material.frameworkVersion, }, write: writeOptions, component, }); }); setImportStyle(rootPath, material); }); } exports.executeCreateForSchema = executeCreateForSchema;