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