diff --git a/packages/consumer/src/pkv/files.ts b/packages/consumer/src/pkv/files.ts new file mode 100644 index 0000000..e1ba125 --- /dev/null +++ b/packages/consumer/src/pkv/files.ts @@ -0,0 +1,89 @@ +import { FetchError, ofetch } from 'ofetch'; +import type { PackFile } from 'publint'; +import { Result } from 'better-result'; +import { join } from 'node:path'; +import * as v from 'valibot'; + +const FileSchema = v.object({ + type: v.literal('file'), + name: v.string(), + hash: v.string(), + size: v.number(), +}); + +interface Directory { + type: 'directory'; + name: string; + files: (Directory | v.InferOutput)[]; +} + +const DirectorySchema: v.GenericSchema = v.object({ + type: v.literal('directory'), + name: v.string(), + files: v.array(v.union([v.lazy(() => DirectorySchema), FileSchema])), +}); + +const ResponseSchema = v.object({ + type: v.literal('npm'), + files: v.array(v.union([v.lazy(() => DirectorySchema), FileSchema])), +}); + +type CreateVFSResult = Result< + PackFile[], + FetchError | v.ValiError +>; + +export async function createPackFiles( + name: string, + version: string, +): Promise { + const raw = await Result.tryPromise(async () => { + return await ofetch(`/npm/${name}@${version}`, { + baseURL: 'https://data.jsdelivr.com/v1/packages', + headers: { + 'User-Agent': 'npm.rest (+https://github.com/e18e/npm.rest)', + }, + }); + }); + + if (raw.isErr()) { + return Result.err(raw.error); + } + + const data = v.safeParse(ResponseSchema, raw.value); + + if (!data.success) { + return Result.err(new v.ValiError(data.issues)); + } + + const files: PackFile[] = []; + + function visit(nodes: Directory['files'], prefix: string) { + for (const node of nodes) { + if (node.type === 'directory') { + visit(node.files, join(prefix, node.name)); + continue; + } + + const path = join(prefix, node.name); + + files.push({ + name: path, + async data() { + return await ofetch(path, { + baseURL: `https://cdn.jsdelivr.net/npm/${name}@${version}`, + headers: { + 'User-Agent': + 'npm.rest (+https://github.com/e18e/npm.rest)', + }, + responseType: 'arrayBuffer', + }); + }, + }); + } + } + + visit(data.output.files, '/'); + + return Result.ok(files); +} diff --git a/packages/consumer/src/pkv/publint.ts b/packages/consumer/src/pkv/publint.ts index 351f574..ecad55e 100644 --- a/packages/consumer/src/pkv/publint.ts +++ b/packages/consumer/src/pkv/publint.ts @@ -1,12 +1,11 @@ -import type { UnpackResult } from '@publint/pack'; +import { publint, type PackFile } from 'publint'; import { Result } from 'better-result'; -import { publint } from 'publint'; -export async function runPublint(tarball: UnpackResult) { +export async function runPublint(files: PackFile[]) { return await Result.tryPromise(async () => { return await publint({ - pkgDir: tarball.rootDir, - pack: { files: tarball.files }, + pkgDir: '/', + pack: { files }, }); }); } diff --git a/packages/consumer/src/pkv/tarball.ts b/packages/consumer/src/pkv/tarball.ts deleted file mode 100644 index 7e9049a..0000000 --- a/packages/consumer/src/pkv/tarball.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Result } from 'better-result'; -import { unpack } from '@publint/pack'; - -export async function downloadTarball(url: string, integrity?: string) { - const result = await Result.tryPromise(async () => { - const response = await fetch(url, { - headers: { - 'User-Agent': 'npm.rest (+https://github.com/e18e/npm.rest)', - }, - integrity, - }); - - if (!response.ok || !response.body) { - throw new Error( - `failed to download tarball "${url}" with ${response.status}`, - ); - } - - const length = Number.parseInt( - response.headers.get('Content-Length') || '', - ); - - if (Number.isNaN(length)) { - throw new Error('Content-Length header is missing'); - } - - return { response, length }; - }); - - if (result.isErr()) { - return result; - } - - return await Result.tryPromise(async () => { - const { rootDir, files } = await unpack(result.value.response.body!); - - return { - unpackedSize: files - .map((file) => file.data.byteLength) - .reduce((a, b) => a + b, 0), - packedSize: result.value.length, - rootDir, - files, - }; - }); -} diff --git a/patches/publint.patch b/patches/publint.patch new file mode 100644 index 0000000..2ee020a --- /dev/null +++ b/patches/publint.patch @@ -0,0 +1,37 @@ +diff --git a/src/index.d.ts b/src/index.d.ts +index 3b355f94e7e3855bda69c1f842dc57ecf210221d..319b6b9449c954bc111caf10de848884c69d12d1 100644 +--- a/src/index.d.ts ++++ b/src/index.d.ts +@@ -133,9 +133,11 @@ export type Message = + | BaseMessage<'IMPORTS_FALLBACK_ARRAY_USE'> + | BaseMessage<'CJS_WITH_ESMODULE_DEFAULT_EXPORT', { filePath?: string }> + ++type MaybePromiseFn = T | (() => Promise) ++ + export interface PackFile { + name: string +- data: string | ArrayBuffer | Uint8Array ++ data: MaybePromiseFn + } + + export interface Options { +diff --git a/src/shared/vfs-tarball.js b/src/shared/vfs-tarball.js +index b9231ea2afeddc6aab5959949157ca5d0faf9122..eea75c7bc7689ddb895950bc8c74c9de2872dc16 100644 +--- a/src/shared/vfs-tarball.js ++++ b/src/shared/vfs-tarball.js +@@ -28,11 +28,12 @@ export function createTarballVfs(files) { + readFile: async (path) => { + const file = files.find((file) => file.name === path) + if (!file) throw new Error(`Unable to read file at path: ${path}`) +- if (typeof file.data === 'string') { +- return file.data ++ const data = typeof file.data === 'function' ? await file.data() : file.data ++ if (typeof data === 'string') { ++ return data + } else { + decoder ??= new TextDecoder() +- return decoder.decode(file.data) ++ return decoder.decode(data) + } + }, + } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9b23a8..8e8c7b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,6 +34,11 @@ catalogs: specifier: ^4.60.0 version: 4.60.0 +patchedDependencies: + publint: + hash: 59cdb1fe5f6515a1949a9ac1976c407c2bb392e07843f3153df8113745460b75 + path: patches/publint.patch + importers: .: @@ -139,7 +144,7 @@ importers: version: 1.5.1 publint: specifier: 'catalog:' - version: 0.3.17 + version: 0.3.17(patch_hash=59cdb1fe5f6515a1949a9ac1976c407c2bb392e07843f3153df8113745460b75) tsx: specifier: ^4.21.0 version: 4.21.0 @@ -186,7 +191,7 @@ importers: version: 0.31.8 publint: specifier: 'catalog:' - version: 0.3.17 + version: 0.3.17(patch_hash=59cdb1fe5f6515a1949a9ac1976c407c2bb392e07843f3153df8113745460b75) packages/docs: devDependencies: @@ -5303,7 +5308,7 @@ snapshots: proc-log@6.1.0: {} - publint@0.3.17: + publint@0.3.17(patch_hash=59cdb1fe5f6515a1949a9ac1976c407c2bb392e07843f3153df8113745460b75): dependencies: '@publint/pack': 0.1.3 package-manager-detector: 1.6.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1b18bfe..05416df 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -21,3 +21,6 @@ onlyBuiltDependencies: - esbuild - workerd - sharp + +patchedDependencies: + publint: patches/publint.patch