Skip to content

Commit 6cba3d7

Browse files
committed
Merge branch 'kinland-feature/117-pnpm-support'
2 parents fe72148 + a3ee6cd commit 6cba3d7

File tree

1 file changed

+79
-45
lines changed

1 file changed

+79
-45
lines changed

lib/run-task.js

+79-45
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// Requirements
1111
// ------------------------------------------------------------------------------
1212

13+
const fs = require('fs')
1314
const path = require('path')
1415
const parseArgs = require('shell-quote').parse
1516
const createHeader = require('./create-header')
@@ -128,65 +129,96 @@ function cleanTaskArg (arg) {
128129
* An array of options which are inserted before the task name.
129130
* @param {object} options.labelState - A state object for printing labels.
130131
* @param {boolean} options.printName - The flag to print task names before running each task.
132+
* @param {object} options.packageInfo - A package.json's information.
133+
* @param {object} options.packageInfo.body - A package.json's JSON object.
134+
* @param {string} options.packageInfo.path - A package.json's file path.
131135
* @returns {Promise}
132136
* A promise object which becomes fullfilled when the npm-script is completed.
133137
* This promise object has an extra method: `abort()`.
134138
* @private
135139
*/
136140
module.exports = function runTask (task, options) {
137141
let cp = null
138-
const promise = new Promise((resolve, reject) => {
139-
ansiStylesPromise.then(({ default: ansiStyles }) => {
140-
const stdin = options.stdin
141-
const stdout = wrapLabeling(task, options.stdout, options.labelState, ansiStyles)
142-
const stderr = wrapLabeling(task, options.stderr, options.labelState, ansiStyles)
143-
const stdinKind = detectStreamKind(stdin, process.stdin)
144-
const stdoutKind = detectStreamKind(stdout, process.stdout)
145-
const stderrKind = detectStreamKind(stderr, process.stderr)
146-
const spawnOptions = { stdio: [stdinKind, stdoutKind, stderrKind] }
147-
148-
// Print task name.
149-
if (options.printName && stdout != null) {
150-
stdout.write(createHeader(
151-
task,
152-
options.packageInfo,
153-
options.stdout.isTTY,
154-
ansiStyles
155-
))
142+
143+
async function asyncRunTask () {
144+
const { default: ansiStyles } = await ansiStylesPromise
145+
146+
const stdin = options.stdin
147+
const stdout = wrapLabeling(task, options.stdout, options.labelState, ansiStyles)
148+
const stderr = wrapLabeling(task, options.stderr, options.labelState, ansiStyles)
149+
const stdinKind = detectStreamKind(stdin, process.stdin)
150+
const stdoutKind = detectStreamKind(stdout, process.stdout)
151+
const stderrKind = detectStreamKind(stderr, process.stderr)
152+
const spawnOptions = { stdio: [stdinKind, stdoutKind, stderrKind] }
153+
154+
// Print task name.
155+
if (options.printName && stdout != null) {
156+
stdout.write(createHeader(
157+
task,
158+
options.packageInfo,
159+
options.stdout.isTTY,
160+
ansiStyles
161+
))
162+
}
163+
164+
// Execute.
165+
let npmPath = options.npmPath
166+
if (!npmPath && process.env.npm_execpath) {
167+
const basename = path.basename(process.env.npm_execpath)
168+
let newBasename = basename
169+
if (basename.startsWith('npx')) {
170+
newBasename = basename.replace('npx', 'npm') // eslint-disable-line no-process-env
171+
} else if (basename.startsWith('pnpx')) {
172+
newBasename = basename.replace('pnpx', 'pnpm') // eslint-disable-line no-process-env
156173
}
157174

158-
// Execute.
159-
const npmPath = options.npmPath || path.basename(process.env.npm_execpath).startsWith('npx') // eslint-disable-line no-process-env
160-
? path.join(path.dirname(process.env.npm_execpath), path.basename(process.env.npm_execpath).replace('npx', 'npm')) // eslint-disable-line no-process-env
175+
npmPath = newBasename === basename
176+
? path.join(path.dirname(process.env.npm_execpath), newBasename)
161177
: process.env.npm_execpath // eslint-disable-line no-process-env
162-
const npmPathIsJs = typeof npmPath === 'string' && /\.m?js/.test(path.extname(npmPath))
163-
const execPath = (npmPathIsJs ? process.execPath : npmPath || 'npm')
164-
const isYarn = process.env.npm_config_user_agent && process.env.npm_config_user_agent.startsWith('yarn') // eslint-disable-line no-process-env
165-
const spawnArgs = ['run']
178+
}
166179

167-
if (npmPathIsJs) {
168-
spawnArgs.unshift(npmPath)
169-
}
170-
if (!isYarn) {
171-
Array.prototype.push.apply(spawnArgs, options.prefixOptions)
172-
} else if (options.prefixOptions.indexOf('--silent') !== -1) {
173-
spawnArgs.push('--silent')
180+
const npmPathIsJs = typeof npmPath === 'string' && /\.(c|m)?js/.test(path.extname(npmPath))
181+
let execPath = (npmPathIsJs ? process.execPath : npmPath || 'npm')
182+
183+
if (!npmPath && !process.env.npm_execpath) {
184+
// When a script is being run via pnpm, npmPath and npm_execpath will be null or undefined
185+
// Attempt to figure out whether we're running via pnpm
186+
const projectRoot = path.dirname(options.packageInfo.path)
187+
const hasPnpmLockfile = fs.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))
188+
const { status: pnpmFound, output: pnpmWhichOutput } = await spawn('which', 'pnpm', { silent: true })
189+
if (hasPnpmLockfile && __dirname.split(path.delimiter).includes('.pnpm') && pnpmFound) {
190+
execPath = pnpmWhichOutput
174191
}
175-
Array.prototype.push.apply(spawnArgs, parseArgs(task).map(cleanTaskArg))
192+
}
176193

177-
cp = spawn(execPath, spawnArgs, spawnOptions)
194+
const isYarn = process.env.npm_config_user_agent && process.env.npm_config_user_agent.startsWith('yarn') // eslint-disable-line no-process-env
178195

179-
// Piping stdio.
180-
if (stdinKind === 'pipe') {
181-
stdin.pipe(cp.stdin)
182-
}
183-
if (stdoutKind === 'pipe') {
184-
cp.stdout.pipe(stdout, { end: false })
185-
}
186-
if (stderrKind === 'pipe') {
187-
cp.stderr.pipe(stderr, { end: false })
188-
}
196+
const spawnArgs = ['run']
189197

198+
if (npmPathIsJs) {
199+
spawnArgs.unshift(npmPath)
200+
}
201+
if (!isYarn) {
202+
Array.prototype.push.apply(spawnArgs, options.prefixOptions)
203+
} else if (options.prefixOptions.indexOf('--silent') !== -1) {
204+
spawnArgs.push('--silent')
205+
}
206+
Array.prototype.push.apply(spawnArgs, parseArgs(task).map(cleanTaskArg))
207+
208+
cp = spawn(execPath, spawnArgs, spawnOptions)
209+
210+
// Piping stdio.
211+
if (stdinKind === 'pipe') {
212+
stdin.pipe(cp.stdin)
213+
}
214+
if (stdoutKind === 'pipe') {
215+
cp.stdout.pipe(stdout, { end: false })
216+
}
217+
if (stderrKind === 'pipe') {
218+
cp.stderr.pipe(stderr, { end: false })
219+
}
220+
221+
return new Promise((resolve, reject) => {
190222
// Register
191223
cp.on('error', (err) => {
192224
cp = null
@@ -197,7 +229,9 @@ module.exports = function runTask (task, options) {
197229
resolve({ task, code, signal })
198230
})
199231
})
200-
})
232+
}
233+
234+
const promise = asyncRunTask()
201235

202236
promise.abort = function abort () {
203237
if (cp != null) {

0 commit comments

Comments
 (0)