-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduces a new `ZipWorkspace` service to allow the exporting of the current workspace as a zip. ***Example*** ```javascript // Define your workspace const workspace = [ { name: 'main.scss', content: '@import "variables"; .body { color: $primaryColor; }', type: 'scss' }, { name: 'variables.scss', content: '$primaryColor: blue;', type: 'scss' } ]; // Instantiate the zip workspace and trigger the download await new ZipWorkspace(testWorkspace).download('testWorkspace.zip'); ```
- Loading branch information
Showing
9 changed files
with
365 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import JSZip from 'jszip'; | ||
|
||
/** | ||
* Bundles a workspace into a zip file and triggers the download. | ||
* | ||
* @example | ||
* // Define your workspace | ||
* const workspace = [ | ||
* { | ||
* name: 'main.scss', | ||
* content: '@import "variables"; .body { color: $primaryColor; }', | ||
* type: 'scss' | ||
* }, | ||
* { | ||
* name: 'variables.scss', | ||
* content: '$primaryColor: blue;', | ||
* type: 'scss' | ||
* } | ||
* ]; | ||
* | ||
* // Instantiate the zip workspace and trigger the download | ||
* await new ZipWorkspace(testWorkspace).download('testWorkspace.zip'); | ||
*/ | ||
export default class ZipWorkspace { | ||
#zip; | ||
|
||
/** | ||
* Initializes a new instance of the ZipWorkspace. | ||
* | ||
* @param {TreeItem[]} workspace An array of TreeItem objects representing the files and directories in the workspace. | ||
*/ | ||
constructor(workspace) { | ||
this.#zip = new JSZip(); | ||
this.#addItems(workspace); | ||
} | ||
|
||
/** | ||
* Recursively adds items to the zip archive. | ||
* | ||
* @param {TreeItem[]} workspace An array of TreeItem objects to add to the zip. Each item can be a file or a folder. | ||
* @param {string} [path=''] The current path in the zip archive. Used for recursive calls to maintain folder structure. | ||
* @private | ||
*/ | ||
#addItems(workspace, path = '') { | ||
workspace.forEach(item => { | ||
const currentPath = [path, item.name].filter(Boolean).join('/'); | ||
|
||
if (item.type === 'folder') { | ||
this.#addItems(item.children, currentPath); | ||
} else { | ||
this.#zip.file(currentPath, item.content); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Generates the zip file and triggers a download in the browser. | ||
* | ||
* @param {string} fileName The name for the downloaded zip file. | ||
* | ||
* @returns {Promise<void>} A promise that resolves when the download has been triggered. | ||
*/ | ||
async download(fileName) { | ||
const content = await this.#zip.generateAsync({ type: 'blob' }); | ||
const url = URL.createObjectURL(content); | ||
const a = document.createElement('a'); | ||
|
||
a.download = fileName; | ||
a.href = url; | ||
document.body.appendChild(a); | ||
a.click(); | ||
document.body.removeChild(a); | ||
URL.revokeObjectURL(url); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { describe, expect, it, vi } from 'vitest'; | ||
import JSZip from 'jszip'; | ||
import ZipWorkspace from '../../src/workspace/zip-workspace.js'; | ||
|
||
// Mocking necessary parts of the browser API | ||
global.URL.createObjectURL = vi.fn(); | ||
global.URL.revokeObjectURL = vi.fn(); | ||
document.body.appendChild = vi.fn(); | ||
document.body.removeChild = vi.fn(); | ||
|
||
describe('ZipWorkspace', () => { | ||
it('creates a ZIP file with the correct structure', async() => { | ||
// Mock JSZip instance to track method calls and verify the structure | ||
const mockJSZipInstance = { | ||
file: vi.fn(), | ||
generateAsync: vi.fn().mockResolvedValue(new Blob()) | ||
}; | ||
|
||
vi.spyOn(JSZip.prototype, 'file').mockImplementation(mockJSZipInstance.file); | ||
vi.spyOn(JSZip.prototype, 'generateAsync').mockImplementation(mockJSZipInstance.generateAsync); | ||
|
||
const workspace = [ | ||
{ | ||
name: 'file.txt', | ||
content: 'Hello, world!', | ||
type: 'file' | ||
}, | ||
{ | ||
name: 'folder', | ||
type: 'folder', | ||
children: [ | ||
{ | ||
name: 'nested.txt', | ||
content: 'Nested file', | ||
type: 'file' | ||
} | ||
] | ||
} | ||
]; | ||
|
||
const zipWorkspace = new ZipWorkspace(workspace); | ||
|
||
await zipWorkspace.download('test.zip'); | ||
|
||
// Check if JSZip was used correctly to add files | ||
expect(mockJSZipInstance.file).toHaveBeenCalledWith('file.txt', 'Hello, world!'); | ||
expect(mockJSZipInstance.file).toHaveBeenCalledWith('folder/nested.txt', 'Nested file'); | ||
expect(mockJSZipInstance.generateAsync).toHaveBeenCalledWith({ type: 'blob' }); | ||
}); | ||
|
||
it('triggers the download of the ZIP file', async() => { | ||
const workspace = [{ | ||
name: 'file.txt', | ||
content: 'Hello, world!', | ||
type: 'file' | ||
}]; | ||
const zipWorkspace = new ZipWorkspace(workspace); | ||
|
||
await zipWorkspace.download('test.zip'); | ||
|
||
// Assert the download was triggered with the correct filename | ||
expect(document.body.appendChild).toHaveBeenCalled(); | ||
expect(document.body.removeChild).toHaveBeenCalled(); | ||
expect(URL.createObjectURL).toHaveBeenCalled(); | ||
expect(URL.revokeObjectURL).toHaveBeenCalled(); | ||
}); | ||
}); |