Skip to content

Commit

Permalink
feat(files): export workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
JiyuShao committed May 20, 2024
1 parent 3bff1e3 commit d046d26
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 9 deletions.
7 changes: 6 additions & 1 deletion packages/extensions/js-runner-and-debugger/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
},
{
"command": "js-runner-and-debugger.memfs.reset",
"title": "JS Runner & Debugger: MemFS reset"
"title": "JS Runner & Debugger: MemFS Reset"
},
{
"command": "js-runner-and-debugger.memfs.exportWorkspace",
"title": "JS Runner & Debugger: MemFS Export Workspace"
}
]
},
Expand All @@ -52,6 +56,7 @@
"@typescript-eslint/parser": "^7.7.1",
"@vscode/test-web": "^0.0.54",
"assert": "^2.1.0",
"jszip": "^3.10.1",
"mocha": "^10.4.0",
"process": "^0.11.10",
"ts-loader": "^9.5.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as vscode from 'vscode';
import { MemFS } from '../utils/memfs';
import { registerCommand } from '../utils/commands';
import { FS_SCHEME } from '../config';
import { exportFile } from '../utils/file';

export function registerFileSystem(context: vscode.ExtensionContext) {
const memFs = new MemFS();
Expand All @@ -20,7 +22,7 @@ export function registerFileSystem(context: vscode.ExtensionContext) {
});

// register filesystem commands
registerCommand(context, 'initExampleFiles', () => {
registerCommand(context, `${FS_SCHEME}.initExampleFiles`, () => {
if (initialized) {
return;
}
Expand Down Expand Up @@ -129,10 +131,16 @@ export function registerFileSystem(context: vscode.ExtensionContext) {
);
});

registerCommand(context, 'reset', () => {
registerCommand(context, `${FS_SCHEME}.reset`, () => {
for (const [name] of memFs.readDirectory(vscode.Uri.parse('memfs:/'))) {
memFs.delete(vscode.Uri.parse(`memfs:/${name}`));
}
initialized = false;
});

registerCommand(context, `${FS_SCHEME}.exportWorkspace`, () => {
memFs.exportToZip().then(content => {
exportFile(content, 'memfs.zip');
});
});
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import vscode from 'vscode';
import { EXTENSION_NAME, FS_SCHEME } from '../config';
import { EXTENSION_NAME } from '../config';

type Commands = {
[key: string]: () => void;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type CommandCallback = (...args: any[]) => any;

// The command has been defined in the package.json file
Expand All @@ -28,12 +27,12 @@ export function registerCommand(
};
commands[command] = newCallback;
const disposable = vscode.commands.registerCommand(
`${EXTENSION_NAME}.${FS_SCHEME}.${command}`,
`${EXTENSION_NAME}.${command}`,
newCallback
);
context.subscriptions.push(disposable);
}

export function executeCommand(command: string) {
vscode.commands.executeCommand(`${EXTENSION_NAME}.${FS_SCHEME}.${command}`);
vscode.commands.executeCommand(`${EXTENSION_NAME}.${command}`);
}
37 changes: 37 additions & 0 deletions packages/extensions/js-runner-and-debugger/src/web/utils/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import vscode from 'vscode';

export async function exportFile(data: Uint8Array, defaultFilename: string) {
try {
// 询问用户保存文件的位置
const uri = await vscode.window.showSaveDialog({
saveLabel: '保存文件',
filters: {
'All files': ['*'],
},
defaultUri: vscode.Uri.file(defaultFilename),
});

if (!uri) {
// 如果用户取消了操作,直接返回
return;
}

// 写入文件系统
await vscode.workspace.fs.writeFile(uri, data);

// 提醒用户文件已下载
vscode.window.showInformationMessage(`文件已保存到:${uri.fsPath}`);
} catch (error) {
vscode.window.showErrorMessage(`保存文件失败: ${error}`);
}
}

export async function blobToUint8Array(blob: Blob) {
// 使用 Blob 的 arrayBuffer 方法获取 ArrayBuffer
const arrayBuffer = await blob.arrayBuffer();

// 使用 ArrayBuffer 创建 Uint8Array
const uint8Array = new Uint8Array(arrayBuffer);

return uint8Array;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// eslint-disable @typescript-eslint/no-explicit-any

import { EXTENSION_NAME_SHORT } from '../config';

function generateShortId(): string {
Expand Down
63 changes: 63 additions & 0 deletions packages/extensions/js-runner-and-debugger/src/web/utils/memfs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

import * as path from './paths';
import * as vscode from 'vscode';
import JSZip from 'jszip';
import { logger } from './logger';
import { blobToUint8Array } from './file';

export class File implements vscode.FileStat {
type: vscode.FileType;
Expand Down Expand Up @@ -235,4 +238,64 @@ export class MemFS implements vscode.FileSystemProvider {
this._bufferedEvents.length = 0;
}, 5);
}

// Custom Method
public async exportToZip(): Promise<Uint8Array> {
const zip = new JSZip();
const stack: {
value: Directory | File;
path: string;
parentZip: JSZip;
}[] = [
{
value: this.root,
path: '',
parentZip: zip,
},
];
// preorder DFS
while (stack.length > 0) {
const currentItem = stack.pop()!;
console.log(`Creating ${currentItem.path}:`, currentItem.value);
if (currentItem.value instanceof Directory) {
// create zip folder
const currentZip =
currentItem.path === ''
? currentItem.parentZip // root
: currentItem.parentZip.folder(currentItem.value.name)!;
currentItem.value.entries.forEach((value, key) => {
stack.push({
value,
path: `${currentItem.path}/${key}`,
parentZip: currentZip,
});
});
} else if (currentItem.value instanceof File) {
if (!currentItem.value.data) {
logger.error(`Creating ${currentItem.path} Failed: File is Empty!`);
continue;
}
currentItem.parentZip.file(
currentItem.value.name,
currentItem.value.data
);
}
logger.debug(`Creating ${currentItem.path} Succeed!`);
}
return zip
.generateAsync({ type: 'blob' })
.then(function (content) {
return blobToUint8Array(content);
})
.then(function (content) {
logger.info(`Export to zip Succeed:`, content);
return content;
})
.catch(error => {
logger.error(`Export to zip Failed!`, error);
throw error;
});
}

public importFromZip() {}
}
29 changes: 29 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d046d26

Please # to comment.