Skip to content

Commit 89c9405

Browse files
Merge pull request alibaba#105 from wangshihao111/feat/multiple-editor
同步代码
2 parents 4eace4c + 1349762 commit 89c9405

File tree

50 files changed

+1222
-346
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1222
-346
lines changed

packages/plugin-multiple-editor/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ await plugins.register(plugin);
5858
4. 如需在 setter 内适用类型定义,请开启 base-editor 的单例模式,仅需在应用入口处调用如下方法即可:
5959
5. 如果低码项目有使用出码,则需对出码进行定制,将 _sourceCodeMap 中的文件生成到项目中,并对文件的引用进行处理,具体处理方式可自行组织
6060

61+
__使用单例模式__
6162
```ts
6263
import { configure } from '@alilc/lowcode-plugin-base-monaco-editor';
6364
configure({ singleton: true });

packages/plugin-multiple-editor/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"release": "fast-publish"
2525
},
2626
"dependencies": {
27-
"@alilc/lowcode-types": "^1.0.3"
27+
"@alilc/lowcode-types": "^1.0.3",
28+
"@babel/types": "^7.16.0"
2829
},
2930
"devDependencies": {
3031
"@alifd/next": "^1.25.29",
@@ -122,6 +123,9 @@
122123
"webpackbar": "^5.0.2",
123124
"workbox-webpack-plugin": "^6.4.1"
124125
},
126+
"resolutions": {
127+
"@types/react": "^17.0.2"
128+
},
125129
"browserslist": {
126130
"production": [
127131
">0.2%",

packages/plugin-multiple-editor/src/Context.tsx

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
sortDir,
2222
} from './utils/files';
2323
import { editorController } from './Controller';
24-
import { getDefaultFileList } from './MultipleFileEditor/util';
2524

2625
export type CurrentFile = {
2726
file?: File;
@@ -39,11 +38,13 @@ export interface EditorContextType {
3938
updateFileTreeByPath(
4039
path: string[],
4140
target: Dir | File,
42-
operation: 'delete' | 'add'
41+
operation: 'delete' | 'add' | 'rename',
42+
newName?: string
4343
): void;
4444
selectFile(file: File | string, path: string[]): void;
4545
selectFileByName(name: string): void;
4646
updateState(state: Partial<StoreState>): void;
47+
updateCurrentFile(content: string): void;
4748
initialFileMap: Record<string, string>;
4849
extraLibs: { path: string; content: string }[];
4950
}
@@ -86,7 +87,9 @@ export const EditorProvider: FC<{
8687
() => editorController.getCodeTemp()?._sourceCodeMap.files || {},
8788
[]
8889
);
89-
const initFileTree = fileMap ? fileMapToTree(fileMap) : new Dir('/');
90+
const initFileTree = fileMap
91+
? fileMapToTree(fileMap)
92+
: new Dir('/', [], [], '');
9093
const initState: StoreState = {
9194
declarations: '',
9295
extraLibs: [],
@@ -103,7 +106,7 @@ export const EditorProvider: FC<{
103106
if (typeof file === 'string') {
104107
const { fileTree } = state;
105108
const targetFile = getFileByPath(fileTree, file, path);
106-
finalFile = targetFile || new File('index.js', '');
109+
finalFile = targetFile || new File('index.js', '', 'index.js');
107110
} else {
108111
finalFile = file;
109112
}
@@ -123,12 +126,14 @@ export const EditorProvider: FC<{
123126
updateFileTree(tree: Dir) {
124127
dispatch({ type: 'update', payload: { fileTree: sortDir(tree) } });
125128
},
126-
updateFileTreeByPath(path, target, operation) {
129+
updateFileTreeByPath(path, target, operation, newName) {
127130
const { fileTree } = state;
128131
if (operation === 'add') {
129132
insertNodeToTree(fileTree, path, target);
130133
} else if (operation === 'delete') {
131134
deleteNodeOnTree(fileTree, path, target);
135+
} else if (operation === 'rename') {
136+
target.name = newName as string;
132137
}
133138
const payload: Partial<StoreState> = { fileTree: sortDir(fileTree) };
134139
// 新增文件时,选中当前文件
@@ -147,12 +152,27 @@ export const EditorProvider: FC<{
147152
updateState(state: Partial<StoreState>) {
148153
dispatch({ type: 'update', payload: state });
149154
},
155+
updateCurrentFile(content) {
156+
if (this.currentFile.file) {
157+
this.currentFile.file.content = content;
158+
this.updateState({
159+
currentFile: {
160+
...this.currentFile,
161+
file: {
162+
...this.currentFile.file,
163+
content,
164+
},
165+
},
166+
});
167+
// this.updateFileTree({...this.fileTree});
168+
}
169+
},
150170
}),
151-
[fileMap, selectFile, softSave, state]
171+
[fileMap, mode, selectFile, softSave, state]
152172
);
153173
useEffect(() => {
154174
const off = editorController.onSourceCodeChange((codeMap) => {
155-
const fileTree = fileMapToTree(codeMap) || new Dir('/');
175+
const fileTree = fileMapToTree(codeMap) || new Dir('/', [], [], '');
156176
dispatch({ type: 'update', payload: { fileTree } });
157177
const targetFile = getFileByPath(fileTree, 'index.js', []);
158178
setTimeout(() => {

packages/plugin-multiple-editor/src/Controller.ts

Lines changed: 93 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import type { Project, Event } from '@alilc/lowcode-shell';
2-
import { skeleton, common } from '@alilc/lowcode-engine';
2+
import { IPublicTypeProjectSchema } from '@alilc/lowcode-types';
3+
import { skeleton } from '@alilc/lowcode-engine';
34
import {
45
beautifyCSS,
56
compatGetSourceCodeMap,
7+
fileMapToTree,
68
getConstructorContent,
79
getDefaultDeclaration,
810
getInitFuncContent,
911
lintIndex,
1012
treeToMap,
1113
} from './utils';
1214
import { FunctionEventParams, Monaco, ObjectType } from './types';
13-
import type { editor } from 'monaco-editor';
15+
import { common } from '@alilc/lowcode-engine';
16+
import { editor } from 'monaco-editor';
1417
import {
1518
addFunction,
1619
focusByFunctionName,
@@ -21,13 +24,14 @@ import { EditorContextType } from './Context';
2124
import { Message } from '@alifd/next';
2225
import { getMethods } from './utils/get-methods';
2326
import { EditorHook, HookKeys } from './EditorHook';
24-
import { Service } from './Service';
27+
import { PluginHooks, Service } from './Service';
28+
import { MonacoSuggestions } from './MonacoSuggestions';
2529

2630
export * from './EditorHook';
2731

2832
export interface EditorControllerState {
2933
declarationsMap: Record<string, string>;
30-
extraLibs: Array<{ path: string; content: string }>;
34+
extraLibs: { path: string; content: string }[];
3135
}
3236

3337
export type HookHandleFn<T = any> = (fn: T) => () => void;
@@ -49,7 +53,9 @@ export class EditorController extends EditorHook {
4953

5054
defaultFiles: ObjectType<string>;
5155

52-
monaco?: Monaco;
56+
useLess?: boolean;
57+
58+
public monaco?: Monaco;
5359

5460
private codeTemp?: CodeTemp;
5561

@@ -59,23 +65,30 @@ export class EditorController extends EditorHook {
5965

6066
private state: EditorControllerState;
6167

62-
codeEditor?: editor.IStandaloneCodeEditor;
68+
public codeEditor?: editor.IStandaloneCodeEditor;
69+
70+
public codeEditorCtx?: EditorContextType;
71+
72+
public service!: Service;
6373

64-
private codeEditorCtx?: EditorContextType;
74+
private loadMonacoPromise?: Promise<any>;
6575

66-
service!: Service;
76+
private monacoSuggestions: MonacoSuggestions;
6777

68-
onImportSchema: HookHandleFn<(schema: any) => void | Promise<void>> =
69-
this.hookFactory(HookKeys.onImport);
78+
public onImportSchema: HookHandleFn<
79+
(schema: IPublicTypeProjectSchema) => void | Promise<void>
80+
> = this.hookFactory(HookKeys.onImport);
7081

71-
onSourceCodeChange: HookHandleFn<(code: any) => void> = this.hookFactory(
72-
HookKeys.onSourceCodeChange
73-
);
82+
public onSourceCodeChange: HookHandleFn<(code: any) => void> =
83+
this.hookFactory(HookKeys.onSourceCodeChange);
7484

75-
onEditCodeChange: HookHandleFn<
85+
public onEditCodeChange: HookHandleFn<
7686
(code: { content: string; file: string }) => void
7787
> = this.hookFactory(HookKeys.onEditCodeChange);
7888

89+
public onMonacoLoaded: HookHandleFn<(monaco: Monaco) => void> =
90+
this.hookFactory(HookKeys.onMonacoLoaded);
91+
7992
constructor() {
8093
super();
8194
this.state = {
@@ -85,6 +98,21 @@ export class EditorController extends EditorHook {
8598
this.listeners = [];
8699
this.defaultFiles = {};
87100
this.extraLibMap = new Map();
101+
this.monacoSuggestions = new MonacoSuggestions(this);
102+
}
103+
104+
async initMonaco() {
105+
if (!this.monaco) {
106+
if (!this.loadMonacoPromise) {
107+
const { getMonaco } = await import(
108+
'@alilc/lowcode-plugin-base-monaco-editor'
109+
);
110+
this.loadMonacoPromise = getMonaco();
111+
}
112+
this.monaco = await this.loadMonacoPromise;
113+
this.triggerHook(HookKeys.onMonacoLoaded, this.monaco);
114+
this.service.triggerHook(PluginHooks.onMonacoLoaded, this.monaco);
115+
}
88116
}
89117

90118
init(project: Project, editor: Event, service: Service) {
@@ -94,6 +122,7 @@ export class EditorController extends EditorHook {
94122
this.setupEventListeners();
95123
this.initCodeTempBySchema(this.getSchema(true));
96124
this.triggerHook(HookKeys.onImport, this.getSchema(true));
125+
this.initMonaco();
97126
}
98127

99128
initCodeEditor(
@@ -102,6 +131,7 @@ export class EditorController extends EditorHook {
102131
) {
103132
this.codeEditor = codeEditor;
104133
this.codeEditorCtx = ctx;
134+
this.monacoSuggestions.init();
105135
}
106136

107137
setCodeTemp(code: any | ((old: CodeTemp) => CodeTemp)) {
@@ -122,7 +152,7 @@ export class EditorController extends EditorHook {
122152
this.applyLibs();
123153
}
124154

125-
addComponentDeclarations(list: Array<[string, string]> = []) {
155+
addComponentDeclarations(list: [string, string][] = []) {
126156
for (const [key, dec] of list) {
127157
this.state.declarationsMap[key] = dec;
128158
}
@@ -131,7 +161,7 @@ export class EditorController extends EditorHook {
131161
}
132162

133163
private publishExtraLib() {
134-
const libs: Array<{ path: string; content: string }> = [];
164+
const libs: { path: string; content: string }[] = [];
135165
this.extraLibMap.forEach((content, path) => libs.push({ content, path }));
136166
this.state.extraLibs = libs;
137167
this.publish();
@@ -151,10 +181,7 @@ export class EditorController extends EditorHook {
151181

152182
private async applyLibs() {
153183
if (!this.monaco) {
154-
const { getMonaco } = await import(
155-
'@alilc/lowcode-plugin-base-monaco-editor'
156-
);
157-
this.monaco = await getMonaco(undefined) as any;
184+
await this.initMonaco();
158185
}
159186
const decStr = Object.keys(this.state.declarationsMap).reduce(
160187
(v, k) => `${v}\n${k}: ${this.state.declarationsMap[k]};\n`,
@@ -173,7 +200,7 @@ export class EditorController extends EditorHook {
173200
});
174201
}
175202

176-
getSchema(pure?: boolean): any {
203+
getSchema(pure?: boolean): IPublicTypeProjectSchema {
177204
const schema = this.project.exportSchema(
178205
common.designerCabin.TransformStage.Save
179206
);
@@ -182,15 +209,21 @@ export class EditorController extends EditorHook {
182209
? treeToMap(this.codeEditorCtx.fileTree)
183210
: this.codeTemp?._sourceCodeMap.files; // 获取最新的fileMap
184211
if (fileMap && !pure) {
185-
if (!this.compileSourceCode(fileMap)) {
186-
throw new Error('编译失败');
212+
try {
213+
if (!this.compileSourceCode(fileMap)) {
214+
// 下面会导致整个页面挂掉,先作为弱依赖,给个提示
215+
throw new Error('编译失败');
216+
}
217+
Object.assign(schema.componentsTree[0], this.codeTemp);
218+
} catch (error) {
219+
console.error(error);
220+
Message.error('源码编译失败,请返回修改');
187221
}
188-
Object.assign(schema.componentsTree[0], this.codeTemp);
189222
}
190223
return schema;
191224
}
192225

193-
importSchema(schema: any) {
226+
importSchema(schema: IPublicTypeProjectSchema) {
194227
this.project.importSchema(schema);
195228
this.initCodeTempBySchema(schema);
196229
this.triggerHook(HookKeys.onImport, schema);
@@ -217,23 +250,22 @@ export class EditorController extends EditorHook {
217250
};
218251
}
219252

220-
initCodeTempBySchema(schema: any) {
253+
public initCodeTempBySchema(schema: IPublicTypeProjectSchema) {
221254
const componentSchema = schema.componentsTree[0] || {};
222-
const { css, methods, state, lifeCycles } = componentSchema;
255+
const { css, methods, state, lifeCycles } = componentSchema as any;
223256
const codeMap = (componentSchema as any)._sourceCodeMap;
224257
const defaultFileMap = {
225258
...this.defaultFiles,
226-
...getDefaultFileList(schema),
259+
...getDefaultFileList(schema, this.useLess),
227260
};
228-
const compatMap = compatGetSourceCodeMap(codeMap);
261+
const compatMap = compatGetSourceCodeMap(codeMap, defaultFileMap);
229262
this.codeTemp = {
230263
css,
231264
methods,
232265
state,
233266
lifeCycles,
234267
_sourceCodeMap: {
235268
...compatMap,
236-
files: defaultFileMap,
237269
},
238270
};
239271
}
@@ -253,8 +285,8 @@ export class EditorController extends EditorHook {
253285
this.editor?.on('common:codeEditor.addFunction', ((
254286
params: FunctionEventParams
255287
) => {
288+
this.codeEditorCtx?.selectFile('index.js', []);
256289
setTimeout(() => {
257-
this.codeEditorCtx?.selectFile('index.js', []);
258290
if (this.monaco && this.codeEditor) {
259291
addFunction(this.codeEditor, params, this.monaco);
260292
}
@@ -306,7 +338,20 @@ export class EditorController extends EditorHook {
306338
pageNode.state = state;
307339
pageNode.methods = methods;
308340
pageNode.lifeCycles = lifeCycles;
309-
pageNode.css = beautifyCSS(fileMap['index.css'] || '', {});
341+
const lessContent = fileMap['index.less'];
342+
// 没有less文件,走之前的逻辑
343+
if (!lessContent) {
344+
pageNode.css = beautifyCSS(fileMap['index.css'] || '', {});
345+
}
346+
if (this.useLess && lessContent) {
347+
window.less?.render(lessContent, {}, (err: any, output: any) => {
348+
if (err) {
349+
Message.error('less 编译失败');
350+
console.error(err);
351+
}
352+
pageNode.css = output?.css || '';
353+
});
354+
}
310355
if (lifeCycles.constructor === {}.constructor) {
311356
lifeCycles.constructor = {
312357
originalCode: 'function constructor() { }',
@@ -341,10 +386,25 @@ export class EditorController extends EditorHook {
341386
return true;
342387
}
343388

344-
resetSaveStatus() {
389+
public resetSaveStatus() {
345390
this.codeEditorCtx?.updateState({ modifiedKeys: [] });
346391
}
347392

393+
// 添加一堆文件
394+
public addFiles(fileMap: ObjectType<string>) {
395+
if (!Object.keys(fileMap || {}).length || !this.codeEditorCtx?.fileTree) {
396+
return;
397+
}
398+
const subTree = fileMapToTree(fileMap);
399+
const { files, dirs } = subTree;
400+
const newTree = { ...this.codeEditorCtx?.fileTree };
401+
newTree.files?.push(...files);
402+
newTree.dirs?.push(...dirs);
403+
this.codeEditorCtx.updateState({
404+
fileTree: newTree,
405+
});
406+
}
407+
348408
triggerHook(key: HookKeys, ...args: any[]): void {
349409
super.triggerHook(key, ...args);
350410
}

0 commit comments

Comments
 (0)