Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.

Commit 9a5f5da

Browse files
sanny-ioymc9
andauthored
chore: add warning for mismatched versions during zen generate (#660)
* chore: add warning for mismatched versions during `zen generate` * chore: swap falsy checks for length checks * chore: do not block generation upon error * Add additional new line. * Trigger Build * Trigger Build * fix: addressing PR comments - use `createRequire` instead of dynamic import for better compatibility - use file search to locate nearest package.json file - use longer padding length when formatting warnings * fix: make sure require base dir is absolute path --------- Co-authored-by: ymc9 <104139426+ymc9@users.noreply.github.com>
1 parent d9cdd9c commit 9a5f5da

File tree

4 files changed

+92
-51
lines changed

4 files changed

+92
-51
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"skipFiles": ["<node_internals>/**"],
1212
"type": "node",
1313
"args": ["generate"],
14-
"cwd": "${workspaceFolder}/samples/blog"
14+
"cwd": "${workspaceFolder}/samples/orm"
1515
},
1616
{
1717
"name": "Debug with TSX",

packages/cli/src/actions/action-utils.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type Model, isDataSource } from '@zenstackhq/language/ast';
33
import { PrismaSchemaGenerator } from '@zenstackhq/sdk';
44
import colors from 'colors';
55
import fs from 'node:fs';
6+
import { createRequire } from 'node:module';
67
import path from 'node:path';
78
import { CliError } from '../cli-error';
89

@@ -142,10 +143,10 @@ function findUp<Multiple extends boolean = false>(
142143
}
143144
const target = names.find((name) => fs.existsSync(path.join(cwd, name)));
144145
if (multiple === false && target) {
145-
return path.join(cwd, target) as FindUpResult<Multiple>;
146+
return path.resolve(cwd, target) as FindUpResult<Multiple>;
146147
}
147148
if (target) {
148-
result.push(path.join(cwd, target));
149+
result.push(path.resolve(cwd, target));
149150
}
150151
const up = path.resolve(cwd, '..');
151152
if (up === cwd) {
@@ -173,3 +174,45 @@ export function getOutputPath(options: { output?: string }, schemaFile: string)
173174
return path.dirname(schemaFile);
174175
}
175176
}
177+
export async function getZenStackPackages(
178+
searchPath: string,
179+
): Promise<Array<{ pkg: string; version: string | undefined }>> {
180+
const pkgJsonFile = findUp(['package.json'], searchPath, false);
181+
if (!pkgJsonFile) {
182+
return [];
183+
}
184+
185+
let pkgJson: {
186+
dependencies?: Record<string, unknown>;
187+
devDependencies?: Record<string, unknown>;
188+
};
189+
try {
190+
pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, 'utf8'));
191+
} catch {
192+
return [];
193+
}
194+
195+
const packages = Array.from(
196+
new Set(
197+
[...Object.keys(pkgJson.dependencies ?? {}), ...Object.keys(pkgJson.devDependencies ?? {})].filter((p) =>
198+
p.startsWith('@zenstackhq/'),
199+
),
200+
),
201+
).sort();
202+
203+
const require = createRequire(pkgJsonFile);
204+
205+
const result = packages.map((pkg) => {
206+
try {
207+
const depPkgJson = require(`${pkg}/package.json`);
208+
if (depPkgJson.private) {
209+
return undefined;
210+
}
211+
return { pkg, version: depPkgJson.version as string };
212+
} catch {
213+
return { pkg, version: undefined };
214+
}
215+
});
216+
217+
return result.filter((p) => !!p);
218+
}

packages/cli/src/actions/generate.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import { watch } from 'chokidar';
1212
import ora, { type Ora } from 'ora';
1313
import { CliError } from '../cli-error';
1414
import * as corePlugins from '../plugins';
15-
import { getOutputPath, getSchemaFile, loadSchemaDocument } from './action-utils';
15+
import { getOutputPath, getSchemaFile, getZenStackPackages, loadSchemaDocument } from './action-utils';
16+
import semver from 'semver';
1617

