This commit is contained in:
Anton Nesterov 2023-02-10 11:17:05 +03:00
commit 3d03526471
7 changed files with 143 additions and 0 deletions

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"deno.enable": true
}

16
deps.ts Normal file
View file

@ -0,0 +1,16 @@
export {
type TarMeta,
Untar,
} from "https://deno.land/std@0.144.0/archive/tar.ts";
export {
copy,
readerFromStreamReader,
} from "https://deno.land/std@0.144.0/streams/mod.ts";
export { join } from "https://deno.land/std@0.144.0/path/mod.ts";
export {
default as cacheDir,
} from "https://deno.land/x/cache_dir@0.2.0/mod.ts";
import * as Transform from "https://deno.land/x/transform@v0.4.0/mod.ts";
export { Transform };
export const { GzDecoder } = Transform.Transformers;

120
mod.ts Normal file
View file

@ -0,0 +1,120 @@
import {
cacheDir,
copy,
GzDecoder,
join,
readerFromStreamReader,
TarMeta,
Transform,
Untar,
} from "./deps.ts";
export const CACHE_DIR = join((await cacheDir()) ?? ".cache", "__pick_cache0");
Deno.mkdir(CACHE_DIR).catch((_) => {});
type TarEntry = TarMeta & Deno.Reader;
interface GithubPickOptions {
repo: string;
version: string;
pick: RegExp[];
}
export async function* githubPick({ repo, version, pick }: GithubPickOptions) {
const _cached = await getFetchCache(`${repo}@${version}`);
if (_cached) {
const reader = await Deno.open(_cached);
yield* tarGzPickFiles(reader, pick);
} else {
yield* githubPickFiles({ repo, version, pick });
}
}
/**
* Reads *.tar.gz file from a version tag and returns generator of the files that match the pick regex.
*
* @param {GithubPickOptions} opts - { repo: "denoland/deno", version: "v1.0.0", pick: [/\.ts$/] }
* @returns {AsyncGenerator<TarEntry>}
*/
export async function* githubPickFiles(
{ repo, version, pick }: GithubPickOptions,
): AsyncGenerator<TarEntry> {
const targz = await fetch(
`https://github.com/${repo}/archive/refs/tags/${version}.tar.gz`,
);
await putFetchCache(targz.body, `${repo}@${version}`);
const reader = readerFromStreamReader(targz.body!.getReader());
yield* tarGzPickFiles(reader, pick);
}
/**
* Pick files from a tar.gz archive.
*
* @param {Deno.Reader} tar - Deno.open('archive.tar.gz')
* @param {RegExp[]} pick - [ /\.ts$/ ]
*/
export async function* tarGzPickFiles(
targz: Deno.Reader | Deno.FsFile,
pick: RegExp[],
) {
const untar = new Untar(
Transform.newReader(
targz,
new GzDecoder(),
),
);
for await (const entry of untar) {
if (entry.type === "file" && pick.some((re) => re.test(entry.fileName))) {
yield entry;
}
}
}
/**
* Writes fetch body to a cache file.
* @param body
* @param name
* @param opts
*/
export async function putFetchCache(body: Response["body"], name: string) {
const reader = readerFromStreamReader(body!.getReader());
const writer = await Deno.open(join(CACHE_DIR, name), { write: true });
await copy(reader, writer);
writer.close();
}
/**
* Get either the path to the cache file or null if it doesn't exist.
* @param {string} name
* @returns {Promise<string | null>}
*/
export async function getFetchCache(name: string) {
try {
const _file = join(CACHE_DIR, name);
await Deno.lstat(_file);
return _file;
} catch (_) {
return null;
}
}
/**
* Clean the fetch cache.
* @returns {Promise<void>}
*/
export async function cleanFetchCache() {
return Deno.remove(CACHE_DIR, { recursive: true });
}
export async function writeTarEntry(
entry: TarEntry,
dir: string,
formatWritePath: (path: string) => string = (path) => path,
) {
const path = formatWritePath(entry.fileName);
const file = await Deno.open(dir + path, {
create: true,
write: true,
});
await copy(entry, file);
file.close();
}

0
pick.ts Normal file
View file

3
readme.md Normal file
View file

@ -0,0 +1,3 @@
# Pick [WIP]
Pick files from remote sources

0
tests/.pick.yaml Normal file
View file

1
tests/mod_test.ts Normal file
View file

@ -0,0 +1 @@
const { test } = Deno;