entity tree panel 跳转到定义,lint等

This commit is contained in:
pqcqaq 2024-10-19 21:50:13 +08:00
parent 2e98c4e96d
commit 6e331b1221
13 changed files with 2525 additions and 1811 deletions

15
.editorconfig Normal file
View File

@ -0,0 +1,15 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
single_quoted_string = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

4
.eslintignore Normal file
View File

@ -0,0 +1,4 @@
node_modules/
packages/react-live/dist/
website/build/
src/app/routers/

8
.eslintrc.json Normal file
View File

@ -0,0 +1,8 @@
{
"parserOptions": {
"ecmaVersion": "latest"
},
"env": {
"es6": true
}
}

10
.prettierrc Normal file
View File

@ -0,0 +1,10 @@
{
"useTabs": false,
"tabWidth": 4,
"printWidth": 80,
"singleQuote": true,
"trailingComma": "es5",
"semi": true,
"endOfLine": "lf",
"jsxSingleQuote": true
}

1
assets/oak-icon.svg Normal file
View File

@ -0,0 +1 @@
<svg t="1729342159733" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1478" width="1280" height="1280"><path d="M512 64c-33.066667 0-63.786667 10.517333-85.290667 32.042667-10.304 10.304-17.728 22.912-23.04 36.672-9.621333-2.901333-19.328-5.909333-28.970666-5.717334-25.941333 0.512-49.749333 11.349333-68.117334 27.882667-25.450667 22.912-40.085333 58.624-38.250666 96.576-36.202667 2.474667-68.992 19.776-88.106667 47.829333-20.288 29.738667-23.466667 70.613333-6.890667 108.330667a118.058667 118.058667 0 0 0-37.418666 32.085333A144.128 144.128 0 0 0 106.666667 527.04c0 40.128 17.258667 80.277333 50.56 107.2 26.197333 21.226667 63.68 29.013333 104.896 27.008 6.165333 31.914667 18.304 61.653333 41.258666 83.328C333.994667 773.504 377.322667 789.333333 426.666667 789.333333v32c0 15.616-5.226667 43.562667-17.258667 63.36-12.010667 19.84-26.368 32.64-57.408 32.64a32 32 0 0 0 0 64h320a32 32 0 0 0 0-64c-31.04 0-45.397333-12.8-57.408-32.64-12.032-19.797333-17.258667-47.744-17.258667-63.36V789.333333c49.344 0 92.672-15.829333 123.306667-44.757333 22.933333-21.674667 35.072-51.413333 41.237333-83.328 41.216 1.984 78.72-5.781333 104.917334-27.008C900.053333 607.317333 917.333333 567.168 917.333333 527.04c0-30.848-9.813333-61.909333-29.248-87.338667a118.058667 118.058667 0 0 0-37.418666-32.085333c16.576-37.717333 13.397333-78.592-6.869334-108.330667-19.136-28.053333-51.925333-45.354667-88.128-47.829333 1.834667-37.952-12.8-73.664-38.250666-96.576-18.368-16.533333-42.176-27.370667-68.117334-27.882667-9.642667-0.192-19.349333 2.816-28.970666 5.717334-5.312-13.76-12.736-26.368-23.04-36.672C575.765333 74.517333 545.066667 64 512 64z m0 64c20.266667 0 32.213333 5.482667 40.042667 13.290667 7.808 7.808 13.290667 19.776 13.290666 40.042666a32 32 0 0 0 54.613334 22.613334c10.666667-10.645333 19.392-13.12 28.096-12.949334 8.704 0.170667 18.24 3.989333 26.538666 11.456 16.597333 14.933333 26.602667 40.981333 11.456 71.253334a32 32 0 0 0 39.893334 44.245333c29.930667-11.221333 53.077333 0.021333 64.938666 17.429333 11.861333 17.386667 15.210667 39.957333-13.504 68.650667A32 32 0 0 0 800 458.666667c16.042667 0 27.349333 6.954667 37.248 19.925333 9.898667 12.949333 16.085333 32.064 16.085333 48.448 0 21.76-9.408 43.434667-26.794666 57.493333-17.365333 14.08-43.178667 22.528-83.584 13.546667A32 32 0 0 0 704 629.333333c0 29.717333-9.92 52.352-27.306667 68.757334C659.349333 714.496 633.365333 725.333333 597.333333 725.333333v-32a32 32 0 1 0-64 0v128c0 26.88 6.229333 62.656 26.282667 96h-95.232c20.053333-33.344 26.282667-69.12 26.282667-96v-128a32 32 0 0 0-32.490667-32.448A32 32 0 0 0 426.666667 693.333333V725.333333c-36.010667 0-61.994667-10.837333-79.36-27.242666-17.386667-16.426667-27.306667-39.04-27.306667-68.757334a32 32 0 0 0-38.954667-31.253333c-40.405333 8.96-66.218667 0.533333-83.584-13.546667C180.053333 570.474667 170.666667 548.8 170.666667 527.04c0-16.384 6.186667-35.498667 16.085333-48.448 9.898667-12.970667 21.205333-19.925333 37.248-19.925333a32 32 0 0 0 22.613333-54.613334c-28.693333-28.714667-25.344-51.285333-13.482666-68.693333 11.861333-17.386667 35.008-28.629333 64.96-17.408a32 32 0 0 0 39.872-44.245333c-15.146667-30.272-5.12-56.32 11.456-71.253334 8.298667-7.466667 17.834667-11.285333 26.538666-11.456 8.704-0.170667 17.429333 2.304 28.074667 12.970667A32 32 0 0 0 458.666667 181.333333c0-20.266667 5.482667-32.213333 13.290666-40.042666C479.765333 133.482667 491.733333 128 512 128z" p-id="1479"></path></svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -2,6 +2,11 @@
"name": "oak-assistant",
"displayName": "oak-assistant",
"description": "OAK框架辅助开发插件",
"author": {
"name": "oak-team"
},
"icon": "assets/oak-icon.svg",
"publisher": "oak-team",
"version": "0.0.1",
"engines": {
"vscode": "^1.94.0"
@ -26,6 +31,12 @@
{
"command": "oak-assistant.create-oak-component",
"title": "创建OAK组件"
},
{
"title": "跳转到entity定义",
"command": "oak-entities.jumpToDefinition",
"when": "view == oak-entities && viewItem == entityItem",
"group": "navigation"
}
],
"menus": {
@ -35,6 +46,30 @@
"command": "oak-assistant.create-oak-component",
"group": "navigation"
}
],
"view/item/context": [
{
"command": "oak-entities.jumpToDefinition",
"when": "view == oak-entities && viewItem == entityItem",
"group": "navigation"
}
]
},
"viewsContainers": {
"activitybar": [
{
"id": "oak-entities",
"title": "Oak Entities",
"icon": "assets/oak-icon.svg"
}
]
},
"views": {
"oak-entities": [
{
"id": "oak-entities",
"name": "Oak Entities"
}
]
}
},
@ -69,6 +104,7 @@
"typescript": "^5.6.2"
},
"dependencies": {
"glob": "^11.0.0",
"lodash": "^4.17.21"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,9 @@ import checkPagesAndNamespace from "./plugins/checkPagesAndNamespace";
import { OakConfiog } from "./types/OakConfig";
import createOakComponent from "./plugins/createOakComponent";
import { analyzeOakAppDomain } from "./utils/entities";
import { createOakTreePanel } from "./plugins/oakTreePanel";
import { setLoadingEntities } from "./utils/status";
import { treePanelCommands } from "./plugins/treePanelCommands";
// 初始化配置
// 查找工作区的根目录中的oak.config.json文件排除src和node_modules目录
@ -71,6 +74,8 @@ vscode.workspace.findFiles("oak.config.json", exclude).then((uris) => {
});
const afterPathSet = async () => {
setLoadingEntities(true);
const stepList: {
name: string;
description: string;
@ -108,6 +113,8 @@ const afterPathSet = async () => {
}
}
);
setLoadingEntities(false);
};
export async function activate(context: vscode.ExtensionContext) {
@ -127,7 +134,9 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
helloOak,
checkPagesAndNamespace(),
createOakComponent()
createOakComponent(),
createOakTreePanel(),
...treePanelCommands
);
}

