diff --git a/CHANGELOG.md b/CHANGELOG.md index 33f61bd1c63d..2d3141494316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - `[docs]` Explain how to rewrite assertions to avoid large irrelevant diff ([#6971](https://github.com/facebook/jest/pull/6971)) - `[examples]` add example using Babel 7 ([#6983](https://github.com/facebook/jest/pull/6983)) - `[docs]` Replace shallow equality with referential identity in `ExpectAPI.md` ([#6991](https://github.com/facebook/jest/pull/6991)) +- `[jest-changed-files]` Refactor to use `execa` over `child_process` ([#6987](https://github.com/facebook/jest/pull/6987)) ## 23.6.0 diff --git a/packages/jest-changed-files/package.json b/packages/jest-changed-files/package.json index 9495084d234b..cabb73c57560 100644 --- a/packages/jest-changed-files/package.json +++ b/packages/jest-changed-files/package.json @@ -8,6 +8,7 @@ "license": "MIT", "main": "build/index.js", "dependencies": { + "execa": "^1.0.0", "throat": "^4.0.0" } } diff --git a/packages/jest-changed-files/src/git.js b/packages/jest-changed-files/src/git.js index 672108ad7f96..b54901ea3afb 100644 --- a/packages/jest-changed-files/src/git.js +++ b/packages/jest-changed-files/src/git.js @@ -11,37 +11,19 @@ import type {Path} from 'types/Config'; import type {Options, SCMAdapter} from 'types/ChangedFiles'; import path from 'path'; -import childProcess from 'child_process'; +import execa from 'execa'; const findChangedFilesUsingCommand = async ( args: Array, cwd: Path, -): Promise> => - new Promise((resolve, reject) => { - const child = childProcess.spawn('git', args, {cwd}); - let stdout = ''; - let stderr = ''; - child.stdout.on('data', data => (stdout += data)); - child.stderr.on('data', data => (stderr += data)); - child.on('error', e => reject(e)); - child.on('close', code => { - if (code === 0) { - stdout = stdout.trim(); - if (stdout === '') { - resolve([]); - } else { - resolve( - stdout - .split('\n') - .filter(s => s !== '') - .map(changedPath => path.resolve(cwd, changedPath)), - ); - } - } else { - reject(code + ': ' + stderr); - } - }); - }); +): Promise> => { + const result = await execa('git', args, {cwd}); + + return result.stdout + .split('\n') + .filter(s => s !== '') + .map(changedPath => path.resolve(cwd, changedPath)); +}; const adapter: SCMAdapter = { findChangedFiles: async ( @@ -54,7 +36,7 @@ const adapter: SCMAdapter = { const includePaths: Array = (options && options.includePaths) || []; if (options && options.lastCommit) { - return await findChangedFilesUsingCommand( + return findChangedFilesUsingCommand( ['show', '--name-only', '--pretty=%b', 'HEAD'].concat(includePaths), cwd, ); @@ -81,7 +63,7 @@ const adapter: SCMAdapter = { ); return [...committed, ...staged, ...unstaged]; } else { - return await findChangedFilesUsingCommand( + return findChangedFilesUsingCommand( ['ls-files', '--other', '--modified', '--exclude-standard'].concat( includePaths, ), @@ -90,19 +72,17 @@ const adapter: SCMAdapter = { } }, - getRoot: async (cwd: string): Promise => - new Promise(resolve => { - try { - let stdout = ''; - const options = ['rev-parse', '--show-toplevel']; - const child = childProcess.spawn('git', options, {cwd}); - child.stdout.on('data', data => (stdout += data)); - child.on('error', () => resolve(null)); - child.on('close', code => resolve(code === 0 ? stdout.trim() : null)); - } catch (e) { - resolve(null); - } - }), + getRoot: async (cwd: string): Promise => { + const options = ['rev-parse', '--show-toplevel']; + + try { + const result = await execa('git', options, {cwd}); + + return result.stdout; + } catch (e) { + return null; + } + }, }; export default adapter; diff --git a/packages/jest-changed-files/src/hg.js b/packages/jest-changed-files/src/hg.js index 029709546cdd..f8429a605d90 100644 --- a/packages/jest-changed-files/src/hg.js +++ b/packages/jest-changed-files/src/hg.js @@ -11,7 +11,7 @@ import type {Path} from 'types/Config'; import type {Options, SCMAdapter} from 'types/ChangedFiles'; import path from 'path'; -import childProcess from 'child_process'; +import execa from 'execa'; const env = Object.assign({}, process.env, { HGPLAIN: 1, @@ -32,55 +32,36 @@ const adapter: SCMAdapter = { findChangedFiles: async ( cwd: string, options: Options, - ): Promise> => - new Promise((resolve, reject) => { - const includePaths: Array = (options && options.includePaths) || []; + ): Promise> => { + const includePaths: Array = (options && options.includePaths) || []; - const args = ['status', '-amnu']; - if (options && options.withAncestor) { - args.push('--rev', `ancestor(${ANCESTORS.join(', ')})`); - } else if (options && options.changedSince) { - args.push('--rev', `ancestor(., ${options.changedSince})`); - } else if (options && options.lastCommit === true) { - args.push('-A'); - } - args.push(...includePaths); - const child = childProcess.spawn('hg', args, {cwd, env}); - let stdout = ''; - let stderr = ''; - child.stdout.on('data', data => (stdout += data)); - child.stderr.on('data', data => (stderr += data)); - child.on('error', (error: Error) => reject(error)); - child.on('close', code => { - if (code === 0) { - stdout = stdout.trim(); - if (stdout === '') { - resolve([]); - } else { - resolve( - stdout - .split('\n') - .map(changedPath => path.resolve(cwd, changedPath)), - ); - } - } else { - reject(new Error(code + ': ' + stderr)); - } - }); - }), + const args = ['status', '-amnu']; + if (options && options.withAncestor) { + args.push('--rev', `ancestor(${ANCESTORS.join(', ')})`); + } else if (options && options.changedSince) { + args.push('--rev', `ancestor(., ${options.changedSince})`); + } else if (options && options.lastCommit === true) { + args.push('-A'); + } + args.push(...includePaths); - getRoot: async (cwd: Path): Promise => - new Promise(resolve => { - try { - let stdout = ''; - const child = childProcess.spawn('hg', ['root'], {cwd, env}); - child.stdout.on('data', data => (stdout += data)); - child.on('error', () => resolve(null)); - child.on('close', code => resolve(code === 0 ? stdout.trim() : null)); - } catch (e) { - resolve(null); - } - }), + const result = await execa('hg', args, {cwd, env}); + + return result.stdout + .split('\n') + .filter(s => s !== '') + .map(changedPath => path.resolve(cwd, changedPath)); + }, + + getRoot: async (cwd: Path): Promise => { + try { + const result = await execa('hg', ['root'], {cwd, env}); + + return result.stdout; + } catch (e) { + return null; + } + }, }; export default adapter; diff --git a/yarn.lock b/yarn.lock index e5831fbe5a5b..ffdc41b7a5d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3929,7 +3929,7 @@ encoding@^0.1.11: dependencies: iconv-lite "~0.4.13" -end-of-stream@^1.0.0: +end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" dependencies: @@ -4404,6 +4404,18 @@ execa@^0.8.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.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" + executable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/executable/-/executable-1.1.0.tgz#877980e9112f3391066da37265de7ad8434ab4d9" @@ -5040,6 +5052,12 @@ get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" +get-stream@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.0.0.tgz#9e074cb898bd2b9ebabb445a1766d7f43576d977" + dependencies: + pump "^3.0.0" + get-uri@2: version "2.0.1" resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.1.tgz#dbdcacacd8c608a38316869368117697a1631c59" @@ -8119,7 +8137,7 @@ on-headers@~1.0.0, on-headers@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" -once@^1.3.0, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -8909,6 +8927,13 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"