Skip to content

Commit

Permalink
feat:Support ZModel format command in CLI (#869)
Browse files Browse the repository at this point in the history
  • Loading branch information
jiashengguo authored Nov 29, 2023
1 parent b9f2e10 commit bf85ceb
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 4 deletions.
21 changes: 21 additions & 0 deletions packages/schema/src/cli/actions/format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getVersion } from '@zenstackhq/runtime';
import { formatDocument } from '../cli-util';
import colors from 'colors';
import ora from 'ora';
import { writeFile } from 'fs/promises';

export async function format(projectPath: string, options: { schema: string }) {
const version = getVersion();
console.log(colors.bold(`⌛️ ZenStack CLI v${version}`));

const schemaFile = options.schema;
const spinner = ora(`Formatting ${schemaFile}`).start();
try {
const formattedDoc = await formatDocument(schemaFile);
await writeFile(schemaFile, formattedDoc);
spinner.succeed();
} catch (e) {
spinner.fail();
throw e;
}
}
1 change: 1 addition & 0 deletions packages/schema/src/cli/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './generate';
export * from './info';
export * from './init';
export * from './repl';
export * from './format';
25 changes: 25 additions & 0 deletions packages/schema/src/cli/cli-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { createZModelServices, ZModelServices } from '../language-server/zmodel-
import { mergeBaseModel, resolveImport, resolveTransitiveImports } from '../utils/ast-utils';
import { getVersion } from '../utils/version-utils';
import { CliError } from './cli-error';
import { ZModelFormatter } from '../language-server/zmodel-formatter';
import { TextDocument } from 'vscode-languageserver-textdocument';

// required minimal version of Prisma
export const requiredPrismaVersion = '4.8.0';
Expand Down Expand Up @@ -251,3 +253,26 @@ export async function checkNewVersion() {
console.log(`A newer version ${colors.cyan(latestVersion)} is available.`);
}
}

export async function formatDocument(fileName: string) {
const services = createZModelServices(NodeFileSystem).ZModel;
const extensions = services.LanguageMetaData.fileExtensions;
if (!extensions.includes(path.extname(fileName))) {
console.error(colors.yellow(`Please choose a file with extension: ${extensions}.`));
throw new CliError('invalid schema file');
}

const langiumDocuments = services.shared.workspace.LangiumDocuments;
const document = langiumDocuments.getOrCreateDocument(URI.file(path.resolve(fileName)));

const formatter = services.lsp.Formatter as ZModelFormatter;

const identifier = { uri: document.uri.toString() };
const options = formatter.getFormatOptions() ?? {
insertSpaces: true,
tabSize: 4,
};

const edits = await formatter.formatDocument(document, { options, textDocument: identifier });
return TextDocument.applyEdits(document.textDocument, edits);
}
16 changes: 16 additions & 0 deletions packages/schema/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ export const replAction = async (options: Parameters<typeof actions.repl>[1]): P
);
};

export const formatAction = async (options: Parameters<typeof actions.format>[1]): Promise<void> => {
await telemetry.trackSpan(
'cli:command:start',
'cli:command:complete',
'cli:command:error',
{ command: 'format' },
() => actions.format(process.cwd(), options)
);
};

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

Expand Down Expand Up @@ -116,6 +126,12 @@ export function createProgram() {
.option('--table', 'enable table format output')
.action(replAction);

program
.command('format')
.description('Format a ZenStack schema file.')
.addOption(schemaOption)
.action(formatAction);

// make sure config is loaded before actions run
program.hook('preAction', async (_, actionCommand) => {
let configFile: string | undefined = actionCommand.opts().config;
Expand Down
6 changes: 2 additions & 4 deletions packages/schema/tests/schema/formatter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ const services = createZModelServices({ ...EmptyFileSystem }).ZModel;
const formatting = expectFormatting(services);

describe('ZModelFormatter', () => {
// eslint-disable-next-line jest/no-disabled-tests
test.skip('declaration formatting', async () => {
it('declaration formatting', async () => {
await formatting({
before: `datasource db { provider = 'postgresql' url = env('DATABASE_URL')} generator js {provider = 'prisma-client-js'}
plugin swrHooks {provider = '@zenstackhq/swr'output = 'lib/hooks'}
model User {id:id String @id name String? }
model User {id String @id name String? }
enum Role {ADMIN USER}`,
after: `datasource db {
provider = 'postgresql'
Expand All @@ -26,7 +25,6 @@ plugin swrHooks {
output = 'lib/hooks'
}
model User {
id
id String @id
name String?
}
Expand Down
43 changes: 43 additions & 0 deletions tests/integration/tests/cli/format.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as fs from 'fs';
import * as tmp from 'tmp';
import { createProgram } from '../../../../packages/schema/src/cli';
describe('CLI format test', () => {
let origDir: string;

beforeEach(() => {
origDir = process.cwd();
const r = tmp.dirSync({ unsafeCleanup: true });
console.log(`Project dir: ${r.name}`);
process.chdir(r.name);
});

afterEach(() => {
process.chdir(origDir);
});

it('basic format', async () => {
const model = `
datasource db {provider="sqlite" url="file:./dev.db"}
generator client {provider = "prisma-client-js"}
model Post {id Int @id() @default(autoincrement())users User[]}`;

const formattedModel = `
datasource db {
provider="sqlite"
url="file:./dev.db"
}
generator client {
provider = "prisma-client-js"
}
model Post {
id Int @id() @default(autoincrement())
users User[]
}`;
// set up schema
fs.writeFileSync('schema.zmodel', model, 'utf-8');
const program = createProgram();
await program.parseAsync(['format'], { from: 'user' });

expect(fs.readFileSync('schema.zmodel', 'utf-8')).toEqual(formattedModel);
});
});

0 comments on commit bf85ceb

Please sign in to comment.