Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
temp stuff
Browse files Browse the repository at this point in the history
fpaul-1A committed Dec 4, 2024
1 parent d6fb083 commit 4d1e0ab
Showing 7 changed files with 140 additions and 54 deletions.
1 change: 1 addition & 0 deletions apps/showcase/package.json
Original file line number Diff line number Diff line change
@@ -54,6 +54,7 @@
"@ngx-translate/core": "~15.0.0",
"@nx/jest": "~19.5.0",
"@o3r-training/showcase-sdk": "workspace:^",
"@o3r-training/training-sdk": "workspace:^",
"@o3r-training/training-tools": "workspace:^",
"@o3r/application": "workspace:^",
"@o3r/components": "workspace:^",

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ import {
} from '@angular/core';
import {
takeUntilDestroyed,
toObservable,
toSignal,
} from '@angular/core/rxjs-interop';
import {
@@ -53,6 +54,7 @@ import {
map,
Observable,
of,
pairwise,
share,
skip,
startWith,
@@ -150,6 +152,8 @@ export class CodeEditorViewComponent implements OnDestroy, OnChanges {
* Service to load files and run commands in the application instance of the webcontainer.
*/
public readonly webContainerService = inject(WebContainerService);

private readonly progressChanged$ = toObservable(this.webContainerService.runner.progress);
/**
* File tree loaded in the project folder within the web container instance.
*/
@@ -176,6 +180,18 @@ export class CodeEditorViewComponent implements OnDestroy, OnChanges {
file: ''
});

/**
* Subject used to notify when monaco editor has been initialized
*/
public readonly monacoReady = new Subject<void>();

/**
* Promise resolved with the global monaco instance
*/
private readonly monacoPromise = firstValueFrom(this.monacoReady.pipe(
map(() => window.monaco)
));

/**
* Configuration for the Monaco Editor
*/
@@ -188,36 +204,33 @@ export class CodeEditorViewComponent implements OnDestroy, OnChanges {
readOnly: (this.editorMode === 'readonly'),
automaticLayout: true,
scrollBeyondLastLine: false,
overflowWidgetsDomNode: this.monacoOverflowWidgets.nativeElement
overflowWidgetsDomNode: this.monacoOverflowWidgets.nativeElement,
model: this.model()
}))
);

public monacoReady = new Subject<void>();
public monacoPromise = firstValueFrom(this.monacoReady.pipe(
map(() => window.monaco)
));

private readonly fileContentLoaded$ = this.form.controls.file.valueChanges.pipe(
takeUntilDestroyed(),
combineLatestWith(this.cwdTree$),
filter(([path, monacoTree]) => !!path && checkIfPathInMonacoTree(monacoTree, path.split('/'))),
switchMap(([path]) => from(this.webContainerService.readFile(`${this.project!.cwd}/${path}`).catch(() => ''))),
takeUntilDestroyed(),
share()
);

private readonly fileContent = toSignal(this.fileContentLoaded$);

/**
* Model used for monaco editor for the currently selected file.
* We need that to associate the opened file to a URI which is necessary to resolve relative paths on imports.
*/
public model = computed(() => {
const value = this.fileContent();
const monaco = window.monaco;
if (!monaco) {
return undefined;
}
const uri = monaco.Uri.from({ scheme: '', path: this.form.controls.file.value! });
const language = editorOptionsLanguage[this.form.controls.file.value!.split('.').at(-1) || ''] || '';
const fileName = this.form.controls.file.value!;
const fileExtension = fileName.split('.').at(-1);
return {
value,
language,
uri
language: editorOptionsLanguage[fileExtension || ''] || '',
uri: `file:///${fileName}`
};
});

@@ -239,56 +252,102 @@ export class CodeEditorViewComponent implements OnDestroy, OnChanges {
});
this.fileContentLoaded$.subscribe((content) => this.form.controls.code.setValue(content));

