-
Notifications
You must be signed in to change notification settings - Fork 239
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
[RRFC] npm run-series && npm run-parallel #190
Comments
@MylesBorins - Just to confirm, you are specifically talking about a built-in mechanism for this right? Is the proposal roughly that we should add a |
for "run parallel", i haven't yet found a tool that properly handles nonzero exit codes, and also handles mixing (and prefixing) streaming stdout/stderr output. If npm can solve this that would be awesome. |
I think this is a real issue, and our current constraints have led to some great and some really awful things. If we make it more powerful we run the risk of encouraging some even more wild usage. At work, we have a system for composing "commands" which is not make. Learning from this, it is hard to get right but is ultimately very useful. If we want to really solve this well, which I am not sure we need to do, then I think we should consider preexisting systems with features like work avoidance and DSLs for declaring the commands. Also, to share, we have a tool which uses the |
FWIW I think that we could likely scope this solution to only |
Removing Agenda label. A next step would be a making a more complete proposal specifically for series. |
As I said in the meeting I think there is also a use case I can see for parallel, although probably in a second RFC: In web development there are often watch tasks that run Typescript, Sass, webpack or whatever. It would be useful to have a way to run them in parallel through npm and if one dies, just kill the entire thing. |
I can definitely find this useful, not even parallel, but running multiple scripts in series as an array: "scripts": {
"build": [ "webpack --verbose", "npm run test", "git add .", "echo 'all done'" ]
}
I think Will |
The array approach is a pretty straightforward way to implement "series", fwiw - I like it. |
Not that I like this line of thought, but to toss out another complicated option:
You could (although I strongly believe we need to move this all outside of "scriptfoo": [{
"build1": "thing",
"build2": "otherthing"
}, "test"] Again, I really dont like the complexity this would add to npm, but if someone were to work on a declarative and simple command runner, one might do it this way. |
Uhh, I like this solution a lot @wesleytodd because it does not introduce commands/flags and is straight forward! Through a flag like Our scripts are looking like that with "scripts": {
"start": "ng serve",
"prod": "ng serve --prod",
"clean": "del dist tmp out-tsc coverage results abstracts .lighthouseci",
"proxy": "make restart",
"build": "run-s build:*",
"build:ng": "ng build --prod",
"build:min": "node scripts/minify-files.js",
"docs": "typedoc --options .typedoc.js src",
"docs-open": "open-cli docs/index.html",
"all": "run-s clean lint build docs test-ci e2e-ci perf",
"−−−−−−−−−−−−−−−---−−−---- TESTS −---−−−−−−---−−−−−−−−−−−": "",
"test": "ng test",
"test-ci": "ng test -c ci",
"e2e": "ng e2e",
"e2e-ci": "ng e2e -c ci",
"−−−−−−−−−−−−−−−---−−−- PERFORMANCE −−−−−−−---−−−−−−−−−−−": "",
"perf": "run-s build proxy perf-lhci perf-open",
"perf-lhci": "lhci collect",
"perf-open": "run-p perf-open:*",
"perf-open:lhci": "lhci open",
"−−−−−−−−−−−−−−−−−−------ LINTING −−−−−−−------−−−−−−−−−−": "",
"lint": "run-p lint:*",
"lint:tsc": "tsc --build tsconfig.app.json",
"lint:tsc-worker": "tsc --build tsconfig.worker.json",
"lint:source": "ng lint",
"lint:cypress": "eslint --ext .ts,.js cypress",
"lint:engine": "eslint --cache *.js scripts/**/*.js",
"lint:scss": "stylelint --cache src/**/*.scss",
"−-−−−−−−−−−−−−−−−----- FORMATING −−−−−−--−−−−−−−−−−-−−": "",
"format": "run-p format:*",
"format:typings": "prettier --write 'src/**/*.d.ts'",
"−-−−−−−−−−−−−−−−−-−----− TOOLS −−−−−−----−−−−−−−−−−-−−": "",
"preinstall": "node scripts/welcome.js && sh scripts/nvm-use.sh",
"postinstall": "node scripts/setup.js",
"generate-graphql": "graphql-codegen && npm run format:typings",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
"release": "node scripts/release.js"
}, This is how it could look like then (sub-tasks would be indented with there title): {
"scripts": {
// "start": "ng serve",
// "prod": "ng serve --prod",
// "clean": "del dist tmp out-tsc coverage results abstracts .lighthouseci",
// "proxy": "make restart",
// "docs": "typedoc --options .typedoc.js src",
// "docs-open": "open-cli docs/index.html",
"all": [
"clean",
"lint",
"build-docs",
"test-ci",
"e2e-ci",
"perf"
],
"−−−−−−−−−−−−−−−---−−−---- BUILD −---−−−−−−---−−−−−−−−−−−": "",
"build": {
"Build application": "build-ng",
"Minify non-minified": "build-min"
},
"build-ng": "ng build --prod",
"build-min": "node scripts/minify-files.js",
// "−−−−−−−−−−−−−−−---−−−---- TESTS −---−−−−−−---−−−−−−−−−−−": "",
// "test": "ng test",
// "test-ci": "ng test -c ci",
// "e2e": "ng e2e",
// "e2e-ci": "ng e2e -c ci",
"−−−−−−−−−−−−−−−---−−−- PERFORMANCE −−−−−−−---−−−−−−−−−−−": "",
"perf": {
"Building": "build",
"Starting proxy": "proxy",
"Running performance tests": "perf:*",
"Opening reports": "perf-open"
},
"perf:lhci": "lhci collect",
"perf-open": "perf-open:*",
"perf-open:lhci": "lhci open",
"−−−−−−−−−−−−−−−−−−------ LINTING −−−−−−−------−−−−−−−−−−": "",
"lint": "lint:*",
"lint:tsc": "tsc --build tsconfig.app.json",
"lint:tsc-worker": "tsc --build tsconfig.worker.json",
"lint:source": "ng lint",
"lint:cypress": "eslint --ext .ts,.js cypress",
"lint:engine": "eslint --cache *.js scripts/**/*.js",
"lint:scss": "stylelint --cache src/**/*.scss",
"−-−−−−−−−−−−−−−−−----- FORMATING −−−−−−--−−−−−−−−−−-−−": "",
"format": "format:*",
"format:typings": "prettier --write 'src/**/*.d.ts'",
// "−-−−−−−−−−−−−−−−−-−----− TOOLS −−−−−−----−−−−−−−−−−-−−": "",
// "preinstall": "node scripts/welcome.js && sh scripts/nvm-use.sh",
// "postinstall": "node scripts/setup.js",
// "generate-graphql": "graphql-codegen && npm run format:typings",
// "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
// "release": "node scripts/release.js"
}
}
|
Perhaps the array should always be the names of other scripts in the script map? This way the individual tasks remain named scripts that can be run separately. In addition it removes the need to inline {
"scripts": {
"build-all": ["npm run build1", "npm run build2"],
"build1": "...",
"build2": "..."
}
} and instead just write: {
"scripts": {
"build-all": ["build1", "build2"],
"build1": "...",
"build2": "..."
}
} |
It might be nice to reserve the sweeter array syntax for parallel runs, or even make the series v parallel thing a flag of npm run itself - |
This feature could be adapted to be used on
executes in parallel the |
I would like to make an argument to allow the dependency graph to be 'statically' extracted.
At Microsoft we have very large JavaScript repositories as well as mixed repositories where nmake (C++), gradle (java), msbuild (c++ and C#) and javascript projects all live together in a single build graph. Lage and Just mentioned in the above paragraph support running locally on a single devbox. The finer grained the nodes that the build engine can operate on the better it can optimize itself.. Lage gives a graph with multiple verbs on a given project... But today it cannot peek into the commands of each verb. i.e. of 'build' is It would be amazing if this functionality could be built-in into npm! I really appreciate that this is being considered! |
I think it would be great to get scripts out of the business of directly running sub-steps in their command invocation and instead have them explicitly declare their dependencies, then have the script runner run sub-steps in parallel or serial as required. So instead of: "scripts": {
"build": "npm run build:css && npm run build:js && npm run build:html && build:img"
"build:css": ... we might have scripts declared as objects with explicit dependencies: "scripts": {
"build": {
"dependencies": [
"build:css",
"build:js",
"build:html",
"build:img",
]
}
"build:css": "...",
"build:html": {
"command": "some-html-build-tool",
// tasks can depend on each other. The runner should figure out the correct order & parallelism
"dependencies": [
"build:css",
]
} |
We built and published (today!) https://github.com/google/wireit, which is an attempt at bringing a declarative script-level dependency graph to npm, including parallel execution. It uses a syntax just like what @justinfagnani showed in the comment above — except in a separate {
"scripts": {
"build": "wireit",
"bundle": "wireit"
},
"wireit": {
"build": {
"command": "tsc",
"files": ["src/**/*.ts", "tsconfig.json"],
"output": ["lib"]
},
"bundle": {
"command": "rollup -c",
"dependencies": ["build"],
"files": ["rollup.config.json"],
"output": ["dist/bundle.js"]
}
}
} (So this works by having the actual npm script delegate to the I concur that it would be awesome to have functionality like this built into npm. I'd be interested in starting/contributing to an RFC along these lines. |
This would be amazing. |
it would be a good start if otherwise an hack and bash/PS7 hack must be done:
not really convinient... |
Is this going to happen? It seems like no one is implementing this. This is massively useful for a number of incredibly common scenarios:
|
My wish list for npm scripts:
For example: {
"scripts": {
// cross-platform operators for commands
"script1": "cmd -a & cmd -b",
"script2": "cmd -c && cmd -d",
"script3": "cmd -e || cmd -f",
"script4": "{ cmd -a & cmd -b } || { cmd -c && cmd -d }",
"script5": "cmd -a | cmd -b",
"script6": "cmd -c 2>&1 outputfile",
"script7": "cmd -d < inputfile",
// cross-platform operators for scripts
"script8": "npm run script1 & npm run script2",
"script9": "{ npm run script1 & npm run script2 } || { npm run script3 && npm run script4 } | format -abc > logfile",
// syntactic sugar and new options
"script10": "npm run script:*",
"script11": "npm run script1 script2 script3 --parallel",
"script12": "npm run lint --workspaces --parallel",
"script13": "npm run build --workspace=a --workspace=b --serial"
}
} Bonus points for being able to set environment variables in scripts cross-platform and support for json comments as shown above (if that's even possible). |
I think as soon as you add running scripts sequentially and concurrently, you quickly end up wanting DAG of scripts where you run common script dependencies only once. You relatively quickly then end up wanting to cache common script dependency results so that subsequent runs are faster. This is what Wireit does, while maintaining npm as the user interface and package.json as the config file. I think it's a great approach, but short of building those features into npm itself, I think npm could also enable pluggable script runners that can add those features. I propose that in #691 |
@llimllib and I found a fairly clean way to do this using npm query .workspace | \
node -p 'JSON.parse(fs.readFileSync(0)).map(ws => ws.name).join("\n")' | \
xargs -I {} -P 0 npm run lint --if-present --workspace "{}" Got the JSON parsing approach from this gist: https://gist.github.com/kristopherjohnson/5065599?permalink_comment_id=4775925#gistcomment-4775925 |
BTW I just adopted nx / lerna and it works great and it also caches. Example project here: https://github.com/bhouston/template-typescript-monorepo |
Motivation ("The Why")
Often an npm script is part of a more complicated pipeline, tools such as grunt + gulp have been created to help manage some of this pipeline. A repo may have multiple steps to a compilation such as clean, build various types of assets (html, css, js), etc. Currently npm itself does not have a mechanism to orchestrate more complex workflow.
Example
Most recently I utilized xargs to accomplish a similar task.
"build": "echo build:clean build:css build:js build:html build:img | xargs -n1 npm run"
if
run-series
were available this could be accomplished in a platform agnostic way via"build": "npm run-series build:clean build:css build:js build:html build:img"
It could also be optimized to run parallel operations
"build": "npm run build:clean && npm run-parallel build:css build:js build:html build:img"
or potentially
"build": "npm run build:clean --then npm run-parallel build:css build:js build:html build:img"
How
Current Behaviour
Currently there is no way to accomplish this beyond using
&&
or&
which is can be platform specificDesired Behaviour
First class support for running multiple scripts in parallel or series
References
The text was updated successfully, but these errors were encountered: