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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zenstack-v3",
"version": "3.0.0-alpha.1",
"version": "3.0.0-alpha.0",
"description": "ZenStack",
"packageManager": "[email protected]",
"scripts": {
Expand Down
11 changes: 6 additions & 5 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "zenstack",
"name": "@zenstackhq/cli",
"publisher": "zenstack",
"displayName": "ZenStack Language Tools",
"displayName": "ZenStack CLI",
"description": "FullStack database toolkit with built-in access control and automatic API generation.",
"version": "3.0.0-alpha.1",
"version": "3.0.0-alpha.0",
"type": "module",
"author": {
"name": "ZenStack Team"
Expand All @@ -28,7 +28,7 @@
"pack": "pnpm pack"
},
"dependencies": {
"@types/node": "^18.0.0",
"@types/node": "^20.0.0",
"@zenstackhq/language": "workspace:*",
"@zenstackhq/runtime": "workspace:*",
"@zenstackhq/sdk": "workspace:*",
Expand All @@ -37,6 +37,7 @@
"commander": "^8.3.0",
"langium": "~3.3.0",
"ora": "^5.4.1",
"package-manager-detector": "^1.3.0",
"tiny-invariant": "^1.3.3",
"ts-pattern": "^4.3.0"
},
Expand All @@ -45,11 +46,11 @@
"typescript": "^5.0.0"
},
"devDependencies": {
"@zenstackhq/testtools": "workspace:*",
"@types/async-exit-hook": "^2.0.0",
"@types/better-sqlite3": "^7.6.13",
"@types/semver": "^7.3.13",
"@types/tmp": "^0.2.6",
"@zenstackhq/testtools": "workspace:*",
"better-sqlite3": "^11.8.1",
"tmp": "^0.2.3"
}
Expand Down
12 changes: 12 additions & 0 deletions packages/cli/src/actions/action-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,15 @@ export async function loadSchemaDocument(schemaFile: string) {
}
return loadResult.model;
}

export function handleSubProcessError(err: unknown) {
if (
err instanceof Error &&
'status' in err &&
typeof err.status === 'number'
) {
process.exit(err.status);
} else {
process.exit(1);
}
}
11 changes: 0 additions & 11 deletions packages/cli/src/actions/common.ts

This file was deleted.

3 changes: 1 addition & 2 deletions packages/cli/src/actions/db.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import path from 'node:path';
import { execPackage } from '../utils/exec-utils';
import { getSchemaFile } from './action-utils';
import { getSchemaFile, handleSubProcessError } from './action-utils';
import { run as runGenerate } from './generate';
import { handleSubProcessError } from './common';

