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
72 changes: 72 additions & 0 deletions packages/schema/src/cli/actions/generate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { PluginError } from '@zenstackhq/sdk';
import colors from 'colors';
import path from 'path';
import { Context } from '../../types';
import { PackageManagers } from '../../utils/pkg-utils';
import { CliError } from '../cli-error';
import {
checkNewVersion,
checkRequiredPackage,
getZenStackPackages,
loadDocument,
requiredPrismaVersion,
} from '../cli-util';
import { PluginRunner } from '../plugin-runner';

type Options = {
schema: string;
packageManager: PackageManagers | undefined;
dependencyCheck: boolean;
versionCheck: boolean;
};

/**
* CLI action for generating code from schema
*/
export async function generate(projectPath: string, options: Options) {
if (options.dependencyCheck) {
checkRequiredPackage('prisma', requiredPrismaVersion);
checkRequiredPackage('@prisma/client', requiredPrismaVersion);
}

// check for multiple versions of Zenstack packages
const packages = getZenStackPackages(projectPath);
if (packages) {
const versions = new Set<string>(packages.map((p) => p.version));
if (versions.size > 1) {
console.warn(
colors.yellow(
'WARNING: Multiple versions of Zenstack packages detected. Run "zenstack info" to see details.'
)
);
}
}

const tasks = [runPlugins(options)];

if (options.versionCheck) {
tasks.push(checkNewVersion());
}

await Promise.all(tasks);
}

async function runPlugins(options: Options) {
const model = await loadDocument(options.schema);
const context: Context = {
schema: model,
schemaPath: path.resolve(options.schema),
outDir: path.dirname(options.schema),
};

try {
await new PluginRunner().run(context);
} catch (err) {
if (err instanceof PluginError) {
console.error(colors.red(`${err.plugin}: ${err.message}`));
throw new CliError(err.message);
} else {
throw err;
}
}
}
3 changes: 3 additions & 0 deletions packages/schema/src/cli/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './generate';
export * from './info';
export * from './init';
44 changes: 44 additions & 0 deletions packages/schema/src/cli/actions/info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import colors from 'colors';
import getLatestVersion from 'get-latest-version';
import ora from 'ora';
import semver from 'semver';
import { getZenStackPackages } from '../cli-util';

/**
* CLI action for getting information about installed ZenStack packages
*/
export async function info(projectPath: string) {
const packages = getZenStackPackages(projectPath);
if (!packages) {
console.error('Unable to locate package.json. Are you in a valid project directory?');
return;
}

console.log('Installed ZenStack Packages:');
const versions = new Set<string>();
for (const { pkg, version } of packages) {
versions.add(version);
console.log(` ${colors.green(pkg.padEnd(20))}\t${version}`);
}

if (versions.size > 1) {
console.warn(colors.yellow('WARNING: Multiple versions of Zenstack packages detected. This may cause issues.'));
} else if (versions.size > 0) {
const spinner = ora('Checking npm registry').start();
const latest = await getLatestVersion('zenstack');

if (!latest) {
spinner.fail('unable to check for latest version');
} else {
spinner.succeed();
const version = [...versions][0];
if (semver.gt(latest, version)) {
console.log(`A newer version of Zenstack is available: ${latest}.`);
} else if (semver.gt(version, latest)) {
console.log('You are using a pre-release version of Zenstack.');
} else {
console.log('You are using the latest version of Zenstack.');
}
}
}
}
77 changes: 77 additions & 0 deletions packages/schema/src/cli/actions/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import colors from 'colors';
import fs from 'fs';
import path from 'path';
import { PackageManagers, ensurePackage, installPackage } from '../../utils/pkg-utils';
import { getVersion } from '../../utils/version-utils';
import { CliError } from '../cli-error';
import { checkNewVersion } from '../cli-util';

type Options = {
prisma: string | undefined;
packageManager: PackageManagers | undefined;
versionCheck: boolean;
tag?: string;
};

/**
* CLI action for initializing an existing project
*/
export async function init(projectPath: string, options: Options) {
if (!fs.existsSync(projectPath)) {
console.error(`Path does not exist: ${projectPath}`);
throw new CliError('project path does not exist');
}

const defaultPrismaSchemaLocation = './prisma/schema.prisma';
let prismaSchema = options.prisma;
if (prismaSchema) {
if (!fs.existsSync(prismaSchema)) {
console.error(`Prisma schema file does not exist: ${prismaSchema}`);
throw new CliError('prisma schema does not exist');
}
} else if (fs.existsSync(defaultPrismaSchemaLocation)) {
prismaSchema = defaultPrismaSchemaLocation;
}

const zmodelFile = path.join(projectPath, './schema.zmodel');
let sampleModelGenerated = false;

if (fs.existsSync(zmodelFile)) {
console.warn(`ZenStack model already exists at ${zmodelFile}, not generating a new one.`);
} else {
if (prismaSchema) {
// copy over schema.prisma
fs.copyFileSync(prismaSchema, zmodelFile);
} else {
// create a new model
const starterContent = fs.readFileSync(path.join(__dirname, '../../res/starter.zmodel'), 'utf-8');
fs.writeFileSync(zmodelFile, starterContent);
sampleModelGenerated = true;
}
}

ensurePackage('prisma', true, options.packageManager, 'latest', projectPath);
ensurePackage('@prisma/client', false, options.packageManager, 'latest', projectPath);

const tag = options.tag ?? getVersion();
installPackage('zenstack', true, options.packageManager, tag, projectPath);
installPackage('@zenstackhq/runtime', false, options.packageManager, tag, projectPath);

if (sampleModelGenerated) {
console.log(`Sample model generated at: ${colors.blue(zmodelFile)}

Please check the following guide on how to model your app:
https://zenstack.dev/#/modeling-your-app.`);
} else if (prismaSchema) {
console.log(
`Your current Prisma schema "${prismaSchema}" has been copied to "${zmodelFile}".
Moving forward please edit this file and run "zenstack generate" to regenerate Prisma schema.`
);
}

console.log(colors.green('\nProject initialized successfully!'));

if (options.versionCheck) {
await checkNewVersion();
}
}
Loading