Codelet logoCodelet

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 holdsWhere it comes fromIn the archive
data:image/png;base64,…dropping a file into the workbenchthe bytes it stands for
A URL, or a path off the pageremote mirroring a binarythe bytes, fetched
One line of bare base64a host that seeded the tree that waythe 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.

Read more in Extensions.