Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(js/cli) generate file types only #536

Closed
wants to merge 5 commits into from
Closed
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
11 changes: 11 additions & 0 deletions js/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"singleQuote": true,
"trailingComma": "all",
"endOfLine": "lf",
"printWidth": 120,
"semi": true,
"bracketSpacing": true,
"bracketSameLine": true,
"arrowParens": "always",
"tabWidth": 2
}
2 changes: 1 addition & 1 deletion js/cli/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function cleanOldBuild() {
return {
name: 'clean-old-build',
buildStart() {
rmSync('./lib', { recursive: true, force: true });
rmSync('./build', { recursive: true, force: true });
},
};
}
Expand Down
144 changes: 28 additions & 116 deletions js/cli/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,140 +1,52 @@
#!/usr/bin/env node

import { readFileSync, mkdirSync, existsSync, writeFileSync } from 'fs';
import { SailsIdlParser } from 'sails-js-parser';
import { Command } from 'commander';
import { Sails } from 'sails-js';
import * as _path from 'path';
import { confirm } from '@inquirer/prompts';

import { generateLib } from './generate/index.js';
import * as config from './config.json';
import { ProjectBuilder } from './generate/index.js';

const program = new Command();

const handler = async (path: string, out: string, name: string, project: boolean) => {
const handler = async (path: string, out: string, name: string, project: boolean, typesOnly: boolean) => {
const parser = new SailsIdlParser();
await parser.init();
const sails = new Sails(parser);

const idl = readFileSync(path, 'utf-8');
const projectBuilder = new ProjectBuilder(sails, name)
.setRootPath(out)
.setIdlPath(path)
.setIsProject(project)
.setTypesOnly(typesOnly);

out = out || '.';
const dir = out;
const libFile = project ? _path.join(dir, 'src', 'lib.ts') : _path.join(dir, 'lib.ts');

if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}

if (!project) {
if (existsSync(libFile)) {
const answer = await confirm({
message: `File ${libFile} exists. Do you want to overwrite?`,
default: false,
});

if (!answer) {
process.exit(0);
}
}
}

let libCode: string;

try {
libCode = generateLib(sails.parseIdl(idl), name);
} catch (e) {
console.log(e.message, e.stack);
process.exit(1);
}

if (!project) {
writeFileSync(libFile, libCode);
console.log(`Lib generated at ${libFile}`);
} else {
const srcDir = _path.join(dir, 'src');
const tsconfigPath = _path.join(dir, 'tsconfig.json');
const pkgJsonPath = _path.join(dir, 'package.json');

let writeTsconfig = true;
let writePkgJson = true;

if (existsSync(tsconfigPath)) {
const answer = await confirm({
message: `File ${tsconfigPath} exists. Do you want to overwrite?`,
default: false,
});

if (!answer) {
writeTsconfig = false;
}
}

if (existsSync(pkgJsonPath)) {
const answer = await confirm({
message: `File ${pkgJsonPath} exists. Do you want to overwrite?`,
default: false,
});

if (!answer) {
writePkgJson = false;
}
}

if (!existsSync(srcDir)) {
mkdirSync(srcDir, { recursive: true });
}

writeFileSync(_path.join(srcDir, 'lib.ts'), libCode);

if (writeTsconfig) {
writeFileSync(_path.join(dir, 'tsconfig.json'), JSON.stringify(config.tsconfig, null, 2));
}

if (writePkgJson) {
writeFileSync(
_path.join(dir, 'package.json'),
JSON.stringify(
{
name,
type: 'module',
dependencies: {
'@gear-js/api': config.versions['gear-js'],
'@polkadot/api': config.versions['polkadot-api'],
'sails-js': config.versions['sails-js'],
},
devDependencies: {
typescript: config.versions['typescript'],
},
scripts: {
build: 'tsc',
},
},
null,
2,
),
);
}

console.log(`Lib generated at ${dir}`);
}
await projectBuilder.build();
};

program
.command('generate <path-to-file.sails.idl>')
.option('--no-project', 'Generate single file without project structure')
.option('-n --name <name>', 'Name of the library', 'program')
.option('-o --out <path-to-dir>', 'Output directory')
.option('-t --types-only', 'Generate only types defined', false)
.description('Generate typescript library based on .sails.idl file')
.action(async (path, options: { out: string; name: string; project: boolean }) => {
try {
await handler(path, options.out, options.name, options.project);
} catch (error) {
console.error(error.message);
process.exit(1);
}
process.exit(0);
});
.action(
async (
path,
options: {
out: string;
name: string;
project: boolean;
typesOnly: boolean;
},
) => {
try {
await handler(path, options.out, options.name, options.project, options.typesOnly);
} catch (error) {
console.error(error.message);
process.exit(1);
}
process.exit(0);
},
);

program.parse();
141 changes: 134 additions & 7 deletions js/cli/src/generate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,143 @@ import { Sails } from 'sails-js';
import { ServiceGenerator } from './service-gen.js';
import { TypesGenerator } from './types-gen.js';
import { Output } from './output.js';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import path from 'path';
import { confirm } from '@inquirer/prompts';
import * as config from '../config.json';