View File

@ -0,0 +1,82 @@
import * as vscode from 'vscode';
import { entityConfig, subscribe } from '../utils/entities';
class OakTreeDataProvider implements vscode.TreeDataProvider<TreeItem> {
private listenerDispose: (() => void) | null = null;
private _onDidChangeTreeData: vscode.EventEmitter<
TreeItem | undefined | null | void
> = new vscode.EventEmitter<TreeItem | undefined | null | void>();
readonly onDidChangeTreeData: vscode.Event<
TreeItem | undefined | null | void
> = this._onDidChangeTreeData.event;
public refresh(): void {
this._onDidChangeTreeData.fire();
}
// 初始化方法
constructor() {
this.listenerDispose = subscribe(() => {
this.refresh();
});
}
getTreeItem(
element: TreeItem
): vscode.TreeItem | Thenable<vscode.TreeItem> {
return element;
}
getChildren(element?: TreeItem): vscode.ProviderResult<TreeItem[]> {
// 最顶层
if (!element) {
return entityConfig.entityNameList.map((entityName) => {
return new EntityItem(
entityName,
vscode.TreeItemCollapsibleState.Collapsed
);
});
}
}
// 销毁方法
dispose() {
if (this.listenerDispose) {
this.listenerDispose();
}
}
}
class TreeItem extends vscode.TreeItem {
constructor(
public readonly label: string,
public readonly collapsibleState: vscode.TreeItemCollapsibleState
) {
super(label, collapsibleState);
}
}
export class EntityItem extends TreeItem {
private readonly entityName: string;
getEntityName() {
return this.entityName;
}
constructor(
public readonly label: string,
public readonly collapsibleState: vscode.TreeItemCollapsibleState
) {
super(label, collapsibleState);
this.entityName = label;
this.contextValue = 'entityItem'; // 添加这行,用于识别右键菜单项
}
}
export const createOakTreePanel = () => {
const treeDataProvider = new OakTreeDataProvider();
const treeView = vscode.window.createTreeView('oak-entities', {
treeDataProvider: treeDataProvider,
});
return treeView;
};