// Reload definition types when finishing install
this.progressChanged$.pipe(
takeUntilDestroyed(),
pairwise(),
filter(([prev, curr]) =>
prev.totalSteps === curr.totalSteps
&& curr.currentStep > prev.currentStep
&& curr.currentStep > 2
&& prev.currentStep <= 2
)
).subscribe(async () => {
await this.reloadDeclarationTypes();
});
void this.monacoPromise.then((monaco) => {
monaco.editor.registerEditorOpener({
openCodeEditor: (_source: Monaco.editor.ICodeEditor, resource: Monaco.Uri, selectionOrPosition?: Monaco.IRange | Monaco.IPosition) => {
if (resource) {
if (resource && this.project?.files) {
const filePath = resource.path.slice(1);
this.form.controls.file.setValue(filePath);
if (selectionOrPosition) {
// TODO find a way to execute that after the new file is loaded
if (monaco.Position.isIPosition(selectionOrPosition)) {
monaco.editor.getEditors()[0].revealPosition(selectionOrPosition);
} else {
monaco.editor.getEditors()[0].revealRange(selectionOrPosition);
// TODO write a proper function to search in the tree
const flatFiles = flattenTree(this.project.files);
if (flatFiles.some((projectFile) => projectFile.filePath === resource.path)) {
this.form.controls.file.setValue(filePath);
if (selectionOrPosition) {
// TODO find a way to execute that after the new file is loaded
if (monaco.Position.isIPosition(selectionOrPosition)) {
monaco.editor.getEditors()[0].revealPosition(selectionOrPosition);
} else {
monaco.editor.getEditors()[0].revealRange(selectionOrPosition);
}
return true;
}
}
return true;
}
return false;
}
});
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
allowNonTsExtensions: true,
skipLibCheck: true,
target: 99,
module: 99,
moduleResolution: 2,
lib: [
'es2022',
'dom'
],
target: monaco.languages.typescript.ScriptTarget.Latest,
module: monaco.languages.typescript.ModuleKind.ESNext,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
paths: {
sdk: [
'libs/sdk/src/index'
],
'sdk/*': [
'libs/sdk/src/*'
'file:///libs/sdk/src/index'
]
}
});
});
}

private async updateAllProject() {
/**
* Unload ahh the files from the global monaco editor
*/
private async cleanAllModelsFromMonaco() {
const monaco = await this.monacoPromise;
monaco.editor.getModels().forEach((m) => m.dispose());
}

/**
* Load all the files from `this.project` as Models in the global monaco editor.
*/
private async loadAllProjectFilesToMonaco() {
const monaco = await this.monacoPromise;
const flatFiles = flattenTree(this.project?.files!);
flatFiles.forEach(({ filePath, content }) => {
const language = editorOptionsLanguage[filePath.split('.').at(-1) || ''] || '';
monaco.editor.createModel(content, language, monaco.Uri.from({ scheme: '', path: filePath }));
monaco.editor.createModel(content, language, monaco.Uri.from({ scheme: 'file', path: filePath }));
});
}

/**
* Load a new project in global monaco editor and update local form accordingly
*/
private async loadNewProject() {
await this.cleanAllModelsFromMonaco();
await this.loadAllProjectFilesToMonaco();
if (this.project?.startingFile) {
this.form.controls.file.setValue(this.project.startingFile);
} else {
this.form.controls.file.setValue('');
this.form.controls.code.setValue('');
}
}

/**
* do even more stuff
*/
public async reloadDeclarationTypes() {
if (this.project?.cwd) {
const declarationTypes = await this.webContainerService.getDeclarationTypes(this.project.cwd);
const monaco = await this.monacoPromise;
monaco.languages.typescript.typescriptDefaults.setExtraLibs(declarationTypes);
// TODO need to refresh the monaco editor to take into account the new lib (not working on first file)
}
}

/**
* @inheritDoc
*/
@@ -311,16 +370,7 @@ export class CodeEditorViewComponent implements OnDestroy, OnChanges {
// Remove link between launch project and terminals
void this.webContainerService.loadProject(this.project.files, this.project.commands, this.project.cwd);
}
void this.monacoPromise.then(async (monaco) => {
monaco.editor.getModels().forEach((m) => m.dispose());
await this.updateAllProject();
if (this.project?.startingFile) {
this.form.controls.file.setValue(this.project.startingFile);
} else {
this.form.controls.file.setValue('');
this.form.controls.code.setValue('');
}
});
void this.loadNewProject();
this.cwd$.next(this.project?.cwd || '');
}
}
27 changes: 27 additions & 0 deletions apps/showcase/src/services/webcontainer/webcontainer.service.ts
Original file line number Diff line number Diff line change
@@ -126,4 +126,31 @@ export class WebContainerService {

return getFilesTreeFromContainer(instance, EXCLUDED_FILES_OR_DIRECTORY);
}