export function generateLib(sails: Sails, className = 'Program'): string {
const out = new Output();
export class ProjectBuilder {
private projectPath = ['.', 'src'];
private isProject: boolean = true;
private typesOnly: boolean = false;

const typesGen = new TypesGenerator(out, sails.program);
typesGen.generate();
constructor(private sails: Sails, private name: string = 'Program') {}

const serviceGen = new ServiceGenerator(out, sails.program, sails.scaleCodecTypes);
private async canCreateFile(filePath: string): Promise<boolean> {
if (!existsSync(filePath)) {
return true;
}

serviceGen.generate(className);
const answer = await confirm({
message: `File ${filePath} exists. Do you want to overwrite?`,
default: false,
});

return out.finalize();
return answer;
}

private generateLib(): string {
const out = new Output();

const serviceGen = new ServiceGenerator(out, this.sails.program, this.sails.scaleCodecTypes, {
noImplementation: this.typesOnly,
});
serviceGen.generate(this.name);

return out.finalize();
}

private generateTypes(): string {
const out = new Output();

const typesGen = new TypesGenerator(out, this.sails.program);
typesGen.generate();

return out.finalize();
}

setIdlPath(path: string) {
const idl = readFileSync(path, 'utf-8');
this.sails.parseIdl(idl);

return this;
}

setRootPath(path: string) {
this.projectPath[0] = path ? path : '.';

return this;
}

setIsProject(isProject: boolean) {
this.isProject = isProject;

return this;
}

setTypesOnly(typesOnly: boolean) {
this.typesOnly = typesOnly;

return this;
}

async build() {
const rootPath = this.projectPath[0];
const srcPath = path.join(...this.projectPath);

const libFilePath = this.isProject ? path.join(...this.projectPath) : this.projectPath[0];

if (!existsSync(libFilePath)) {
mkdirSync(libFilePath, { recursive: true });
}

const libCode = this.generateLib();
const libFile = path.join(libFilePath, 'lib.ts');
if (await this.canCreateFile(libFile)) {
writeFileSync(libFile, libCode);
} else {
process.exit(0);
}

const typesCode = this.generateTypes();
const typesFile = path.join(libFilePath, 'global.d.ts');
if (await this.canCreateFile(typesFile)) {
writeFileSync(typesFile, typesCode);
} else {
process.exit(0);
}

if (!this.isProject) {
console.log(`Lib generated at ${libFilePath}`);
return;
}

if (!existsSync(srcPath)) {
mkdirSync(srcPath, { recursive: true });
}

const tsconfigPath = path.join(rootPath, 'tsconfig.json');
const pkgJsonPath = path.join(rootPath, 'package.json');

if (await this.canCreateFile(tsconfigPath)) {
writeFileSync(tsconfigPath, JSON.stringify(config.tsconfig, null, 2));
}

if (await this.canCreateFile(pkgJsonPath)) {
writeFileSync(
pkgJsonPath,
JSON.stringify(
{
name: this.name,
type: 'module',
dependencies: {
'@gear-js/api': config.versions['gear-js'],
'@polkadot/api': config.versions['polkadot-api'],
'sails-js': config.versions['sails-js'],
},
devDependencies: {
typescript: config.versions['typescript'],
},
scripts: {
build: 'tsc',
},
},
null,
2,
),
);
}

console.log(`Lib generated at ${srcPath}`);
}
}
17 changes: 17 additions & 0 deletions js/cli/src/generate/service-gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class ServiceGenerator extends BaseGenerator {
out: Output,
private _program: ISailsProgram,
private scaleTypes: Record<string, any>,
private options?: { noImplementation?: boolean },
) {
super(out);
}
Expand Down Expand Up @@ -85,6 +86,10 @@ export class ServiceGenerator extends BaseGenerator {
args !== null ? ', ' + args : ''
}): TransactionBuilder<null>`,
() => {
if (this.options?.noImplementation) {
this._out.line(`throw new Error('Not implemented')`);
return;
}
this._out
.line(`const builder = new TransactionBuilder<null>(`, false)
.increaseIndent()
Expand Down Expand Up @@ -114,6 +119,10 @@ export class ServiceGenerator extends BaseGenerator {
.block(
`${getFuncName(name)}CtorFromCodeId(codeId: ${HEX_STRING_TYPE}${args !== null ? ', ' + args : ''})`,
() => {
if (this.options?.noImplementation) {
this._out.line(`throw new Error('Not implemented')`);
return;
}
this._out
.line(`const builder = new TransactionBuilder<null>(`, false)
.increaseIndent()
Expand Down Expand Up @@ -161,6 +170,10 @@ export class ServiceGenerator extends BaseGenerator {
const returnType = this.getType(def, decodeMethod);

this._out.line().block(this.getFuncSignature(name, params, returnType, isQuery), () => {
if (this.options?.noImplementation) {
this._out.line(`throw new Error('Not implemented')`);
return;
}
if (isQuery) {
this._out
.line(createPayload(service.name, name, params))
Expand Down Expand Up @@ -228,6 +241,10 @@ export class ServiceGenerator extends BaseGenerator {
.block(
`public subscribeTo${event.name}Event(callback: (data: ${jsType}) => void | Promise<void>): Promise<() => void>`,
() => {
if (this.options?.noImplementation) {
this._out.line(`throw new Error('Not implemented')`);
return;
}
this._out
.line(
`return this._program.api.gearEvents.subscribeToGearEvent('UserMessageSent', ({ data: { message } }) => {`,
Expand Down
Loading