import { z } from 'zod' import { assetDependencyValueSchema, type AssetDependencies } from './schemas.js' const stringRecordSchema = z.record(z.string(), z.string()) const assetDependenciesRecordSchema = z.record(z.string(), assetDependencyValueSchema) const packageJsonSchema = z .object({ name: z.string().optional(), private: z.boolean().optional(), dependencies: stringRecordSchema.optional(), assetDependencies: assetDependenciesRecordSchema.optional(), }) .catchall(z.unknown()) export type PackageJson = z.infer export function packageJsonAssetDependenciesFromFiles( files: Record, ): AssetDependencies { const bytes = files['package.json'] if (!bytes) return {} return packageJsonAssetDependencies(new TextDecoder().decode(bytes), 'package.json') } export function packageJsonAssetDependencies(json: string, source: string): AssetDependencies { const parsed = parsePackageJson(json, source) const dependencies = parsed.assetDependencies if (dependencies === undefined) return {} return assetDependenciesRecordSchema.parse(dependencies) } // A template ships a full app, so its package.json `dependencies` ARE its npm dependencies: capture // them on upload (like assetDependencies above) so installing the template into an existing project // brings them. Without this they are lost — the zip's package.json is only used when installing into // an empty directory; installing over an existing package.json merges the uploaded npm-deps metadata. export function packageJsonNpmDependenciesFromFiles( files: Record, ): Record { const bytes = files['package.json'] if (!bytes) return {} const parsed = parsePackageJson(new TextDecoder().decode(bytes), 'package.json') const dependencies = parsed.dependencies if (dependencies === undefined) return {} return stringRecordSchema.parse(dependencies) } export function parsePackageJson(json: string, source: string): PackageJson { const parsed = packageJsonSchema.safeParse(JSON.parse(json)) if (!parsed.success) { throw new Error(`${source} must contain a valid package.json object`) } return parsed.data }