Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/tasks/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Effect, FileSystem } from "effect";
import path from "node:path";

import {
dependencyVersionMap,
getCreateTemplateDependencies,
getDependencyVersion,
PRISMA_DENO_CLI_PACKAGE,
Expand All @@ -17,7 +18,7 @@ type PackageJson = {
devDependencies?: Record<string, string>;
scripts?: Record<string, string>;
resolutions?: Record<string, string>;
overrides?: Record<string, string>;
overrides?: Record<string, string | Record<string, string>>;
[key: string]: unknown;
};

Expand Down Expand Up @@ -156,6 +157,21 @@ export const addPackageDependencyEffect = Effect.fn("Dependencies.add")(function
yield* writePackageJson(packageJsonPath, packageJson);
});

// alchemy declares an optional peer on mongodb 6 for resources the scaffold
// never loads, and @prisma/orm-mongo requires mongodb 7. npm fails the install
// on that conflict; the other package managers only warn.
const resolveAlchemyMongoPeerForNpm = Effect.fn("Dependencies.resolveAlchemyMongoPeerForNpm")(
function* (projectDir: string) {
const packageJsonPath = path.join(projectDir, "package.json");
const packageJson = yield* readPackageJson(packageJsonPath);
packageJson.overrides = {
...packageJson.overrides,
alchemy: { mongodb: dependencyVersionMap.mongodb },
Comment thread
coderabbitai[bot] marked this conversation as resolved.
};
yield* writePackageJson(packageJsonPath, packageJson);
},
);

export const writePrismaDependenciesEffect = Effect.fn("Dependencies.writePrisma")(function* (
provider: DatabaseProvider,
packageManager: PackageManager,
Expand Down Expand Up @@ -187,6 +203,9 @@ export const writePrismaDependenciesEffect = Effect.fn("Dependencies.writePrisma
projectDir: path.join(projectDir, "packages/database"),
});
}
if (provider === "mongo" && packageManager === "npm") {
yield* resolveAlchemyMongoPeerForNpm(projectDir);
}
});

export const writeCreateTemplateDependenciesEffect = Effect.fn("Dependencies.writeTemplate")(
Expand Down
5 changes: 5 additions & 0 deletions templates/create/_package-manager/pnpm-workspace.yaml.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ minimumReleaseAgeExclude:
- "@prisma/*"
overrides:
effect: "4.0.0-rc.115"
{{#if (eq provider "mongo")}}
peerDependencyRules:
allowedVersions:
"alchemy>mongodb": "7"
{{/if}}
{{/if}}
68 changes: 66 additions & 2 deletions tests/e2e/create-prisma.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
import { tmpdir } from "node:os";
import path from "node:path";

import { MongoMemoryReplSet } from "mongodb-memory-server";

import { runCreateCommand } from "../../src/commands/create";
import { dependencyVersionMap } from "../../src/constants/dependencies";
import type { CreateCommandResult } from "../../src/result";
Expand Down Expand Up @@ -128,11 +130,16 @@ async function fetchUntilResponding(url: string, deadline: number): Promise<Resp
}
}

async function verifyBuiltServer(projectDir: string, port: number, expectedStatus: number) {
async function verifyBuiltServer(
projectDir: string,
port: number,
expectedStatus: number,
env: Record<string, string> = {},
) {
const process = Bun.spawn({
cmd: ["node", "dist/server.mjs"],
cwd: projectDir,
env: { ...Bun.env, PORT: String(port) },
env: { ...Bun.env, ...env, PORT: String(port) },
stdout: "pipe",
stderr: "pipe",
});
Expand Down Expand Up @@ -682,6 +689,63 @@ describe("create-prisma e2e", () => {
TEST_TIMEOUT,
);

test(
"installs a MongoDB app with npm and runs it against MongoDB",
async () => {
const rootDir = await mkdtemp(path.join(tmpdir(), "create-prisma-mongo-npm-e2e-"));
tempRoots.push(rootDir);
const { result, exitCode } = await runCreatePrismaJson(rootDir, [
"mongo-npm-app",
"--template",
"minimal",
"--provider",
"mongodb",
"--authoring",
"psl",
"--package-manager",
"npm",
"--no-deploy",
"--json",
]);

expect(result).toMatchObject({
ok: true,
project: { databaseProvider: "mongo", packageManager: "npm" },
deployment: null,
});
expect(exitCode).toBe(0);

const projectDir = path.join(rootDir, "mongo-npm-app");
const packageJson = JSON.parse(
await readFile(path.join(projectDir, "package.json"), "utf8"),
) as Record<string, any>;
expect(packageJson.overrides.alchemy).toEqual({ mongodb: dependencyVersionMap.mongodb });
const installedDriver = JSON.parse(
await readFile(path.join(projectDir, "node_modules/mongodb/package.json"), "utf8"),
) as { version: string };
expect(installedDriver.version).toMatch(/^7\./);
expect(
await pathExists(path.join(projectDir, "node_modules/alchemy/node_modules/mongodb")),
).toBe(false);

await runCommand(projectDir, ["npm", "run", "contract:emit"]);
await runCommand(projectDir, ["npm", "run", "build"]);
await runCommand(projectDir, ["npx", "tsc", "--noEmit"]);

const mongod = await MongoMemoryReplSet.create({ replSet: { count: 1 } });
try {
const body = await verifyBuiltServer(projectDir, 46_160, 200, {
DATABASE_URL: mongod.getUri("app"),
});
const { users } = JSON.parse(body) as { users: Array<{ name: string }> };
expect(users.map((user) => user.name)).toEqual(["Alice", "Bob", "Carol"]);
} finally {
await mongod.stop();
}
},
TEST_TIMEOUT,
);

test(
"generates and checks a minimal Deno Prisma Postgres app",
async () => {
Expand Down
10 changes: 9 additions & 1 deletion tests/install.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -577,13 +577,21 @@ describe("generated templates", () => {
' - "@prisma/*"',
"overrides:",
` effect: "${dependencyVersionMap.effect}"`,
...(provider === "mongo"
? ["peerDependencyRules:", " allowedVersions:", ' "alchemy>mongodb": "7"']
: []),
"",
].join("\n"),
);
} else if (packageManager === "yarn") {
expect(packageJson.resolutions?.effect).toBe(dependencyVersionMap.effect);
} else {
expect(packageJson.overrides?.effect).toBe(dependencyVersionMap.effect);
expect(packageJson.overrides).toEqual({
effect: dependencyVersionMap.effect,
...(provider === "mongo" && packageManager === "npm"
? { alchemy: { mongodb: dependencyVersionMap.mongodb } }
: {}),
});
}
} finally {
await rm(projectDir, { recursive: true, force: true });
Expand Down
Loading