From a769c12c6db9f281973e75a8ce03d2b6fef5ce2d Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Mon, 4 Jul 2022 16:47:58 -0400 Subject: [PATCH] Edit Secure Variables as JSON (#13461) * Toying with insert and update helpers before translation func * Working prototype that lets you switch between json and tabular * No longer add the bonus items row in json mode * Trimmed the ivy from the codemirror (#13503) * Trimmed the ivy from the codemirror * editedJSONItems removal * De-debugger * Replaced other instances of IvyCodeMirror throughout the app (#13528) * Replaced other instances of IvyCodeMirror throughout the app * PR requests for codemirror modifier * Screen reader setting as param * Trying a simpler codemirror test helper * Lint removal * Screen Reader Label added for a11y * JSONViewer cleanup * JSON editor added to /new and all variables stringified before save or translate * Give users a foothold when editing an empty item in JSON mode * Copy the empty KV * No duplicate keys in KV * Better handling of cursor snapping in json edit field * Catch formatting errors on the fly * Basic tests for JSON to Table and Table to JSON in form --- ui/app/components/json-viewer.js | 10 +- ui/app/components/secure-variable-form.hbs | 101 ++++++---- ui/app/components/secure-variable-form.js | 180 +++++++++++++++--- ui/app/controllers/variables/new.js | 22 ++- ui/app/controllers/variables/variable/edit.js | 23 ++- ui/app/modifiers/code-mirror.js | 57 ++++++ ui/app/routes/variables/variable.js | 6 +- .../styles/components/secure-variables.scss | 12 ++ ui/app/templates/components/job-dispatch.hbs | 16 +- ui/app/templates/components/job-editor.hbs | 15 +- ui/app/templates/components/json-viewer.hbs | 14 +- ui/app/templates/variables/new.hbs | 12 ++ ui/app/templates/variables/variable/edit.hbs | 15 +- ui/app/templates/variables/variable/index.hbs | 1 + ui/ember-cli-build.js | 3 + ui/package.json | 5 +- ui/tests/helpers/codemirror.js | 18 +- .../components/secure-variable-form-test.js | 145 +++++++++++++- ui/yarn.lock | 114 ++++++----- 19 files changed, 593 insertions(+), 176 deletions(-) create mode 100644 ui/app/modifiers/code-mirror.js diff --git a/ui/app/components/json-viewer.js b/ui/app/components/json-viewer.js index 148f3d70a..c3e74f9c9 100644 --- a/ui/app/components/json-viewer.js +++ b/ui/app/components/json-viewer.js @@ -1,16 +1,8 @@ import Component from '@ember/component'; -import { computed } from '@ember/object'; import { classNames, classNameBindings } from '@ember-decorators/component'; import classic from 'ember-classic-decorator'; @classic @classNames('json-viewer') @classNameBindings('fluidHeight:has-fluid-height') -export default class JsonViewer extends Component { - json = null; - - @computed('json') - get jsonStr() { - return JSON.stringify(this.json, null, 2); - } -} +export default class JsonViewer extends Component {} diff --git a/ui/app/components/secure-variable-form.hbs b/ui/app/components/secure-variable-form.hbs index 55bb34859..6c43a3174 100644 --- a/ui/app/components/secure-variable-form.hbs +++ b/ui/app/components/secure-variable-form.hbs @@ -1,7 +1,6 @@ -
- {{!-- TODO: {{if this.parseError 'is-danger'}} on inputs --}} +{{did-update this.onViewChange @view}} +{{did-insert this.establishKeyValues}} +
- {{#each this.keyValues as |entry iter|}} -
- - - {{#if (eq entry this.keyValues.lastObject)}} - - {{else}} - + {{#if (eq this.view "json")}} +
+
+ {{#if this.JSONError}} +

+ {{this.JSONError}} +

{{/if}} - {{#each-in entry.warnings as |k v|}} - - {{v}} - - {{/each-in}}
- {{/each}} + {{else}} + {{#each this.keyValues as |entry iter|}} +
+ + + {{#if (eq entry this.keyValues.lastObject)}} + + {{else}} + + {{/if}} + {{#each-in entry.warnings as |k v|}} + + {{v}} + + {{/each-in}} +
+ {{/each}} + {{/if}}
{{#if this.hasPayload}}
- + onUpdate=(action (mut this.payload)) + mode="javascript" + screenReaderLabel="Payload definition" + }} + />
{{else}}
diff --git a/ui/app/templates/components/job-editor.hbs b/ui/app/templates/components/job-editor.hbs index 98fef792c..50d3ea47b 100644 --- a/ui/app/templates/components/job-editor.hbs +++ b/ui/app/templates/components/job-editor.hbs @@ -51,17 +51,14 @@ {{/if}}
-
diff --git a/ui/app/templates/components/json-viewer.hbs b/ui/app/templates/components/json-viewer.hbs index d5f3cc576..1c67d3380 100644 --- a/ui/app/templates/components/json-viewer.hbs +++ b/ui/app/templates/components/json-viewer.hbs @@ -1,11 +1,9 @@ - + screenReaderLabel="JSON Viewer" + }} +/> diff --git a/ui/app/templates/variables/new.hbs b/ui/app/templates/variables/new.hbs index b405b6467..c5c7efd19 100644 --- a/ui/app/templates/variables/new.hbs +++ b/ui/app/templates/variables/new.hbs @@ -1,9 +1,21 @@ {{page-title "New Secure Variable"}} +
+

+ Create a Secure Variable + JSON +

+
diff --git a/ui/app/templates/variables/variable/edit.hbs b/ui/app/templates/variables/variable/edit.hbs index 1283418f5..8ab737f18 100644 --- a/ui/app/templates/variables/variable/edit.hbs +++ b/ui/app/templates/variables/variable/edit.hbs @@ -1,6 +1,6 @@ {{page-title "Edit Secure Variable"}} -

+

Edit {{this.model.path}} + JSON +

- + diff --git a/ui/app/templates/variables/variable/index.hbs b/ui/app/templates/variables/variable/index.hbs index 4edbbf967..19b0993bf 100644 --- a/ui/app/templates/variables/variable/index.hbs +++ b/ui/app/templates/variables/variable/index.hbs @@ -41,6 +41,7 @@ class="button is-info is-inverted is-small" @model={{this.model}} @route="variables.variable.edit" + @query={{hash view=this.view}} > Edit diff --git a/ui/ember-cli-build.js b/ui/ember-cli-build.js index 86987115f..5f2be0d7d 100644 --- a/ui/ember-cli-build.js +++ b/ui/ember-cli-build.js @@ -47,6 +47,9 @@ module.exports = function (defaults) { // along with the exports of each module as its value. app.import('node_modules/xterm/css/xterm.css'); + app.import('node_modules/jsonlint/lib/jsonlint.js'); + app.import('node_modules/codemirror/addon/lint/lint.css'); + app.import('node_modules/codemirror/lib/codemirror.css'); return app.toTree(); }; diff --git a/ui/package.json b/ui/package.json index 7f76f4ccc..a479b1078 100644 --- a/ui/package.json +++ b/ui/package.json @@ -52,6 +52,7 @@ "base64-js": "^1.3.1", "broccoli-asset-rev": "^3.0.0", "bulma": "0.9.3", + "codemirror": "^5.58.2", "core-js": "3.19.1", "d3-array": "^3.1.1", "d3-axis": "^3.0.0", @@ -128,7 +129,7 @@ "http-proxy": "^1.1.6", "husky": "^4.2.5", "is-ip": "^3.1.0", - "ivy-codemirror": "IvyApp/ivy-codemirror#c3b7f49f8e6492878619f8055695581240cce21a", + "jsonlint": "^1.6.3", "lint-staged": "^11.2.6", "loader.js": "^4.7.0", "lodash.intersection": "^4.4.0", @@ -174,7 +175,6 @@ "@hashicorp/ember-flight-icons": "^2.0.5", "@percy/cli": "^1.1.0", "@percy/ember": "^3.0.0", - "codemirror": "^5.56.0", "curved-arrows": "^0.1.0", "d3": "^7.3.0", "lru_map": "^0.4.1", @@ -182,7 +182,6 @@ "title-case": "^3.0.3" }, "resolutions": { - "ivy-codemirror/codemirror": "^5.56.0", "ember-auto-import": "^2.4.0" } } diff --git a/ui/tests/helpers/codemirror.js b/ui/tests/helpers/codemirror.js index 923db4563..1c9174cc8 100644 --- a/ui/tests/helpers/codemirror.js +++ b/ui/tests/helpers/codemirror.js @@ -1,18 +1,6 @@ -const invariant = (truthy, error) => { - if (!truthy) throw new Error(error); -}; - -export function getCodeMirrorInstance(container) { - return function (selector) { - const cmService = container.lookup('service:code-mirror'); - - const element = document.querySelector(selector); - invariant(element, `Selector ${selector} matched no elements`); - - const cm = cmService.instanceFor(element.id); - invariant(cm, `No registered CodeMirror instance for ${selector}`); - - return cm; +export function getCodeMirrorInstance() { + return function () { + return document.querySelector('.CodeMirror').CodeMirror; }; } diff --git a/ui/tests/integration/components/secure-variable-form-test.js b/ui/tests/integration/components/secure-variable-form-test.js index fe2caf46b..0b7405af5 100644 --- a/ui/tests/integration/components/secure-variable-form-test.js +++ b/ui/tests/integration/components/secure-variable-form-test.js @@ -2,12 +2,15 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; import { hbs } from 'ember-cli-htmlbars'; import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit'; -import { click, typeIn, findAll, render } from '@ember/test-helpers'; +import { click, typeIn, find, findAll, render } from '@ember/test-helpers'; import { setupMirage } from 'ember-cli-mirage/test-support'; +import setupCodeMirror from 'nomad-ui/tests/helpers/codemirror'; +import { codeFillable, code } from 'nomad-ui/tests/pages/helpers/codemirror'; module('Integration | Component | secure-variable-form', function (hooks) { setupRenderingTest(hooks); setupMirage(hooks); + setupCodeMirror(hooks); test('passes an accessibility audit', async function (assert) { assert.expect(1); @@ -244,12 +247,150 @@ module('Integration | Component | secure-variable-form', function (hooks) { ); await render(hbs``); - await typeIn('.key-value label:nth-child(1) input', 'foo'); await typeIn('.key-value label:nth-child(1) input', 'superSecret'); assert.dom('.key-value-error').doesNotExist(); + + find('.key-value:nth-child(2) label:nth-child(1) input').value = ''; + await typeIn('.key-value label:nth-child(1) input', 'super.secret'); assert.dom('.key-value-error').exists(); }); + + test('warns you when you create a duplicate key', async function (assert) { + this.set( + 'mockedModel', + server.create('variable', { + keyValues: [{ key: 'myKey', value: 'myVal' }], + }) + ); + + await render(hbs``); + + await click('.key-value button.add-more'); + + await typeIn( + '.key-value:nth-child(3) label:nth-child(1) input', + 'myWonderfulKey' + ); + assert.dom('.key-value-error').doesNotExist(); + + find('.key-value:nth-child(3) label:nth-child(1) input').value = ''; + + await typeIn('.key-value:nth-child(3) label:nth-child(1) input', 'myKey'); + assert.dom('.key-value-error').exists(); + }); + }); + + module('Views', function () { + test('Allows you to swap between JSON and Key/Value Views', async function (assert) { + this.set( + 'mockedModel', + server.create('variable', { + path: '', + keyValues: [{ key: '', value: '' }], + }) + ); + + this.set( + 'existingVariables', + server.createList('variable', 1, { + path: 'baz/bat', + }) + ); + + this.set('view', 'table'); + + await render( + hbs`` + ); + assert.dom('.key-value').exists(); + assert.dom('.CodeMirror').doesNotExist(); + + this.set('view', 'json'); + assert.dom('.key-value').doesNotExist(); + assert.dom('.CodeMirror').exists(); + }); + + test('Persists Key/Values table data to JSON', async function (assert) { + const keyValues = [ + { key: 'foo', value: '123' }, + { key: 'bar', value: '456' }, + ]; + this.set( + 'mockedModel', + server.create('variable', { + path: '', + keyValues, + }) + ); + + this.set('view', 'json'); + + await render( + hbs`` + ); + + const keyValuesAsJSON = keyValues.reduce((acc, { key, value }) => { + acc[key] = value; + return acc; + }, {}); + + assert.equal( + code('.editor-wrapper').get(), + JSON.stringify(keyValuesAsJSON, null, 2), + 'JSON editor contains the key values, stringified, by default' + ); + + this.set('view', 'table'); + + await click('.key-value button.add-more'); + + await typeIn('.key-value:last-of-type label:nth-child(1) input', 'howdy'); + await typeIn( + '.key-value:last-of-type label:nth-child(2) input', + 'partner' + ); + + this.set('view', 'json'); + + assert.ok( + code('[data-test-json-editor]').get().includes('"howdy": "partner"'), + 'JSON editor contains the new key value' + ); + }); + + test('Persists JSON data to Key/Values table', async function (assert) { + const keyValues = [{ key: '', value: '' }]; + this.set( + 'mockedModel', + server.create('variable', { + path: '', + keyValues, + }) + ); + + this.set('view', 'json'); + + await render( + hbs`` + ); + + codeFillable('[data-test-json-editor]').get()( + JSON.stringify({ golden: 'gate' }, null, 2) + ); + this.set('view', 'table'); + assert.equal( + find(`.key-value:last-of-type label:nth-child(1) input`).value, + 'golden', + 'Key persists from JSON to Table' + ); + + assert.equal( + find(`.key-value:last-of-type label:nth-child(2) input`).value, + 'gate', + 'Value persists from JSON to Table' + ); + }); }); }); diff --git a/ui/yarn.lock b/ui/yarn.lock index cd77a12e7..7cbe7ae2c 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -4704,6 +4704,11 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +JSV@^4.0.x: + version "4.0.2" + resolved "https://registry.yarnpkg.com/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57" + integrity sha512-ZJ6wx9xaKJ3yFUhq5/sk82PJMuUyLk277I8mQeyDgCTjGdjWJIvPfaU5LIXaMuaN2UO1X3kZH4+lgphublZUHw== + abab@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" @@ -4982,6 +4987,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178" + integrity sha512-3iF4FIKdxaVYT3JqQuY3Wat/T2t7TRbbQ94Fu50ZUCbLy4TFbTzr90NOHQodQkNqmeEGCw8WbeP78WNi6SKYUA== + ansi-to-html@^0.6.11: version "0.6.14" resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.14.tgz#65fe6d08bba5dd9db33f44a20aec331e0010dad8" @@ -6585,7 +6595,7 @@ broccoli-funnel@2.0.1: symlink-or-copy "^1.0.0" walk-sync "^0.3.1" -broccoli-funnel@^1.0.1, broccoli-funnel@^1.1.0: +broccoli-funnel@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/broccoli-funnel/-/broccoli-funnel-1.2.0.tgz#cddc3afc5ff1685a8023488fff74ce6fb5a51296" integrity sha1-zdw6/F/xaFqAI0iP/3TOb7WlEpY= @@ -6669,20 +6679,6 @@ broccoli-kitchen-sink-helpers@^0.3.1: glob "^5.0.10" mkdirp "^0.5.1" -broccoli-merge-trees@^1.1.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/broccoli-merge-trees/-/broccoli-merge-trees-1.2.4.tgz#a001519bb5067f06589d91afa2942445a2d0fdb5" - integrity sha1-oAFRm7UGfwZYnZGvopQkRaLQ/bU= - dependencies: - broccoli-plugin "^1.3.0" - can-symlink "^1.0.0" - fast-ordered-set "^1.0.2" - fs-tree-diff "^0.5.4" - heimdalljs "^0.2.1" - heimdalljs-logger "^0.1.7" - rimraf "^2.4.3" - symlink-or-copy "^1.0.0" - broccoli-merge-trees@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/broccoli-merge-trees/-/broccoli-merge-trees-2.0.1.tgz#14d4b7fc1a90318c12b16f843e6ba2693808100c" @@ -7460,6 +7456,15 @@ chalk@^4.1.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" + integrity sha512-sQfYDlfv2DGVtjdoQqxS0cEZDroyG8h6TamA6rvxwlrU5BaSLDx9xhatBYl2pxZ7gmpNaPFVwBtdGdu5rQ+tYQ== + dependencies: + ansi-styles "~1.0.0" + has-color "~0.1.0" + strip-ansi "~0.1.0" + character-entities-legacy@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" @@ -7774,10 +7779,10 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -codemirror@^5.47.0, codemirror@^5.56.0: - version "5.63.3" - resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.63.3.tgz#97042a242027fe0c87c09b36bc01931d37b76527" - integrity sha512-1C+LELr+5grgJYqwZKqxrcbPsHFHapVaVAloBsFBASbpLnQqLw1U8yXJ3gT5D+rhxIiSpo+kTqN+hQ+9ialIXw== +codemirror@^5.58.2: + version "5.65.6" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.6.tgz#fc313797331cbeb3bcab0652d1ec9d0f40c23ab5" + integrity sha512-zNihMSMoDxK9Gqv9oEyDT8oM51rcRrQ+IEo2zyS48gJByBq5Fj8XuNEguMra+MuIOuh6lkpnLUJeL70DoTt6yw== collapse-white-space@^1.0.2: version "1.0.6" @@ -9607,18 +9612,6 @@ ember-cli-moment-shim@^3.8.0: moment "^2.19.3" moment-timezone "^0.5.13" -ember-cli-node-assets@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/ember-cli-node-assets/-/ember-cli-node-assets-0.2.2.tgz#d2d55626e7cc6619f882d7fe55751f9266022708" - integrity sha1-0tVWJufMZhn4gtf+VXUfkmYCJwg= - dependencies: - broccoli-funnel "^1.0.1" - broccoli-merge-trees "^1.1.1" - broccoli-source "^1.1.0" - debug "^2.2.0" - lodash "^4.5.1" - resolve "^1.1.7" - ember-cli-normalize-entity-name@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ember-cli-normalize-entity-name/-/ember-cli-normalize-entity-name-1.0.0.tgz#0b14f7bcbc599aa117b5fddc81e4fd03c4bad5b7" @@ -11277,7 +11270,7 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fast-ordered-set@^1.0.0, fast-ordered-set@^1.0.2: +fast-ordered-set@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/fast-ordered-set/-/fast-ordered-set-1.0.3.tgz#3fbb36634f7be79e4f7edbdb4a357dee25d184eb" integrity sha1-P7s2Y097555PftvbSjV97iXRhOs= @@ -12359,6 +12352,11 @@ has-bigints@^1.0.1: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-color@~0.1.0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" + integrity sha512-kaNz5OTAYYmt646Hkqw50/qyxP2vFnTVu5AQ1Zmk22Kk5+4Qx6BpO8+u7IKsML5fOsFk0ZT0AcCJNYwcvaLBvw== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -13534,14 +13532,6 @@ iterate-value@^1.0.2: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" -ivy-codemirror@IvyApp/ivy-codemirror#c3b7f49f8e6492878619f8055695581240cce21a: - version "2.1.0" - resolved "https://codeload.github.com/IvyApp/ivy-codemirror/tar.gz/c3b7f49f8e6492878619f8055695581240cce21a" - dependencies: - codemirror "^5.47.0" - ember-cli-babel "^7.7.3" - ember-cli-node-assets "^0.2.2" - jest-worker@^26.5.0: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" @@ -13746,6 +13736,14 @@ jsonify@~0.0.0: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= +jsonlint@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/jsonlint/-/jsonlint-1.6.3.tgz#cb5e31efc0b78291d0d862fbef05900adf212988" + integrity sha512-jMVTMzP+7gU/IyC6hvKyWpUU8tmTkK5b3BPNuMI9U8Sit+YAWLlZwB6Y6YrdCxfg2kNz05p3XY3Bmm4m26Nv3A== + dependencies: + JSV "^4.0.x" + nomnom "^1.5.x" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -14292,7 +14290,7 @@ lodash.values@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c= -lodash@^4.17.10, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.5.1: +lodash@^4.17.10, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -15167,6 +15165,14 @@ node-watch@0.7.3: resolved "https://registry.yarnpkg.com/node-watch/-/node-watch-0.7.3.tgz#6d4db88e39c8d09d3ea61d6568d80e5975abc7ab" integrity sha512-3l4E8uMPY1HdMMryPRUAl+oIHtXtyiTlIiESNSVSNxcPfzAFzeTbXFQkZfAwBbo0B1qMSG8nUABx+Gd+YrbKrQ== +nomnom@^1.5.x: + version "1.8.1" + resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7" + integrity sha512-5s0JxqhDx9/rksG2BTMVN1enjWSvPidpoSgViZU4ZXULyTe+7jxcCRLB6f42Z0l1xYJpleCBtSyY6Lwg3uu5CQ== + dependencies: + chalk "~0.4.0" + underscore "~1.6.0" + nopt@^3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -17197,14 +17203,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.7, resolve@^1.10.1, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.19.0, resolve@^1.3.3, resolve@^1.8.1: - version "1.19.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" - integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== - dependencies: - is-core-module "^2.1.0" - path-parse "^1.0.6" - resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.4.0, resolve@^1.5.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" @@ -17214,6 +17212,14 @@ resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.4 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.10.1, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.19.0, resolve@^1.3.3, resolve@^1.8.1: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + resolve@^1.14.2, resolve@^1.3.2: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" @@ -18322,6 +18328,11 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" + integrity sha512-behete+3uqxecWlDAm5lmskaSaISA+ThQ4oNNBDTBJt0x2ppR6IPqfZNuj6BLaLJ/Sji4TPZlcRyOis8wXQTLg== + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -19101,6 +19112,11 @@ underscore@>=1.8.3: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== +underscore@~1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" + integrity sha512-z4o1fvKUojIWh9XuaVLUDdf86RQiq13AC1dmHbTpoyuu+bquHms76v16CjycCbec87J7z0k//SiQVk0sMdFmpQ== + unfetch@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"