diff --git a/packages/playground-v2/src/ata/ata.test.ts b/packages/playground-v2/src/ata/ata.test.ts index 856e30f3c2c2..4f6da58d5e57 100644 --- a/packages/playground-v2/src/ata/ata.test.ts +++ b/packages/playground-v2/src/ata/ata.test.ts @@ -37,8 +37,11 @@ function registry(fixtures: Record) { init?.signal?.throwIfAborted() if (failures.delete(url)) return new Response("", { status: 503 }) const tree = /^https:\/\/unpkg\.com\/(.+)@([^/]+)\/\?meta$/.exec(url) + // Browsers reject unpkg's redirects to exact ?meta URLs for lacking CORS headers. + if (tree && tree[2] !== "1.0.0") throw new TypeError("Failed to fetch") + const manifest = /^https:\/\/unpkg\.com\/(.+)@[^/]+\/package\.json$/.exec(url) const file = /^https:\/\/unpkg\.com\/(.+)@1\.0\.0(\/.+)$/.exec(url) - const name = tree?.[1] ?? file?.[1] + const name = tree?.[1] ?? manifest?.[1] ?? file?.[1] const fixture = name ? fixtures[name] : undefined if (!fixture) return new Response("", { status: 404 }) if (tree) { @@ -48,7 +51,7 @@ function registry(fixtures: Record) { files: ["/package.json", ...Object.keys(fixture.files)].map(path => ({ path })), }) } - if (file?.[2] === "/package.json") { + if (manifest) { return Response.json({ name, version: "1.0.0", types: "./index.d.ts", dependencies: fixture.dependencies }) } const text = file && fixture.files[file[2]] diff --git a/packages/playground-v2/src/ata/index.ts b/packages/playground-v2/src/ata/index.ts index 16cbadcfa4e5..a00beef3a911 100644 --- a/packages/playground-v2/src/ata/index.ts +++ b/packages/playground-v2/src/ata/index.ts @@ -372,25 +372,28 @@ export class PackageTypeAcquirer { onFiles: (count: number) => void, onFile: () => void ): Promise { - let tree: unknown + // unpkg's redirects from a tag or range to an exact version omit CORS + // headers for ?meta, but include them for package files. + let metadata: unknown try { - tree = await this.json(`https://unpkg.com/${name}@${encodeURIComponent(selector)}/?meta`, signal) + metadata = await this.json(`https://unpkg.com/${name}@${encodeURIComponent(selector)}/package.json`, signal) } catch (error) { signal.throwIfAborted() if (error instanceof HttpError && error.status === 404 && !name.startsWith("@types/")) return undefined throw error } if ( - !record(tree) || - tree.package !== name || - typeof tree.version !== "string" || - !/^\d+\.\d+\.\d+[-+\w.]*$/.test(tree.version) + !record(metadata) || + metadata.name !== name || + typeof metadata.version !== "string" || + !/^\d+\.\d+\.\d+[-+\w.]*$/.test(metadata.version) ) { throw new Error(`Could not resolve ${name}@${selector} to an exact version`) } - const version = tree.version + const version = metadata.version const base = `https://unpkg.com/${name}@${version}` - if (!Array.isArray(tree.files)) throw new Error(`Invalid file index for ${name}@${version}`) + const tree = await this.json(`${base}/?meta`, signal) + if (!record(tree) || !Array.isArray(tree.files)) throw new Error(`Invalid file index for ${name}@${version}`) const paths = tree.files .map(entry => { if (!record(entry) || typeof entry.path !== "string" || !safePackagePath(entry.path)) { @@ -400,8 +403,6 @@ export class PackageTypeAcquirer { }) .filter(path => !path.includes("/node_modules/")) if (!paths.some(isDeclaration)) return undefined - const metadata = await this.json(`${base}/package.json`, signal) - if (!record(metadata)) throw new Error(`Invalid package.json for ${name}@${version}`) const dependencies = new Map() for (const field of ["peerDependencies", "dependencies"]) { const entries = metadata[field]