@zenstackhq/cli 3.1.1 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +88 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +89 -6
- package/dist/index.js.map +1 -1
- package/package.json +17 -9
- package/.turbo/turbo-build.log +0 -22
- package/eslint.config.js +0 -4
- package/scripts/post-build.ts +0 -20
- package/src/actions/action-utils.ts +0 -146
- package/src/actions/check.ts +0 -22
- package/src/actions/db.ts +0 -51
- package/src/actions/format.ts +0 -27
- package/src/actions/generate.ts +0 -226
- package/src/actions/index.ts +0 -10
- package/src/actions/info.ts +0 -71
- package/src/actions/init.ts +0 -61
- package/src/actions/migrate.ts +0 -149
- package/src/actions/seed.ts +0 -38
- package/src/actions/templates.ts +0 -58
- package/src/cli-error.ts +0 -4
- package/src/constants.ts +0 -5
- package/src/index.ts +0 -233
- package/src/plugins/index.ts +0 -2
- package/src/plugins/prisma.ts +0 -21
- package/src/plugins/typescript.ts +0 -40
- package/src/telemetry.ts +0 -139
- package/src/utils/exec-utils.ts +0 -61
- package/src/utils/is-ci.ts +0 -5
- package/src/utils/is-container.ts +0 -23
- package/src/utils/is-docker.ts +0 -31
- package/src/utils/is-wsl.ts +0 -18
- package/src/utils/machine-id-utils.ts +0 -76
- package/src/utils/version-utils.ts +0 -50
- package/test/check.test.ts +0 -101
- package/test/db.test.ts +0 -61
- package/test/format.test.ts +0 -33
- package/test/generate.test.ts +0 -76
- package/test/init.test.ts +0 -14
- package/test/migrate.test.ts +0 -72
- package/test/plugins/custom-plugin.test.ts +0 -50
- package/test/plugins/prisma-plugin.test.ts +0 -81
- package/test/ts-schema-gen.test.ts +0 -445
- package/test/utils.ts +0 -23
- package/tsconfig.json +0 -4
- package/tsup.config.ts +0 -13
- package/vitest.config.ts +0 -4
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { loadDocument } from '@zenstackhq/language';
|
|
2
|
-
import { isDataSource } from '@zenstackhq/language/ast';
|
|
3
|
-
import { PrismaSchemaGenerator } from '@zenstackhq/sdk';
|
|
4
|
-
import colors from 'colors';
|
|
5
|
-
import fs from 'node:fs';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import { CliError } from '../cli-error';
|
|
8
|
-
|
|
9
|
-
export function getSchemaFile(file?: string) {
|
|
10
|
-
if (file) {
|
|
11
|
-
if (!fs.existsSync(file)) {
|
|
12
|
-
throw new CliError(`Schema file not found: ${file}`);
|
|
13
|
-
}
|
|
14
|
-
return file;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const pkgJsonConfig = getPkgJsonConfig(process.cwd());
|
|
18
|
-
if (pkgJsonConfig.schema) {
|
|
19
|
-
if (!fs.existsSync(pkgJsonConfig.schema)) {
|
|
20
|
-
throw new CliError(`Schema file not found: ${pkgJsonConfig.schema}`);
|
|
21
|
-
}
|
|
22
|
-
if (fs.statSync(pkgJsonConfig.schema).isDirectory()) {
|
|
23
|
-
const schemaPath = path.join(pkgJsonConfig.schema, 'schema.zmodel');
|
|
24
|
-
if (!fs.existsSync(schemaPath)) {
|
|
25
|
-
throw new CliError(`Schema file not found: ${schemaPath}`);
|
|
26
|
-
}
|
|
27
|
-
return schemaPath;
|
|
28
|
-
} else {
|
|
29
|
-
return pkgJsonConfig.schema;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (fs.existsSync('./schema.zmodel')) {
|
|
34
|
-
return './schema.zmodel';
|
|
35
|
-
} else if (fs.existsSync('./zenstack/schema.zmodel')) {
|
|
36
|
-
return './zenstack/schema.zmodel';
|
|
37
|
-
} else {
|
|
38
|
-
throw new CliError(
|
|
39
|
-
'Schema file not found in default locations ("./schema.zmodel" or "./zenstack/schema.zmodel").',
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export async function loadSchemaDocument(schemaFile: string) {
|
|
45
|
-
const loadResult = await loadDocument(schemaFile);
|
|
46
|
-
if (!loadResult.success) {
|
|
47
|
-
loadResult.errors.forEach((err) => {
|
|
48
|
-
console.error(colors.red(err));
|
|
49
|
-
});
|
|
50
|
-
throw new CliError('Schema contains errors. See above for details.');
|
|
51
|
-
}
|
|
52
|
-
loadResult.warnings.forEach((warn) => {
|
|
53
|
-
console.warn(colors.yellow(warn));
|
|
54
|
-
});
|
|
55
|
-
return loadResult.model;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function handleSubProcessError(err: unknown) {
|
|
59
|
-
if (err instanceof Error && 'status' in err && typeof err.status === 'number') {
|
|
60
|
-
process.exit(err.status);
|
|
61
|
-
} else {
|
|
62
|
-
process.exit(1);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export async function generateTempPrismaSchema(zmodelPath: string, folder?: string) {
|
|
67
|
-
const model = await loadSchemaDocument(zmodelPath);
|
|
68
|
-
if (!model.declarations.some(isDataSource)) {
|
|
69
|
-
throw new CliError('Schema must define a datasource');
|
|
70
|
-
}
|
|
71
|
-
const prismaSchema = await new PrismaSchemaGenerator(model).generate();
|
|
72
|
-
if (!folder) {
|
|
73
|
-
folder = path.dirname(zmodelPath);
|
|
74
|
-
}
|
|
75
|
-
const prismaSchemaFile = path.resolve(folder, '~schema.prisma');
|
|
76
|
-
fs.writeFileSync(prismaSchemaFile, prismaSchema);
|
|
77
|
-
return prismaSchemaFile;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function getPkgJsonConfig(startPath: string) {
|
|
81
|
-
const result: { schema: string | undefined; output: string | undefined; seed: string | undefined } = {
|
|
82
|
-
schema: undefined,
|
|
83
|
-
output: undefined,
|
|
84
|
-
seed: undefined,
|
|
85
|
-
};
|
|
86
|
-
const pkgJsonFile = findUp(['package.json'], startPath, false);
|
|
87
|
-
|
|
88
|
-
if (!pkgJsonFile) {
|
|
89
|
-
return result;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
let pkgJson: any = undefined;
|
|
93
|
-
try {
|
|
94
|
-
pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, 'utf8'));
|
|
95
|
-
} catch {
|
|
96
|
-
return result;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (pkgJson.zenstack && typeof pkgJson.zenstack === 'object') {
|
|
100
|
-
result.schema =
|
|
101
|
-
pkgJson.zenstack.schema && typeof pkgJson.zenstack.schema === 'string'
|
|
102
|
-
? path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.schema)
|
|
103
|
-
: undefined;
|
|
104
|
-
result.output =
|
|
105
|
-
pkgJson.zenstack.output && typeof pkgJson.zenstack.output === 'string'
|
|
106
|
-
? path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.output)
|
|
107
|
-
: undefined;
|
|
108
|
-
result.seed =
|
|
109
|
-
typeof pkgJson.zenstack.seed === 'string' && pkgJson.zenstack.seed ? pkgJson.zenstack.seed : undefined;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return result;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
type FindUpResult<Multiple extends boolean> = Multiple extends true ? string[] | undefined : string | undefined;
|
|
116
|
-
|
|
117
|
-
function findUp<Multiple extends boolean = false>(
|
|
118
|
-
names: string[],
|
|
119
|
-
cwd: string = process.cwd(),
|
|
120
|
-
multiple: Multiple = false as Multiple,
|
|
121
|
-
result: string[] = [],
|
|
122
|
-
): FindUpResult<Multiple> {
|
|
123
|
-
if (!names.some((name) => !!name)) {
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
const target = names.find((name) => fs.existsSync(path.join(cwd, name)));
|
|
127
|
-
if (multiple === false && target) {
|
|
128
|
-
return path.join(cwd, target) as FindUpResult<Multiple>;
|
|
129
|
-
}
|
|
130
|
-
if (target) {
|
|
131
|
-
result.push(path.join(cwd, target));
|
|
132
|
-
}
|
|
133
|
-
const up = path.resolve(cwd, '..');
|
|
134
|
-
if (up === cwd) {
|
|
135
|
-
return (multiple && result.length > 0 ? result : undefined) as FindUpResult<Multiple>;
|
|
136
|
-
}
|
|
137
|
-
return findUp(names, up, multiple, result);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export async function requireDataSourceUrl(schemaFile: string) {
|
|
141
|
-
const zmodel = await loadSchemaDocument(schemaFile);
|
|
142
|
-
const dataSource = zmodel.declarations.find(isDataSource);
|
|
143
|
-
if (!dataSource?.fields.some((f) => f.name === 'url')) {
|
|
144
|
-
throw new CliError('The schema\'s "datasource" must have a "url" field to use this command.');
|
|
145
|
-
}
|
|
146
|
-
}
|
package/src/actions/check.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import colors from 'colors';
|
|
2
|
-
import { getSchemaFile, loadSchemaDocument } from './action-utils';
|
|
3
|
-
|
|
4
|
-
type Options = {
|
|
5
|
-
schema?: string;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* CLI action for checking a schema's validity.
|
|
10
|
-
*/
|
|
11
|
-
export async function run(options: Options) {
|
|
12
|
-
const schemaFile = getSchemaFile(options.schema);
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
await loadSchemaDocument(schemaFile);
|
|
16
|
-
console.log(colors.green('✓ Schema validation completed successfully.'));
|
|
17
|
-
} catch (error) {
|
|
18
|
-
console.error(colors.red('✗ Schema validation failed.'));
|
|
19
|
-
// Re-throw to maintain CLI exit code behavior
|
|
20
|
-
throw error;
|
|
21
|
-
}
|
|
22
|
-
}
|
package/src/actions/db.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { execPrisma } from '../utils/exec-utils';
|
|
3
|
-
import { generateTempPrismaSchema, getSchemaFile, handleSubProcessError, requireDataSourceUrl } from './action-utils';
|
|
4
|
-
|
|
5
|
-
type Options = {
|
|
6
|
-
schema?: string;
|
|
7
|
-
acceptDataLoss?: boolean;
|
|
8
|
-
forceReset?: boolean;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* CLI action for db related commands
|
|
13
|
-
*/
|
|
14
|
-
export async function run(command: string, options: Options) {
|
|
15
|
-
switch (command) {
|
|
16
|
-
case 'push':
|
|
17
|
-
await runPush(options);
|
|
18
|
-
break;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async function runPush(options: Options) {
|
|
23
|
-
const schemaFile = getSchemaFile(options.schema);
|
|
24
|
-
|
|
25
|
-
// validate datasource url exists
|
|
26
|
-
await requireDataSourceUrl(schemaFile);
|
|
27
|
-
|
|
28
|
-
// generate a temp prisma schema file
|
|
29
|
-
const prismaSchemaFile = await generateTempPrismaSchema(schemaFile);
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
// run prisma db push
|
|
33
|
-
const cmd = [
|
|
34
|
-
'db push',
|
|
35
|
-
` --schema "${prismaSchemaFile}"`,
|
|
36
|
-
options.acceptDataLoss ? ' --accept-data-loss' : '',
|
|
37
|
-
options.forceReset ? ' --force-reset' : '',
|
|
38
|
-
' --skip-generate',
|
|
39
|
-
].join('');
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
execPrisma(cmd);
|
|
43
|
-
} catch (err) {
|
|
44
|
-
handleSubProcessError(err);
|
|
45
|
-
}
|
|
46
|
-
} finally {
|
|
47
|
-
if (fs.existsSync(prismaSchemaFile)) {
|
|
48
|
-
fs.unlinkSync(prismaSchemaFile);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
package/src/actions/format.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { formatDocument } from '@zenstackhq/language';
|
|
2
|
-
import colors from 'colors';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import { getSchemaFile } from './action-utils';
|
|
5
|
-
|
|
6
|
-
type Options = {
|
|
7
|
-
schema?: string;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* CLI action for formatting a ZModel schema file.
|
|
12
|
-
*/
|
|
13
|
-
export async function run(options: Options) {
|
|
14
|
-
const schemaFile = getSchemaFile(options.schema);
|
|
15
|
-
let formattedContent: string;
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
formattedContent = await formatDocument(fs.readFileSync(schemaFile, 'utf-8'));
|
|
19
|
-
} catch (error) {
|
|
20
|
-
console.error(colors.red('✗ Schema formatting failed.'));
|
|
21
|
-
// Re-throw to maintain CLI exit code behavior
|
|
22
|
-
throw error;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
fs.writeFileSync(schemaFile, formattedContent, 'utf-8');
|
|
26
|
-
console.log(colors.green('✓ Schema formatting completed successfully.'));
|
|
27
|
-
}
|
package/src/actions/generate.ts
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { invariant } from '@zenstackhq/common-helpers';
|
|
2
|
-
import { isPlugin, LiteralExpr, Plugin, type Model } from '@zenstackhq/language/ast';
|
|
3
|
-
import { getLiteral, getLiteralArray } from '@zenstackhq/language/utils';
|
|
4
|
-
import { type CliPlugin } from '@zenstackhq/sdk';
|
|
5
|
-
import colors from 'colors';
|
|
6
|
-
import { createJiti } from 'jiti';
|
|
7
|
-
import fs from 'node:fs';
|
|
8
|
-
import path from 'node:path';
|
|
9
|
-
import { pathToFileURL } from 'node:url';
|
|
10
|
-
import ora, { type Ora } from 'ora';
|
|
11
|
-
import { CliError } from '../cli-error';
|
|
12
|
-
import * as corePlugins from '../plugins';
|
|
13
|
-
import { getPkgJsonConfig, getSchemaFile, loadSchemaDocument } from './action-utils';
|
|
14
|
-
|
|
15
|
-
type Options = {
|
|
16
|
-
schema?: string;
|
|
17
|
-
output?: string;
|
|
18
|
-
silent: boolean;
|
|
19
|
-
lite: boolean;
|
|
20
|
-
liteOnly: boolean;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* CLI action for generating code from schema
|
|
25
|
-
*/
|
|
26
|
-
export async function run(options: Options) {
|
|
27
|
-
const start = Date.now();
|
|
28
|
-
|
|
29
|
-
const schemaFile = getSchemaFile(options.schema);
|
|
30
|
-
|
|
31
|
-
const model = await loadSchemaDocument(schemaFile);
|
|
32
|
-
const outputPath = getOutputPath(options, schemaFile);
|
|
33
|
-
|
|
34
|
-
await runPlugins(schemaFile, model, outputPath, options);
|
|
35
|
-
|
|
36
|
-
if (!options.silent) {
|
|
37
|
-
console.log(colors.green(`Generation completed successfully in ${Date.now() - start}ms.\n`));
|
|
38
|
-
console.log(`You can now create a ZenStack client with it.
|
|
39
|
-
|
|
40
|
-
\`\`\`ts
|
|
41
|
-
import { ZenStackClient } from '@zenstackhq/orm';
|
|
42
|
-
import { schema } from '${path.relative('.', outputPath)}/schema';
|
|
43
|
-
|
|
44
|
-
const client = new ZenStackClient(schema, {
|
|
45
|
-
dialect: { ... }
|
|
46
|
-
});
|
|
47
|
-
\`\`\`
|
|
48
|
-
|
|
49
|
-
Check documentation: https://zenstack.dev/docs/`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function getOutputPath(options: Options, schemaFile: string) {
|
|
54
|
-
if (options.output) {
|
|
55
|
-
return options.output;
|
|
56
|
-
}
|
|
57
|
-
const pkgJsonConfig = getPkgJsonConfig(process.cwd());
|
|
58
|
-
if (pkgJsonConfig.output) {
|
|
59
|
-
return pkgJsonConfig.output;
|
|
60
|
-
} else {
|
|
61
|
-
return path.dirname(schemaFile);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async function runPlugins(schemaFile: string, model: Model, outputPath: string, options: Options) {
|
|
66
|
-
const plugins = model.declarations.filter(isPlugin);
|
|
67
|
-
const processedPlugins: { cliPlugin: CliPlugin; pluginOptions: Record<string, unknown> }[] = [];
|
|
68
|
-
|
|
69
|
-
for (const plugin of plugins) {
|
|
70
|
-
const provider = getPluginProvider(plugin);
|
|
71
|
-
|
|
72
|
-
let cliPlugin: CliPlugin | undefined;
|
|
73
|
-
if (provider.startsWith('@core/')) {
|
|
74
|
-
cliPlugin = (corePlugins as any)[provider.slice('@core/'.length)];
|
|
75
|
-
if (!cliPlugin) {
|
|
76
|
-
throw new CliError(`Unknown core plugin: ${provider}`);
|
|
77
|
-
}
|
|
78
|
-
} else {
|
|
79
|
-
cliPlugin = await loadPluginModule(provider, path.dirname(schemaFile));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (cliPlugin) {
|
|
83
|
-
const pluginOptions = getPluginOptions(plugin);
|
|
84
|
-
|
|
85
|
-
// merge CLI options
|
|
86
|
-
if (provider === '@core/typescript') {
|
|
87
|
-
if (pluginOptions['lite'] === undefined) {
|
|
88
|
-
pluginOptions['lite'] = options.lite;
|
|
89
|
-
}
|
|
90
|
-
if (pluginOptions['liteOnly'] === undefined) {
|
|
91
|
-
pluginOptions['liteOnly'] = options.liteOnly;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
processedPlugins.push({ cliPlugin, pluginOptions });
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const defaultPlugins = [
|
|
100
|
-
{
|
|
101
|
-
plugin: corePlugins['typescript'],
|
|
102
|
-
options: { lite: options.lite, liteOnly: options.liteOnly },
|
|
103
|
-
},
|
|
104
|
-
];
|
|
105
|
-
defaultPlugins.forEach(({ plugin, options }) => {
|
|
106
|
-
if (!processedPlugins.some((p) => p.cliPlugin === plugin)) {
|
|
107
|
-
// default plugins are run before user plugins
|
|
108
|
-
processedPlugins.unshift({ cliPlugin: plugin, pluginOptions: options });
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
for (const { cliPlugin, pluginOptions } of processedPlugins) {
|
|
113
|
-
invariant(
|
|
114
|
-
typeof cliPlugin.generate === 'function',
|
|
115
|
-
`Plugin ${cliPlugin.name} does not have a generate function`,
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
// run plugin generator
|
|
119
|
-
let spinner: Ora | undefined;
|
|
120
|
-
|
|
121
|
-
if (!options.silent) {
|
|
122
|
-
spinner = ora(cliPlugin.statusText ?? `Running plugin ${cliPlugin.name}`).start();
|
|
123
|
-
}
|
|
124
|
-
try {
|
|
125
|
-
await cliPlugin.generate({
|
|
126
|
-
schemaFile,
|
|
127
|
-
model,
|
|
128
|
-
defaultOutputPath: outputPath,
|
|
129
|
-
pluginOptions,
|
|
130
|
-
});
|
|
131
|
-
spinner?.succeed();
|
|
132
|
-
} catch (err) {
|
|
133
|
-
spinner?.fail();
|
|
134
|
-
console.error(err);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function getPluginProvider(plugin: Plugin) {
|
|
140
|
-
const providerField = plugin.fields.find((f) => f.name === 'provider');
|
|
141
|
-
invariant(providerField, `Plugin ${plugin.name} does not have a provider field`);
|
|
142
|
-
const provider = (providerField.value as LiteralExpr).value as string;
|
|
143
|
-
return provider;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function getPluginOptions(plugin: Plugin): Record<string, unknown> {
|
|
147
|
-
const result: Record<string, unknown> = {};
|
|
148
|
-
for (const field of plugin.fields) {
|
|
149
|
-
if (field.name === 'provider') {
|
|
150
|
-
continue; // skip provider
|
|
151
|
-
}
|
|
152
|
-
const value = getLiteral(field.value) ?? getLiteralArray(field.value);
|
|
153
|
-
if (value === undefined) {
|
|
154
|
-
console.warn(`Plugin "${plugin.name}" option "${field.name}" has unsupported value, skipping`);
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
result[field.name] = value;
|
|
158
|
-
}
|
|
159
|
-
return result;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async function loadPluginModule(provider: string, basePath: string) {
|
|
163
|
-
let moduleSpec = provider;
|
|
164
|
-
if (moduleSpec.startsWith('.')) {
|
|
165
|
-
// relative to schema's path
|
|
166
|
-
moduleSpec = path.resolve(basePath, moduleSpec);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const importAsEsm = async (spec: string) => {
|
|
170
|
-
try {
|
|
171
|
-
const result = (await import(spec)).default as CliPlugin;
|
|
172
|
-
return result;
|
|
173
|
-
} catch (err) {
|
|
174
|
-
throw new CliError(`Failed to load plugin module from ${spec}: ${(err as Error).message}`);
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
const jiti = createJiti(pathToFileURL(basePath).toString());
|
|
179
|
-
const importAsTs = async (spec: string) => {
|
|
180
|
-
try {
|
|
181
|
-
const result = (await jiti.import(spec, { default: true })) as CliPlugin;
|
|
182
|
-
return result;
|
|
183
|
-
} catch (err) {
|
|
184
|
-
throw new CliError(`Failed to load plugin module from ${spec}: ${(err as Error).message}`);
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const esmSuffixes = ['.js', '.mjs'];
|
|
189
|
-
const tsSuffixes = ['.ts', '.mts'];
|
|
190
|
-
|
|
191
|
-
if (fs.existsSync(moduleSpec) && fs.statSync(moduleSpec).isFile()) {
|
|
192
|
-
// try provider as ESM file
|
|
193
|
-
if (esmSuffixes.some((suffix) => moduleSpec.endsWith(suffix))) {
|
|
194
|
-
return await importAsEsm(pathToFileURL(moduleSpec).toString());
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// try provider as TS file
|
|
198
|
-
if (tsSuffixes.some((suffix) => moduleSpec.endsWith(suffix))) {
|
|
199
|
-
return await importAsTs(moduleSpec);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// try ESM index files in provider directory
|
|
204
|
-
for (const suffix of esmSuffixes) {
|
|
205
|
-
const indexPath = path.join(moduleSpec, `index${suffix}`);
|
|
206
|
-
if (fs.existsSync(indexPath)) {
|
|
207
|
-
return await importAsEsm(pathToFileURL(indexPath).toString());
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// try TS index files in provider directory
|
|
212
|
-
for (const suffix of tsSuffixes) {
|
|
213
|
-
const indexPath = path.join(moduleSpec, `index${suffix}`);
|
|
214
|
-
if (fs.existsSync(indexPath)) {
|
|
215
|
-
return await importAsTs(indexPath);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// last resort, try to import as esm directly
|
|
220
|
-
try {
|
|
221
|
-
return (await import(moduleSpec)).default as CliPlugin;
|
|
222
|
-
} catch {
|
|
223
|
-
// plugin may not export a generator so we simply ignore the error here
|
|
224
|
-
return undefined;
|
|
225
|
-
}
|
|
226
|
-
}
|
package/src/actions/index.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { run as check } from './check';
|
|
2
|
-
import { run as db } from './db';
|
|
3
|
-
import { run as format } from './format';
|
|
4
|
-
import { run as generate } from './generate';
|
|
5
|
-
import { run as info } from './info';
|
|
6
|
-
import { run as init } from './init';
|
|
7
|
-
import { run as migrate } from './migrate';
|
|
8
|
-
import { run as seed } from './seed';
|
|
9
|
-
|
|
10
|
-
export { check, db, format, generate, info, init, migrate, seed };
|
package/src/actions/info.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import colors from 'colors';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* CLI action for getting information about installed ZenStack packages
|
|
6
|
-
*/
|
|
7
|
-
export async function run(projectPath: string) {
|
|
8
|
-
const packages = await getZenStackPackages(projectPath);
|
|
9
|
-
if (!packages) {
|
|
10
|
-
console.error('Unable to locate package.json. Are you in a valid project directory?');
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
console.log('Installed ZenStack Packages:');
|
|
15
|
-
const versions = new Set<string>();
|
|
16
|
-
for (const { pkg, version } of packages) {
|
|
17
|
-
if (version) {
|
|
18
|
-
versions.add(version);
|
|
19
|
-
}
|
|
20
|
-
console.log(` ${colors.green(pkg.padEnd(20))}\t${version}`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (versions.size > 1) {
|
|
24
|
-
console.warn(colors.yellow('WARNING: Multiple versions of Zenstack packages detected. This may cause issues.'));
|
|
25
|
-
}
|
|
26
|
-
}
|
|
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
|
-
}
|
package/src/actions/init.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import colors from 'colors';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import ora from 'ora';
|
|
5
|
-
import { detect, resolveCommand } from 'package-manager-detector';
|
|
6
|
-
import { CliError } from '../cli-error';
|
|
7
|
-
import { execSync } from '../utils/exec-utils';
|
|
8
|
-
import { STARTER_ZMODEL } from './templates';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* CLI action for getting information about installed ZenStack packages
|
|
12
|
-
*/
|
|
13
|
-
export async function run(projectPath: string) {
|
|
14
|
-
const packages = [
|
|
15
|
-
{ name: '@zenstackhq/cli@latest', dev: true },
|
|
16
|
-
{ name: '@zenstackhq/orm@latest', dev: false },
|
|
17
|
-
];
|
|
18
|
-
let pm = await detect();
|
|
19
|
-
if (!pm) {
|
|
20
|
-
pm = { agent: 'npm', name: 'npm' };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
console.log(colors.gray(`Using package manager: ${pm.agent}`));
|
|
24
|
-
|
|
25
|
-
for (const pkg of packages) {
|
|
26
|
-
const resolved = resolveCommand(pm.agent, 'add', [
|
|
27
|
-
pkg.name,
|
|
28
|
-
...(pkg.dev ? [pm.agent.startsWith('yarn') || pm.agent === 'bun' ? '--dev' : '--save-dev'] : []),
|
|
29
|
-
]);
|
|
30
|
-
if (!resolved) {
|
|
31
|
-
throw new CliError(`Unable to determine how to install package "${pkg.name}". Please install it manually.`);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const spinner = ora(`Installing "${pkg.name}"`).start();
|
|
35
|
-
try {
|
|
36
|
-
execSync(`${resolved.command} ${resolved.args.join(' ')}`, {
|
|
37
|
-
cwd: projectPath,
|
|
38
|
-
});
|
|
39
|
-
spinner.succeed();
|
|
40
|
-
} catch (e) {
|
|
41
|
-
spinner.fail();
|
|
42
|
-
throw e;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const generationFolder = 'zenstack';
|
|
47
|
-
|
|
48
|
-
if (!fs.existsSync(path.join(projectPath, generationFolder))) {
|
|
49
|
-
fs.mkdirSync(path.join(projectPath, generationFolder));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!fs.existsSync(path.join(projectPath, generationFolder, 'schema.zmodel'))) {
|
|
53
|
-
fs.writeFileSync(path.join(projectPath, generationFolder, 'schema.zmodel'), STARTER_ZMODEL);
|
|
54
|
-
} else {
|
|
55
|
-
console.log(colors.yellow('Schema file already exists. Skipping generation of sample.'));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
console.log(colors.green('ZenStack project initialized successfully!'));
|
|
59
|
-
console.log(colors.gray(`See "${generationFolder}/schema.zmodel" for your database schema.`));
|
|
60
|
-
console.log(colors.gray('Run `zenstack generate` to compile the the schema into a TypeScript file.'));
|
|
61
|
-
}
|