Skip to content

Commit

Permalink
chore: clean up code gen using dedent
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanFlurry committed Mar 6, 2024
1 parent fcfc3bd commit 2e17afd
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 237 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Open Game Backend

## Prerequisites

- Deno
- Docker ([#125](https://github.com/rivet-gg/opengb/issues/125))
- Git

## Install

<!--
Expand Down
202 changes: 120 additions & 82 deletions deno.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/build/deps.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { crypto } from "https://deno.land/std@0.208.0/crypto/mod.ts";
export { encodeHex } from "https://deno.land/std@0.208.0/encoding/hex.ts";
export * as tjs from "npm:typescript-json-schema@^0.62.0";
import dedent from "npm:dedent@^1.5.1";
export { dedent };
227 changes: 118 additions & 109 deletions src/build/gen.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { dedent } from "./deps.ts";
import { dirname, join, relative } from "../deps.ts";
import {
Module,
Expand All @@ -18,25 +19,26 @@ export async function compileModuleHelper(
const runtimePath = genRuntimeModPath(project);

// Generate source
const dbImports = [
'import prisma from "./prisma/esm.js";',
"export { prisma };",
"export const Prisma = prisma.Prisma;",
];
const source = [
autoGenHeader(),
"",
`import { ModuleContext as ModuleContextInner } from "${runtimePath}";`,
`import { Registry as RegistryTypeInner } from "./registry.d.ts";`,
"",
...(module.db ? dbImports : []),
"",
`export { RuntimeError } from "${runtimePath}";`,
`export type ModuleContext = ModuleContextInner<RegistryTypeInner, ${
module.db ? "prisma.PrismaClient" : "undefined"
}>;`,
"",
].join("\n");
let dbImports = "";
if (module.db) {
dbImports = dedent`
import prisma from "./prisma/esm.js";
export { prisma };
export const Prisma = prisma.Prisma;
`;
}

const source = dedent`
import { ModuleContext as ModuleContextInner } from "${runtimePath}";
import { ${module.name}$$Registry as RegistryTypeInner } from "./registry.d.ts";
${dbImports}
export { RuntimeError } from "${runtimePath}";
export type ModuleContext = ModuleContextInner<RegistryTypeInner, ${
module.db ? "prisma.PrismaClient" : "undefined"
}};
`;

// Write source
const helperPath = moduleGenPath(project, module);
Expand All @@ -50,26 +52,25 @@ export async function compileTestHelper(
) {
const runtimePath = genRuntimeModPath(project);

const source = [
autoGenHeader(),
'import * as module from "./mod.ts";',
`import { Runtime, TestContext as TestContextInner } from "${runtimePath}";`,
`import { Registry as RegistryTypeInner } from "./registry.d.ts";`,
`import config from "${project.path}/_gen/runtime_config.ts";`,
"",
'export * from "./mod.ts";',
"",
`export type TestContext = TestContextInner<RegistryTypeInner, ${
module.db ? "module.prisma.PrismaClient" : "undefined"
}>;`,
"",
"export type TestFn = (ctx: TestContext) => Promise<void>;",
"",
"export function test(name: string, fn: TestFn) {",
` Runtime.test(config, "${module.name}", name, fn);`,
"}",
"",
].join("\n");
const source = dedent`
${autoGenHeader()}
import * as module from "./mod.ts";
import { Runtime, TestContext as TestContextInner } from "${runtimePath}";
import { Registry as RegistryTypeInner } from "./registry.d.ts";
import config from "${project.path}/_gen/runtime_config.ts";
export * from "./mod.ts";
export type TestContext = TestContextInner<RegistryTypeInner, ${
module.db ? "module.prisma.PrismaClient" : "undefined"
}};
export type TestFn = (ctx: TestContext) => Promise<void>;
export function test(name: string, fn: TestFn) {
Runtime.test(config, "${module.name}", name, fn);
}
`;

// Write source
const helperPath = testGenPath(project, module);
Expand All @@ -84,21 +85,20 @@ export async function compileScriptHelper(
) {
const runtimePath = genRuntimeModPath(project);

const source = [
autoGenHeader(),
'import * as module from "../mod.ts";',
`import { ScriptContext as ScriptContextInner } from "${runtimePath}";`,
`import { Registry as RegistryTypeInner } from "../registry.d.ts";`,
"",
module.db ? 'import { PrismaClient } from "../prisma/index.d.ts";' : "", // NOTE: This is not used anywhere
"",
'export * from "../mod.ts";',
"",
`export type ScriptContext = ScriptContextInner<RegistryTypeInner, ${
module.db ? "module.prisma.PrismaClient" : "undefined"
}>;`,
"",
].join("\n");
const source = dedent`
${autoGenHeader()}
import * as module from "../mod.ts";
import { ScriptContext as ScriptContextInner } from "${runtimePath}";
import { Registry as RegistryTypeInner } from "../registry.d.ts";
${module.db ? 'import { PrismaClient } from "../prisma/index.d.ts";' : ""}
export * from "../mod.ts";
export type ScriptContext = ScriptContextInner<RegistryTypeInner, ${
module.db ? "module.prisma.PrismaClient" : "undefined"
}};
`;

// Write source
const helperPath = scriptGenPath(project, module, script);
Expand All @@ -113,12 +113,11 @@ export async function compileTypeHelpers(project: Project) {
"registry.d.ts",
);

const modules: string[] = [];

let moduleTypesSource = "";
let moduleRegistrySource = "";
for (const module of project.modules.values()) {
const scripts: string[] = [];

const moduleInterfaceName = `${module.name}Module`;
let scriptImportsSource = "";
let scriptInterfaceSource = "";
for (const script of module.scripts.values()) {
const scriptId = `${module.name}$$${script.name}`;

Expand All @@ -131,58 +130,63 @@ export async function compileTypeHelpers(project: Project) {
`${script.name}.ts`,
);

const pathComment = `// ${module.name}/${script.name}`;
const importLine =
`import type { Request as ${requestTypeName}, Response as ${responseTypeName} } from ${
JSON.stringify(importPath)
};`;

const interfaceDef = [
`interface ${moduleInterfaceName} {`,
`\t${script.name}: {`,
`\t\trequest: ${requestTypeName};`,
`\t\tresponse: ${responseTypeName};`,
`\t};`,
`}`,
].join("\n");

scripts.push([pathComment, importLine, interfaceDef].join("\n"));
scriptImportsSource += dedent`
// ${module.name}/${script.name}
import type { Request as ${requestTypeName}, Response as ${responseTypeName} } from ${
JSON.stringify(importPath)
};
`;

scriptInterfaceSource += dedent`
${script.name}: {
request: ${requestTypeName};
response: ${responseTypeName};
};
`;
}

const moduleComment = [
"//",
`// Types for ${module.name}`,
"//",
"",
`interface ${moduleInterfaceName} {}`,
].join("\n");
const moduleInterfaceName = `${module.name}$$Module`;
moduleTypesSource += dedent`
//
// Types for ${module.name}
//
const scriptBody = scripts.join("\n\n");
${scriptImportsSource}
const interfaceDef =
`interface Registry {\n\t${module.name}: ${moduleInterfaceName};\n}`;
interface ${moduleInterfaceName} {
${scriptInterfaceSource}
}
`;

modules.push([moduleComment, scriptBody, interfaceDef].join("\n"));
moduleRegistrySource += dedent`
${module.name}: ${moduleInterfaceName};
`;
}

const source = `${autoGenHeader()}${
modules.join("\n\n\n\n")
}\n\n${registryTypes.trim()}`;
const source = dedent`
${autoGenHeader()}
export type RequestOf<T> = T extends { request: any } ? T["request"] : never;
export type ResponseOf<T> = T extends { response: any } ? T["response"] : never;
export type RegistryCallFn<ThisType> = <M extends keyof Registry & string, S extends keyof Registry[M] & string>(
this: ThisType,
module: M,
script: S,
req: RequestOf<Registry[M][S]>,
) => Promise<ResponseOf<Registry[M][S]>>;
interface Registry {
${moduleRegistrySource}
}
${moduleTypesSource}
`;

await Deno.mkdir(dirname(typedefPath), { recursive: true });
await Deno.writeTextFile(typedefPath, source);
}

const registryTypes = `
export type RequestOf<T> = T extends { request: any } ? T["request"] : never;
export type ResponseOf<T> = T extends { response: any } ? T["response"] : never;
export type RegistryCallFn<ThisType> = <M extends keyof Registry & string, S extends keyof Registry[M] & string>(
this: ThisType,
module: M,
script: S,
req: RequestOf<Registry[M][S]>,
) => Promise<ResponseOf<Registry[M][S]>>;`;

export async function compileModuleTypeHelper(
project: Project,
module: Module,
Expand All @@ -193,15 +197,20 @@ export async function compileModuleTypeHelper(
"registry.d.ts",
);

let source = autoGenHeader();

source += `import { Registry as FullRegistry } from "${typedefPath}";\n`;
source += "export interface Registry {\n";
source += `\t${module.name}: FullRegistry["${module.name}"];\n`;
for (const dependencyName in module.config.dependencies) {
source += `\t${dependencyName}: FullRegistry["${dependencyName}"];\n`;
}
source += "}\n";
const moduleDependencies = Object.keys(module.config.dependencies || {})
.map((dependencyName) =>
`${dependencyName}: FullRegistry["${dependencyName}"]`
)
.join(";\n\t");

const source = dedent`
${autoGenHeader()}
import { Registry as FullRegistry } from "${typedefPath}";
export interface Registry {
${module.name}: FullRegistry["${module.name}"];
${moduleDependencies}
}
`;

const helperPath = typeGenPath(project, module);
await Deno.mkdir(dirname(helperPath), { recursive: true });
Expand Down
13 changes: 8 additions & 5 deletions src/build/misc.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
export function autoGenHeader(skip: boolean = false) {
if (skip) return "";
import { dedent } from "./deps.ts";

return `// This file is auto-generated by the Open Game Backend build system.
// Do not edit this file directly.
`;
export function autoGenHeader() {
return dedent`
// This file is auto-generated by the Open Game Backend build system.
// Do not edit this file directly.
//
// Generated at ${new Date().toISOString()}
`;
}
38 changes: 0 additions & 38 deletions src/cli/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import { build, DbDriver, Format, Runtime } from "../../build/mod.ts";
import { ensurePostgresRunning } from "../../utils/postgres_daemon.ts";

export const startCommand = new Command<GlobalOpts>()
.option("--no-format", "Don't format modules")
.option("--no-build", "Don't build source files")
.option("--no-migrate", "Don't migrate database")
// .option("--no-lint", "Don't lint the codebase")
.option("--no-check", "Don't check source files before running")
.option("--no-watch", "Don't automatically restart server on changes")
.action(
Expand All @@ -19,21 +16,6 @@ export const startCommand = new Command<GlobalOpts>()

const entrypointPath = join(project.path, "_gen", "entrypoint.ts");

// TODO: Only format local modules
// Fmt project
if (opts.format) {
const cmd = await new Deno.Command("deno", {
args: [
"fmt",
project.path,
],
stdout: "inherit",
stderr: "inherit",
})
.output();
if (!cmd.success) throw new Error("Format failed");
}

// Build project
if (opts.build) {
await build(project, {
Expand All @@ -43,26 +25,6 @@ export const startCommand = new Command<GlobalOpts>()
});
}

// Migrate project
if (opts.migrate) {
// TODO
}

// TODO: Only lint local modules
// Lint project
// if (opts.lint) {
// const cmd = await new Deno.Command("deno", {
// args: [
// "lint",
// project.path,
// ],
// stdout: "inherit",
// stderr: "inherit",
// })
// .output();
// if (!cmd.success) throw new Error("Format failed");
// }

// Determine args
const args = [
"--allow-env",
Expand Down
2 changes: 1 addition & 1 deletion src/project/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export async function loadModule(
);
throw new Error(
`Found extra scripts not registered in module.yaml:\n\n${
scriptList.join("")
scriptList.join(", ")
}\nAdd these scripts to the module.yaml file.`,
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/template/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export async function run(
if (createTest) {
// Write default config
const testTs =
`import { TestContext, Runtime } from "@generated/${moduleName}/test.ts";
`import { TestContext, Runtime } from "../_gen/test.ts";
export { assertEquals } from "https://deno.land/std@0.208.0/assert/mod.ts";
test("e2e", async (ctx: TestContext) => {
Expand Down
Loading

0 comments on commit 2e17afd

Please sign in to comment.