View File

@ -0,0 +1,66 @@
import * as vscode from 'vscode';
import { pathConfig } from '../utils/paths';
import { join, resolve } from 'path';
import { toUpperFirst } from '../utils/stringUtils';
import * as fs from 'fs';
import * as glob from 'glob';
import { EntityItem } from './oakTreePanel';
const pushToEntityDefinition = vscode.commands.registerCommand(
'oak-entities.jumpToDefinition',
async (item: EntityItem) => {
if (!item) {
// 在explorer中定位到指定文件夹
const dir = pathConfig.entityHome;
const uri = vscode.Uri.file(dir);
await vscode.commands.executeCommand('revealInExplorer', uri);
return;
}
const fileName = toUpperFirst(`${item.getEntityName()}.ts`);
const possiblePaths: string[] = [];
// 搜索 pathConfig.entityHome
const entityHomePath = join(pathConfig.entityHome, fileName);
if (fs.existsSync(entityHomePath)) {
possiblePaths.push(entityHomePath);
}
// 搜索 node_modules 中以 oak 开头的包
const nodeModulesPath = resolve(pathConfig.projectHome, 'node_modules');
const oakPackages = glob.sync('oak-*/src/entities', {
cwd: nodeModulesPath,
});
for (const pkg of oakPackages) {
const pkgEntityPath = join(nodeModulesPath, pkg, fileName);
if (fs.existsSync(pkgEntityPath)) {
possiblePaths.push(pkgEntityPath);
}
}
if (possiblePaths.length === 0) {
vscode.window.showErrorMessage(
`没有找到entity的定义文件: ${item.getEntityName()}`
);
return;
}
let selectedPath: string;
if (possiblePaths.length === 1) {
selectedPath = possiblePaths[0];
} else {
const selected = await vscode.window.showQuickPick(
possiblePaths.map((path) => ({ label: path, description: '' })),
{ placeHolder: '选择一个entity定义文件' }
);
if (!selected) {
return;
}
selectedPath = selected.label;
}
const entityDefinitionUri = vscode.Uri.file(selectedPath);
await vscode.window.showTextDocument(entityDefinitionUri);
}
);
export const treePanelCommands = [pushToEntityDefinition];

