Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Write maid scripts to package.json #26

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- [Asynchronous task](#asynchronous-task)
- [py/python](#pypython)
- [Use a custom maidfile](#use-a-custom-maidfile)
- [Synchronizing tasks with package.json scripts](#synchronizing-tasks-with-packagejson-scripts)
- [Development](#development)
- [lint](#lint)
- [test](#test)
Expand Down Expand Up @@ -266,6 +267,33 @@ Unlike a `maidfile.md` which uses all `h2` headers as tasks, in `README.md` only

Alternatively, if you're not using `maidfile.md`, you can also use `--section h2_header` and `--path foo.md` flags to customize it.

### Synchronizing tasks with package.json scripts

You're team may currently use `yarn` or `npm run` to execute tasks. In order to reduce workflow churn maid provides a way of automatically updating the scripts section of your package.json with the relavent maid tasks.

```bash
maid update-scripts
```

It's recommended to use this technique along side [lint-staged](https://github.com/okonet/lint-staged) and [husky](https://github.com/typicode/husky) to automatically update your package.json in a precommit hook.

```json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"README.md": ["maid update-scripts --git-add", "git add"]
}
}
```

**Note:** The `--git-add` flag will automatically stage the `package.json`. If you have other changes to the package.json when that happens you could end up commiting those changes without intending to.

`--no-write` allows you to test the command without actually updating the package.json. Useful for both debugging and checking to ensure there are no command conflicts

## Development

<!-- maid-tasks -->
Expand All @@ -286,6 +314,8 @@ If you want to automatically fix lint errors, try adding `--fix` plugin to the c

Use [AVA](https://github.com/avajs/ava) to run unit tests.

Run task `lint` before this.

```bash
yarn ava "${@:1}"
```
Expand Down
21 changes: 21 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,27 @@ cli.command('*', 'Run a task in current working directory', (input, flags) => {
return runner.runFile(taskName)
})

cli
.command(
'update-scripts',
'Write maid tasks to package.json scripts',
(input, flags) => {
const runner = require('..')(flags)
const updateScripts = require('../lib/updateScripts')
updateScripts(runner, flags)
}
)
.option('git-add', {
desc: 'Runs git-add on the changed package.json file',
type: 'boolean',
default: false
})
.option('write', {
type: 'boolean',
default: true,
desc: 'Write output to the package.json'
})

cli.command('help', 'Display task description', (input, flags) => {
const runner = require('..')(flags)
return runner.getHelp(input)
Expand Down
8 changes: 8 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ class Maid {
}
}

listTasks() {
return this.maidfile.tasks.map(task => task.name)
}

getFilepath() {
return this.maidfile.filepath
}

async runTasks(taskNames, inParallel) {
if (!taskNames || taskNames.length === 0) return

Expand Down
14 changes: 10 additions & 4 deletions lib/runCLICommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ const path = require('path')
const spawn = require('cross-spawn')
const MaidError = require('./MaidError')

module.exports = ({ task, type = task.type, resolve, reject }) => {
const cmd = spawn(type, ['-c', task.script, ...process.argv.slice(2)], {
const exec = (cmd, args) =>
spawn(cmd, args, {
stdio: 'inherit',
env: Object.assign({}, process.env, {
env: {
...process.env,
PATH: `${path.resolve('node_modules/.bin')}:${process.env.PATH}`
})
}
})

module.exports = ({ task, type = task.type, resolve, reject }) => {
const cmd = exec(type, ['-c', task.script, ...process.argv.slice(2)])

cmd.on('close', code => {
if (code === 0) {
resolve()
Expand All @@ -20,3 +24,5 @@ module.exports = ({ task, type = task.type, resolve, reject }) => {

return cmd
}

module.exports.exec = exec
88 changes: 88 additions & 0 deletions lib/updateScripts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const loadFile = require('./loadFile')
const MaidError = require('./MaidError')
const fs = require('fs')
const path = require('path')
const { exec } = require('./runCLICommand')
const logger = require('./logger')

const flattenObj = (a, b) => ({
...a,
...b
})

const getPassThroughArgs = flags => {
const passThroughArgs = []

if (flags.quiet) {
passThroughArgs.push('--quiet')
}

if (flags.section) {
passThroughArgs.push('-s', flags.section)
}

const pathIndex = process.argv.findIndex(arg => arg.match(/-p|--path/)) + 1
if (pathIndex) {
passThroughArgs.push('-p', process.argv[pathIndex])
}

return passThroughArgs.join(' ')
}

const checkForTaskConflicts = (maidTasks, customScripts) => {
const conflictingTasks = maidTasks.filter(task =>
Object.keys(customScripts).includes(task)
)

if (conflictingTasks.length) {
throw new MaidError(
`Conflicts between maidfile and package.json. Please check these scripts: \n\t
${conflictingTasks.join(', ')}`
)
}
}

module.exports = (maid, flags) => {
const { path: pkgPath, data: pkg } = loadFile.loadSync(['package.json'])
if (!pkgPath) return null

const maidExec =
path.basename(process.argv[0]) === 'node'
? `node ${path.relative(process.cwd(), process.argv[1])}`
: 'maid'

const { scripts = {} } = pkg
const tasks = maid
.listTasks()
.filter(
(task, index, tasks) =>
!tasks.includes((task.match(/(?:pre|post)(.*)/) || [])[1])
)
const passThroughArgs = getPassThroughArgs(flags)

const baseScripts = Object.keys(scripts)
.filter(task => !scripts[task].startsWith(maidExec))
.map(task => ({ [task]: scripts[task] }))
.reduce(flattenObj, {})

checkForTaskConflicts(tasks, baseScripts)

const finalScripts = tasks
.map(task => ({
[task]: `${maidExec} ${passThroughArgs} ${task}`.replace(/\s+/g, ' ')
}))
.reduce(flattenObj, baseScripts)

if (flags.write) {
fs.writeFileSync(
pkgPath,
JSON.stringify({ ...pkg, ...{ scripts: finalScripts } }, null, 2)
)
} else {
logger.log('\n', finalScripts)
}

if (flags.gitAdd) {
exec('git', ['add', pkgPath])
}
}
15 changes: 10 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"lib"
],
"scripts": {
"maid": "node bin/cli",
"test": "yarn maid lint && yarn maid test"
"lint": "node bin/cli lint",
"test": "node bin/cli test",
"toc": "node bin/cli toc"
},
"author": "egoist <[email protected]>",
"license": "MIT",
Expand Down Expand Up @@ -59,12 +60,16 @@
},
"lint-staged": {
"*.js": [
"yarn maid lint --fix",
"node bin/cli lint --fix",
"git add"
],
"package.json": [
"node bin/cli update-scripts --no-write"
],
"README.md": [
"yarn maid toc",
"node bin/cli toc",
"node bin/cli update-scripts --git-add",
"git add"
]
}
}
}