Exporter
Right-click any folder in the explorer to export it as a tarball, a zip, a folder on your disk, or text on the clipboard.
exporter adds one command, Export…, to a folder's context menu in the explorer and to the
command palette. Running it asks where the folder should go — a .tar.gz, a .zip, straight into
a folder on your disk, or the clipboard as text — then walks it and puts it there.
import { exporter } from "codelet/extensions/exporter";
import { FileSystem, Workbench } from "codelet/workbench";
const workbench = new Workbench({
parent: document.getElementById("app")!,
fs: new FileSystem({ "/src/index.ts": "export const hi = 1;\n" }),
extensions: [exporter],
});exporter takes no options.
#Try it
Right-click the src folder in the explorer, choose Export…, and pick a format — your
browser downloads src.tar.gz or src.zip, holding src/index.ts and src/greet.ts. On a
Chromium browser there is a third row, Folder, which writes the same tree onto your disk
instead.
#Where the command appears
Right-click any folder's row for that folder alone, under its own name. For the whole workspace,
as workspace: the button in the explorer's heading, the tree's own context menu below the last
row, or Export: Export… from the palette. The format is asked as a second step rather than
offered as a second command, so the item reads the same wherever you meet it — and it is asked
before anything is read, so escaping the pick costs nothing.
#No library, either direction
Nothing is fetched and nothing is bundled. Both formats are written out directly: ustar is a
512-byte header per entry, and zip is its three records with a CRC-32 of this package's own
writing. Compression is the browser's — CompressionStream as gzip around the tarball, as raw
deflate per zip entry — and a browser that has none downloads a plain .tar, or a zip whose
entries are stored rather than deflated, instead of failing.
Either archive always has a single top-level directory named after the folder, so extracting it can't spray files over wherever you extract it.
#Straight onto your disk
The third row, Folder, skips the archive: it opens your browser's directory picker and writes
the tree out as itself. It needs showDirectoryPicker, which today means a Chromium browser —
where that is missing the row isn't offered, rather than offered and failing.
What lands is the same shape as the archives: a single directory named after the folder you
exported, created inside the one you picked, so choosing Downloads can't spray a workspace over
it. It's a merge rather than a mirror — a file of the same name is overwritten, everything else
in the target is left alone, and nothing is ever deleted.
Note
Picking the folder is part of the same click as picking the format, because the browser only opens a directory picker while a gesture of yours is still live. That's why you're asked before the tree is read rather than after.
Chrome refuses some directories outright — system paths, and your home directory itself, though not folders inside it. That's the browser's own blocklist rather than anything this extension can grant: pick a folder one level down and it goes through.
#Onto the clipboard
The last row, Contents, copies the whole folder as one markdown document instead of writing a file: a fenced block per file, headed by its language and path.
```ts src/index.ts
import { greet } from "./greet";
```The fence is always a backtick longer than the longest run inside the file, so a markdown file can't close its own block. Binary files are named with their size rather than dumped, and a file the tree holds only the address of says so instead of being fetched — copying text is the one export not worth pulling four megabytes down for.
Copying shows a notification saying what went — no file arrives and nothing on disk changes, so there'd otherwise be nothing to tell you it worked.
Note
An actual .zip on the clipboard isn't possible from a web page: browsers accept only text, HTML
and a couple of image types on a clipboard write, and putting a file there — the kind you could
paste into Finder or Explorer — isn't exposed to the web at all. Hence text.
#One file
Right-clicking a file row gets Copy Contents instead of Export… — an archive of a single file being nothing anyone wanted. It copies that file's text as-is, with no fence and no path around it, and it's in the palette too, where it works on whatever you have open.
What the workspace holds is what you get: a binary file mirrored in by
remote copies as the address the tree holds for it, the same string the
editor shows you. A file whose text is really bytes — a dropped-in image, say — refuses with a
warning rather than filling your clipboard with a megabyte of base64.
#What lands in the archive
A FileSystem holds every file as text, so a file's bytes in the archive are usually its text,
UTF-8 encoded — unless that text is where the bytes actually are, the same address form
media reads:
| The file holds | Where it comes from | In the archive |
|---|---|---|
data:image/png;base64,… | dropping a file into the workbench | the bytes it stands for |
| A URL, or a path off the page | remote mirroring a binary | the bytes, fetched |
| One line of bare base64 | a host that seeded the tree that way | the bytes, if they aren't valid text |
That's what makes dropping a folder of images into the workbench and exporting it again give you the images back, and what makes exporting a remote-backed workspace give you its binaries rather than a folder of one-line text files.
Note
The fetch for a remote-backed file is the page's own — a remote whose authentication is a
wrapped fetch (the fetch option on httpTransport) isn't reachable
from here, so its files come back as the addresses they are. A fetch that fails leaves the file
as that address and is reported in an output channel named Export, which the
Logs extension shows if you're running it — the download still happens.
Directories are kept, including empty ones.