diff --git a/.gitignore b/.gitignore index 3c3629e..eb03e3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +*.log diff --git a/lib/index.js b/lib/index.js index 0c6b358..0fba344 100644 --- a/lib/index.js +++ b/lib/index.js @@ -40,6 +40,10 @@ class Maid { } async runTask(taskName, throwWhenNoMatchedTask = true) { + // type Task = { name: string, type: string, script: string } + // type Task = { name: 'lint', type: 'js', script: 'var bar = 123' } + // type Task = { name: 'test', type: 'bash', script: 'tap *.js' } + // type Task = { name: 'meow', type: 'python', script: 'print("meooow")' } const task = taskName && this.maidfile && @@ -58,23 +62,30 @@ class Maid { const start = Date.now() logger.log(`Starting '${chalk.cyan(task.name)}'...`) + await new Promise((resolve, reject) => { const handleError = err => { throw new MaidError(`Task '${task.name}' failed.\n${err.stack}`) } + // task.type = bash|sh if (checkTypes(task, ['sh', 'bash'])) { - return runCLICommand({ task, resolve, reject }) + runCLICommand({ task, resolve, reject }) + return } + // task.type = python|py if (checkTypes(task, ['py', 'python'])) { - return runCLICommand({ type: 'python', task, resolve, reject }) + runCLICommand({ task, resolve, reject }) + return } if (checkTypes(task, ['js', 'javascript'])) { let res + try { res = requireFromString(task.script, this.maidfile.filepath) } catch (err) { return handleError(err) } + res = res.default || res return resolve( typeof res === 'function' diff --git a/lib/parseMarkdown.js b/lib/parseMarkdown.js index c77955e..6ae9bf5 100644 --- a/lib/parseMarkdown.js +++ b/lib/parseMarkdown.js @@ -149,7 +149,7 @@ module.exports = (content, { section, filepath } = {}) => { // Currently only use the first one for (const token of sectionTokens) { if (token.type === 'fence') { - task.script = token.content + task.script = token.content.trim() task.type = token.info break } diff --git a/lib/runCLICommand.js b/lib/runCLICommand.js index 0648784..6b3f8b0 100644 --- a/lib/runCLICommand.js +++ b/lib/runCLICommand.js @@ -1,16 +1,31 @@ const path = require('path') -const spawn = require('cross-spawn') +const execa = require('execa') const MaidError = require('./MaidError') module.exports = ({ task, type = task.type, resolve, reject }) => { - const cmd = spawn(type, ['-c', task.script, ...process.argv.slice(2)], { + const modulesBin = path.resolve(path.join('node_modules', '.bin')) + const isShell = /(ba|z)?sh/.test(type) + const isPython = /py(thon)?/.test(type) + const args = process.argv.slice(2) + + let script = null + + if (isShell) { + script = [task.script, ...args] + } else if (isPython) { + script = ['python', '-c', task.script, ...args] + } else { + script = [type, task.script, ...args] + } + + const promise = execa.shell(script.join(' '), { stdio: 'inherit', env: Object.assign({}, process.env, { - PATH: `${path.resolve('node_modules/.bin')}:${process.env.PATH}` + PATH: `${modulesBin}:${process.env.PATH}` }) }) - cmd.on('close', code => { + promise.on('close', code => { if (code === 0) { resolve() } else { @@ -18,5 +33,10 @@ module.exports = ({ task, type = task.type, resolve, reject }) => { } }) - return cmd + // we don't need to return + // 1. because this function is executed in new Promise(() => {}) + // 2. we have its resolve and reject, so it's better to use them + promise.then(resolve).catch(err => { + reject(new MaidError(`task "${task.name}" failed with "${err.message}"`)) + }) } diff --git a/package.json b/package.json index db03732..d342c23 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "cac": "^5.0.10", "chalk": "^2.4.1", "cross-spawn": "^6.0.5", + "execa": "^0.10.0", "fancy-log": "^1.3.2", "joycon": "^1.0.4", "markdown-it": "^8.4.1", diff --git a/test/snapshots/parseMarkdown.test.js.md b/test/snapshots/parseMarkdown.test.js.md index f47dd9f..593c808 100644 --- a/test/snapshots/parseMarkdown.test.js.md +++ b/test/snapshots/parseMarkdown.test.js.md @@ -16,8 +16,7 @@ Generated by [AVA](https://ava.li). before: [], description: '', name: 'key', - script: `console.log('key')␊ - `, + script: 'console.log(\'key\')', type: 'js', }, { @@ -25,8 +24,7 @@ Generated by [AVA](https://ava.li). before: [], description: 'hehehe', name: 'goodbye', - script: `echo goodbye␊ - `, + script: 'echo goodbye', type: 'sh', }, ], @@ -55,8 +53,7 @@ Generated by [AVA](https://ava.li). ␊ But it's very complex so I'm writing some instructions.`, name: 'hey', - script: `console.log('key')␊ - `, + script: 'console.log(\'key\')', type: 'js', }, { @@ -64,12 +61,11 @@ Generated by [AVA](https://ava.li). before: [], description: 'hehehe', name: 'goodbye', - script: `echo goodbye␊ - `, + script: 'echo goodbye', type: 'sh', }, ], - + } ## use readme @@ -85,4 +81,4 @@ Generated by [AVA](https://ava.li). name: 'dev', }, ], - } \ No newline at end of file + } diff --git a/test/snapshots/parseMarkdown.test.js.snap b/test/snapshots/parseMarkdown.test.js.snap index 08ca79c..af90af1 100644 Binary files a/test/snapshots/parseMarkdown.test.js.snap and b/test/snapshots/parseMarkdown.test.js.snap differ diff --git a/yarn.lock b/yarn.lock index c61f5c4..16bfc0d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1085,7 +1085,7 @@ cross-spawn@^5.0.1, cross-spawn@^5.1.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^6.0.5: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" dependencies: @@ -1427,6 +1427,18 @@ esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" +execa@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" + dependencies: + cross-spawn "^6.0.0" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"