diff --git a/apps/swc-plugins/app/api/update/runtimes/route.ts b/apps/swc-plugins/app/api/update/runtimes/route.ts new file mode 100644 index 0000000..cd7b01b --- /dev/null +++ b/apps/swc-plugins/app/api/update/runtimes/route.ts @@ -0,0 +1,13 @@ +import { UpdateRuntimesInputSchema } from "@/lib/api/updater/router"; +import { createCaller } from "@/lib/server"; +import { NextRequest, NextResponse } from "next/server"; + +export const POST = async (req: NextRequest) => { + const body = UpdateRuntimesInputSchema.parse(await req.json()); + + const api = await createCaller(); + + await api.updater.updateRuntimes(body); + + return NextResponse.json({ ok: true }); +}; diff --git a/apps/swc-plugins/lib/api/updater/router.ts b/apps/swc-plugins/lib/api/updater/router.ts index 7bbdc1a..ca29946 100644 --- a/apps/swc-plugins/lib/api/updater/router.ts +++ b/apps/swc-plugins/lib/api/updater/router.ts @@ -1,6 +1,7 @@ import { publicProcedure, router } from "@/lib/base"; import { db } from "@/lib/prisma"; import { TRPCError } from "@trpc/server"; +import semver from "semver"; import { z } from "zod"; function validateToken(token: string) { @@ -14,19 +15,24 @@ function validateToken(token: string) { }); } -const NpmPackageVersionSchema = z.object({ +const PackageVersionSchema = z.object({ version: z.string(), swcCoreVersion: z.string(), }); -const NpmPackageSchema = z.object({ +const PackageSchema = z.object({ name: z.string(), - versions: z.array(NpmPackageVersionSchema), + versions: z.array(PackageVersionSchema), }); export const UpdateWasmPluginsInputSchema = z.object({ token: z.string(), - pkgs: z.array(NpmPackageSchema), + pkgs: z.array(PackageSchema), +}); + +export const UpdateRuntimesInputSchema = z.object({ + token: z.string(), + pkgs: z.array(PackageSchema), }); export const updaterRouter = router({ @@ -83,4 +89,74 @@ export const updaterRouter = router({ } } }), + + updateRuntimes: publicProcedure + .input(UpdateRuntimesInputSchema) + .output(z.void()) + .mutation(async ({ input, ctx }) => { + validateToken(input.token); + + const compatRanges = await db.compatRange.findMany({ + select: { + id: true, + from: true, + to: true, + }, + }); + + // Runtimes has so many versions so we need a faster logic. + function byVersion(swcCoreVersion: string) { + for (const range of compatRanges) { + if ( + semver.gte(swcCoreVersion, range.from) && + (range.to === "*" || semver.lte(swcCoreVersion, range.to)) + ) { + return range; + } + } + } + + for (const pkg of input.pkgs) { + const runtime = await db.swcRuntime.upsert({ + where: { + name: pkg.name, + }, + create: { + name: pkg.name, + }, + update: {}, + }); + + for (const version of pkg.versions) { + const swcCoreVersion = version.swcCoreVersion; + const compatRange = byVersion(swcCoreVersion); + + if (!compatRange) { + throw new TRPCError({ + code: "NOT_FOUND", + message: `Compat range not found for SWC core version ${swcCoreVersion}`, + }); + } + + await db.swcRuntimeVersion.upsert({ + where: { + runtimeId_version: { + runtimeId: runtime.id, + version: version.version, + }, + }, + create: { + runtimeId: runtime.id, + version: version.version, + compatRangeId: compatRange.id, + swcCoreVersion, + }, + update: { + compatRangeId: compatRange.id, + swcCoreVersion, + }, + }); + } + } + }), }); diff --git a/apps/swc-plugins/scripts/import-runtime.mjs b/apps/swc-plugins/scripts/import-runtime.mjs deleted file mode 100755 index e6c4bec..0000000 --- a/apps/swc-plugins/scripts/import-runtime.mjs +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env zx -// -// Run this script using an absolute path to the runtime directory. -import path from "path"; -import semver from "semver"; -import toml from "toml"; -import { $ } from "zx"; - -const runtimeName = process.argv[2]; -const runtimeDir = process.argv[3]; - -if (!runtimeName || !runtimeDir) { - console.error("Runtime name and directory are required"); - process.exit(1); -} - -const $$ = $({ cwd: runtimeDir }); - -const repositoryRoot = (await $$`git rev-parse --show-toplevel`.text()).trim(); -const cargoLockPath = path.resolve(`${runtimeDir}/Cargo.lock`); -const relativePathToCargoLock = path.relative(repositoryRoot, cargoLockPath); - -console.log("Runtime name:", runtimeName); -console.log("Runtime dir:", runtimeDir); -console.log("Repository root:", repositoryRoot); -console.log("Cargo.lock path:", cargoLockPath); -console.log("Relative path to Cargo.lock:", relativePathToCargoLock); - -// Get all git tags -const gitTags = (await $$`git tag`.text()).trim().split("\n").reverse(); - -const data = { - runtime: runtimeName, - versions: [], -}; - -// For each tag, get the content of `${runtimeDir}/Cargo.lock`. -for (const tag of gitTags) { - let tagVersion = tag.replace("v", "").replace("@farmfe/core@", ""); - if (!semver.valid(tagVersion)) { - console.log(`Skipping tag ${tag} because it is not a valid semver`); - continue; - } - - try { - const cargoLock = - await $$`git show ${tag}:${relativePathToCargoLock}`.text(); - - const parsed = toml.parse(cargoLock); - const packages = parsed.package; - - for (const pkg of packages) { - if (pkg.name === "swc_core") { - const swcCoreVersion = pkg.version; - - data.versions.push({ - version: tagVersion, - swcCoreVersion, - }); - console.log(`Found swc_core version ${swcCoreVersion} for tag ${tag}`); - } - } - - // Send the data to the server - if (data.versions.length >= 10) { - await fetch("http://localhost:50000/import/runtime", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }); - data.versions = []; - } - } catch (e) { - console.error(`Failed to parse Cargo.lock for tag ${tag}: ${e}`); - } -} - -await fetch("http://localhost:50000/import/runtime", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), -});