From 35e5591e6b39d86c8db259fd914c450f525d1d48 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Thu, 11 Aug 2022 11:59:37 +0800 Subject: [PATCH 01/14] feat(cli): init cli package and add init command --- package.json | 4 ++ packages/cli/.eslintrc.json | 18 +++++ packages/cli/README.md | 12 ++++ packages/cli/jest.config.ts | 14 ++++ packages/cli/package.json | 5 ++ packages/cli/project.json | 58 ++++++++++++++++ packages/cli/src/commands/base.ts | 7 ++ packages/cli/src/commands/index.ts | 2 + packages/cli/src/commands/init.ts | 105 +++++++++++++++++++++++++++++ packages/cli/src/index.ts | 34 ++++++++++ packages/cli/tsconfig.json | 23 +++++++ packages/cli/tsconfig.lib.json | 10 +++ packages/cli/tsconfig.spec.json | 9 +++ tsconfig.base.json | 7 +- workspace.json | 1 + yarn.lock | 53 +++++++++------ 16 files changed, 337 insertions(+), 25 deletions(-) create mode 100644 packages/cli/.eslintrc.json create mode 100644 packages/cli/README.md create mode 100644 packages/cli/jest.config.ts create mode 100644 packages/cli/package.json create mode 100644 packages/cli/project.json create mode 100644 packages/cli/src/commands/base.ts create mode 100644 packages/cli/src/commands/index.ts create mode 100644 packages/cli/src/commands/init.ts create mode 100644 packages/cli/src/index.ts create mode 100644 packages/cli/tsconfig.json create mode 100644 packages/cli/tsconfig.lib.json create mode 100644 packages/cli/tsconfig.spec.json diff --git a/package.json b/package.json index 5b9fa5b2..e33525ff 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,10 @@ "dependencies": { "@koa/cors": "^3.3.0", "class-validator": "^0.13.2", + "commander": "^9.4.0", "dayjs": "^1.11.2", "glob": "^8.0.1", + "inquirer": "^8.0.0", "inversify": "^6.0.1", "joi": "^17.6.0", "js-yaml": "^4.1.0", @@ -22,6 +24,7 @@ "lodash": "^4.17.21", "nunjucks": "^3.2.3", "openapi3-ts": "^2.0.2", + "ora": "^5.4.1", "reflect-metadata": "^0.1.13", "tslib": "^2.3.0", "tslog": "^3.3.3", @@ -37,6 +40,7 @@ "@nrwl/workspace": "14.0.3", "@types/from2": "^2.3.1", "@types/glob": "^7.2.0", + "@types/inquirer": "^8.0.0", "@types/jest": "27.4.1", "@types/js-yaml": "^4.0.5", "@types/koa": "^2.13.4", diff --git a/packages/cli/.eslintrc.json b/packages/cli/.eslintrc.json new file mode 100644 index 00000000..9d9c0db5 --- /dev/null +++ b/packages/cli/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/packages/cli/README.md b/packages/cli/README.md new file mode 100644 index 00000000..06e32266 --- /dev/null +++ b/packages/cli/README.md @@ -0,0 +1,12 @@ +# cli + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build cli` to build the library. + +## Running unit tests + +Run `nx test cli` to execute the unit tests via [Jest](https://jestjs.io). + diff --git a/packages/cli/jest.config.ts b/packages/cli/jest.config.ts new file mode 100644 index 00000000..e941f150 --- /dev/null +++ b/packages/cli/jest.config.ts @@ -0,0 +1,14 @@ +module.exports = { + displayName: 'cli', + preset: '../../jest.preset.ts', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + transform: { + '^.+\\.[tj]s$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/packages/cli', +}; diff --git a/packages/cli/package.json b/packages/cli/package.json new file mode 100644 index 00000000..6cadf1d1 --- /dev/null +++ b/packages/cli/package.json @@ -0,0 +1,5 @@ +{ + "name": "@vulcan-sql/cli", + "version": "0.1.0", + "type": "commonjs" +} diff --git a/packages/cli/project.json b/packages/cli/project.json new file mode 100644 index 00000000..7dca24b3 --- /dev/null +++ b/packages/cli/project.json @@ -0,0 +1,58 @@ +{ + "root": "packages/cli", + "sourceRoot": "packages/cli/src", + "targets": { + "build": { + "executor": "@nrwl/js:tsc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/packages/cli", + "main": "packages/cli/src/index.ts", + "tsConfig": "packages/cli/tsconfig.lib.json", + "assets": ["packages/cli/*.md"] + } + }, + "publish": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "node tools/scripts/publish.mjs cli {args.ver} {args.tag}", + "cwd": "dist/packages/cli" + }, + "dependsOn": [ + { + "projects": "self", + "target": "build" + } + ] + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["packages/cli/**/*.ts"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/packages/cli"], + "options": { + "jestConfig": "packages/cli/jest.config.ts", + "passWithNoTests": true + } + }, + "run": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "node src/index.js {args.cmd}", + "cwd": "dist/packages/cli" + }, + "dependsOn": [ + { + "projects": "self", + "target": "build" + } + ] + } + }, + "tags": [] +} diff --git a/packages/cli/src/commands/base.ts b/packages/cli/src/commands/base.ts new file mode 100644 index 00000000..b24e08c5 --- /dev/null +++ b/packages/cli/src/commands/base.ts @@ -0,0 +1,7 @@ +import { Logger } from 'tslog'; + +export abstract class Command { + constructor(protected logger: Logger) {} + + abstract handle(options: O): Promise; +} diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts new file mode 100644 index 00000000..349b2925 --- /dev/null +++ b/packages/cli/src/commands/index.ts @@ -0,0 +1,2 @@ +export * from './init'; +export * from './base'; diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts new file mode 100644 index 00000000..09e7423c --- /dev/null +++ b/packages/cli/src/commands/init.ts @@ -0,0 +1,105 @@ +import { Command } from './base'; +import * as inquirer from 'inquirer'; +import { promises as fs } from 'fs'; +import * as path from 'path'; +import { version } from '../../package.json'; +import * as ora from 'ora'; +import { exec } from 'child_process'; + +const validators: Record< + string, + { + regex: RegExp; + errorMessage: string; + } +> = { + projectName: { + regex: /^[a-zA-Z0-9_-]+$/, + errorMessage: `Project name should contain only letters, numbers, or dashes.`, + }, +}; + +const validateAnswer = (name: string) => (input: string) => { + const validator = validators[name]; + if (!validator.regex.test(input)) { + throw new Error(validator.errorMessage); + } + return true; +}; + +interface InitCommandOptions { + projectName: string; + version: string; +} + +export class InitCommand extends Command { + public async handle(options: Partial): Promise { + const question = []; + + if (!options.projectName) { + question.push({ + type: 'input', + name: 'projectName', + message: 'Project name:', + default: 'my-first-vulcan-project', + validate: validateAnswer('projectName'), + }); + } else { + validateAnswer('projectName')(options.projectName); + } + + options = { + ...{ version }, + ...options, + ...(await inquirer.prompt(question)), + }; + + await this.createProject(options as InitCommandOptions); + } + + public async createProject(options: InitCommandOptions): Promise { + const projectPath = path.resolve(process.cwd(), options.projectName); + await fs.mkdir(projectPath); + const existedFiles = await fs.readdir(projectPath); + if (existedFiles.length > 0) + throw new Error(`Path ${projectPath} is not empty`); + + const installSpinner = ora('Creating project...').start(); + try { + await fs.writeFile( + path.resolve(projectPath, 'package.json'), + JSON.stringify( + { + name: options.projectName, + dependencies: { + '@vulcan-sql/core': options.version, + }, + }, + null, + 2 + ), + 'utf-8' + ); + installSpinner.succeed('Project has been created.'); + installSpinner.start('Installing dependencies...'); + await this.execAndWait(`yarn --silent`, projectPath); + installSpinner.succeed(`Dependencies have been installed.`); + } catch (e) { + installSpinner.fail(); + throw e; + } finally { + installSpinner.stop(); + } + } + + private async execAndWait(command: string, cwd: string) { + return new Promise((resolve, reject) => { + exec(command, { cwd }, (error, _, stderr) => { + if (error) { + reject(stderr); + } + resolve(); + }); + }); + } +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts new file mode 100644 index 00000000..7fab38a2 --- /dev/null +++ b/packages/cli/src/index.ts @@ -0,0 +1,34 @@ +import { program } from 'commander'; +import { InitCommand } from './commands'; +import { Logger } from 'tslog'; + +// We don't use createLogger helper from core package because CLI will be installed before all packages. +const logger = new Logger({ + name: 'CLI', + minLevel: 'info', + exposeErrorCodeFrame: false, + displayFilePath: 'hidden', + displayFunctionName: false, +}); + +const initCommand = new InitCommand(logger); + +program.exitOverride(); + +program + .command('init') + .option('-p --project-name ') + .action(async (options) => { + await initCommand.handle(options); + }); + +(async () => { + try { + await program.parseAsync(); + } catch (e: any) { + // Ignore error with exit code = 0, e.g. commander.helpDisplayed error + if (e?.exitCode === 0) return; + logger.prettyError(e, true, false, false); + process.exit(1); + } +})(); diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json new file mode 100644 index 00000000..e5d6cc36 --- /dev/null +++ b/packages/cli/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/packages/cli/tsconfig.lib.json b/packages/cli/tsconfig.lib.json new file mode 100644 index 00000000..7bfc80f7 --- /dev/null +++ b/packages/cli/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["**/*.ts"], + "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"] +} diff --git a/packages/cli/tsconfig.spec.json b/packages/cli/tsconfig.spec.json new file mode 100644 index 00000000..546f1287 --- /dev/null +++ b/packages/cli/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 8feae6ea..ec1de97e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -32,6 +32,7 @@ "@vulcan-sql/build/schema-parser/*": [ "packages/build/src/lib/schema-parser/*" ], + "@vulcan-sql/cli": ["packages/cli/src/index"], "@vulcan-sql/core": ["packages/core/src/index"], "@vulcan-sql/core/artifact-builder": [ "packages/core/src/lib/artifact-builder/index" @@ -66,10 +67,10 @@ ], "@vulcan-sql/extension-dbt": ["packages/extension-dbt/src/index"], "@vulcan-sql/integration-testing": [ - "packages/integration-testing/src/index.ts" + "packages/integration-testing/src/index" ], "@vulcan-sql/serve": ["packages/serve/src/index"], - "@vulcan-sql/serve/app": ["packages/serve/src/lib/app.ts"], + "@vulcan-sql/serve/app": ["packages/serve/src/lib/app"], "@vulcan-sql/serve/config": ["packages/serve/src/lib/config"], "@vulcan-sql/serve/containers": ["packages/serve/src/containers/index"], "@vulcan-sql/serve/loader": ["packages/serve/src/lib/loader"], @@ -93,7 +94,7 @@ "@vulcan-sql/serve/route/*": ["packages/serve/src/lib/route/*"], "@vulcan-sql/serve/utils": ["packages/serve/src/lib/utils/index"], "@vulcan-sql/serve/utils/*": ["packages/serve/src/lib/utils/*"], - "@vulcan-sql/test-utility": ["packages/test-utility/src/index.ts"] + "@vulcan-sql/test-utility": ["packages/test-utility/src/index"] } }, "exclude": ["node_modules", "tmp"] diff --git a/workspace.json b/workspace.json index 9ca4b734..62ca7e4e 100644 --- a/workspace.json +++ b/workspace.json @@ -2,6 +2,7 @@ "version": 2, "projects": { "build": "packages/build", + "cli": "packages/cli", "core": "packages/core", "extension-dbt": "packages/extension-dbt", "integration-testing": "packages/integration-testing", diff --git a/yarn.lock b/yarn.lock index fcbfa4f4..5e49e161 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1124,6 +1124,13 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-1.8.2.tgz#7315b4c4c54f82d13fa61c228ec5c2ea5cc9e0e1" integrity sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w== +"@types/inquirer@^8.0.0": + version "8.2.2" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.2.tgz#7ec5f166026b55b10df011521c8a63920cb84a7a" + integrity sha512-HXoFOl+KS4yvmUjisi83VpuSBOeeXIzdJfoT/v2pUruqybpHy0Dz1DyXy3E2jNH0cSVKJZV92VOnFBwJR6k83A== + dependencies: + "@types/through" "*" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" @@ -1348,6 +1355,13 @@ dependencies: "@types/superagent" "*" +"@types/through@*": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" + integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== + dependencies: + "@types/node" "*" + "@types/uuid@^8.3.4": version "8.3.4" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" @@ -2021,15 +2035,10 @@ cli-spinners@2.6.1: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== -cli-spinners@^2.5.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== cliui@^7.0.2: version "7.0.4" @@ -2129,10 +2138,10 @@ commander@^6.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== -commitizen@^4.0.3, commitizen@^4.2.5: - version "4.2.5" - resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-4.2.5.tgz#48e5a5c28334c6e8ed845cc24fc9f072efd3961e" - integrity sha512-9sXju8Qrz1B4Tw7kC5KhnvwYQN88qs2zbiB8oyMsnXZyJ24PPGiNM3nHr73d32dnE3i8VJEXddBFIbOgYSEXtQ== +commitizen@^4.0.3: + version "4.2.4" + resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-4.2.4.tgz#a3e5b36bd7575f6bf6e7aa19dbbf06b0d8f37165" + integrity sha512-LlZChbDzg3Ir3O2S7jSo/cgWp5/QwylQVr59K4xayVq8S4/RdKzSyJkghAiZZHfhh5t4pxunUoyeg0ml1q/7aw== dependencies: cachedir "2.3.0" cz-conventional-changelog "3.3.0" @@ -3440,10 +3449,10 @@ ini@^1.3.4: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -inquirer@8.2.4: - version "8.2.4" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4" - integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg== +inquirer@6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -4741,10 +4750,10 @@ ms@2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= nanomatch@^1.2.9: version "1.2.13" @@ -5514,7 +5523,7 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -run-async@^2.4.0: +run-async@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== From 4aee4766f5239a77ae27b53d7d671b383a0af9db Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Thu, 11 Aug 2022 15:16:19 +0800 Subject: [PATCH 02/14] feat(cli, core): write some initial files for demo - write some initial files for demo - fix in-memory code loader name --- packages/cli/package.json | 23 +++++++++- packages/cli/project.json | 42 ++++++++++++++++++- packages/cli/src/commands/init.ts | 42 +++++++++++++++++++ packages/cli/src/index.ts | 2 + packages/cli/src/schemas/init/sqls/user.sql | 4 ++ packages/cli/src/schemas/init/sqls/user.yaml | 8 ++++ packages/cli/src/schemas/init/vulcan.yaml | 23 ++++++++++ packages/cli/tsconfig.json | 3 +- packages/cli/tsconfig.lib.json | 2 +- .../code-loader/inMemoryCodeLoader.ts | 2 +- packages/core/src/options/templateEngine.ts | 2 +- 11 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 packages/cli/src/schemas/init/sqls/user.sql create mode 100644 packages/cli/src/schemas/init/sqls/user.yaml create mode 100644 packages/cli/src/schemas/init/vulcan.yaml diff --git a/packages/cli/package.json b/packages/cli/package.json index 6cadf1d1..e58e949f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,5 +1,26 @@ { "name": "@vulcan-sql/cli", "version": "0.1.0", - "type": "commonjs" + "type": "commonjs", + "bin": { + "vulcan": "./src/index.js" + }, + "publishConfig": { + "access": "public" + }, + "keywords": [ + "vulcan", + "vulcan-sql", + "data", + "sql", + "database", + "data-warehouse", + "data-lake", + "api-builder" + ], + "repository": { + "type": "git", + "url": "https://github.com/Canner/vulcan.git" + }, + "license": "MIT" } diff --git a/packages/cli/project.json b/packages/cli/project.json index 7dca24b3..7612c3ee 100644 --- a/packages/cli/project.json +++ b/packages/cli/project.json @@ -44,12 +44,52 @@ "executor": "@nrwl/workspace:run-commands", "options": { "command": "node src/index.js {args.cmd}", - "cwd": "dist/packages/cli" + "cwd": "dist/packages/cli", + "forwardAllArgs": false }, "dependsOn": [ { "projects": "self", "target": "build" + }, + { + "projects": "self", + "target": "cp-assets" + } + ] + }, + "cp-assets": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "commands": [ + { + "command": "cp -r packages/cli/src/schemas dist/packages/cli/src", + "forwardAllArgs": false + } + ] + }, + "dependsOn": [ + { + "projects": "self", + "target": "build" + } + ] + }, + "install": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "commands": [ + { + "command": "chmod +x ./src/index.js && npm i -g . && echo done.", + "forwardAllArgs": false + } + ], + "cwd": "dist/packages/cli" + }, + "dependsOn": [ + { + "projects": "self", + "target": "cp-assets" } ] } diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 09e7423c..e47ccc0f 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -5,6 +5,8 @@ import * as path from 'path'; import { version } from '../../package.json'; import * as ora from 'ora'; import { exec } from 'child_process'; +import * as nunjucks from 'nunjucks'; +import * as glob from 'glob'; const validators: Record< string, @@ -73,6 +75,7 @@ export class InitCommand extends Command { name: options.projectName, dependencies: { '@vulcan-sql/core': options.version, + // TODO: Install build/serve package when they are ready }, }, null, @@ -84,6 +87,13 @@ export class InitCommand extends Command { installSpinner.start('Installing dependencies...'); await this.execAndWait(`yarn --silent`, projectPath); installSpinner.succeed(`Dependencies have been installed.`); + installSpinner.start('Writing initial content...'); + await this.addInitFiles(projectPath, options); + installSpinner.succeed('Initial done.'); + + this.logger.info( + `Project has been initialized. Run "cd ${projectPath} && vulcan start" to start the server.` + ); } catch (e) { installSpinner.fail(); throw e; @@ -102,4 +112,36 @@ export class InitCommand extends Command { }); }); } + + private async addInitFiles(projectPath: string, options: InitCommandOptions) { + const files = await this.listFiles( + path.resolve(__dirname, '..', 'schemas', 'init', '**/*.*') + ); + for (const file of files) { + const relativePath = path.relative( + path.resolve(__dirname, '..', 'schemas', 'init'), + file + ); + let templateContent = await fs.readFile(file, 'utf8'); + if (path.extname(file) === '.yaml') { + // Only render yaml files because sql files have some template scripts which are used by Vulcan. + templateContent = nunjucks.renderString(templateContent, { + options, + }); + } + const targetPath = path.resolve(projectPath, relativePath); + const dir = path.dirname(targetPath); + await fs.mkdir(dir, { recursive: true }); + await fs.writeFile(targetPath, templateContent, 'utf8'); + } + } + + private async listFiles(path: string) { + return new Promise((resolve, reject) => { + glob(path, (err, files) => { + if (err) return reject(err); + return resolve(files); + }); + }); + } } diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 7fab38a2..8d50f93f 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,3 +1,5 @@ +#!/usr/bin/env node + import { program } from 'commander'; import { InitCommand } from './commands'; import { Logger } from 'tslog'; diff --git a/packages/cli/src/schemas/init/sqls/user.sql b/packages/cli/src/schemas/init/sqls/user.sql new file mode 100644 index 00000000..6f9bab8a --- /dev/null +++ b/packages/cli/src/schemas/init/sqls/user.sql @@ -0,0 +1,4 @@ +select * +from public.users +where id = "{{ context.params.id }}" +limit 1; \ No newline at end of file diff --git a/packages/cli/src/schemas/init/sqls/user.yaml b/packages/cli/src/schemas/init/sqls/user.yaml new file mode 100644 index 00000000..6880eaa3 --- /dev/null +++ b/packages/cli/src/schemas/init/sqls/user.yaml @@ -0,0 +1,8 @@ +url: /user/:id +request: + - fieldName: id + fieldIn: path + description: user id + validators: + - uuid + - required diff --git a/packages/cli/src/schemas/init/vulcan.yaml b/packages/cli/src/schemas/init/vulcan.yaml new file mode 100644 index 00000000..51895f59 --- /dev/null +++ b/packages/cli/src/schemas/init/vulcan.yaml @@ -0,0 +1,23 @@ +name: '{{ options.projectName }}' +description: A starter Vulcan project +version: 0.1.0 +template: + provider: LocalFile + # Path to .sql files + folderPath: sqls + codeLoader: InMemory +artifact: + provider: LocalFile + serializer: JSON + # Path to build result + filePath: result.json +schema-parser: + reader: LocalFile + # Path to .yaml files + folderPath: sqls +document-generator: + specs: + - oas3 + folderPath: . +types: + - RESTFUL diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index e5d6cc36..9be5c4ac 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -8,7 +8,8 @@ "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "sourceMap": false }, "files": [], "include": [], diff --git a/packages/cli/tsconfig.lib.json b/packages/cli/tsconfig.lib.json index 7bfc80f7..bc8e5e0d 100644 --- a/packages/cli/tsconfig.lib.json +++ b/packages/cli/tsconfig.lib.json @@ -5,6 +5,6 @@ "declaration": true, "types": ["node"] }, - "include": ["**/*.ts"], + "include": ["**/*.ts", "../../types/*.d.ts"], "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"] } diff --git a/packages/core/src/lib/template-engine/code-loader/inMemoryCodeLoader.ts b/packages/core/src/lib/template-engine/code-loader/inMemoryCodeLoader.ts index 917c556c..f6cee168 100644 --- a/packages/core/src/lib/template-engine/code-loader/inMemoryCodeLoader.ts +++ b/packages/core/src/lib/template-engine/code-loader/inMemoryCodeLoader.ts @@ -6,7 +6,7 @@ import { } from '@vulcan-sql/core/models'; @VulcanInternalExtension() -@VulcanExtensionId('inMemory') +@VulcanExtensionId('InMemory') export class InMemoryCodeLoader extends CodeLoader { private source = new Map(); diff --git a/packages/core/src/options/templateEngine.ts b/packages/core/src/options/templateEngine.ts index 3d7a7d1e..39d95b9a 100644 --- a/packages/core/src/options/templateEngine.ts +++ b/packages/core/src/options/templateEngine.ts @@ -15,7 +15,7 @@ export class TemplateEngineOptions implements ITemplateEngineOptions { @IsString() @IsOptional() - public readonly codeLoader: string = 'inMemory'; + public readonly codeLoader: string = 'InMemory'; constructor( @inject(TYPES.TemplateEngineInputOptions) From c4f96dccc6b9118275e485ec0d4ac91beddc97f0 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Fri, 12 Aug 2022 16:34:05 +0800 Subject: [PATCH 03/14] fix(build): unite options injection --- packages/build/src/lib/vulcanBuilder.ts | 9 +++++++-- packages/build/test/builder/builder.spec.ts | 4 ++-- .../src/example1/buildAndServe.spec.ts | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/build/src/lib/vulcanBuilder.ts b/packages/build/src/lib/vulcanBuilder.ts index 303601bf..fb6686b1 100644 --- a/packages/build/src/lib/vulcanBuilder.ts +++ b/packages/build/src/lib/vulcanBuilder.ts @@ -9,9 +9,14 @@ import { import { DocumentGenerator } from './document-generator'; export class VulcanBuilder { - public async build(options: IBuildOptions) { + private options: IBuildOptions; + constructor(options: IBuildOptions) { + this.options = options; + } + + public async build() { const container = new Container(); - await container.load(options); + await container.load(this.options); const schemaParser = container.get(TYPES.SchemaParser); const templateEngine = container.get( CORE_TYPES.TemplateEngine diff --git a/packages/build/test/builder/builder.spec.ts b/packages/build/test/builder/builder.spec.ts index 1e6a8b5d..d264e608 100644 --- a/packages/build/test/builder/builder.spec.ts +++ b/packages/build/test/builder/builder.spec.ts @@ -13,7 +13,6 @@ import { it('Builder.build should work', async () => { // Arrange - const builder = new VulcanBuilder(); const options: IBuildOptions = { 'schema-parser': { reader: SchemaReaderType.LocalFile, @@ -34,7 +33,8 @@ it('Builder.build should work', async () => { }, extensions: {}, }; + const builder = new VulcanBuilder(options); // Act, Assert - await expect(builder.build(options)).resolves.not.toThrow(); + await expect(builder.build()).resolves.not.toThrow(); }); diff --git a/packages/integration-testing/src/example1/buildAndServe.spec.ts b/packages/integration-testing/src/example1/buildAndServe.spec.ts index 39bac818..0bac3e22 100644 --- a/packages/integration-testing/src/example1/buildAndServe.spec.ts +++ b/packages/integration-testing/src/example1/buildAndServe.spec.ts @@ -53,8 +53,8 @@ afterEach(async () => { }); it('Example1: Build and serve should work', async () => { - const builder = new VulcanBuilder(); - await builder.build(projectConfig); + const builder = new VulcanBuilder(projectConfig); + await builder.build(); server = new VulcanServer(projectConfig); const httpServer = await server.start(3000); From 7b5b54749dd507575e1cb1a027978f98c0a18177 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Fri, 12 Aug 2022 16:41:24 +0800 Subject: [PATCH 04/14] feat(cli): implement start command --- .gitignore | 3 ++ packages/cli/src/commands/index.ts | 1 + packages/cli/src/commands/init.ts | 2 +- packages/cli/src/commands/start.ts | 61 ++++++++++++++++++++++++++++++ packages/cli/src/index.ts | 13 ++++++- 5 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 packages/cli/src/commands/start.ts diff --git a/.gitignore b/.gitignore index f5a5e0b5..96261570 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ testem.log # System Files .DS_Store Thumbs.db + +# testing folder from cli +my-first-vulcan-project \ No newline at end of file diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 349b2925..37c90067 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -1,2 +1,3 @@ export * from './init'; export * from './base'; +export * from './start'; diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index e47ccc0f..0e2fa79f 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -85,7 +85,7 @@ export class InitCommand extends Command { ); installSpinner.succeed('Project has been created.'); installSpinner.start('Installing dependencies...'); - await this.execAndWait(`yarn --silent`, projectPath); + await this.execAndWait(`npm install --silent`, projectPath); installSpinner.succeed(`Dependencies have been installed.`); installSpinner.start('Writing initial content...'); await this.addInitFiles(projectPath, options); diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts new file mode 100644 index 00000000..b64f21fa --- /dev/null +++ b/packages/cli/src/commands/start.ts @@ -0,0 +1,61 @@ +import { Command } from './base'; +import * as jsYAML from 'js-yaml'; +import { promises as fs } from 'fs'; +import * as path from 'path'; +import * as ora from 'ora'; + +export interface StartCommandOptions { + config: string; + port: number; +} + +const defaultConfig: StartCommandOptions = { + config: './vulcan.yaml', + port: 3000, +}; + +export class StartCommand extends Command { + public async handle(options: Partial): Promise { + options = { + ...defaultConfig, + ...options, + }; + await this.buildAndServe(options as StartCommandOptions); + } + + public async buildAndServe(options: StartCommandOptions) { + const configPath = path.resolve(process.cwd(), options.config); + const config: any = jsYAML.load(await fs.readFile(configPath, 'utf-8')); + + // Import dependencies. We use dynamic import here to import dependencies at runtime. + const { VulcanBuilder } = await import( + this.localModulePath('@vulcan-sql/build') + ); + const { VulcanServer } = await import( + this.localModulePath('@vulcan-sql/serve') + ); + + // Build project + const spinner = ora('Building project...').start(); + try { + const builder = new VulcanBuilder(config); + await builder.build(); + spinner.succeed('Built successfully.'); + + // Start server + this.logger.info('Starting server...'); + const server = new VulcanServer(config); + await server.start(options.port); + this.logger.info(`Server is listening at port ${options.port}.`); + } catch (e) { + spinner.fail(); + throw e; + } finally { + spinner.stop(); + } + } + + private localModulePath(moduleName: string): string { + return path.resolve(process.cwd(), 'node_modules', moduleName); + } +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 8d50f93f..ce52add7 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node import { program } from 'commander'; -import { InitCommand } from './commands'; +import { InitCommand, StartCommand } from './commands'; import { Logger } from 'tslog'; // We don't use createLogger helper from core package because CLI will be installed before all packages. @@ -14,6 +14,7 @@ const logger = new Logger({ }); const initCommand = new InitCommand(logger); +const startCommand = new StartCommand(logger); program.exitOverride(); @@ -24,6 +25,14 @@ program await initCommand.handle(options); }); +program + .command('start') + .option('-c --config ') + .option('-p --port ') + .action(async (options) => { + await startCommand.handle(options); + }); + (async () => { try { await program.parseAsync(); @@ -31,6 +40,6 @@ program // Ignore error with exit code = 0, e.g. commander.helpDisplayed error if (e?.exitCode === 0) return; logger.prettyError(e, true, false, false); - process.exit(1); + process.exit(e?.exitCode ?? 1); } })(); From a56dec708ae4b0c45f88662fd189f957fd883a01 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Fri, 12 Aug 2022 16:58:04 +0800 Subject: [PATCH 05/14] feat(cli): implement version command --- packages/cli/src/commands/index.ts | 1 + packages/cli/src/commands/version.ts | 37 ++++++++++++++++++++++++++++ packages/cli/src/index.ts | 7 +++++- 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 packages/cli/src/commands/version.ts diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 37c90067..1d63ed14 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -1,3 +1,4 @@ export * from './init'; export * from './base'; export * from './start'; +export * from './version'; diff --git a/packages/cli/src/commands/version.ts b/packages/cli/src/commands/version.ts new file mode 100644 index 00000000..85533cbc --- /dev/null +++ b/packages/cli/src/commands/version.ts @@ -0,0 +1,37 @@ +import { Command } from './base'; +import * as path from 'path'; +import { promises as fs } from 'fs'; + +export class VersionCommand extends Command { + public async handle(): Promise { + this.logger.info(`cli version: ${await this.cliVersion()}`); + for (const pkg of ['core', 'build', 'serve']) { + this.logger.info( + `${pkg} version: ${await this.localModuleVersion(`@vulcan-sql/${pkg}`)}` + ); + } + } + + private async cliVersion() { + return JSON.parse( + await fs.readFile( + path.resolve(__dirname, '..', '..', 'package.json'), + 'utf8' + ) + ).version; + } + + private async localModuleVersion(moduleName: string): Promise { + try { + const packageJson = path.resolve( + process.cwd(), + 'node_modules', + moduleName, + 'package.json' + ); + return JSON.parse(await fs.readFile(packageJson, 'utf8')).version; + } catch { + return '-'; + } + } +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index ce52add7..b5fbf1bf 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node import { program } from 'commander'; -import { InitCommand, StartCommand } from './commands'; +import { InitCommand, StartCommand, VersionCommand } from './commands'; import { Logger } from 'tslog'; // We don't use createLogger helper from core package because CLI will be installed before all packages. @@ -15,9 +15,14 @@ const logger = new Logger({ const initCommand = new InitCommand(logger); const startCommand = new StartCommand(logger); +const versionCommand = new VersionCommand(logger); program.exitOverride(); +program.command('version').action(async () => { + await versionCommand.handle(); +}); + program .command('init') .option('-p --project-name ') From 76e75f40c695cf0bd1d7d537bf6e3317681ef3f9 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Fri, 12 Aug 2022 17:20:42 +0800 Subject: [PATCH 06/14] feat(cli): implement build,serve commands --- packages/cli/src/commands/base.ts | 7 - packages/cli/src/commands/build.ts | 44 ++++++ packages/cli/src/commands/index.ts | 1 - packages/cli/src/commands/init.ts | 213 ++++++++++++++------------- packages/cli/src/commands/serve.ts | 38 +++++ packages/cli/src/commands/start.ts | 68 +-------- packages/cli/src/commands/version.ts | 58 ++++---- packages/cli/src/index.ts | 40 ++--- packages/cli/src/utils/index.ts | 2 + packages/cli/src/utils/logger.ts | 10 ++ packages/cli/src/utils/module.ts | 5 + 11 files changed, 266 insertions(+), 220 deletions(-) delete mode 100644 packages/cli/src/commands/base.ts create mode 100644 packages/cli/src/commands/build.ts create mode 100644 packages/cli/src/commands/serve.ts create mode 100644 packages/cli/src/utils/index.ts create mode 100644 packages/cli/src/utils/logger.ts create mode 100644 packages/cli/src/utils/module.ts diff --git a/packages/cli/src/commands/base.ts b/packages/cli/src/commands/base.ts deleted file mode 100644 index b24e08c5..00000000 --- a/packages/cli/src/commands/base.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Logger } from 'tslog'; - -export abstract class Command { - constructor(protected logger: Logger) {} - - abstract handle(options: O): Promise; -} diff --git a/packages/cli/src/commands/build.ts b/packages/cli/src/commands/build.ts new file mode 100644 index 00000000..f0756f2d --- /dev/null +++ b/packages/cli/src/commands/build.ts @@ -0,0 +1,44 @@ +import * as jsYAML from 'js-yaml'; +import { promises as fs } from 'fs'; +import * as path from 'path'; +import * as ora from 'ora'; +import { localModulePath } from '../utils'; + +export interface BuildCommandOptions { + config: string; +} + +const defaultOptions: BuildCommandOptions = { + config: './vulcan.yaml', +}; + +export const buildVulcan = async (options: BuildCommandOptions) => { + const configPath = path.resolve(process.cwd(), options.config); + const config: any = jsYAML.load(await fs.readFile(configPath, 'utf-8')); + + // Import dependencies. We use dynamic import here to import dependencies at runtime. + const { VulcanBuilder } = await import(localModulePath('@vulcan-sql/build')); + + // Build project + const spinner = ora('Building project...').start(); + try { + const builder = new VulcanBuilder(config); + await builder.build(); + spinner.succeed('Built successfully.'); + } catch (e) { + spinner.fail(); + throw e; + } finally { + spinner.stop(); + } +}; + +export const handleBuild = async ( + options: Partial +): Promise => { + options = { + ...defaultOptions, + ...options, + }; + await buildVulcan(options as BuildCommandOptions); +}; diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 1d63ed14..f9f1cf5a 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -1,4 +1,3 @@ export * from './init'; -export * from './base'; export * from './start'; export * from './version'; diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 0e2fa79f..04a75fb6 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -1,4 +1,3 @@ -import { Command } from './base'; import * as inquirer from 'inquirer'; import { promises as fs } from 'fs'; import * as path from 'path'; @@ -7,6 +6,12 @@ import * as ora from 'ora'; import { exec } from 'child_process'; import * as nunjucks from 'nunjucks'; import * as glob from 'glob'; +import { logger } from '../utils'; + +interface InitCommandOptions { + projectName: string; + version: string; +} const validators: Record< string, @@ -29,119 +34,119 @@ const validateAnswer = (name: string) => (input: string) => { return true; }; -interface InitCommandOptions { - projectName: string; - version: string; -} - -export class InitCommand extends Command { - public async handle(options: Partial): Promise { - const question = []; +export const createProject = async ( + options: InitCommandOptions +): Promise => { + const projectPath = path.resolve(process.cwd(), options.projectName); + await fs.mkdir(projectPath); + const existedFiles = await fs.readdir(projectPath); + if (existedFiles.length > 0) + throw new Error(`Path ${projectPath} is not empty`); - if (!options.projectName) { - question.push({ - type: 'input', - name: 'projectName', - message: 'Project name:', - default: 'my-first-vulcan-project', - validate: validateAnswer('projectName'), - }); - } else { - validateAnswer('projectName')(options.projectName); - } - - options = { - ...{ version }, - ...options, - ...(await inquirer.prompt(question)), - }; - - await this.createProject(options as InitCommandOptions); - } - - public async createProject(options: InitCommandOptions): Promise { - const projectPath = path.resolve(process.cwd(), options.projectName); - await fs.mkdir(projectPath); - const existedFiles = await fs.readdir(projectPath); - if (existedFiles.length > 0) - throw new Error(`Path ${projectPath} is not empty`); - - const installSpinner = ora('Creating project...').start(); - try { - await fs.writeFile( - path.resolve(projectPath, 'package.json'), - JSON.stringify( - { - name: options.projectName, - dependencies: { - '@vulcan-sql/core': options.version, - // TODO: Install build/serve package when they are ready - }, + const installSpinner = ora('Creating project...').start(); + try { + await fs.writeFile( + path.resolve(projectPath, 'package.json'), + JSON.stringify( + { + name: options.projectName, + dependencies: { + '@vulcan-sql/core': options.version, + // TODO: Install build/serve package when they are ready }, - null, - 2 - ), - 'utf-8' - ); - installSpinner.succeed('Project has been created.'); - installSpinner.start('Installing dependencies...'); - await this.execAndWait(`npm install --silent`, projectPath); - installSpinner.succeed(`Dependencies have been installed.`); - installSpinner.start('Writing initial content...'); - await this.addInitFiles(projectPath, options); - installSpinner.succeed('Initial done.'); + }, + null, + 2 + ), + 'utf-8' + ); + installSpinner.succeed('Project has been created.'); + installSpinner.start('Installing dependencies...'); + await execAndWait(`npm install --silent`, projectPath); + installSpinner.succeed(`Dependencies have been installed.`); + installSpinner.start('Writing initial content...'); + await addInitFiles(projectPath, options); + installSpinner.succeed('Initial done.'); - this.logger.info( - `Project has been initialized. Run "cd ${projectPath} && vulcan start" to start the server.` - ); - } catch (e) { - installSpinner.fail(); - throw e; - } finally { - installSpinner.stop(); - } + logger.info( + `Project has been initialized. Run "cd ${projectPath} && vulcan start" to start the server.` + ); + } catch (e) { + installSpinner.fail(); + throw e; + } finally { + installSpinner.stop(); } +}; - private async execAndWait(command: string, cwd: string) { - return new Promise((resolve, reject) => { - exec(command, { cwd }, (error, _, stderr) => { - if (error) { - reject(stderr); - } - resolve(); - }); +export const execAndWait = async (command: string, cwd: string) => { + return new Promise((resolve, reject) => { + exec(command, { cwd }, (error, _, stderr) => { + if (error) { + reject(stderr); + } + resolve(); }); - } + }); +}; - private async addInitFiles(projectPath: string, options: InitCommandOptions) { - const files = await this.listFiles( - path.resolve(__dirname, '..', 'schemas', 'init', '**/*.*') +const addInitFiles = async ( + projectPath: string, + options: InitCommandOptions +) => { + const files = await listFiles( + path.resolve(__dirname, '..', 'schemas', 'init', '**/*.*') + ); + for (const file of files) { + const relativePath = path.relative( + path.resolve(__dirname, '..', 'schemas', 'init'), + file ); - for (const file of files) { - const relativePath = path.relative( - path.resolve(__dirname, '..', 'schemas', 'init'), - file - ); - let templateContent = await fs.readFile(file, 'utf8'); - if (path.extname(file) === '.yaml') { - // Only render yaml files because sql files have some template scripts which are used by Vulcan. - templateContent = nunjucks.renderString(templateContent, { - options, - }); - } - const targetPath = path.resolve(projectPath, relativePath); - const dir = path.dirname(targetPath); - await fs.mkdir(dir, { recursive: true }); - await fs.writeFile(targetPath, templateContent, 'utf8'); + let templateContent = await fs.readFile(file, 'utf8'); + if (path.extname(file) === '.yaml') { + // Only render yaml files because sql files have some template scripts which are used by Vulcan. + templateContent = nunjucks.renderString(templateContent, { + options, + }); } + const targetPath = path.resolve(projectPath, relativePath); + const dir = path.dirname(targetPath); + await fs.mkdir(dir, { recursive: true }); + await fs.writeFile(targetPath, templateContent, 'utf8'); } +}; - private async listFiles(path: string) { - return new Promise((resolve, reject) => { - glob(path, (err, files) => { - if (err) return reject(err); - return resolve(files); - }); +const listFiles = async (path: string) => { + return new Promise((resolve, reject) => { + glob(path, (err, files) => { + if (err) return reject(err); + return resolve(files); + }); + }); +}; + +export const handleInit = async ( + options: Partial +): Promise => { + const question = []; + + if (!options.projectName) { + question.push({ + type: 'input', + name: 'projectName', + message: 'Project name:', + default: 'my-first-vulcan-project', + validate: validateAnswer('projectName'), }); + } else { + validateAnswer('projectName')(options.projectName); } -} + + options = { + ...{ version }, + ...options, + ...(await inquirer.prompt(question)), + }; + + await createProject(options as InitCommandOptions); +}; diff --git a/packages/cli/src/commands/serve.ts b/packages/cli/src/commands/serve.ts new file mode 100644 index 00000000..0f7c41d6 --- /dev/null +++ b/packages/cli/src/commands/serve.ts @@ -0,0 +1,38 @@ +import * as jsYAML from 'js-yaml'; +import { promises as fs } from 'fs'; +import * as path from 'path'; +import { localModulePath, logger } from '../utils'; + +export interface ServeCommandOptions { + config: string; + port: number; +} + +const defaultOptions: ServeCommandOptions = { + config: './vulcan.yaml', + port: 3000, +}; + +export const serveVulcan = async (options: ServeCommandOptions) => { + const configPath = path.resolve(process.cwd(), options.config); + const config: any = jsYAML.load(await fs.readFile(configPath, 'utf-8')); + + // Import dependencies. We use dynamic import here to import dependencies at runtime. + const { VulcanServer } = await import(localModulePath('@vulcan-sql/serve')); + + // Start server + logger.info(`Starting server...`); + const server = new VulcanServer(config); + await server.start(options.port); + logger.info(`Server is listening at port ${options.port}.`); +}; + +export const handleServe = async ( + options: Partial +): Promise => { + options = { + ...defaultOptions, + ...options, + }; + await serveVulcan(options as ServeCommandOptions); +}; diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index b64f21fa..84f16ac3 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -1,61 +1,9 @@ -import { Command } from './base'; -import * as jsYAML from 'js-yaml'; -import { promises as fs } from 'fs'; -import * as path from 'path'; -import * as ora from 'ora'; - -export interface StartCommandOptions { - config: string; - port: number; -} - -const defaultConfig: StartCommandOptions = { - config: './vulcan.yaml', - port: 3000, +import { BuildCommandOptions, handleBuild } from './build'; +import { handleServe, ServeCommandOptions } from './serve'; + +export const handleStart = async ( + options: Partial +): Promise => { + await handleBuild(options); + await handleServe(options); }; - -export class StartCommand extends Command { - public async handle(options: Partial): Promise { - options = { - ...defaultConfig, - ...options, - }; - await this.buildAndServe(options as StartCommandOptions); - } - - public async buildAndServe(options: StartCommandOptions) { - const configPath = path.resolve(process.cwd(), options.config); - const config: any = jsYAML.load(await fs.readFile(configPath, 'utf-8')); - - // Import dependencies. We use dynamic import here to import dependencies at runtime. - const { VulcanBuilder } = await import( - this.localModulePath('@vulcan-sql/build') - ); - const { VulcanServer } = await import( - this.localModulePath('@vulcan-sql/serve') - ); - - // Build project - const spinner = ora('Building project...').start(); - try { - const builder = new VulcanBuilder(config); - await builder.build(); - spinner.succeed('Built successfully.'); - - // Start server - this.logger.info('Starting server...'); - const server = new VulcanServer(config); - await server.start(options.port); - this.logger.info(`Server is listening at port ${options.port}.`); - } catch (e) { - spinner.fail(); - throw e; - } finally { - spinner.stop(); - } - } - - private localModulePath(moduleName: string): string { - return path.resolve(process.cwd(), 'node_modules', moduleName); - } -} diff --git a/packages/cli/src/commands/version.ts b/packages/cli/src/commands/version.ts index 85533cbc..a2814c22 100644 --- a/packages/cli/src/commands/version.ts +++ b/packages/cli/src/commands/version.ts @@ -1,37 +1,35 @@ -import { Command } from './base'; import * as path from 'path'; import { promises as fs } from 'fs'; +import { logger } from '../utils'; -export class VersionCommand extends Command { - public async handle(): Promise { - this.logger.info(`cli version: ${await this.cliVersion()}`); - for (const pkg of ['core', 'build', 'serve']) { - this.logger.info( - `${pkg} version: ${await this.localModuleVersion(`@vulcan-sql/${pkg}`)}` - ); - } - } +const cliVersion = async () => { + return JSON.parse( + await fs.readFile( + path.resolve(__dirname, '..', '..', 'package.json'), + 'utf8' + ) + ).version; +}; - private async cliVersion() { - return JSON.parse( - await fs.readFile( - path.resolve(__dirname, '..', '..', 'package.json'), - 'utf8' - ) - ).version; +const localModuleVersion = async (moduleName: string): Promise => { + try { + const packageJson = path.resolve( + process.cwd(), + 'node_modules', + moduleName, + 'package.json' + ); + return JSON.parse(await fs.readFile(packageJson, 'utf8')).version; + } catch { + return '-'; } +}; - private async localModuleVersion(moduleName: string): Promise { - try { - const packageJson = path.resolve( - process.cwd(), - 'node_modules', - moduleName, - 'package.json' - ); - return JSON.parse(await fs.readFile(packageJson, 'utf8')).version; - } catch { - return '-'; - } +export const handleVersion = async (): Promise => { + logger.info(`cli version: ${await cliVersion()}`); + for (const pkg of ['core', 'build', 'serve']) { + logger.info( + `${pkg} version: ${await localModuleVersion(`@vulcan-sql/${pkg}`)}` + ); } -} +}; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index b5fbf1bf..0de1e335 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,33 +1,37 @@ #!/usr/bin/env node import { program } from 'commander'; -import { InitCommand, StartCommand, VersionCommand } from './commands'; -import { Logger } from 'tslog'; - -// We don't use createLogger helper from core package because CLI will be installed before all packages. -const logger = new Logger({ - name: 'CLI', - minLevel: 'info', - exposeErrorCodeFrame: false, - displayFilePath: 'hidden', - displayFunctionName: false, -}); - -const initCommand = new InitCommand(logger); -const startCommand = new StartCommand(logger); -const versionCommand = new VersionCommand(logger); +import { handleInit, handleStart, handleVersion } from './commands'; +import { handleBuild } from './commands/build'; +import { handleServe } from './commands/serve'; +import { logger } from './utils'; program.exitOverride(); program.command('version').action(async () => { - await versionCommand.handle(); + await handleVersion(); }); program .command('init') .option('-p --project-name ') .action(async (options) => { - await initCommand.handle(options); + await handleInit(options); + }); + +program + .command('build') + .option('-c --config ') + .action(async (options) => { + await handleBuild(options); + }); + +program + .command('serve') + .option('-c --config ') + .option('-p --port ') + .action(async (options) => { + await handleServe(options); }); program @@ -35,7 +39,7 @@ program .option('-c --config ') .option('-p --port ') .action(async (options) => { - await startCommand.handle(options); + await handleStart(options); }); (async () => { diff --git a/packages/cli/src/utils/index.ts b/packages/cli/src/utils/index.ts new file mode 100644 index 00000000..0c982263 --- /dev/null +++ b/packages/cli/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from './module'; +export * from './logger'; diff --git a/packages/cli/src/utils/logger.ts b/packages/cli/src/utils/logger.ts new file mode 100644 index 00000000..938c851e --- /dev/null +++ b/packages/cli/src/utils/logger.ts @@ -0,0 +1,10 @@ +import { Logger } from 'tslog'; + +// We don't use createLogger helper from core package because CLI will be installed before all packages. +export const logger = new Logger({ + name: 'CLI', + minLevel: 'info', + exposeErrorCodeFrame: false, + displayFilePath: 'hidden', + displayFunctionName: false, +}); diff --git a/packages/cli/src/utils/module.ts b/packages/cli/src/utils/module.ts new file mode 100644 index 00000000..96bc8bbb --- /dev/null +++ b/packages/cli/src/utils/module.ts @@ -0,0 +1,5 @@ +import * as path from 'path'; + +export const localModulePath = (moduleName: string): string => { + return path.resolve(process.cwd(), 'node_modules', moduleName); +}; From bb3e43ded02a9a509f5be1223aed395f84273581 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Mon, 15 Aug 2022 13:47:43 +0800 Subject: [PATCH 07/14] build: update nx config and package.json for every package - add descriptions, license, repo ... for each package - use dependencies instead of peerdependencies of most dependency - set peerdependencise for Vulcan imports --- nx.json | 2 +- packages/build/package.json | 26 ++++++++- packages/build/project.json | 23 +++++++- packages/cli/package.json | 1 + packages/cli/project.json | 71 ++++++++++------------- packages/core/package.json | 22 ++++++- packages/core/project.json | 15 ++++- packages/extension-dbt/package.json | 27 ++++++++- packages/extension-dbt/project.json | 23 +++++++- packages/integration-testing/project.json | 10 ---- packages/serve/package.json | 26 ++++++++- packages/serve/project.json | 23 +++++++- packages/test-utility/package.json | 26 ++++++++- packages/test-utility/project.json | 23 +++++++- 14 files changed, 248 insertions(+), 70 deletions(-) diff --git a/nx.json b/nx.json index af41aa5b..d4ac6441 100644 --- a/nx.json +++ b/nx.json @@ -11,7 +11,7 @@ "default": { "runner": "nx/tasks-runners/default", "options": { - "cacheableOperations": ["build", "lint", "test", "e2e"] + "cacheableOperations": ["build", "lint", "test", "e2e", "tsc"] } } } diff --git a/packages/build/package.json b/packages/build/package.json index 9dc2884d..0fbf0261 100644 --- a/packages/build/package.json +++ b/packages/build/package.json @@ -1,5 +1,27 @@ { "name": "@vulcan-sql/build", - "version": "0.0.1", - "type": "commonjs" + "description": "Vulcan package for building projects", + "version": "0.1.0", + "type": "commonjs", + "publishConfig": { + "access": "public" + }, + "keywords": [ + "vulcan", + "vulcan-sql", + "data", + "sql", + "database", + "data-warehouse", + "data-lake", + "api-builder" + ], + "repository": { + "type": "git", + "url": "https://github.com/Canner/vulcan.git" + }, + "license": "MIT", + "peerDependencies": { + "@vulcan-sql/core": "0.1.0" + } } diff --git a/packages/build/project.json b/packages/build/project.json index 9d03ff4a..bb976052 100644 --- a/packages/build/project.json +++ b/packages/build/project.json @@ -3,14 +3,33 @@ "sourceRoot": "packages/build/src", "targets": { "build": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "yarn ts-node ./tools/scripts/replaceAlias.ts build" + }, + "dependsOn": [ + { + "projects": "self", + "target": "tsc" + } + ] + }, + "tsc": { "executor": "@nrwl/js:tsc", "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/packages/build", "main": "packages/build/src/index.ts", "tsConfig": "packages/build/tsconfig.lib.json", - "assets": ["packages/build/*.md"] - } + "assets": ["packages/build/*.md"], + "buildableProjectDepsInPackageJsonType": "dependencies" + }, + "dependsOn": [ + { + "projects": "dependencies", + "target": "build" + } + ] }, "lint": { "executor": "@nrwl/linter:eslint", diff --git a/packages/cli/package.json b/packages/cli/package.json index e58e949f..c531ff36 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,5 +1,6 @@ { "name": "@vulcan-sql/cli", + "description": "CLI tools for Vulcan", "version": "0.1.0", "type": "commonjs", "bin": { diff --git a/packages/cli/project.json b/packages/cli/project.json index 7612c3ee..1720c2b7 100644 --- a/packages/cli/project.json +++ b/packages/cli/project.json @@ -3,14 +3,42 @@ "sourceRoot": "packages/cli/src", "targets": { "build": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "commands": [ + { + "command": "yarn ts-node ./tools/scripts/replaceAlias.ts cli", + "forwardAllArgs": false + }, + { + "command": "chmod +x dist/packages/cli/src/index.js", + "forwardAllArgs": false + } + ] + }, + "dependsOn": [ + { + "projects": "self", + "target": "tsc" + } + ] + }, + "tsc": { "executor": "@nrwl/js:tsc", "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/packages/cli", "main": "packages/cli/src/index.ts", "tsConfig": "packages/cli/tsconfig.lib.json", - "assets": ["packages/cli/*.md"] - } + "assets": ["packages/cli/*.md", "packages/cli/src/schemas/**/*.*"], + "buildableProjectDepsInPackageJsonType": "dependencies" + }, + "dependsOn": [ + { + "projects": "dependencies", + "target": "build" + } + ] }, "publish": { "executor": "@nrwl/workspace:run-commands", @@ -40,47 +68,12 @@ "passWithNoTests": true } }, - "run": { - "executor": "@nrwl/workspace:run-commands", - "options": { - "command": "node src/index.js {args.cmd}", - "cwd": "dist/packages/cli", - "forwardAllArgs": false - }, - "dependsOn": [ - { - "projects": "self", - "target": "build" - }, - { - "projects": "self", - "target": "cp-assets" - } - ] - }, - "cp-assets": { - "executor": "@nrwl/workspace:run-commands", - "options": { - "commands": [ - { - "command": "cp -r packages/cli/src/schemas dist/packages/cli/src", - "forwardAllArgs": false - } - ] - }, - "dependsOn": [ - { - "projects": "self", - "target": "build" - } - ] - }, "install": { "executor": "@nrwl/workspace:run-commands", "options": { "commands": [ { - "command": "chmod +x ./src/index.js && npm i -g . && echo done.", + "command": "npm i -g . && echo done.", "forwardAllArgs": false } ], @@ -89,7 +82,7 @@ "dependsOn": [ { "projects": "self", - "target": "cp-assets" + "target": "build" } ] } diff --git a/packages/core/package.json b/packages/core/package.json index 32398770..027be9dd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,24 @@ { "name": "@vulcan-sql/core", - "version": "0.0.1", + "description": "Core package of Vulcan", + "version": "0.1.0", "type": "commonjs", - "dependencies": {} + "publishConfig": { + "access": "public" + }, + "keywords": [ + "vulcan", + "vulcan-sql", + "data", + "sql", + "database", + "data-warehouse", + "data-lake", + "api-builder" + ], + "repository": { + "type": "git", + "url": "https://github.com/Canner/vulcan.git" + }, + "license": "MIT" } diff --git a/packages/core/project.json b/packages/core/project.json index 329fc7c4..bcd385ac 100644 --- a/packages/core/project.json +++ b/packages/core/project.json @@ -3,13 +3,26 @@ "sourceRoot": "packages/core/src", "targets": { "build": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "yarn ts-node ./tools/scripts/replaceAlias.ts core" + }, + "dependsOn": [ + { + "projects": "self", + "target": "tsc" + } + ] + }, + "tsc": { "executor": "@nrwl/js:tsc", "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/packages/core", "main": "packages/core/src/index.ts", "tsConfig": "packages/core/tsconfig.lib.json", - "assets": ["packages/core/*.md"] + "assets": ["packages/core/*.md"], + "buildableProjectDepsInPackageJsonType": "dependencies" } }, "lint": { diff --git a/packages/extension-dbt/package.json b/packages/extension-dbt/package.json index 42f544bb..3af7c9c8 100644 --- a/packages/extension-dbt/package.json +++ b/packages/extension-dbt/package.json @@ -1,5 +1,28 @@ { "name": "@vulcan-sql/extension-dbt", - "version": "0.0.1", - "type": "commonjs" + "description": "Using dbt models form Vulcan projects", + "version": "0.1.0", + "type": "commonjs", + "publishConfig": { + "access": "public" + }, + "keywords": [ + "vulcan", + "vulcan-sql", + "data", + "sql", + "database", + "data-warehouse", + "data-lake", + "api-builder", + "dbt" + ], + "repository": { + "type": "git", + "url": "https://github.com/Canner/vulcan.git" + }, + "license": "MIT", + "peerDependencies": { + "@vulcan-sql/core": "0.x" + } } diff --git a/packages/extension-dbt/project.json b/packages/extension-dbt/project.json index 8e9f87d6..b9a4fc11 100644 --- a/packages/extension-dbt/project.json +++ b/packages/extension-dbt/project.json @@ -3,14 +3,33 @@ "sourceRoot": "packages/extension-dbt/src", "targets": { "build": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "yarn ts-node ./tools/scripts/replaceAlias.ts extension-dbt" + }, + "dependsOn": [ + { + "projects": "self", + "target": "tsc" + } + ] + }, + "tsc": { "executor": "@nrwl/js:tsc", "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/packages/extension-dbt", "main": "packages/extension-dbt/src/index.ts", "tsConfig": "packages/extension-dbt/tsconfig.lib.json", - "assets": ["packages/extension-dbt/*.md"] - } + "assets": ["packages/extension-dbt/*.md"], + "buildableProjectDepsInPackageJsonType": "dependencies" + }, + "dependsOn": [ + { + "projects": "dependencies", + "target": "build" + } + ] }, "lint": { "executor": "@nrwl/linter:eslint", diff --git a/packages/integration-testing/project.json b/packages/integration-testing/project.json index c9858133..5667c298 100644 --- a/packages/integration-testing/project.json +++ b/packages/integration-testing/project.json @@ -2,16 +2,6 @@ "root": "packages/integration-testing", "sourceRoot": "packages/integration-testing/src", "targets": { - "build": { - "executor": "@nrwl/js:tsc", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/packages/integration-testing", - "main": "packages/integration-testing/src/index.ts", - "tsConfig": "packages/integration-testing/tsconfig.lib.json", - "assets": ["packages/integration-testing/*.md"] - } - }, "lint": { "executor": "@nrwl/linter:eslint", "outputs": ["{options.outputFile}"], diff --git a/packages/serve/package.json b/packages/serve/package.json index 3df64700..68429dd5 100644 --- a/packages/serve/package.json +++ b/packages/serve/package.json @@ -1,7 +1,27 @@ { "name": "@vulcan-sql/serve", - "version": "0.0.1", + "description": "Vulcan package for serving projects", + "version": "0.1.0", "type": "commonjs", - "dependencies": {}, - "devDependencies": {} + "publishConfig": { + "access": "public" + }, + "keywords": [ + "vulcan", + "vulcan-sql", + "data", + "sql", + "database", + "data-warehouse", + "data-lake", + "api-builder" + ], + "repository": { + "type": "git", + "url": "https://github.com/Canner/vulcan.git" + }, + "license": "MIT", + "peerDependencies": { + "@vulcan-sql/core": "0.1.0" + } } diff --git a/packages/serve/project.json b/packages/serve/project.json index 2108d3ec..df19ea28 100644 --- a/packages/serve/project.json +++ b/packages/serve/project.json @@ -10,14 +10,33 @@ } }, "build": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "yarn ts-node ./tools/scripts/replaceAlias.ts serve" + }, + "dependsOn": [ + { + "projects": "self", + "target": "tsc" + } + ] + }, + "tsc": { "executor": "@nrwl/js:tsc", "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/packages/serve", "main": "packages/serve/src/index.ts", "tsConfig": "packages/serve/tsconfig.lib.json", - "assets": ["packages/serve/*.md"] - } + "assets": ["packages/serve/*.md"], + "buildableProjectDepsInPackageJsonType": "dependencies" + }, + "dependsOn": [ + { + "projects": "dependencies", + "target": "build" + } + ] }, "lint": { "executor": "@nrwl/linter:eslint", diff --git a/packages/test-utility/package.json b/packages/test-utility/package.json index 24adc2e4..35b11f4a 100644 --- a/packages/test-utility/package.json +++ b/packages/test-utility/package.json @@ -1,5 +1,27 @@ { "name": "@vulcan/test-utility", - "version": "0.0.1", - "type": "commonjs" + "version": "0.1.0", + "type": "commonjs", + "publishConfig": { + "access": "public" + }, + "keywords": [ + "vulcan", + "vulcan-sql", + "data", + "sql", + "database", + "data-warehouse", + "data-lake", + "api-builder", + "dev-tools" + ], + "repository": { + "type": "git", + "url": "https://github.com/Canner/vulcan.git" + }, + "license": "MIT", + "peerDependencies": { + "@vulcan-sql/core": "0.1.0" + } } diff --git a/packages/test-utility/project.json b/packages/test-utility/project.json index 6d3d68ea..2fd0e04d 100644 --- a/packages/test-utility/project.json +++ b/packages/test-utility/project.json @@ -3,14 +3,33 @@ "sourceRoot": "packages/test-utility/src", "targets": { "build": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "yarn ts-node ./tools/scripts/replaceAlias.ts test-utility" + }, + "dependsOn": [ + { + "projects": "self", + "target": "tsc" + } + ] + }, + "tsc": { "executor": "@nrwl/js:tsc", "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/packages/test-utility", "main": "packages/test-utility/src/index.ts", "tsConfig": "packages/test-utility/tsconfig.lib.json", - "assets": ["packages/test-utility/*.md"] - } + "assets": ["packages/test-utility/*.md"], + "buildableProjectDepsInPackageJsonType": "dependencies" + }, + "dependsOn": [ + { + "projects": "dependencies", + "target": "build" + } + ] }, "lint": { "executor": "@nrwl/linter:eslint", From 703d4f1d695811b6dc7cc242d65e07d00765b2db Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Mon, 15 Aug 2022 17:28:17 +0800 Subject: [PATCH 08/14] feat(cli): add command descriptions --- packages/cli/src/index.ts | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 0de1e335..5c060032 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -8,36 +8,55 @@ import { logger } from './utils'; program.exitOverride(); -program.command('version').action(async () => { - await handleVersion(); -}); +program + .command('version') + .description('show the version of CLI and Vulcan packages') + .action(async () => { + await handleVersion(); + }); program .command('init') - .option('-p --project-name ') + .description('create a new Vulcan project') + .option('-p --project-name ', 'specify project name') .action(async (options) => { await handleInit(options); }); program .command('build') - .option('-c --config ') + .description('build Vulcan project') + .option( + '-c --config ', + 'path to Vulcan config file', + './vulcan.yaml' + ) .action(async (options) => { await handleBuild(options); }); program .command('serve') - .option('-c --config ') - .option('-p --port ') + .description('serve Vulcan project') + .option( + '-c --config ', + 'path to Vulcan config file', + './vulcan.yaml' + ) + .option('-p --port ', 'server port', '3000') .action(async (options) => { await handleServe(options); }); program .command('start') - .option('-c --config ') - .option('-p --port ') + .description('build and serve Vulcan project') + .option( + '-c --config ', + 'path to Vulcan config file', + './vulcan.yaml' + ) + .option('-p --port ', 'server port', '3000') .action(async (options) => { await handleStart(options); }); From d625fb88d353f815d0602dce8c2e85a0bc2d66f7 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Mon, 15 Aug 2022 17:37:51 +0800 Subject: [PATCH 09/14] feat(cli): catching sigint signal and close server before exit --- packages/cli/src/commands/serve.ts | 7 ++++++- packages/cli/src/utils/index.ts | 1 + packages/cli/src/utils/shutdown.ts | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 packages/cli/src/utils/shutdown.ts diff --git a/packages/cli/src/commands/serve.ts b/packages/cli/src/commands/serve.ts index 0f7c41d6..b372b89b 100644 --- a/packages/cli/src/commands/serve.ts +++ b/packages/cli/src/commands/serve.ts @@ -1,7 +1,7 @@ import * as jsYAML from 'js-yaml'; import { promises as fs } from 'fs'; import * as path from 'path'; -import { localModulePath, logger } from '../utils'; +import { addShutdownJob, localModulePath, logger } from '../utils'; export interface ServeCommandOptions { config: string; @@ -25,6 +25,11 @@ export const serveVulcan = async (options: ServeCommandOptions) => { const server = new VulcanServer(config); await server.start(options.port); logger.info(`Server is listening at port ${options.port}.`); + addShutdownJob(async () => { + logger.info(`Stopping server...`); + await server.close(); + logger.info(`Server stopped`); + }); }; export const handleServe = async ( diff --git a/packages/cli/src/utils/index.ts b/packages/cli/src/utils/index.ts index 0c982263..3d957931 100644 --- a/packages/cli/src/utils/index.ts +++ b/packages/cli/src/utils/index.ts @@ -1,2 +1,3 @@ export * from './module'; export * from './logger'; +export * from './shutdown'; diff --git a/packages/cli/src/utils/shutdown.ts b/packages/cli/src/utils/shutdown.ts new file mode 100644 index 00000000..bcbecb42 --- /dev/null +++ b/packages/cli/src/utils/shutdown.ts @@ -0,0 +1,16 @@ +import { logger } from './logger'; + +type JobBeforeShutdown = () => Promise; + +const shutdownJobs: JobBeforeShutdown[] = []; + +export const addShutdownJob = (job: JobBeforeShutdown) => { + shutdownJobs.push(job); +}; + +process.on('SIGINT', async () => { + logger.info('Ctrl-C signal caught, stopping services...'); + await Promise.all(shutdownJobs.map((job) => job())); + logger.info('Bye.'); + process.exit(0); +}); From 87f5dbfd74e56247a37a440ec7f5cc953b5aef79 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Tue, 16 Aug 2022 11:47:55 +0800 Subject: [PATCH 10/14] build: release 0.1.0-alpha.1 for dev usage --- packages/build/package.json | 4 ++-- packages/build/project.json | 13 +++++++++++++ packages/cli/package.json | 2 +- packages/cli/project.json | 2 +- packages/cli/src/schemas/init/vulcan.yaml | 2 +- packages/core/package.json | 2 +- packages/core/project.json | 13 +++++++++++++ packages/extension-dbt/package.json | 2 +- packages/extension-dbt/project.json | 13 +++++++++++++ packages/serve/package.json | 4 ++-- packages/serve/project.json | 13 +++++++++++++ packages/test-utility/package.json | 6 +++--- packages/test-utility/project.json | 13 +++++++++++++ tools/scripts/publish.mjs | 13 +++++++++++++ 14 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 tools/scripts/publish.mjs diff --git a/packages/build/package.json b/packages/build/package.json index 0fbf0261..8fda415d 100644 --- a/packages/build/package.json +++ b/packages/build/package.json @@ -1,7 +1,7 @@ { "name": "@vulcan-sql/build", "description": "Vulcan package for building projects", - "version": "0.1.0", + "version": "0.1.0-alpha.1", "type": "commonjs", "publishConfig": { "access": "public" @@ -22,6 +22,6 @@ }, "license": "MIT", "peerDependencies": { - "@vulcan-sql/core": "0.1.0" + "@vulcan-sql/core": "0.1.0-alpha.1" } } diff --git a/packages/build/project.json b/packages/build/project.json index bb976052..dbc39b24 100644 --- a/packages/build/project.json +++ b/packages/build/project.json @@ -45,6 +45,19 @@ "jestConfig": "packages/build/jest.config.ts", "passWithNoTests": true } + }, + "publish": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "node ../../../tools/scripts/publish.mjs {args.tag}", + "cwd": "dist/packages/build" + }, + "dependsOn": [ + { + "projects": "self", + "target": "build" + } + ] } }, "tags": [] diff --git a/packages/cli/package.json b/packages/cli/package.json index c531ff36..04277a04 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@vulcan-sql/cli", "description": "CLI tools for Vulcan", - "version": "0.1.0", + "version": "0.1.0-alpha.1", "type": "commonjs", "bin": { "vulcan": "./src/index.js" diff --git a/packages/cli/project.json b/packages/cli/project.json index 1720c2b7..5093672a 100644 --- a/packages/cli/project.json +++ b/packages/cli/project.json @@ -43,7 +43,7 @@ "publish": { "executor": "@nrwl/workspace:run-commands", "options": { - "command": "node tools/scripts/publish.mjs cli {args.ver} {args.tag}", + "command": "node ../../../tools/scripts/publish.mjs {args.tag}", "cwd": "dist/packages/cli" }, "dependsOn": [ diff --git a/packages/cli/src/schemas/init/vulcan.yaml b/packages/cli/src/schemas/init/vulcan.yaml index 51895f59..4ed27194 100644 --- a/packages/cli/src/schemas/init/vulcan.yaml +++ b/packages/cli/src/schemas/init/vulcan.yaml @@ -1,6 +1,6 @@ name: '{{ options.projectName }}' description: A starter Vulcan project -version: 0.1.0 +version: 0.1.0-alpha.1 template: provider: LocalFile # Path to .sql files diff --git a/packages/core/package.json b/packages/core/package.json index 027be9dd..99828230 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@vulcan-sql/core", "description": "Core package of Vulcan", - "version": "0.1.0", + "version": "0.1.0-alpha.1", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/core/project.json b/packages/core/project.json index bcd385ac..e2f1ce38 100644 --- a/packages/core/project.json +++ b/packages/core/project.json @@ -39,6 +39,19 @@ "jestConfig": "packages/core/jest.config.ts", "passWithNoTests": true } + }, + "publish": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "node ../../../tools/scripts/publish.mjs {args.tag}", + "cwd": "dist/packages/core" + }, + "dependsOn": [ + { + "projects": "self", + "target": "build" + } + ] } }, "tags": [] diff --git a/packages/extension-dbt/package.json b/packages/extension-dbt/package.json index 3af7c9c8..0778380c 100644 --- a/packages/extension-dbt/package.json +++ b/packages/extension-dbt/package.json @@ -1,7 +1,7 @@ { "name": "@vulcan-sql/extension-dbt", "description": "Using dbt models form Vulcan projects", - "version": "0.1.0", + "version": "0.1.0-alpha.1", "type": "commonjs", "publishConfig": { "access": "public" diff --git a/packages/extension-dbt/project.json b/packages/extension-dbt/project.json index b9a4fc11..a00b540f 100644 --- a/packages/extension-dbt/project.json +++ b/packages/extension-dbt/project.json @@ -45,6 +45,19 @@ "jestConfig": "packages/extension-dbt/jest.config.ts", "passWithNoTests": true } + }, + "publish": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "node ../../../tools/scripts/publish.mjs {args.tag}", + "cwd": "dist/packages/extension-dbt" + }, + "dependsOn": [ + { + "projects": "self", + "target": "build" + } + ] } }, "tags": [] diff --git a/packages/serve/package.json b/packages/serve/package.json index 68429dd5..c09d35cf 100644 --- a/packages/serve/package.json +++ b/packages/serve/package.json @@ -1,7 +1,7 @@ { "name": "@vulcan-sql/serve", "description": "Vulcan package for serving projects", - "version": "0.1.0", + "version": "0.1.0-alpha.1", "type": "commonjs", "publishConfig": { "access": "public" @@ -22,6 +22,6 @@ }, "license": "MIT", "peerDependencies": { - "@vulcan-sql/core": "0.1.0" + "@vulcan-sql/core": "0.1.0-alpha.1" } } diff --git a/packages/serve/project.json b/packages/serve/project.json index df19ea28..f0985a1d 100644 --- a/packages/serve/project.json +++ b/packages/serve/project.json @@ -52,6 +52,19 @@ "jestConfig": "packages/serve/jest.config.ts", "passWithNoTests": true } + }, + "publish": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "node ../../../tools/scripts/publish.mjs {args.tag}", + "cwd": "dist/packages/serve" + }, + "dependsOn": [ + { + "projects": "self", + "target": "build" + } + ] } }, "tags": [] diff --git a/packages/test-utility/package.json b/packages/test-utility/package.json index 35b11f4a..07a56f71 100644 --- a/packages/test-utility/package.json +++ b/packages/test-utility/package.json @@ -1,6 +1,6 @@ { - "name": "@vulcan/test-utility", - "version": "0.1.0", + "name": "@vulcan-sql/test-utility", + "version": "0.1.0-alpha.1", "type": "commonjs", "publishConfig": { "access": "public" @@ -22,6 +22,6 @@ }, "license": "MIT", "peerDependencies": { - "@vulcan-sql/core": "0.1.0" + "@vulcan-sql/core": "0.1.0-alpha.1" } } diff --git a/packages/test-utility/project.json b/packages/test-utility/project.json index 2fd0e04d..e572b8bf 100644 --- a/packages/test-utility/project.json +++ b/packages/test-utility/project.json @@ -45,6 +45,19 @@ "jestConfig": "packages/test-utility/jest.config.ts", "passWithNoTests": true } + }, + "publish": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "node ../../../tools/scripts/publish.mjs {args.tag}", + "cwd": "dist/packages/test-utility" + }, + "dependsOn": [ + { + "projects": "self", + "target": "build" + } + ] } }, "tags": [] diff --git a/tools/scripts/publish.mjs b/tools/scripts/publish.mjs new file mode 100644 index 00000000..6523a17d --- /dev/null +++ b/tools/scripts/publish.mjs @@ -0,0 +1,13 @@ +import { execSync } from 'child_process'; + +if (process.env.READY_FOR_PUBLISH !== 'true') { + console.log(`Set env READY_FOR_PUBLISH=true before running publish commands.`) + process.exit(1); +} + +// Executing publish script: node path/to/publish.mjs {tag} +// Default "tag" to "alpha" so we won't publish the "latest" tag by accident. +const [, , tag = 'alpha'] = process.argv; + +// Execute "npm publish" to publish +execSync(`npm publish --tag ${tag}`); From ad667a45a292524321025e7196eaffb31eecf293 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Tue, 16 Aug 2022 12:41:55 +0800 Subject: [PATCH 11/14] test(cli): add test cases for basic commands --- .gitignore | 3 +- packages/cli/src/cli.ts | 61 ++++++++++++++++++++++++++++ packages/cli/src/commands/init.ts | 3 +- packages/cli/src/index.ts | 60 +--------------------------- packages/cli/src/utils/shutdown.ts | 6 ++- packages/cli/test/cli.spec.ts | 64 ++++++++++++++++++++++++++++++ packages/cli/tsconfig.spec.json | 8 +++- 7 files changed, 142 insertions(+), 63 deletions(-) create mode 100644 packages/cli/src/cli.ts create mode 100644 packages/cli/test/cli.spec.ts diff --git a/.gitignore b/.gitignore index 96261570..e1f5df3a 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,5 @@ testem.log Thumbs.db # testing folder from cli -my-first-vulcan-project \ No newline at end of file +my-first-vulcan-project +test-vulcan-project \ No newline at end of file diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts new file mode 100644 index 00000000..e25fa5f8 --- /dev/null +++ b/packages/cli/src/cli.ts @@ -0,0 +1,61 @@ +import { program } from 'commander'; +import { handleInit, handleStart, handleVersion } from './commands'; +import { handleBuild } from './commands/build'; +import { handleServe } from './commands/serve'; + +program.exitOverride(); + +program + .command('version') + .description('show the version of CLI and Vulcan packages') + .action(async () => { + await handleVersion(); + }); + +program + .command('init') + .description('create a new Vulcan project') + .option('-p --project-name ', 'specify project name') + .action(async (options) => { + await handleInit(options); + }); + +program + .command('build') + .description('build Vulcan project') + .option( + '-c --config ', + 'path to Vulcan config file', + './vulcan.yaml' + ) + .action(async (options) => { + await handleBuild(options); + }); + +program + .command('serve') + .description('serve Vulcan project') + .option( + '-c --config ', + 'path to Vulcan config file', + './vulcan.yaml' + ) + .option('-p --port ', 'server port', '3000') + .action(async (options) => { + await handleServe(options); + }); + +program + .command('start') + .description('build and serve Vulcan project') + .option( + '-c --config ', + 'path to Vulcan config file', + './vulcan.yaml' + ) + .option('-p --port ', 'server port', '3000') + .action(async (options) => { + await handleStart(options); + }); + +export { program }; diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 04a75fb6..6662d68b 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -52,7 +52,8 @@ export const createProject = async ( name: options.projectName, dependencies: { '@vulcan-sql/core': options.version, - // TODO: Install build/serve package when they are ready + '@vulcan-sql/build': options.version, + '@vulcan-sql/serve': options.version, }, }, null, diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 5c060032..caa9967b 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,65 +1,7 @@ #!/usr/bin/env node -import { program } from 'commander'; -import { handleInit, handleStart, handleVersion } from './commands'; -import { handleBuild } from './commands/build'; -import { handleServe } from './commands/serve'; import { logger } from './utils'; - -program.exitOverride(); - -program - .command('version') - .description('show the version of CLI and Vulcan packages') - .action(async () => { - await handleVersion(); - }); - -program - .command('init') - .description('create a new Vulcan project') - .option('-p --project-name ', 'specify project name') - .action(async (options) => { - await handleInit(options); - }); - -program - .command('build') - .description('build Vulcan project') - .option( - '-c --config ', - 'path to Vulcan config file', - './vulcan.yaml' - ) - .action(async (options) => { - await handleBuild(options); - }); - -program - .command('serve') - .description('serve Vulcan project') - .option( - '-c --config ', - 'path to Vulcan config file', - './vulcan.yaml' - ) - .option('-p --port ', 'server port', '3000') - .action(async (options) => { - await handleServe(options); - }); - -program - .command('start') - .description('build and serve Vulcan project') - .option( - '-c --config ', - 'path to Vulcan config file', - './vulcan.yaml' - ) - .option('-p --port ', 'server port', '3000') - .action(async (options) => { - await handleStart(options); - }); +import { program } from './cli'; (async () => { try { diff --git a/packages/cli/src/utils/shutdown.ts b/packages/cli/src/utils/shutdown.ts index bcbecb42..f1f6a251 100644 --- a/packages/cli/src/utils/shutdown.ts +++ b/packages/cli/src/utils/shutdown.ts @@ -8,9 +8,13 @@ export const addShutdownJob = (job: JobBeforeShutdown) => { shutdownJobs.push(job); }; -process.on('SIGINT', async () => { +export const runShutdownJobs = async () => { logger.info('Ctrl-C signal caught, stopping services...'); await Promise.all(shutdownJobs.map((job) => job())); logger.info('Bye.'); +}; + +process.on('SIGINT', async () => { + await runShutdownJobs(); process.exit(0); }); diff --git a/packages/cli/test/cli.spec.ts b/packages/cli/test/cli.spec.ts new file mode 100644 index 00000000..53566796 --- /dev/null +++ b/packages/cli/test/cli.spec.ts @@ -0,0 +1,64 @@ +import { program } from '../src/cli'; +import { promises as fs } from 'fs'; +import * as jsYAML from 'js-yaml'; +import * as path from 'path'; +import { runShutdownJobs } from '../src/utils'; +import * as supertest from 'supertest'; + +const projectName = 'test-vulcan-project'; + +const workspaceRoot = path.resolve(__dirname, '..', '..', '..'); +const projectRoot = path.resolve(workspaceRoot, projectName); + +beforeAll(async () => { + await fs.rm(projectRoot, { recursive: true, force: true }); + await program.parseAsync(['node', 'vulcan', 'init', '-p', projectName]); + process.chdir(projectRoot); +}, 30000); + +afterAll(async () => { + await fs.rm(projectRoot, { recursive: true, force: true }); +}); + +afterEach(async () => { + await runShutdownJobs(); +}); + +it('Init command should create new folder with default config', async () => { + // Action + const config: any = jsYAML.load( + await fs.readFile(path.resolve(projectRoot, 'vulcan.yaml'), 'utf8') + ); + // Assert + expect(config.name).toBe(projectName); +}); + +it('Build command should make result.json', async () => { + // Action + await program.parseAsync(['node', 'vulcan', 'build']); + // Assert + expect( + fs.readFile(path.resolve(projectRoot, 'result.json'), 'utf-8') + ).resolves.not.toThrow(); +}); + +it('Serve command should start Vulcan server', async () => { + // Action + await program.parseAsync(['node', 'vulcan', 'build']); + await program.parseAsync(['node', 'vulcan', 'serve', '-p', '12345']); + const agent = supertest('http://localhost:12345'); + const result = await agent.get('/'); + // Assert + expect(result.statusCode).toBe(200); + await runShutdownJobs(); +}); + +it('Start command should build the project and start Vulcan server', async () => { + // Action + await program.parseAsync(['node', 'vulcan', 'start', '-p', '12345']); + const agent = supertest('http://localhost:12345'); + const result = await agent.get('/'); + // Assert + expect(result.statusCode).toBe(200); + await runShutdownJobs(); +}); diff --git a/packages/cli/tsconfig.spec.json b/packages/cli/tsconfig.spec.json index 546f1287..e1cfac50 100644 --- a/packages/cli/tsconfig.spec.json +++ b/packages/cli/tsconfig.spec.json @@ -5,5 +5,11 @@ "module": "commonjs", "types": ["jest", "node"] }, - "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] + "include": [ + "jest.config.ts", + "**/*.test.ts", + "**/*.spec.ts", + "**/*.d.ts.", + "../../types/*.d.ts" + ] } From b045fe10cb39de2ccf602cda271528290b65096d Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Tue, 16 Aug 2022 16:43:11 +0800 Subject: [PATCH 12/14] fix(cli): fix glob search pattern on Windows --- packages/cli/src/commands/init.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 6662d68b..5dc123cb 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -117,9 +117,13 @@ const addInitFiles = async ( } }; -const listFiles = async (path: string) => { +const listFiles = async (pattern: string) => { return new Promise((resolve, reject) => { - glob(path, (err, files) => { + // Windows use backslash when using path.resolve, e.g. C:\\Users\\xxxxx\\cli. but glob accepts only forward ward slash + // Glob: https://github.com/isaacs/node-glob#windows + // Path separator: https://nodejs.org/api/path.html#pathsep + const normalizedPattern = pattern.split(path.sep).join('/'); + glob(normalizedPattern, (err, files) => { if (err) return reject(err); return resolve(files); }); From 2f758785ba7f3b3a9a698df6035c6dc76e87766b Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Thu, 18 Aug 2022 09:36:33 +0800 Subject: [PATCH 13/14] build(npm): fix dependencies issues --- yarn.lock | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5e49e161..23630a00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2035,10 +2035,15 @@ cli-spinners@2.6.1: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== -cli-width@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" - integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== +cli-spinners@^2.5.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" + integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^7.0.2: version "7.0.4" @@ -2138,10 +2143,15 @@ commander@^6.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== -commitizen@^4.0.3: - version "4.2.4" - resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-4.2.4.tgz#a3e5b36bd7575f6bf6e7aa19dbbf06b0d8f37165" - integrity sha512-LlZChbDzg3Ir3O2S7jSo/cgWp5/QwylQVr59K4xayVq8S4/RdKzSyJkghAiZZHfhh5t4pxunUoyeg0ml1q/7aw== +commander@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.0.tgz#bc4a40918fefe52e22450c111ecd6b7acce6f11c" + integrity sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw== + +commitizen@^4.0.3, commitizen@^4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-4.2.5.tgz#48e5a5c28334c6e8ed845cc24fc9f072efd3961e" + integrity sha512-9sXju8Qrz1B4Tw7kC5KhnvwYQN88qs2zbiB8oyMsnXZyJ24PPGiNM3nHr73d32dnE3i8VJEXddBFIbOgYSEXtQ== dependencies: cachedir "2.3.0" cz-conventional-changelog "3.3.0" @@ -3449,10 +3459,10 @@ ini@^1.3.4: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -inquirer@6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" - integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== +inquirer@8.2.4, inquirer@^8.0.0: + version "8.2.4" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4" + integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -4750,10 +4760,10 @@ ms@2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nanomatch@^1.2.9: version "1.2.13" @@ -5523,7 +5533,7 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -run-async@^2.2.0: +run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== From 49ad739d943a21819165df2044a82006e4dd5415 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Fri, 19 Aug 2022 16:37:11 +0800 Subject: [PATCH 14/14] test(cli): add test for version commands --- packages/cli/test/cli.spec.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/cli/test/cli.spec.ts b/packages/cli/test/cli.spec.ts index 53566796..14e62edf 100644 --- a/packages/cli/test/cli.spec.ts +++ b/packages/cli/test/cli.spec.ts @@ -62,3 +62,10 @@ it('Start command should build the project and start Vulcan server', async () => expect(result.statusCode).toBe(200); await runShutdownJobs(); }); + +it('Version command should execute without error', async () => { + // Action, Assert + await expect( + program.parseAsync(['node', 'vulcan', 'version']) + ).resolves.not.toThrow(); +});