View File

@ -1,337 +1,380 @@
import * as vscode from "vscode";
import { EntityShape } from "oak-domain/lib/types";
import { StorageDesc } from "oak-domain/lib/types/Storage";
import { join, dirname } from "path";
import fs from "fs";
import * as ts from "typescript";
import * as vscode from 'vscode';
import { EntityShape } from 'oak-domain/lib/types';
import { StorageDesc } from 'oak-domain/lib/types/Storage';
import { join, dirname } from 'path';
import fs from 'fs';
import * as ts from 'typescript';
import { debounce } from 'lodash';
import { random } from 'lodash';
export type EntityDict = {
[key: string]: StorageDesc<EntityShape>;
[key: string]: StorageDesc<EntityShape>;
};
const entityDict: EntityDict = {};
// 发布订阅模式
const subscribers: Map<number, () => void> = new Map();
const updateDeounced = debounce(() => {
subscribers.forEach((callback) => callback());
}, 100);
const entityDict: EntityDict = new Proxy({} as EntityDict, {
set(target, key, value) {
target[key as string] = value;
updateDeounced();
return true;
},
});
export const subscribe = (callback: () => void) => {
/**
*
*
* @param callback
* @returns key
*/
const setToSubscribers = (callback: () => void) => {
const key = random(0, 100000);
if (subscribers.has(key)) {
return setToSubscribers(callback);
}
subscribers.set(key, callback);
return key;
};
const key = setToSubscribers(callback);
return () => {
subscribers.delete(key);
};
};
const genEntityNameList = (): string[] => {
return Object.keys(entityDict);
return Object.keys(entityDict);
};
export const entityConfig = {
get entityNameList() {
return genEntityNameList();
},
get entityNameList() {
return genEntityNameList();
},
};
function resolveImportPath(importPath: string, currentDir: string): string {
if (importPath.startsWith(".")) {
return join(currentDir, `${importPath}.ts`);
}
// 处理非相对路径的导入(如 node_modules
return importPath;
if (importPath.startsWith('.')) {
return join(currentDir, `${importPath}.ts`);
}
// 处理非相对路径的导入(如 node_modules
return importPath;
}
function getEvaluateNodeForShorthandProperty(
program: ts.Program,
node: ts.ShorthandPropertyAssignment,
typeChecker: ts.TypeChecker
program: ts.Program,
node: ts.ShorthandPropertyAssignment,
typeChecker: ts.TypeChecker
): any {
const symbol = typeChecker.getSymbolAtLocation(node.name);
if (!symbol) {
return undefined;
}
const symbol = typeChecker.getSymbolAtLocation(node.name);
if (!symbol) {
return undefined;
}
// 获取符号的声明
const declarations = symbol.declarations;
if (!declarations || declarations.length === 0) {
return undefined;
}
// 获取符号的声明
const declarations = symbol.declarations;
if (!declarations || declarations.length === 0) {
return undefined;
}
const declaration = declarations[0];
// 从当前文件的所有导入中找到对应的导入
const sourceFile = declaration.getSourceFile();
let propertyName = "";
const importDeclaration = sourceFile.statements.find((statement) => {
// 在这里找到import { actions } from "./Actions" 这样的形式
if (ts.isImportDeclaration(statement)) {
const moduleSpecifier = statement.moduleSpecifier;
if (ts.isStringLiteral(moduleSpecifier)) {
const imports = statement.importClause?.namedBindings;
// 如果导入了node.name
if (imports && ts.isNamedImports(imports)) {
// 这里需要注意如果是import { generalActions as actions } from "./Actions" 这样的形式要拿到as的内容和node.name进行比较
return imports.elements.some((element) => {
if (ts.isImportSpecifier(element)) {
if (element.propertyName) {
propertyName = element.propertyName.getText();
// 这里要确保是as actions里的actions和node.name进行比较
return element
.getText()
.endsWith(` as ${node.getText()}`);
}
// 这里是import { actions } from "./Actions" 这样的形式
propertyName = element.name.getText();
return element.name.getText() === node.getText();
}
return false;
});
}
}
}
return false;
}) as ts.ImportDeclaration | undefined;
const declaration = declarations[0];
// 从当前文件的所有导入中找到对应的导入
const sourceFile = declaration.getSourceFile();
let propertyName = '';
const importDeclaration = sourceFile.statements.find((statement) => {
// 在这里找到import { actions } from "./Actions" 这样的形式
if (ts.isImportDeclaration(statement)) {
const moduleSpecifier = statement.moduleSpecifier;
if (ts.isStringLiteral(moduleSpecifier)) {
const imports = statement.importClause?.namedBindings;
// 如果导入了node.name
if (imports && ts.isNamedImports(imports)) {
// 这里需要注意如果是import { generalActions as actions } from "./Actions" 这样的形式要拿到as的内容和node.name进行比较
return imports.elements.some((element) => {
if (ts.isImportSpecifier(element)) {
if (element.propertyName) {
propertyName = element.propertyName.getText();
// 这里要确保是as actions里的actions和node.name进行比较
return element
.getText()
.endsWith(` as ${node.getText()}`);
}
// 这里是import { actions } from "./Actions" 这样的形式
propertyName = element.name.getText();
return element.name.getText() === node.getText();
}
return false;
});
}
}
}
return false;
}) as ts.ImportDeclaration | undefined;
// 这里对包内的genericActions做特殊处理
if (propertyName === "genericActions") {
return [
"count",
"stat",
"download",
"select",
"aggregate",
"create",
"remove",
"update",
];
}
// 这里对包内的genericActions做特殊处理
if (propertyName === 'genericActions') {
return [
'count',
'stat',
'download',
'select',
'aggregate',
'create',
'remove',
'update',
];
}
if (importDeclaration) {
// 得到导入的路径
const importPath = (
importDeclaration.moduleSpecifier as ts.StringLiteral
).text;
const currentSourceFile = node.getSourceFile();
const resolvedPath = resolveImportPath(
importPath,
dirname(currentSourceFile.fileName)
);
if (importDeclaration) {
// 得到导入的路径
const importPath = (
importDeclaration.moduleSpecifier as ts.StringLiteral
).text;
const currentSourceFile = node.getSourceFile();
const resolvedPath = resolveImportPath(
importPath,
dirname(currentSourceFile.fileName)
);
// 创建新的程序来解析导入的文件
const importProgram = ts.createProgram(
[resolvedPath],
program.getCompilerOptions()
);
const importSourceFile = importProgram.getSourceFile(resolvedPath);
// 创建新的程序来解析导入的文件
const importProgram = ts.createProgram(
[resolvedPath],
program.getCompilerOptions()
);
const importSourceFile = importProgram.getSourceFile(resolvedPath);
if (importSourceFile) {
let foundDeclaration: ts.Node | undefined;
ts.forEachChild(importSourceFile, (child) => {
if (ts.isVariableStatement(child)) {
const declaration = child.declarationList.declarations[0];
if (
ts.isIdentifier(declaration.name) &&
declaration.name.text === propertyName
) {
foundDeclaration = declaration;
}
} else if (
ts.isFunctionDeclaration(child) &&
child.name &&
child.name.text === propertyName
) {
foundDeclaration = child;
} else if (
ts.isExportAssignment(child) &&
ts.isIdentifier(child.expression) &&
child.expression.text === propertyName
) {
foundDeclaration = child;
}
});
if (importSourceFile) {
let foundDeclaration: ts.Node | undefined;
ts.forEachChild(importSourceFile, (child) => {
if (ts.isVariableStatement(child)) {
const declaration = child.declarationList.declarations[0];
if (
ts.isIdentifier(declaration.name) &&
declaration.name.text === propertyName
) {
foundDeclaration = declaration;
}
} else if (
ts.isFunctionDeclaration(child) &&
child.name &&
child.name.text === propertyName
) {
foundDeclaration = child;
} else if (
ts.isExportAssignment(child) &&
ts.isIdentifier(child.expression) &&
child.expression.text === propertyName
) {
foundDeclaration = child;
}
});
if (foundDeclaration) {
return evaluateNode(
importProgram,
foundDeclaration,
importProgram.getTypeChecker()
);
}
}
}
// 如果没有找到导入声明,则假设它是当前文件中的定义
return evaluateNode(program, declaration, typeChecker);
if (foundDeclaration) {
return evaluateNode(
importProgram,
foundDeclaration,
importProgram.getTypeChecker()
);
}
}
}
// 如果没有找到导入声明,则假设它是当前文件中的定义
return evaluateNode(program, declaration, typeChecker);
}
function evaluateNode(
program: ts.Program,
node: ts.Node,
typeChecker: ts.TypeChecker
program: ts.Program,
node: ts.Node,
typeChecker: ts.TypeChecker
): any {
if (ts.isObjectLiteralExpression(node)) {
return node.properties.reduce((obj: any, prop) => {
if (ts.isShorthandPropertyAssignment(prop)) {
// 得到标识符的名称
const name = prop.name.getText();
const evaluated = getEvaluateNodeForShorthandProperty(
program,
prop,
typeChecker
);
obj[name] = evaluated;
} else if (ts.isPropertyAssignment(prop)) {
const name = prop.name.getText();
obj[name] = evaluateNode(
program,
prop.initializer,
typeChecker
);
}
return obj;
}, {});
} else if (ts.isArrayLiteralExpression(node)) {
return node.elements.map((element) =>
evaluateNode(program, element, typeChecker)
);
} else if (ts.isStringLiteral(node)) {
return node.text;
} else if (ts.isNumericLiteral(node)) {
return Number(node.text);
} else if (node.kind === ts.SyntaxKind.TrueKeyword) {
return true;
} else if (node.kind === ts.SyntaxKind.FalseKeyword) {
return false;
} else if (ts.isIdentifier(node)) {
// 处理导入的标识符
const symbol = typeChecker.getSymbolAtLocation(node);
if (symbol && symbol.valueDeclaration) {
return evaluateNode(program, symbol.valueDeclaration, typeChecker);
}
} else if (ts.isVariableDeclaration(node) && node.initializer) {
// 处理变量声明
return evaluateNode(program, node.initializer, typeChecker);
}
// 对于其他类型的节点,可能需要进一步处理
return undefined;
if (ts.isObjectLiteralExpression(node)) {
return node.properties.reduce((obj: any, prop) => {
if (ts.isShorthandPropertyAssignment(prop)) {
// 得到标识符的名称
const name = prop.name.getText();
const evaluated = getEvaluateNodeForShorthandProperty(
program,
prop,
typeChecker
);
obj[name] = evaluated;
} else if (ts.isPropertyAssignment(prop)) {
const name = prop.name.getText();
obj[name] = evaluateNode(
program,
prop.initializer,
typeChecker
);
}
return obj;
}, {});
} else if (ts.isArrayLiteralExpression(node)) {
return node.elements.map((element) =>
evaluateNode(program, element, typeChecker)
);
} else if (ts.isStringLiteral(node)) {
return node.text;
} else if (ts.isNumericLiteral(node)) {
return Number(node.text);
} else if (node.kind === ts.SyntaxKind.TrueKeyword) {
return true;
} else if (node.kind === ts.SyntaxKind.FalseKeyword) {
return false;
} else if (ts.isIdentifier(node)) {
// 处理导入的标识符
const symbol = typeChecker.getSymbolAtLocation(node);
if (symbol && symbol.valueDeclaration) {
return evaluateNode(program, symbol.valueDeclaration, typeChecker);
}
} else if (ts.isVariableDeclaration(node) && node.initializer) {
// 处理变量声明
return evaluateNode(program, node.initializer, typeChecker);
}
// 对于其他类型的节点,可能需要进一步处理
return undefined;
}
function parseDescFile(
filePath: string,
program: ts.Program
filePath: string,
program: ts.Program
): StorageDesc<EntityShape> | null {
const sourceFile = program.getSourceFile(filePath);
if (!sourceFile) {
vscode.window.showWarningMessage(`无法解析文件: ${filePath}`);
return null;
}
const sourceFile = program.getSourceFile(filePath);
if (!sourceFile) {
vscode.window.showWarningMessage(`无法解析文件: ${filePath}`);
return null;
}
const typeChecker = program.getTypeChecker();
let descObject: StorageDesc<EntityShape> | null = null;
const typeChecker = program.getTypeChecker();
let descObject: StorageDesc<EntityShape> | null = null;
ts.forEachChild(sourceFile, (node) => {
if (ts.isVariableStatement(node)) {
const declaration = node.declarationList.declarations[0];
if (
ts.isIdentifier(declaration.name) &&
declaration.name.text === "desc"
) {
if (
declaration.initializer &&
ts.isObjectLiteralExpression(declaration.initializer)
) {
descObject = evaluateNode(
program,
declaration.initializer,
typeChecker
);
}
}
}
});
ts.forEachChild(sourceFile, (node) => {
if (ts.isVariableStatement(node)) {
const declaration = node.declarationList.declarations[0];
if (
ts.isIdentifier(declaration.name) &&
declaration.name.text === 'desc'
) {
if (
declaration.initializer &&
ts.isObjectLiteralExpression(declaration.initializer)
) {
descObject = evaluateNode(
program,
declaration.initializer,
typeChecker
);
}
}
}
});
return descObject;
return descObject;
}
export const analyzeOakAppDomain = (path: string) => {
const storageFile = join(path, "Storage.ts");
// 开始分析先清空entityDict
Object.keys(entityDict).forEach((key) => {
delete entityDict[key];
});
if (!fs.existsSync(storageFile)) {
vscode.window.showErrorMessage(
"Storage.ts文件不存在请先尝试make:domain"
);
return;
}
const storageFile = join(path, 'Storage.ts');
const program = ts.createProgram([storageFile], {});
const sourceFile = program.getSourceFile(storageFile);
if (!fs.existsSync(storageFile)) {
vscode.window.showErrorMessage(
'Storage.ts文件不存在请先尝试make:domain'
);
return;
}
if (!sourceFile) {
vscode.window.showErrorMessage("无法解析Storage.ts文件");
return;
}
const program = ts.createProgram([storageFile], {});
const sourceFile = program.getSourceFile(storageFile);
let storageSchemaNode: ts.Node | undefined;
if (!sourceFile) {
vscode.window.showErrorMessage('无法解析Storage.ts文件');
return;
}
ts.forEachChild(sourceFile, (node) => {
if (ts.isVariableStatement(node)) {
const declaration = node.declarationList.declarations[0];
if (
ts.isIdentifier(declaration.name) &&
declaration.name.text === "storageSchema"
) {
storageSchemaNode = declaration.initializer;
}
}
});
let storageSchemaNode: ts.Node | undefined;
if (
!storageSchemaNode ||
!ts.isObjectLiteralExpression(storageSchemaNode)
) {
vscode.window.showErrorMessage("无法找到storageSchema或格式不正确");
return;
}
ts.forEachChild(sourceFile, (node) => {
if (ts.isVariableStatement(node)) {
const declaration = node.declarationList.declarations[0];
if (
ts.isIdentifier(declaration.name) &&
declaration.name.text === 'storageSchema'
) {
storageSchemaNode = declaration.initializer;
}
}
});
const importMap: { [key: string]: string } = {};
if (
!storageSchemaNode ||
!ts.isObjectLiteralExpression(storageSchemaNode)
) {
vscode.window.showErrorMessage('无法找到storageSchema或格式不正确');
return;
}
ts.forEachChild(sourceFile, (node) => {
if (ts.isImportDeclaration(node)) {
const moduleSpecifier = node.moduleSpecifier;
if (ts.isStringLiteral(moduleSpecifier)) {
const importPath = moduleSpecifier.text;
const importClause = node.importClause;
if (
importClause &&
importClause.namedBindings &&
ts.isNamedImports(importClause.namedBindings)
) {
importClause.namedBindings.elements.forEach((element) => {
if (
element.propertyName &&
element.propertyName.text === "desc"
) {
importMap[element.name.text] = importPath;
}
});
}
}
}
});
const importMap: { [key: string]: string } = {};
storageSchemaNode.properties.forEach((prop) => {
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
const entityName = prop.name.text;
if (ts.isIdentifier(prop.initializer)) {
const descName = prop.initializer.text;
const importPath = importMap[descName];
if (importPath) {
const resolvedPath = resolveImportPath(
importPath,
dirname(storageFile)
);
const descObject = parseDescFile(resolvedPath, program);
if (descObject) {
entityDict[entityName] = descObject;
}
} else {
vscode.window.showWarningMessage(
`未找到 ${descName} 的导入路径`
);
}
} else {
vscode.window.showWarningMessage(
`${entityName} 的值不是预期的标识符`
);
}
}
});
ts.forEachChild(sourceFile, (node) => {
if (ts.isImportDeclaration(node)) {
const moduleSpecifier = node.moduleSpecifier;
if (ts.isStringLiteral(moduleSpecifier)) {
const importPath = moduleSpecifier.text;
const importClause = node.importClause;
if (
importClause &&
importClause.namedBindings &&
ts.isNamedImports(importClause.namedBindings)
) {
importClause.namedBindings.elements.forEach((element) => {
if (
element.propertyName &&
element.propertyName.text === 'desc'
) {
importMap[element.name.text] = importPath;
}
});
}
}
}
});
console.log("entityDict:", entityDict);
storageSchemaNode.properties.forEach((prop) => {
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
const entityName = prop.name.text;
if (ts.isIdentifier(prop.initializer)) {
const descName = prop.initializer.text;
const importPath = importMap[descName];
if (importPath) {
const resolvedPath = resolveImportPath(
importPath,
dirname(storageFile)
);
const descObject = parseDescFile(resolvedPath, program);
if (descObject) {
entityDict[entityName] = descObject;
}
} else {
vscode.window.showWarningMessage(
`未找到 ${descName} 的导入路径`
);
}
} else {
vscode.window.showWarningMessage(
`${entityName} 的值不是预期的标识符`
);
}
}
});
console.log('entityDict:', entityDict);
};

24
src/utils/status.ts Normal file
View File

@ -0,0 +1,24 @@
const globalStatus = {
isLoadingEntities: false,
};
export const setLoadingEntities = (loading: boolean) => {
globalStatus.isLoadingEntities = loading;
};
export const isLoadingEntities = () => {
return globalStatus.isLoadingEntities;
};
export const waitUntilEntitiesLoaded = async () => {
return new Promise<boolean>((resolve) => {
const check = () => {
if (globalStatus.isLoadingEntities) {
setTimeout(check, 100);
} else {
resolve(true);
}
};
check();
});
};

4
src/utils/stringUtils.ts Normal file
View File

@ -0,0 +1,4 @@
// 将驼峰命名的entityName转换为大写开头
export const toUpperFirst = (entityName: string) => {
return entityName.charAt(0).toUpperCase() + entityName.slice(1);
};