@lcap/builder
Version:
lcap builder utils
260 lines (255 loc) • 10.4 kB
JavaScript
;
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;