Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"skipFiles": ["<node_internals>/**"],
"type": "node",
"args": ["generate"],
"cwd": "${workspaceFolder}/samples/blog"
"cwd": "${workspaceFolder}/samples/orm"
},
{
"name": "Debug with TSX",
Expand Down
43 changes: 43 additions & 0 deletions packages/cli/src/actions/action-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { isDataSource } from '@zenstackhq/language/ast';
import { PrismaSchemaGenerator } from '@zenstackhq/sdk';
import colors from 'colors';
import fs from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import { CliError } from '../cli-error';

Expand Down Expand Up @@ -156,3 +157,45 @@ export function getOutputPath(options: { output?: string }, schemaFile: string)
return path.dirname(schemaFile);
}
}
export async function getZenStackPackages(
searchPath: string,
): Promise<Array<{ pkg: string; version: string | undefined }>> {
const pkgJsonFile = findUp(['package.json'], searchPath, false);
if (!pkgJsonFile) {
return [];
}

let pkgJson: {
dependencies?: Record<string, unknown>;
devDependencies?: Record<string, unknown>;
};
try {
pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, 'utf8'));
} catch {
return [];
}

const packages = Array.from(
new Set(
[...Object.keys(pkgJson.dependencies ?? {}), ...Object.keys(pkgJson.devDependencies ?? {})].filter((p) =>
p.startsWith('@zenstackhq/'),
),
),
).sort();

const require = createRequire(import.meta.url);

const result = packages.map((pkg) => {
try {
const depPkgJson = require(`${pkg}/package.json`);
Comment thread
ymc9 marked this conversation as resolved.
Outdated
if (depPkgJson.private) {
return undefined;
}
return { pkg, version: depPkgJson.version as string };
} catch {
return { pkg, version: undefined };
}
});

return result.filter((p) => !!p);
}
45 changes: 44 additions & 1 deletion packages/cli/src/actions/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { watch } from 'chokidar';
import ora, { type Ora } from 'ora';
import { CliError } from '../cli-error';
import * as corePlugins from '../plugins';
import { getOutputPath, getSchemaFile, loadSchemaDocument } from './action-utils';
import { getOutputPath, getSchemaFile, getZenStackPackages, loadSchemaDocument } from './action-utils';
import semver from 'semver';

type Options = {
schema?: string;
Expand All @@ -27,6 +28,11 @@ type Options = {
* CLI action for generating code from schema
*/
export async function run(options: Options) {
try {
await checkForMismatchedPackages(process.cwd());
} catch (err) {
console.warn(colors.yellow(`Failed to check for mismatched ZenStack packages: ${err}`));
}
const model = await pureGenerate(options, false);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if (options.watch) {
Expand Down Expand Up @@ -315,3 +321,40 @@ async function loadPluginModule(provider: string, basePath: string) {
return undefined;
}
}

async function checkForMismatchedPackages(projectPath: string) {
const packages = await getZenStackPackages(projectPath);
if (!packages.length) {
return false;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const versions = new Set<string>();
for (const { version } of packages) {
if (version) {
versions.add(version);
}
}

if (versions.size > 1) {
const message =
'WARNING: Multiple versions of ZenStack packages detected.\n\tThis will probably cause issues and break your types.';
const slashes = '/'.repeat(73);
const latestVersion = semver.sort(Array.from(versions)).reverse()[0]!;

console.warn(colors.yellow(`${slashes}\n\n\t${message}\n`));
for (const { pkg, version } of packages) {
if (!version) continue;

if (version === latestVersion) {
console.log(`\t${pkg.padEnd(32)}\t${colors.green(version)}`);
} else {
console.log(`\t${pkg.padEnd(32)}\t${colors.yellow(version)}`);
}
}
console.warn(`\n${colors.yellow(slashes)}`);

return true;
}

return false;
}
49 changes: 2 additions & 47 deletions packages/cli/src/actions/info.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import colors from 'colors';
import path from 'node:path';
import { getZenStackPackages } from './action-utils';

/**
* CLI action for getting information about installed ZenStack packages
*/
export async function run(projectPath: string) {
const packages = await getZenStackPackages(projectPath);
if (!packages) {
if (!packages.length) {
console.error('Unable to locate package.json. Are you in a valid project directory?');
return;
}
Expand All @@ -24,48 +24,3 @@ export async function run(projectPath: string) {
console.warn(colors.yellow('WARNING: Multiple versions of Zenstack packages detected. This may cause issues.'));
}
}

async function getZenStackPackages(projectPath: string): Promise<Array<{ pkg: string; version: string | undefined }>> {
let pkgJson: {
dependencies: Record<string, unknown>;
devDependencies: Record<string, unknown>;
};
const resolvedPath = path.resolve(projectPath);
try {
pkgJson = (
await import(path.join(resolvedPath, 'package.json'), {
with: { type: 'json' },
})
).default;
} catch {
return [];
}

const packages = Array.from(
new Set(
[...Object.keys(pkgJson.dependencies ?? {}), ...Object.keys(pkgJson.devDependencies ?? {})].filter(
(p) => p.startsWith('@zenstackhq/') || p === 'zenstack',
),
),
).sort();

const result = await Promise.all(
packages.map(async (pkg) => {
try {
const depPkgJson = (
await import(`${pkg}/package.json`, {
with: { type: 'json' },
})
).default;
if (depPkgJson.private) {
return undefined;
}
return { pkg, version: depPkgJson.version as string };
} catch {
return { pkg, version: undefined };
}
}),
);

return result.filter((p) => !!p);
}
Loading