Skip to content

Commit 5b26154

Browse files
authored
Merge branch 'get-convex:main' into feat/preloadedQuery
2 parents 5b14938 + 278b70a commit 5b26154

27 files changed

+2345
-705
lines changed

management-openapi.json

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"schema": {
2222
"type": "array",
2323
"items": {
24-
"$ref": "#/components/schemas/Team"
24+
"$ref": "#/components/schemas/TeamResponse"
2525
}
2626
}
2727
}
@@ -397,7 +397,15 @@
397397
"ReferralCode": {
398398
"type": "string"
399399
},
400-
"Team": {
400+
"TeamId": {
401+
"type": "integer",
402+
"format": "int64",
403+
"minimum": 0
404+
},
405+
"TeamName": {
406+
"type": "string"
407+
},
408+
"TeamResponse": {
401409
"type": "object",
402410
"required": [
403411
"id",
@@ -445,19 +453,17 @@
445453
"slug": {
446454
"$ref": "#/components/schemas/TeamSlug"
447455
},
456+
"ssoLoginId": {
457+
"type": [
458+
"string",
459+
"null"
460+
]
461+
},
448462
"suspended": {
449463
"type": "boolean"
450464
}
451465
}
452466
},
453-
"TeamId": {
454-
"type": "integer",
455-
"format": "int64",
456-
"minimum": 0
457-
},
458-
"TeamName": {
459-
"type": "string"
460-
},
461467
"TeamSlug": {
462468
"type": "string"
463469
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@
196196
"test-not-silent": "vitest",
197197
"typecheck": "tsc --noEmit --emitDeclarationOnly false",
198198
"test-esm": "node ./scripts/test-esm.mjs && ./scripts/checkdeps.mjs && ./scripts/checkimports.mjs",
199+
"compare-codegen": "node scripts/compare-codegen.mjs",
199200
"generateManagementApiSpec": "openapi-typescript ./management-openapi.json --output ./src/cli/generatedApi.ts --root-types --root-types-no-schema-prefix",
200201
"checkManagementApiSpec": "openapi-typescript ./management-openapi.json --output ./src/cli/generatedApi.ts --root-types --root-types-no-schema-prefix --check",
201202
"generateFunctionLogsApiSpec": "openapi-typescript ./function-logs-openapi.json --output ./src/cli/lib/generatedFunctionLogsApi.ts --root-types --root-types-no-schema-prefix",

schemas/convex.schema.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@
5454
"type": "object",
5555
"description": "Configuration for Convex code generation.",
5656
"properties": {
57+
"legacyJavaScriptFileType": {
58+
"type": "boolean",
59+
"description": "When true (or omitted, default is true), generates separate .js and .d.ts files for the Convex API. When false generates combined .ts files instead. The .ts format is more modern and recommended for TypeScript projects.\n\nNote: This option must be true when using generateCommonJSApi.",
60+
"default": true
61+
},
62+
"legacyComponentApi": {
63+
"type": "boolean",
64+
"description": "When true (or omitted, default is true), generates inline expanded types for component APIs. When false, generates import-based component API references instead. The import-based format is more modern and recommended for projects using components.\n\nNote: When false, component.ts files are automatically generated for each component.",
65+
"default": true
66+
},
5767
"staticApi": {
5868
"type": "boolean",
5969
"description": "When true, generates static `api.d.ts` files, which improves autocomplete and incremental typechecking performance in large codebases.\n\nDocumentation: https://docs.convex.dev/production/project-configuration#using-static-code-generation-beta",

scripts/build.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env node
2-
/* eslint-disable @typescript-eslint/no-var-requires */
2+
/* eslint-disable @typescript-eslint/no-require-imports */
33
const fs = require("fs");
44
const path = require("path");
55
const process = require("process");

scripts/compare-codegen.mjs

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Script to compare directly-generated JS/DTS files with TS-compiled outputs.
4+
*
5+
* This creates two directories with the different codegen approaches and
6+
* automatically opens the diffs in VS Code.
7+
*/
8+
9+
import { apiCodegen } from "../dist/esm/cli/codegen_templates/api.js";
10+
import { serverCodegen } from "../dist/esm/cli/codegen_templates/server.js";
11+
import { noSchemaDataModelDTS } from "../dist/esm/cli/codegen_templates/dataModel.js";
12+
import * as fs from "fs";
13+
import * as path from "path";
14+
import * as os from "os";
15+
import { execSync } from "child_process";
16+
import prettier from "prettier";
17+
18+
const modulePaths = ["foo.ts", "bar/baz.ts"];
19+
20+
console.log("Generating codegen outputs for comparison...\n");
21+
22+
// Generate all versions of our files
23+
const apiJsDts = apiCodegen(modulePaths, { generateJavaScriptApi: true });
24+
const apiJs = apiJsDts.JS;
25+
const apiDts = apiJsDts.DTS;
26+
const apiTsResult = apiCodegen(modulePaths, { generateJavaScriptApi: false });
27+
const apiTs = apiTsResult.TS;
28+
29+
const serverJsDts = serverCodegen({ generateJavaScriptApi: true });
30+
const serverJs = serverJsDts.JS;
31+
const serverDts = serverJsDts.DTS;
32+
const serverTsResult = serverCodegen({ generateJavaScriptApi: false });
33+
const serverTs = serverTsResult.TS;
34+
35+
const dataModelDts = noSchemaDataModelDTS();
36+
37+
const tmpDir = fs.mkdtempSync(
38+
path.join(os.tmpdir(), "convex-codegen-compare-"),
39+
);
40+
41+
try {
42+
// Create directory structure
43+
const directDir = path.join(tmpDir, "direct");
44+
const compiledDir = path.join(tmpDir, "compiled");
45+
const tsSourceDir = path.join(tmpDir, "ts_source");
46+
47+
fs.mkdirSync(directDir, { recursive: true });
48+
fs.mkdirSync(compiledDir, { recursive: true });
49+
fs.mkdirSync(tsSourceDir);
50+
51+
// Write directly-generated JS/DTS files (formatted with prettier)
52+
const formattedApiJs = await prettier.format(apiJs, {
53+
parser: "typescript",
54+
pluginSearchDirs: false,
55+
});
56+
const formattedApiDts = await prettier.format(apiDts, {
57+
parser: "typescript",
58+
pluginSearchDirs: false,
59+
});
60+
const formattedServerJs = await prettier.format(serverJs, {
61+
parser: "typescript",
62+
pluginSearchDirs: false,
63+
});
64+
const formattedServerDts = await prettier.format(serverDts, {
65+
parser: "typescript",
66+
pluginSearchDirs: false,
67+
});
68+
const formattedDataModelDts = await prettier.format(dataModelDts, {
69+
parser: "typescript",
70+
pluginSearchDirs: false,
71+
});
72+
73+
fs.writeFileSync(path.join(directDir, "api.js"), formattedApiJs);
74+
fs.writeFileSync(path.join(directDir, "api.d.ts"), formattedApiDts);
75+
fs.writeFileSync(path.join(directDir, "server.js"), formattedServerJs);
76+
fs.writeFileSync(path.join(directDir, "server.d.ts"), formattedServerDts);
77+
fs.writeFileSync(
78+
path.join(directDir, "dataModel.d.ts"),
79+
formattedDataModelDts,
80+
);
81+
82+
// Format TS files with prettier BEFORE compilation (matching runtime behavior)
83+
const formattedApiTs = await prettier.format(apiTs, {
84+
parser: "typescript",
85+
pluginSearchDirs: false,
86+
});
87+
const formattedServerTs = await prettier.format(serverTs, {
88+
parser: "typescript",
89+
pluginSearchDirs: false,
90+
});
91+
const formattedDataModelTs = await prettier.format(dataModelDts, {
92+
parser: "typescript",
93+
pluginSearchDirs: false,
94+
});
95+
96+
// Write the formatted TS files we'll compile
97+
fs.writeFileSync(path.join(tsSourceDir, "api.ts"), formattedApiTs);
98+
fs.writeFileSync(path.join(tsSourceDir, "server.ts"), formattedServerTs);
99+
fs.writeFileSync(
100+
path.join(tsSourceDir, "dataModel.ts"),
101+
formattedDataModelTs,
102+
);
103+
104+
// Create tsconfig.json for compilation
105+
const tsconfig = {
106+
compilerOptions: {
107+
target: "ES2020",
108+
module: "ES2020",
109+
declaration: true,
110+
emitDeclarationOnly: false,
111+
skipLibCheck: true,
112+
esModuleInterop: true,
113+
moduleResolution: "bundler",
114+
outDir: compiledDir,
115+
rootDir: tsSourceDir,
116+
paths: {
117+
"convex/server": [
118+
path.join(
119+
tmpDir,
120+
"node_modules/convex/dist/esm-types/server/index.d.ts",
121+
),
122+
],
123+
"convex/values": [
124+
path.join(
125+
tmpDir,
126+
"node_modules/convex/dist/esm-types/values/index.d.ts",
127+
),
128+
],
129+
},
130+
},
131+
include: [path.join(tsSourceDir, "*.ts")],
132+
};
133+
fs.writeFileSync(
134+
path.join(tmpDir, "tsconfig.json"),
135+
JSON.stringify(tsconfig, null, 2),
136+
);
137+
138+
// Set up the convex package
139+
const convexPackageDir = path.join(tmpDir, "node_modules", "convex");
140+
const convexDistDir = path.join(convexPackageDir, "dist");
141+
fs.mkdirSync(convexDistDir, { recursive: true });
142+
143+
const projectRoot = process.cwd();
144+
const distDir = path.join(projectRoot, "dist");
145+
146+
// Copy package.json
147+
fs.copyFileSync(
148+
path.join(projectRoot, "package.json"),
149+
path.join(convexPackageDir, "package.json"),
150+
);
151+
152+
// Symlink dist directories
153+
fs.symlinkSync(path.join(distDir, "esm"), path.join(convexDistDir, "esm"));
154+
fs.symlinkSync(
155+
path.join(distDir, "esm-types"),
156+
path.join(convexDistDir, "esm-types"),
157+
);
158+
159+
// Create stub user modules that are imported by api.ts
160+
for (const modulePath of modulePaths) {
161+
const fullPath = path.join(tmpDir, modulePath);
162+
const dir = path.dirname(fullPath);
163+
fs.mkdirSync(dir, { recursive: true });
164+
fs.writeFileSync(fullPath.replace(/\.ts$/, ".js"), "export const foo = 1;");
165+
fs.writeFileSync(
166+
fullPath.replace(/\.ts$/, ".d.ts"),
167+
"export declare const foo: number;",
168+
);
169+
}
170+
171+
// Compile the TS files
172+
const tscPath = path.join(projectRoot, "node_modules", ".bin", "tsc");
173+
try {
174+
execSync(`${tscPath} --project ${path.join(tmpDir, "tsconfig.json")}`, {
175+
cwd: tmpDir,
176+
stdio: "pipe",
177+
});
178+
} catch (error) {
179+
console.error("TypeScript compilation failed:");
180+
console.error(error.stdout?.toString());
181+
console.error(error.stderr?.toString());
182+
process.exit(1);
183+
}
184+
185+
// Format the compiled outputs with prettier
186+
for (const file of [
187+
"api.js",
188+
"api.d.ts",
189+
"server.js",
190+
"server.d.ts",
191+
"dataModel.d.ts",
192+
]) {
193+
const filePath = path.join(compiledDir, file);
194+
const content = fs.readFileSync(filePath, "utf-8");
195+
const formatted = await prettier.format(content, {
196+
parser: "typescript",
197+
pluginSearchDirs: false,
198+
});
199+
fs.writeFileSync(filePath, formatted);
200+
}
201+
202+
console.log("✓ Generated codegen outputs\n");
203+
console.log("Directories created:");
204+
console.log(` Direct (JS/DTS): ${directDir}`);
205+
console.log(` Compiled (TS): ${compiledDir}`);
206+
console.log(` TS Source: ${tsSourceDir}`);
207+
console.log();
208+
console.log("To compare with diff:");
209+
console.log(` diff -r ${directDir} ${compiledDir}`);
210+
console.log();
211+
console.log("To open diffs in VS Code:");
212+
const filesToCompare = [
213+
"api.d.ts",
214+
"api.js",
215+
"server.d.ts",
216+
"server.js",
217+
"dataModel.d.ts",
218+
];
219+
for (const file of filesToCompare) {
220+
console.log(` code --diff ${directDir}/${file} ${compiledDir}/${file}`);
221+
}
222+
console.log();
223+
} catch (error) {
224+
console.error("Error:", error);
225+
// Clean up on error
226+
fs.rmSync(tmpDir, { recursive: true, force: true });
227+
process.exit(1);
228+
}

src/bundler/.eslintrc.cjs

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/bundler/index.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,18 @@ export const actionsDir = "actions";
2626
export function* walkDir(
2727
fs: Filesystem,
2828
dirPath: string,
29+
shouldSkipDir?: (dirPath: string) => boolean,
2930
depth?: number,
3031
): Generator<{ isDir: boolean; path: string; depth: number }, void, void> {
3132
depth = depth ?? 0;
3233
for (const dirEntry of fs.listDir(dirPath).sort(consistentPathSort)) {
3334
const childPath = path.join(dirPath, dirEntry.name);
3435
if (dirEntry.isDirectory()) {
36+
if (shouldSkipDir && shouldSkipDir(childPath)) {
37+
continue;
38+
}
3539
yield { isDir: true, path: childPath, depth };
36-
yield* walkDir(fs, childPath, depth + 1);
40+
yield* walkDir(fs, childPath, shouldSkipDir, depth + 1);
3741
} else if (dirEntry.isFile()) {
3842
yield { isDir: false, path: childPath, depth };
3943
}
@@ -357,7 +361,22 @@ export async function entryPoints(
357361
): Promise<string[]> {
358362
const entryPoints = [];
359363

360-
for (const { isDir, path: fpath, depth } of walkDir(ctx.fs, dir)) {
364+
// Don't deploy directories in convex/ that define components
365+
// as this leads to double-deploying.
366+
const looksLikeNestedComponent = (dirPath: string): boolean => {
367+
const config = path.join(dirPath, "convex.config.ts");
368+
const isComponentDefinition = ctx.fs.exists(config);
369+
if (isComponentDefinition) {
370+
logVerbose(chalk.yellow(`Skipping component directory ${dirPath}`));
371+
}
372+
return isComponentDefinition;
373+
};
374+
375+
for (const { isDir, path: fpath, depth } of walkDir(
376+
ctx.fs,
377+
dir,
378+
looksLikeNestedComponent,
379+
)) {
361380
if (isDir) {
362381
continue;
363382
}

0 commit comments

Comments
 (0)