Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

重构代码同步模式 #190

Merged
merged 6 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading