From 948b6e32b2da878f6d6c2e378a45d015dd6ceb0a Mon Sep 17 00:00:00 2001 From: Andrew Sprouse Date: Tue, 11 Jun 2019 23:04:20 -0400 Subject: [PATCH] Fix `yarn list --json` stdOut parsing. fixes #388 --- lib/packagers/yarn.js | 14 ++++++++++---- lib/packagers/yarn.test.js | 28 ++++++++++++++++++---------- lib/utils.js | 16 +++++++++++++++- lib/utils.test.js | 16 ++++++++++++++++ 4 files changed, 59 insertions(+), 15 deletions(-) diff --git a/lib/packagers/yarn.js b/lib/packagers/yarn.js index 2c2b45f4c..014ab6827 100644 --- a/lib/packagers/yarn.js +++ b/lib/packagers/yarn.js @@ -1,7 +1,7 @@ 'use strict'; /** * Yarn packager. - * + * * Yarn specific packagerOptions (default): * flat (false) - Use --flat with install * ignoreScripts (false) - Do not execute scripts during install @@ -58,7 +58,13 @@ class Yarn { return BbPromise.reject(err); }) .then(processOutput => processOutput.stdout) - .then(depJson => BbPromise.try(() => JSON.parse(depJson))) + .then((stdout) => + BbPromise.try(() => { + const lines = Utils.splitLines(stdout); + const parsedLines = _.map(lines, Utils.safeJsonParse); + return _.find(parsedLines, (line) => line && line.type === 'tree'); + }) + ) .then(parsedTree => { const convertTrees = trees => _.reduce(trees, (__, tree) => { const splitModule = _.split(tree.name, '@'); @@ -77,7 +83,7 @@ class Yarn { const trees = _.get(parsedTree, 'data.trees', []); const result = { problems: [], - dependencies: convertTrees(trees) + dependencies: convertTrees(trees) }; return result; }); @@ -87,7 +93,7 @@ class Yarn { const fileVersionMatcher = /[^"/]@(?:file:)?((?:\.\/|\.\.\/).*?)[":,]/gm; const replacements = []; let match; - + // Detect all references and create replacement line strings while ((match = fileVersionMatcher.exec(lockfile)) !== null) { replacements.push({ diff --git a/lib/packagers/yarn.test.js b/lib/packagers/yarn.test.js index d77ac154d..eb7341504 100644 --- a/lib/packagers/yarn.test.js +++ b/lib/packagers/yarn.test.js @@ -62,14 +62,22 @@ describe('yarn', () => { }); it('should transform yarn trees to npm dependencies', () => { - const testYarnResult = `{"type":"tree","data":{"type":"list","trees":[ - {"name":"archiver@2.1.1","children":[],"hint":null,"color":"bold", - "depth":0},{"name":"bluebird@3.5.1","children":[],"hint":null,"color": - "bold","depth":0},{"name":"fs-extra@4.0.3","children":[],"hint":null, - "color":"bold","depth":0},{"name":"mkdirp@0.5.1","children":[{"name": - "minimist@0.0.8","children":[],"hint":null,"color":"bold","depth":0}], - "hint":null,"color":null,"depth":0},{"name":"@sls/webpack@1.0.0", - "children":[],"hint":null,"color":"bold","depth":0}]}}`; + const testYarnResult = + '{"type":"activityStart","data":{"id":0}}\n' + + '{"type":"activityTick","data":{"id":0,"name":"archiver@^2.1.1"}}\n' + + '{"type":"activityTick","data":{"id":0,"name":"bluebird@^3.5.1"}}\n' + + '{"type":"activityTick","data":{"id":0,"name":"fs-extra@^4.0.3"}}\n' + + '{"type":"activityTick","data":{"id":0,"name":"mkdirp@^0.5.1"}}\n' + + '{"type":"activityTick","data":{"id":0,"name":"minimist@^0.0.8"}}\n' + + '{"type":"activityTick","data":{"id":0,"name":"@sls/webpack@^1.0.0"}}\n' + + '{"type":"tree","data":{"type":"list","trees":[' + + '{"name":"archiver@2.1.1","children":[],"hint":null,"color":"bold",' + + '"depth":0},{"name":"bluebird@3.5.1","children":[],"hint":null,"color":' + + '"bold","depth":0},{"name":"fs-extra@4.0.3","children":[],"hint":null,' + + '"color":"bold","depth":0},{"name":"mkdirp@0.5.1","children":[{"name":' + + '"minimist@0.0.8","children":[],"hint":null,"color":"bold","depth":0}],' + + '"hint":null,"color":null,"depth":0},{"name":"@sls/webpack@1.0.0",' + + '"children":[],"hint":null,"color":"bold","depth":0}]}}\n'; const expectedResult = { problems: [], dependencies: { @@ -198,7 +206,7 @@ describe('yarn', () => { version "5.5.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9" `; - + expect(yarnModule.rebaseLockfile('../../project', testContent)).to.equal(expectedContent); }); }); @@ -286,4 +294,4 @@ describe('yarn', () => { }); }); -}); \ No newline at end of file +}); diff --git a/lib/utils.js b/lib/utils.js index 1db482d3b..58c23aa92 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -15,7 +15,7 @@ function guid() { /** * Remove the specified module from the require cache. - * @param {string} moduleName + * @param {string} moduleName */ function purgeCache(moduleName) { return searchAndProcessCache(moduleName, function (mod) { @@ -97,10 +97,24 @@ function spawnProcess(command, args, options) { }); } +function safeJsonParse(str) { + try { + return JSON.parse(str); + } catch (e) { + return null; + } +} + +function splitLines(str) { + return _.split(str, /\r?\n/); +} + module.exports = { guid, purgeCache, searchAndProcessCache, SpawnError, spawnProcess, + safeJsonParse, + splitLines, }; diff --git a/lib/utils.test.js b/lib/utils.test.js index 658934df3..653b9fa00 100644 --- a/lib/utils.test.js +++ b/lib/utils.test.js @@ -110,4 +110,20 @@ describe('Utils', () => { return expect(Utils.spawnProcess('cmd', [])).to.be.rejectedWith(Utils.SpawnError); }); }); + + describe('safeJsonParse', () => { + it('should parse valid JSON', () => { + expect(Utils.safeJsonParse('{"foo": "bar"}')).to.deep.equal({ foo: 'bar' }); + }); + + it('should return null for invalid JSON', () => { + expect(Utils.safeJsonParse('{"foo":')).to.equal(null); + }); + }); + + describe('splitLines', () => { + it('should split on new line characters', () => { + expect(Utils.splitLines('a\r\nb\nc')).to.deep.equal([ 'a', 'b', 'c' ]); + }); + }); });