/**
* This does stuff
* @param project
* @param maxDepth
* @param path
*/
public async getDeclarationTypes(project: string, maxDepth = 3, path = 'node_modules'): Promise<{ filePath: string; content: string }[]> {
// TODO read that from project devDependencies?
const dependenciesWhitelist = /@ama-sdk|@angular|@o3r|rxjs/;
const instance = await this.runner.instancePromise;
const basePath = `${project}/${path}`;
const dependencies = await instance.fs.readdir(basePath, { encoding: 'utf8', withFileTypes: true });
return (await Promise.all(dependencies.map(async (dirEntry) => {
if (dirEntry.isDirectory() && (dependenciesWhitelist.test(dirEntry.name) || dependenciesWhitelist.test(path))) {
const files = await instance.fs.readdir(`${basePath}/${dirEntry.name}`, { encoding: 'utf8', withFileTypes: true });
const indexFile = files.find((entry) => entry.isFile() && entry.name === 'index.d.ts');
if (indexFile) {
return [{
filePath: `file:///${path}/${dirEntry.name}/index.d.ts`,
content: await instance.fs.readFile(`${basePath}/${dirEntry.name}/${indexFile.name}`, 'utf8')
}];
}
return maxDepth > 1 ? await this.getDeclarationTypes(project, maxDepth - 1, `${path}/${dirEntry.name}`) : [];
}
}))).flat().filter((entry) => !!entry);
}
}
4 changes: 2 additions & 2 deletions apps/showcase/src/styles.scss
Original file line number Diff line number Diff line change
@@ -34,8 +34,8 @@ h1, h2 {
--df-btn-icononly-size: 1.85em;
}

.markdown-clipboard-toolbar button {
--df-btn-icononly-size: 1.85em;
[clipboard] pre[class*=language-]>code[class*=language-] {
padding-right: 2.5em;
}

// Start overrides of design factory
6 changes: 6 additions & 0 deletions tsconfig.base.json
Original file line number Diff line number Diff line change
@@ -64,9 +64,15 @@
"@o3r-training/showcase-sdk/fixtures": [
"packages/@o3r-training/showcase-sdk/src/fixtures/jest"
],
"@o3r-training/showcase-sdk/spec": [
"packages/@o3r-training/showcase-sdk/src/spec"
],
"@o3r-training/training-sdk": [
"packages/@o3r-training/training-sdk/src"
],
"@o3r-training/training-sdk/spec": [
"packages/@o3r-training/training-sdk/src/spec"
],
"@o3r-training/training-tools": [
"packages/@o3r-training/training-tools/src/public_api"
],
2 changes: 2 additions & 0 deletions tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -11,7 +11,9 @@
"@ama-terasu/core": ["packages/@ama-terasu/core/dist", "packages/@ama-terasu/core/src/public_api"],
"@ama-terasu/schematics": ["packages/@ama-terasu/schematics/dist", "packages/@ama-terasu/schematics/src/public_api"],
"@o3r-training/showcase-sdk": ["packages/@o3r-training/showcase-sdk/dist", "packages/@o3r-training/showcase-sdk/src/index"],
"@o3r-training/showcase-sdk/*": ["packages/@o3r-training/showcase-sdk/dist/*", "packages/@o3r-training/showcase-sdk/src/*"],
"@o3r-training/training-sdk": ["packages/@o3r-training/training-sdk/dist", "packages/@o3r-training/training-sdk/src/index"],
"@o3r-training/training-sdk/*": ["packages/@o3r-training/training-sdk/dist/*", "packages/@o3r-training/training-sdk/src/*"],
"@o3r-training/training-tools": ["packages/@o3r-training/training-tools/dist", "packages/@o3r-training/training-tools/src/public_api"],
"@o3r/amaterasu-api-spec": ["packages/@o3r/amaterasu/amaterasu-api-spec/dist", "packages/@o3r/amaterasu/amaterasu-api-spec/src"],
"@o3r/amaterasu-dodo": ["packages/@o3r/amaterasu/amaterasu-dodo/dist", "packages/@o3r/amaterasu/amaterasu-dodo/src"],

0 comments on commit 4d1e0ab

Please sign in to comment.