diff --git a/package.json b/package.json index fcc34fcf..4101fb96 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@tweenjs/tween.js": "^18.4.2", "admin-lte": "^3.0.1", "blessing-skin-shell": "^0.2.0", + "commander": "^5.0.0", "echarts": "^4.6.0", "jquery": "^3.4.1", "lodash.debounce": "^4.0.8", diff --git a/resources/assets/src/scripts/cli/ClosetCommand.ts b/resources/assets/src/scripts/cli/ClosetCommand.ts index b59d7790..2d3aeee5 100644 --- a/resources/assets/src/scripts/cli/ClosetCommand.ts +++ b/resources/assets/src/scripts/cli/ClosetCommand.ts @@ -1,36 +1,25 @@ import type { Stdio } from 'blessing-skin-shell' +import { Command } from 'commander' import * as fetch from '../net' import { User, Texture } from '../types' - -type SubCommand = 'add' | 'remove' +import { hackStdout, overrideExit } from './configureStdio' type Response = fetch.ResponseBody<{ user: User; texture: Texture }> export default async function closet(stdio: Stdio, args: string[]) { - if (args.includes('-h') || args.includes('--help')) { - stdio.println('Usage: closet ') - return + const program = new Command() + + /* istanbul ignore next */ + if (process.env.NODE_ENV !== 'test') { + process.stdout = hackStdout(stdio) + overrideExit(program, stdio) } - const command = args[0] as SubCommand | undefined - const uid = args[1] - const tid = args[2] - - if (!command) { - stdio.println('Supported subcommand: add, remove.') - return - } - if (!uid) { - stdio.println('User ID must be provided.') - return - } - if (!tid) { - stdio.println('Texture ID must be provided.') - return - } - - switch (command) { - case 'add': { + program.name('closet').version('0.1.0') + program + .command('add ') + .description("add texture to someone's closet") + .action(async (uid: string, tid: string) => { const { code, data } = await fetch.post( `/admin/closet/${uid}`, { tid }, @@ -43,9 +32,11 @@ export default async function closet(stdio: Stdio, args: string[]) { } else { stdio.println('Error occurred.') } - break - } - case 'remove': { + }) + program + .command('remove ') + .description("remove texture from someone's closet") + .action(async (uid: string, tid: string) => { const { code, data } = await fetch.del(`/admin/closet/${uid}`, { tid, }) @@ -57,7 +48,7 @@ export default async function closet(stdio: Stdio, args: string[]) { } else { stdio.println('Error occurred.') } - break - } - } + }) + + await program.parseAsync(args, { from: 'user' }) } diff --git a/resources/assets/src/scripts/cli/configureStdio.ts b/resources/assets/src/scripts/cli/configureStdio.ts new file mode 100644 index 00000000..0a56bb29 --- /dev/null +++ b/resources/assets/src/scripts/cli/configureStdio.ts @@ -0,0 +1,22 @@ +import { Stdio } from 'blessing-skin-shell' +import type Commander from 'commander' + +/* istanbul ignore next */ +export function hackStdout(stdio: Stdio) { + return { + write(msg: string) { + stdio.print(msg.replace(/\n/g, '\r\n')) + }, + } as NodeJS.WriteStream +} + +/* istanbul ignore next */ +export function overrideExit(program: Commander.Command, stdio: Stdio) { + Error.captureStackTrace = () => {} + + return program.exitOverride(error => { + if (!error.message.startsWith('(')) { + stdio.print(error.message.replace(/\n/g, '\r\n')) + } + }) +} diff --git a/resources/assets/tests/scripts/cli/ClosetCommand.test.ts b/resources/assets/tests/scripts/cli/ClosetCommand.test.ts index f82318dd..530ea583 100644 --- a/resources/assets/tests/scripts/cli/ClosetCommand.test.ts +++ b/resources/assets/tests/scripts/cli/ClosetCommand.test.ts @@ -4,46 +4,6 @@ import { Stdio } from './stdio' jest.mock('@/scripts/net') -test('help message', async () => { - let stdio = new Stdio() - await runCommand(stdio, ['-h']) - expect(stdio.getStdout()).toInclude('Usage') - - stdio = new Stdio() - await runCommand(stdio, ['--help']) - expect(stdio.getStdout()).toInclude('Usage') -}) - -test('missing subcommand', async () => { - const stdio = new Stdio() - await runCommand(stdio, []) - expect(fetch.post).not.toBeCalled() - expect(fetch.del).not.toBeCalled() -}) - -test('unsupported subcommand', async () => { - const stdio = new Stdio() - await runCommand(stdio, ['abc']) - expect(fetch.post).not.toBeCalled() - expect(fetch.del).not.toBeCalled() -}) - -test('missing uid', async () => { - const stdio = new Stdio() - await runCommand(stdio, ['add']) - expect(stdio.getStdout()).toInclude('User ID') - expect(fetch.post).not.toBeCalled() - expect(fetch.del).not.toBeCalled() -}) - -test('missing tid', async () => { - const stdio = new Stdio() - await runCommand(stdio, ['add', '1']) - expect(stdio.getStdout()).toInclude('Texture ID') - expect(fetch.post).not.toBeCalled() - expect(fetch.del).not.toBeCalled() -}) - describe('add texture', () => { it('succeeded', async () => { fetch.post.mockResolvedValue({ diff --git a/webpack.config.js b/webpack.config.js index f0677cf7..4b9d75a8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -150,7 +150,10 @@ const config = { hotOnly: true, stats: 'errors-only', }, - stats: 'errors-only', + node: { + child_process: 'empty', + fs: 'empty', + }, } if (devMode) { diff --git a/yarn.lock b/yarn.lock index 379bf48f..ee9c86ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2934,6 +2934,11 @@ commander@^2.20.0, commander@~2.20.0: resolved "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== +commander@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.0.0.tgz#dbf1909b49e5044f8fdaf0adc809f0c0722bdfd0" + integrity sha512-JrDGPAKjMGSP1G0DUoaceEJ3DZgAfr/q6X7FVk4+U5KxUSKviYGM2k6zWkfyyBHy5rAtzgYJFa1ro2O9PtoxwQ== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -7114,7 +7119,7 @@ node-int64@^0.4.0: node-libs-browser@^2.2.1: version "2.2.1" - resolved "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== dependencies: assert "^1.1.1"