diff --git a/build-react-contracts.mjs b/build-react-contracts.mjs index ed07a9e..e4d6736 100644 --- a/build-react-contracts.mjs +++ b/build-react-contracts.mjs @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { execFileSync } from "node:child_process"; +import { installDependencies } from "./install-dependencies.mjs"; const config = JSON.parse(await fs.readFile(process.argv[2], "utf8")); const resource = JSON.parse(await fs.readFile(config.interface, "utf8")); if (resource.kind !== "interface") throw Error("React props belong to an interface resource"); @@ -29,14 +30,24 @@ const metadata = await fs.readFile(packageFile, "utf8").then(JSON.parse, (error) throw error; }); await fs.writeFile(packageFile, JSON.stringify({ ...metadata, type: "module" })); -if (config.dependencies) await fs.symlink(config.dependencies, path.join(work, "node_modules")); +const modules = path.join(output, "node_modules"); +await fs.mkdir(modules, { recursive: true }); +await installDependencies({ + destination: modules, + application: config.dependencies, + selected: config.reactSdk ? { "@quixos/camino-react": config.reactSdk } : {}, + reserved: ["@quixos/camino-react"], +}); +await fs.symlink(modules, path.join(work, "node_modules")); const members = Object.create(null); let index = 0; for (const [id, entry] of Object.entries(config.members)) { const member = (iface.template?.members ?? iface.members).find((member) => member.id === id); if ( member?.kind !== "module" || - !["org.quixos.react.component/1", "org.quixos.react.subject-only/1"].includes(member.contract) + !["org.quixos.react.component/1", "org.quixos.react.component/2", "org.quixos.react.subject-only/1"].includes( + member.contract, + ) ) throw Error("Props entry is not a React module member: " + id); if ( @@ -52,7 +63,7 @@ for (const [id, entry] of Object.entries(config.members)) { `import type {${entry.export} as Props} from ${typeImport}; type Assert = T; type ObjectProps = Assert; -type ReservedRendererProps = Assert extends never ? true : false>; +type ReservedRendererProps = Assert extends never ? true : false>; ${member.contract === "org.quixos.react.subject-only/1" ? "type SubjectOnly = Assert<{} extends Props ? true : false>;" : ""} export type {Props};\n`, ); @@ -69,6 +80,8 @@ execFileSync( "--strict", "--target", "es2024", + "--lib", + "es2024,dom,dom.iterable,esnext.disposable", "--module", "nodenext", "--moduleResolution", @@ -88,7 +101,6 @@ for (const file of declarations) { await fs.copyFile(path.join(work, file), path.join(output, "types", file)); } await fs.writeFile(path.join(output, "package.json"), JSON.stringify({ type: "module" })); -if (config.dependencies) await fs.symlink(config.dependencies, path.join(output, "node_modules")); await fs.writeFile( path.join(output, "react-contracts.json"), JSON.stringify({ schemaVersion: 1, interfaceRevisionId: iface.revisionId, members }, null, 2), diff --git a/react-contracts.nix b/react-contracts.nix index acd5b9b..d9c5bab 100644 --- a/react-contracts.nix +++ b/react-contracts.nix @@ -11,6 +11,7 @@ let source = src; interface = context.interface; inherit members dependencies; + reactSdk = context.reactSdk or null; node = "${pkgs.nodejs_24}/bin/node"; tsc = "${context.typescript}/node_modules/typescript/bin/tsc"; }