Skip to content

Commit

Permalink
feat: add isError and errorMessage to File & add isAstSynced to Module (
Browse files Browse the repository at this point in the history
#190)

* feat: update file without sync ast

* feat: parse and display file errors

* fix: update

* fix: refactor toolbar

* fix: add activeViews config to toolbarItem

* fix: update icon
  • Loading branch information
wwsun authored Jul 30, 2024
1 parent 82bc5db commit 8dd289c
Show file tree
Hide file tree
Showing 25 changed files with 325 additions and 160 deletions.
10 changes: 5 additions & 5 deletions apps/playground/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,19 +113,19 @@ export default function App() {
actions={
<Box px="l">
<Toolbar>
<Toolbar.Item key="routeSwitch" placement="left" />
<Toolbar.Item key="addPage" placement="left">
<Toolbar.Item key="routeSwitch" placement="left" activeViews={['design']} />
<Toolbar.Item key="addPage" placement="left" activeViews={['design']}>
<Action
tooltip="添加页面"
shape="outline"
icon={<PlusOutlined />}
onClick={() => setShowNewPageModal(true)}
/>
</Toolbar.Item>
<Toolbar.Item key="history" placement="left" />
<Toolbar.Item key="preview" placement="left" />
<Toolbar.Item key="history" placement="left" activeViews={['design']} />
<Toolbar.Item key="preview" placement="left" activeViews={['design']} />
<Toolbar.Item key="togglePanel" placement="right" activeViews={['design']} />
<Toolbar.Item key="modeSwitch" placement="right" />
<Toolbar.Item key="togglePanel" placement="right" />
<Toolbar.Separator />
<Toolbar.Item placement="right">
<Space>
Expand Down
20 changes: 4 additions & 16 deletions packages/core/src/helpers/ast/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import generator, { GeneratorOptions } from '@babel/generator';
import * as t from '@babel/types';
import { logger, wrapCode } from '@music163/tango-helpers';
import { Dict, logger, wrapCode } from '@music163/tango-helpers';
import { formatCode } from '../string';

const defaultGeneratorOptions: GeneratorOptions = {
Expand All @@ -25,19 +25,7 @@ export function ast2code(ast: t.Node, options: GeneratorOptions = defaultGenerat
return code;
}

const bracketPattern = /^\(.+\)$/s;

/**
* 是否被 () 包裹
*
* @example ({ foo: 'foo' }) -> true
* @example { foo: 'foo' } -> false
*
* @param str 目标字符串
*/
function isWrappingWithBrackets(str: string) {
return bracketPattern.test(str);
}
const bracketPattern = /^\(.+\)$/;

/**
* 将表达式生成为块级代码
Expand All @@ -54,7 +42,7 @@ export function expression2code(node: t.Expression) {

const isWrappingExpression = t.isObjectExpression(node) || t.isFunctionExpression(node);

if (isWrappingExpression && isWrappingWithBrackets(ret)) {
if (isWrappingExpression && bracketPattern.test(ret)) {
// 如果是对象,输出包含 ({}),则去掉首尾的括号
ret = ret.slice(1, -1);
}
Expand Down Expand Up @@ -195,7 +183,7 @@ export function node2value(node: t.Node, isWrapCode = true): any {
);
if (isSimpleObject) {
// simple object: { key1, key2, key3 }
ret = node.properties.reduce((prev, propertyNode) => {
ret = node.properties.reduce<Dict>((prev, propertyNode) => {
if (propertyNode.type === 'ObjectProperty') {
const key = keyNode2value(propertyNode.key);
const value = node2value(propertyNode.value, isWrapCode);
Expand Down
8 changes: 2 additions & 6 deletions packages/core/src/helpers/ast/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,7 @@ export function isValidExpressionCode(code: string) {
* @returns
*/
export function code2ast(code: string): t.File {
try {
return parse(code, babelParserConfig);
} catch (err) {
logger.error('[code2ast failed!]', err);
}
return parse(code, babelParserConfig);
}

/**
Expand Down Expand Up @@ -175,7 +171,7 @@ export function value2node(
* 将 js 普通对象解析为 t.Node
*/
export function object2node(
obj: object,
obj: Dict,
getValueNode: (value: any, key?: string) => t.Expression = value2node,
) {
if (!isPlainObject(obj)) {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/helpers/ast/traverse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1009,15 +1009,15 @@ export function serviceConfig2Node(payload: object) {
});
}

export function updateServiceConfigToServiceFile(ast: t.File, config: Dict<object>) {
export function updateServiceConfigToServiceFile(ast: t.File, config: Dict<Dict>) {
traverse(ast, {
CallExpression(path) {
const calleeName = keyNode2value(path.node.callee) as string;
if (isDefineService(calleeName) && path.node.arguments.length) {
const configNode = path.node.arguments[0];

if (t.isObjectExpression(configNode)) {
const newPropertiesNodeMap = Object.keys(config).reduce((properties, key) => {
const newPropertiesNodeMap = Object.keys(config).reduce<Dict>((properties, key) => {
const serviceConfig = config[key];
const property = t.objectProperty(t.identifier(key), serviceConfig2Node(serviceConfig));
properties[key] = property;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/models/component-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class TangoComponentsEntryModule extends TangoModule {

constructor(workspace: IWorkspace, props: IFileConfig) {
super(workspace, props, false);
this.update(props.code, false, false);
this.update(props.code, true, false);
makeObservable(this, {
_code: observable,
_cleanCode: observable,
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/models/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ export class TangoFile {
*/
lastModified: number;

/**
* 文件解析是否出错
*/
isError: boolean;

/**
* 文件解析错误消息
*/
errorMessage: string;

_code: string;
_cleanCode: string;

Expand All @@ -40,6 +50,7 @@ export class TangoFile {
this.filename = props.filename;
this.type = props.type;
this.lastModified = Date.now();
this.isError = false;

// 这里主要是为了解决 umi ts 编译错误的问题,@see https://github.com/umijs/umi/issues/7594
if (isSyncCode) {
Expand Down
20 changes: 18 additions & 2 deletions packages/core/src/models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
IPageConfigData,
IImportSpecifierSourceData,
IImportSpecifierData,
IFileError,
} from '../types';
import { TangoFile, TangoJsonFile } from './file';
import { TangoRouteModule } from './route-module';
Expand Down Expand Up @@ -192,7 +193,18 @@ export interface IWorkspace {
renameFile: (oldFilename: string, newFilename: string) => void;
renameFolder: (oldFoldername: string, newFoldername: string) => void;

updateFile: (filename: string, code: string, shouldFormatCode?: boolean) => void;
/**
* 更新文件
* @param filename 文件名
* @param code 代码
* @param isSyncAst 是否同步 ast
*/
updateFile: (filename: string, code: string, isSyncAst?: boolean) => void;

/**
* 检查并同步文件的 ast
*/
syncFiles: () => void;

listFiles: () => Record<string, string>;
getFile: (filename: string) => TangoFile;
Expand Down Expand Up @@ -298,8 +310,12 @@ export interface IWorkspace {
get bizComps(): string[];
get baseComps(): string[];
get localComps(): string[];
get fileErrors(): IFileError[];
/**
* 是否是合法的项目
* 是否是有效的项目
* - 包含 tango.config.json
* - 包含视图模块
* - 没有文件错误
*/
get isValid(): boolean;
}
78 changes: 60 additions & 18 deletions packages/core/src/models/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import { IWorkspace } from './interfaces';
export class TangoModule extends TangoFile {
ast: t.File;

/**
* ast 是否与 code 保持同步
*/
isAstSynced: boolean;

/**
* 导入的依赖列表
*/
Expand All @@ -33,23 +38,53 @@ export class TangoModule extends TangoFile {
/**
* 基于最新的 ast 进行同步
* @param code 如果传入 code,则基于 code 进行同步
* @param isFormatCode 是否格式化代码
* @param refreshWorkspace 是否刷新 workspace
* @param isSyncAst 是否同步 ast
* @param isRefreshWorkspace 是否刷新 workspace
*/
update(code?: string, isFormatCode = true, refreshWorkspace = true) {
update(code?: string, isSyncAst = true, isRefreshWorkspace = true) {
this.lastModified = Date.now();
if (isNil(code)) {
this._syncByAst();
} else {
this._syncByCode(code, isFormatCode);
}

this._analysisAst();

this.workspace.onFilesChange([this.filename]);
try {
if (isNil(code)) {
this._syncByAst();
} else {
this._syncByCode(code, isSyncAst);
}

if (isSyncAst) {
this._analysisAst();
}

this.isAstSynced = isSyncAst;
this.isError = false;
this.errorMessage = undefined;

this.workspace.onFilesChange([this.filename]);
if (isRefreshWorkspace) {
this.workspace.refresh([this.filename]);
}
} catch (err: any) {
this.isError = true;
this.errorMessage = err.message;
}
}

if (refreshWorkspace) {
this.workspace.refresh([this.filename]);
/**
* 基于当前的代码重新生成 ast
*/
updateAst() {
if (!this.isAstSynced) {
try {
this.ast = code2ast(this._code);
this._analysisAst();
this.isAstSynced = true;
this.isError = false;
this.errorMessage = undefined;
} catch (err: any) {
this.isAstSynced = false;
this.isError = true;
this.errorMessage = err.message;
}
}
}

Expand All @@ -75,22 +110,26 @@ export class TangoModule extends TangoFile {
/**
* 基于输入的源码进行同步
* @param code 源码
* @param isFormatCode 是否格式化代码
* @param isSyncAst 是否同步 ast
* @returns
*/
_syncByCode(code: string, isFormatCode = true) {
_syncByCode(code: string, isSyncAst = true) {
if (code === this._code) {
return;
}

// 提前格式化代码
if (isFormatCode) {
try {
code = formatCode(code);
} catch (err) {
// err ignored, format code failed
}

this._code = code;
this._cleanCode = code;
this.ast = code2ast(code);
if (isSyncAst) {
this.ast = code2ast(code);
}
}

_analysisAst() {
Expand All @@ -105,14 +144,17 @@ export class TangoModule extends TangoFile {
export class TangoJsModule extends TangoModule {
constructor(workspace: IWorkspace, props: IFileConfig) {
super(workspace, props, false);
this.update(props.code, false, false);
this.update(props.code, true, false);

makeObservable(this, {
_code: observable,
_cleanCode: observable,
isError: observable,
errorMessage: observable,
code: computed,
cleanCode: computed,
update: action,
updateAst: action,
});
}
}
6 changes: 5 additions & 1 deletion packages/core/src/models/route-module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { action, computed, makeObservable, observable, toJS } from 'mobx';
import { logger } from '@music163/tango-helpers';
import {
traverseRouteFile,
addRouteToRouteFile,
Expand Down Expand Up @@ -27,10 +28,13 @@ export class TangoRouteModule extends TangoModule {
_routes: observable,
_code: observable,
_cleanCode: observable,
isError: observable,
errorMessage: observable,
routes: computed,
code: computed,
cleanCode: computed,
update: action,
updateAst: action,
});
}

Expand Down Expand Up @@ -74,7 +78,7 @@ export class TangoRouteModule extends TangoModule {
*/
removeRoute(route: string) {
if (route === '/') {
console.warn('index route should not be removed!');
logger.warn('index route should not be removed!');
return;
}
const record = this.getRouteByRoutePath(route);
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/models/service-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,14 @@ export class TangoServiceModule extends TangoModule {
_baseConfig: observable,
_code: observable,
_cleanCode: observable,
isError: observable,
errorMessage: observable,
serviceFunctions: computed,
baseConfig: computed,
cleanCode: computed,
code: computed,
update: action,
updateAst: action,
});
}

Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/models/store-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@ export class TangoStoreEntryModule extends TangoModule {
_stores: observable,
_code: observable,
_cleanCode: observable,
isError: observable,
errorMessage: observable,
stores: computed,
code: computed,
cleanCode: computed,
update: action,
updateAst: action,
});
}

Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/models/view-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,14 @@ export class TangoViewModule extends TangoModule implements IViewFile {
_code: observable,
_cleanCode: observable,

isError: observable,
errorMessage: observable,

code: computed,
cleanCode: computed,

update: action,
updateAst: action,
});
}

Expand Down Expand Up @@ -320,7 +324,7 @@ export class TangoViewModule extends TangoModule implements IViewFile {
updateNodeAttributes(nodeId: string, config: Record<string, any>, relatedImports?: string[]) {
if (relatedImports && relatedImports.length) {
// 导入依赖的组件
const newImportData = relatedImports.reduce((prev, name) => {
const newImportData = relatedImports.reduce<Dict<IImportSpecifierData[]>>((prev, name) => {
const proto = this.workspace.getPrototype(name);
const { source, specifiers } = prototype2importDeclarationData(proto, this.filename);
const existSpecifiers: IImportSpecifierData[] = prev[source];
Expand Down
Loading

0 comments on commit 8dd289c

Please sign in to comment.