type CommonOptions = {
schema?: string;
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/actions/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export async function run(options: Options) {
import { ZenStackClient } from '@zenstackhq/runtime';
import { schema } from '${outputPath}/schema';

const db = new ZenStackClient(schema);
const client = new ZenStackClient(schema);
\`\`\`
`);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { run as db } from './db';
import { run as generate } from './generate';
import { run as info } from './info';
import { run as init } from './init';
import { run as migrate } from './migrate';

export { db, generate, info, migrate };
export { db, generate, info, init, migrate };
16 changes: 8 additions & 8 deletions packages/cli/src/actions/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ async function getZenStackPackages(
return [];
}

const packages = [
...Object.keys(pkgJson.dependencies ?? {}).filter((p) =>
p.startsWith('@zenstackhq/')
),
...Object.keys(pkgJson.devDependencies ?? {}).filter((p) =>
p.startsWith('@zenstackhq/')
),
];
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) => {
Expand Down
82 changes: 82 additions & 0 deletions packages/cli/src/actions/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import colors from 'colors';
import fs from 'node:fs';
import path from 'node:path';
import ora from 'ora';
import { detect, resolveCommand } from 'package-manager-detector';
import { CliError } from '../cli-error';
import { execSync } from '../utils/exec-utils';
import { STARTER_ZMODEL } from './templates';

/**
* CLI action for getting information about installed ZenStack packages
*/
export async function run(projectPath: string) {
const packages = [
{ name: '@zenstackhq/cli', dev: true },
{ name: '@zenstackhq/runtime', dev: false },
];
let pm = await detect();
if (!pm) {
pm = { agent: 'npm', name: 'npm' };
}

console.log(colors.gray(`Using package manager: ${pm.agent}`));

for (const pkg of packages) {
const resolved = resolveCommand(pm.agent, 'install', [
pkg.name,
...(pkg.dev ? [pm.agent === 'yarn' ? '--dev' : '--save-dev'] : []),
]);
if (!resolved) {
throw new CliError(
`Unable to determine how to install package "${pkg.name}". Please install it manually.`
);
}

const spinner = ora(`Installing "${pkg.name}"`).start();
try {
execSync(`${resolved.command} ${resolved.args.join(' ')}`, {
cwd: projectPath,
});
spinner.succeed();
} catch (e) {
spinner.fail();
throw e;
}
}

const generationFolder = 'zenstack';

if (!fs.existsSync(path.join(projectPath, generationFolder))) {
fs.mkdirSync(path.join(projectPath, generationFolder));
}

if (
!fs.existsSync(
path.join(projectPath, generationFolder, 'schema.zmodel')
)
) {
fs.writeFileSync(
path.join(projectPath, generationFolder, 'schema.zmodel'),
STARTER_ZMODEL
);
} else {
console.log(
colors.yellow(
'Schema file already exists. Skipping generation of sample.'
)
);
}

console.log(colors.green('ZenStack project initialized successfully!'));
console.log(
colors.gray(
`See "${generationFolder}/schema.zmodel" for your database schema.`
)
);
console.log(
colors.gray(
'Run `zenstack generate` to compile the the schema into a TypeScript file.'
)
);
}
52 changes: 52 additions & 0 deletions packages/cli/src/actions/templates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
export const STARTER_ZMODEL = `// This is a sample model to get you started.

/// A sample data source using local sqlite db.
datasource db {
provider = 'sqlite'
url = 'file:./dev.db'
}

/// User model
model User {
id String @id @default(cuid())
email String @unique @email @length(6, 32)
posts Post[]
}

/// Post model
model Post {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String @length(1, 256)
content String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
}
`;

export const STARTER_MAIN_TS = `import { ZenStackClient } from '@zenstackhq/runtime';
import { schema } from './zenstack/schema';

async function main() {
const client = new ZenStackClient(schema);
const user = await client.user.create({
data: {
email: '[email protected]',
posts: {
create: [
{
title: 'Hello World',
content: 'This is a test post',
},
],
},
},
include: { posts: true }
});
console.log('User created:', user);
}

main();
`;
34 changes: 22 additions & 12 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import { Command, Option } from 'commander';
import * as actions from './actions';
import { getVersion } from './utils/version-utils';

const infoAction = async (projectPath: string): Promise<void> => {
await actions.info(projectPath);
};

const generateAction = async (
options: Parameters<typeof actions.generate>[0]
): Promise<void> => {
Expand All @@ -22,6 +18,14 @@ const dbAction = async (command: string, options: any): Promise<void> => {
await actions.db(command, options);
};

const infoAction = async (projectPath: string): Promise<void> => {
await actions.info(projectPath);
};

const initAction = async (projectPath: string): Promise<void> => {
await actions.init(projectPath);
};

export function createProgram() {
const program = new Command('zenstack');

Expand All @@ -43,14 +47,6 @@ export function createProgram() {
`schema file (with extension ${schemaExtensions}). Defaults to "schema.zmodel" unless specified in package.json.`
);

program
.command('info')
.description(
'Get information of installed ZenStack and related packages.'
)
.argument('[path]', 'project path', '.')
.action(infoAction);

program
.command('generate')
.description('Run code generation.')
Expand Down Expand Up @@ -121,6 +117,20 @@ export function createProgram() {
)
.action((options) => dbAction('push', options));

program
.command('info')
.description(
'Get information of installed ZenStack and related packages.'
)
.argument('[path]', 'project path', '.')
.action(infoAction);

program
.command('init')
.description('Initialize an existing project for ZenStack.')
.argument('[path]', 'project path', '.')
.action(initAction);

return program;
}

Expand Down
3 changes: 3 additions & 0 deletions packages/create-zenstack/bin/cli
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node --no-warnings

import '../dist/index.js';
42 changes: 42 additions & 0 deletions packages/create-zenstack/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "create-zenstack",
"version": "3.0.0-alpha.0",
"description": "Create a new ZenStack project",
"type": "module",
"scripts": {
"build": "tsup-node",
"watch": "tsup-node --watch",
"lint": "eslint src --ext ts",
"pack": "pnpm pack"
},
"keywords": [],
"author": "ZenStack Team",
"license": "MIT",
"files": [
"dist"
],
"bin": {
"create-zenstack": "bin/cli"
},
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"dependencies": {
"colors": "1.4.0",
"commander": "^8.3.0",
"ora": "^5.4.1"
},
"devDependencies": {
"@types/colors": "^1.2.4",
"@types/node": "^20.0.0"
}
}
Loading