1718
type Options = {
1819
schema?: string;
@@ -27,6 +28,11 @@ type Options = {
2728
* CLI action for generating code from schema
2829
*/
2930
export async function run(options: Options) {
31+
try {
32+
await checkForMismatchedPackages(process.cwd());
33+
} catch (err) {
34+
console.warn(colors.yellow(`Failed to check for mismatched ZenStack packages: ${err}`));
35+
}
3036
const model = await pureGenerate(options, false);
3137

3238
if (options.watch) {
@@ -315,3 +321,40 @@ async function loadPluginModule(provider: string, basePath: string) {
315321
return undefined;
316322
}
317323
}
324+
325+
async function checkForMismatchedPackages(projectPath: string) {
326+
const packages = await getZenStackPackages(projectPath);
327+
if (!packages.length) {
328+
return false;
329+
}
330+
331+
const versions = new Set<string>();
332+
for (const { version } of packages) {
333+
if (version) {
334+
versions.add(version);
335+
}
336+
}
337+
338+
if (versions.size > 1) {
339+
const message =
340+
'WARNING: Multiple versions of ZenStack packages detected.\n\tThis will probably cause issues and break your types.';
341+
const slashes = '/'.repeat(73);
342+
const latestVersion = semver.sort(Array.from(versions)).reverse()[0]!;
343+
344+
console.warn(colors.yellow(`${slashes}\n\n\t${message}\n`));
345+
for (const { pkg, version } of packages) {
346+
if (!version) continue;
347+
348+
if (version === latestVersion) {
349+
console.log(`\t${pkg.padEnd(32)}\t${colors.green(version)}`);
350+
} else {
351+
console.log(`\t${pkg.padEnd(32)}\t${colors.yellow(version)}`);
352+
}
353+
}
354+
console.warn(`\n${colors.yellow(slashes)}`);
355+
356+
return true;
357+
}
358+
359+
return false;
360+
}

packages/cli/src/actions/info.ts

Lines changed: 2 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import colors from 'colors';
2-
import path from 'node:path';
2+
import { getZenStackPackages } from './action-utils';
33

44
/**
55
* CLI action for getting information about installed ZenStack packages
66
*/
77
export async function run(projectPath: string) {
88
const packages = await getZenStackPackages(projectPath);
9-
if (!packages) {
9+
if (!packages.length) {
1010
console.error('Unable to locate package.json. Are you in a valid project directory?');
1111
return;
1212
}
@@ -24,48 +24,3 @@ export async function run(projectPath: string) {
2424
console.warn(colors.yellow('WARNING: Multiple versions of Zenstack packages detected. This may cause issues.'));
2525
}
2626
}
27-
28-
async function getZenStackPackages(projectPath: string): Promise<Array<{ pkg: string; version: string | undefined }>> {
29-
let pkgJson: {
30-
dependencies: Record<string, unknown>;
31-
devDependencies: Record<string, unknown>;
32-
};
33-
const resolvedPath = path.resolve(projectPath);
34-
try {
35-
pkgJson = (
36-
await import(path.join(resolvedPath, 'package.json'), {
37-
with: { type: 'json' },
38-
})
39-
).default;
40-
} catch {
41-
return [];
42-
}
43-
44-
const packages = Array.from(
45-
new Set(
46-
[...Object.keys(pkgJson.dependencies ?? {}), ...Object.keys(pkgJson.devDependencies ?? {})].filter(
47-
(p) => p.startsWith('@zenstackhq/') || p === 'zenstack',
48-
),
49-
),
50-
).sort();
51-
52-
const result = await Promise.all(
53-
packages.map(async (pkg) => {
54-
try {
55-
const depPkgJson = (
56-
await import(`${pkg}/package.json`, {
57-
with: { type: 'json' },
58-
})
59-
).default;
60-
if (depPkgJson.private) {
61-
return undefined;
62-
}
63-
return { pkg, version: depPkgJson.version as string };
64-
} catch {
65-
return { pkg, version: undefined };
66-
}
67-
}),
68-
);
69-
70-
return result.filter((p) => !!p);
71-
}

0 commit comments

Comments
 (0)