chore: src/modules/plugin/service/types.ts
This commit is contained in:
@@ -0,0 +1,256 @@
|
|||||||
|
import { BaseService } from '@cool-midway/core';
|
||||||
|
import { App, IMidwayApplication, Inject, Provide } from '@midwayjs/core';
|
||||||
|
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
import { Utils } from '../../../comm/utils';
|
||||||
|
import { PluginInfoEntity } from '../entity/info';
|
||||||
|
import { PluginService } from './info';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件类型服务
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
export class PluginTypesService extends BaseService {
|
||||||
|
@App()
|
||||||
|
app: IMidwayApplication;
|
||||||
|
|
||||||
|
@InjectEntityModel(PluginInfoEntity)
|
||||||
|
pluginInfoEntity: Repository<PluginInfoEntity>;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
pluginService: PluginService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
utils: Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成d.ts文件
|
||||||
|
* @param tsContent
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async dtsContent(tsContent: string) {
|
||||||
|
let output = '';
|
||||||
|
|
||||||
|
const compilerHost: ts.CompilerHost = {
|
||||||
|
fileExists: ts.sys.fileExists,
|
||||||
|
getCanonicalFileName: ts.sys.useCaseSensitiveFileNames
|
||||||
|
? s => s
|
||||||
|
: s => s.toLowerCase(),
|
||||||
|
getCurrentDirectory: ts.sys.getCurrentDirectory,
|
||||||
|
getDefaultLibFileName: options => ts.getDefaultLibFilePath(options),
|
||||||
|
getDirectories: ts.sys.getDirectories,
|
||||||
|
getNewLine: () => ts.sys.newLine,
|
||||||
|
getSourceFile: (fileName, languageVersion) => {
|
||||||
|
if (fileName === 'file.ts') {
|
||||||
|
return ts.createSourceFile(
|
||||||
|
fileName,
|
||||||
|
tsContent,
|
||||||
|
languageVersion,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const filePath = ts.sys.resolvePath(fileName);
|
||||||
|
return ts.sys.readFile(filePath)
|
||||||
|
? ts.createSourceFile(
|
||||||
|
filePath,
|
||||||
|
ts.sys.readFile(filePath)!,
|
||||||
|
languageVersion,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
},
|
||||||
|
readFile: ts.sys.readFile,
|
||||||
|
useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames,
|
||||||
|
writeFile: (fileName, content) => {
|
||||||
|
if (fileName.includes('file.d.ts')) {
|
||||||
|
output = content || output;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const options: ts.CompilerOptions = {
|
||||||
|
declaration: true,
|
||||||
|
emitDeclarationOnly: true,
|
||||||
|
outDir: './',
|
||||||
|
skipLibCheck: true,
|
||||||
|
skipDefaultLibCheck: true,
|
||||||
|
noEmitOnError: false,
|
||||||
|
target: ts.ScriptTarget.ES2018,
|
||||||
|
strict: false,
|
||||||
|
module: ts.ModuleKind.Node16,
|
||||||
|
moduleResolution: ts.ModuleResolutionKind.Node16,
|
||||||
|
types: ['node'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const program = ts.createProgram(['file.ts'], options, compilerHost);
|
||||||
|
program.emit();
|
||||||
|
|
||||||
|
if (!output) {
|
||||||
|
// Provide a default value if the output is still empty
|
||||||
|
output = '/* No declaration content generated */';
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成d.ts文件
|
||||||
|
* @param key
|
||||||
|
* @param tsContent
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async generateDtsFile(key: string, tsContent: string) {
|
||||||
|
const env = this.app.getEnv();
|
||||||
|
// 不是本地开发环境不生成d.ts文件
|
||||||
|
if (env != 'local' || !tsContent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 基础路径
|
||||||
|
const basePath = path.join(this.app.getBaseDir(), '..', 'typings');
|
||||||
|
// pluginDts文件路径
|
||||||
|
const pluginDtsPath = path.join(basePath, 'plugin.d.ts');
|
||||||
|
// plugin文件夹路径
|
||||||
|
const pluginPath = path.join(basePath, `${key}.d.ts`);
|
||||||
|
// 生成d.ts文件
|
||||||
|
const dtsContent = await this.dtsContent(tsContent);
|
||||||
|
|
||||||
|
// 读取plugin.d.ts文件内容
|
||||||
|
let pluginDtsContent = fs.readFileSync(pluginDtsPath, 'utf-8');
|
||||||
|
|
||||||
|
// 根据key判断是否在PluginMap中存在
|
||||||
|
const keyWithHyphen = key.includes('-');
|
||||||
|
const importStatement = keyWithHyphen
|
||||||
|
? `import * as ${key.replace(/-/g, '_')} from './${key}';`
|
||||||
|
: `import * as ${key} from './${key}';`;
|
||||||
|
const pluginMapEntry = keyWithHyphen
|
||||||
|
? `'${key}': ${key.replace(/-/g, '_')}.CoolPlugin;`
|
||||||
|
: `${key}: ${key}.CoolPlugin;`;
|
||||||
|
|
||||||
|
// 检查import语句是否已经存在,若不存在则添加
|
||||||
|
if (!pluginDtsContent.includes(importStatement)) {
|
||||||
|
pluginDtsContent = `${importStatement}\n${pluginDtsContent}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查PluginMap中的键是否存在,若不存在则添加
|
||||||
|
if (pluginDtsContent.includes(pluginMapEntry)) {
|
||||||
|
// 键存在则覆盖
|
||||||
|
const regex = new RegExp(
|
||||||
|
`(\\s*${keyWithHyphen ? `'${key}'` : key}:\\s*[^;]+;)`
|
||||||
|
);
|
||||||
|
pluginDtsContent = pluginDtsContent.replace(regex, pluginMapEntry);
|
||||||
|
} else {
|
||||||
|
// 键不存在则追加
|
||||||
|
const pluginMapRegex = /interface\s+PluginMap\s*{([^}]*)}/;
|
||||||
|
pluginDtsContent = pluginDtsContent.replace(
|
||||||
|
pluginMapRegex,
|
||||||
|
(match, p1) => {
|
||||||
|
return match.replace(p1, `${p1.trim()}\n ${pluginMapEntry}`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化内容
|
||||||
|
pluginDtsContent = await this.formatContent(pluginDtsContent);
|
||||||
|
|
||||||
|
// 延迟2秒写入文件
|
||||||
|
setTimeout(async () => {
|
||||||
|
// 写入d.ts文件,如果存在则覆盖
|
||||||
|
fs.writeFile(pluginPath, await this.formatContent(dtsContent), () => {});
|
||||||
|
|
||||||
|
// 写入plugin.d.ts文件
|
||||||
|
fs.writeFile(pluginDtsPath, pluginDtsContent, () => {});
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除d.ts文件中的指定key
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
async deleteDtsFile(key: string) {
|
||||||
|
const env = this.app.getEnv();
|
||||||
|
// 不是本地开发环境不删除d.ts文件
|
||||||
|
if (env != 'local') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 基础路径
|
||||||
|
const basePath = path.join(this.app.getBaseDir(), '..', 'typings');
|
||||||
|
// pluginDts文件路径
|
||||||
|
const pluginDtsPath = path.join(basePath, 'plugin.d.ts');
|
||||||
|
// plugin文件夹路径
|
||||||
|
const pluginPath = path.join(basePath, `${key}.d.ts`);
|
||||||
|
|
||||||
|
// 读取plugin.d.ts文件内容
|
||||||
|
let pluginDtsContent = fs.readFileSync(pluginDtsPath, 'utf-8');
|
||||||
|
|
||||||
|
// 根据key判断是否在PluginMap中存在
|
||||||
|
const keyWithHyphen = key.includes('-');
|
||||||
|
const importStatement = keyWithHyphen
|
||||||
|
? `import \\* as ${key.replace(/-/g, '_')} from '\\./${key}';`
|
||||||
|
: `import \\* as ${key} from '\\./${key}';`;
|
||||||
|
const pluginMapEntry = keyWithHyphen
|
||||||
|
? `'${key}': ${key.replace(/-/g, '_')}.CoolPlugin;`
|
||||||
|
: `${key}: ${key}.CoolPlugin;`;
|
||||||
|
|
||||||
|
// 删除import语句
|
||||||
|
const importRegex = new RegExp(`${importStatement}\\n`, 'g');
|
||||||
|
pluginDtsContent = pluginDtsContent.replace(importRegex, '');
|
||||||
|
|
||||||
|
// 删除PluginMap中的键
|
||||||
|
const pluginMapRegex = new RegExp(`\\s*${pluginMapEntry}`, 'g');
|
||||||
|
pluginDtsContent = pluginDtsContent.replace(pluginMapRegex, '');
|
||||||
|
|
||||||
|
// 格式化内容
|
||||||
|
pluginDtsContent = await this.formatContent(pluginDtsContent);
|
||||||
|
|
||||||
|
// 延迟2秒写入文件
|
||||||
|
setTimeout(async () => {
|
||||||
|
// 删除插件d.ts文件
|
||||||
|
if (fs.existsSync(pluginPath)) {
|
||||||
|
fs.unlink(pluginPath, () => {});
|
||||||
|
}
|
||||||
|
// 写入plugin.d.ts文件
|
||||||
|
fs.writeFile(pluginDtsPath, pluginDtsContent, () => {});
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化内容
|
||||||
|
* @param content
|
||||||
|
*/
|
||||||
|
async formatContent(content: string) {
|
||||||
|
// 使用prettier格式化内容
|
||||||
|
const prettier = require('prettier');
|
||||||
|
return prettier.format(content, {
|
||||||
|
parser: 'typescript',
|
||||||
|
singleQuote: true,
|
||||||
|
trailingComma: 'all',
|
||||||
|
bracketSpacing: true,
|
||||||
|
arrowParens: 'avoid',
|
||||||
|
printWidth: 80,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新生成d.ts文件
|
||||||
|
*/
|
||||||
|
async reGenerate() {
|
||||||
|
const pluginInfos = await this.pluginInfoEntity
|
||||||
|
.createQueryBuilder('a')
|
||||||
|
.where('a.status = :status', { status: 1 })
|
||||||
|
.select(['a.id', 'a.status', 'a.tsContent', 'a.keyName'])
|
||||||
|
.getMany();
|
||||||
|
for (const pluginInfo of pluginInfos) {
|
||||||
|
const data = await this.pluginService.getData(pluginInfo.keyName);
|
||||||
|
if (!data) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const tsContent = data.tsContent?.data;
|
||||||
|
if (tsContent) {
|
||||||
|
await this.generateDtsFile(pluginInfo.keyName, tsContent);
|
||||||
|
await this.utils.sleep(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user