Remove Ivy Codemirror (#14659)

* setup

* fix mode issue

* actions

* readonly styling

* remove ivycodemirror from package json

* wip

* test coverage for json editor

* text fixes

* fix tests

* fix cursor issue

* changelog

* clean up

* fix

* address pr comments

* unused css and it overides other styling.

* fix

* fix comment
This commit is contained in:
Angel Garbarino 2022-03-29 10:25:16 -06:00 committed by GitHub
parent 33888c3340
commit 0455d31b84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 232 additions and 187 deletions

3
changelog/14659.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
ui: Replaces the IvyCodemirror wrapper with a custom ember modifier.
```

View File

@ -14,50 +14,24 @@ import { action } from '@ember/object';
* @param {Function} [valueUpdated] - action to preform when you edit the codemirror value.
* @param {Function} [onFocusOut] - action to preform when you focus out of codemirror.
* @param {string} [helpText] - helper text.
* @param {object} [options] - option object that overrides codemirror default options such as the styling.
* @param {Object} [extraKeys] - Provides keyboard shortcut methods for things like saving on shift + enter.
* @param {Array} [gutters] - An array of CSS class names or class name / CSS string pairs, each of which defines a width (and optionally a background), and which will be used to draw the background of the gutters.
* @param {string} [mode] - The mode defined for styling. Right now we only import ruby so mode must but be ruby or defaults to javascript. If you wanted another language you need to import it into the modifier.
* @param {Boolean} [readOnly] - Sets the view to readOnly, allowing for copying but no editing. It also hides the cursor. Defaults to false.
* @param {String} [theme] - Specify or customize the look via a named "theme" class in scss.
* @param {String} [value] - Value within the display. Generally, a json string.
* @param {String} [viewportMargin] - Size of viewport. Often set to "Infinity" to load/show all text regardless of length.
*/
const JSON_EDITOR_DEFAULTS = {
// IMPORTANT: `gutters` must come before `lint` since the presence of
// `gutters` is cached internally when `lint` is toggled
gutters: ['CodeMirror-lint-markers'],
tabSize: 2,
mode: 'application/json',
lineNumbers: true,
lint: { lintOnChange: false },
theme: 'hashi',
readOnly: false,
showCursorWhenSelecting: true,
};
export default class JsonEditorComponent extends Component {
value = null;
valueUpdated = null;
onFocusOut = null;
readOnly = false;
options = null;
constructor() {
super(...arguments);
this.options = { ...JSON_EDITOR_DEFAULTS, ...this.args.options };
if (this.options.autoHeight) {
this.options.viewportMargin = Infinity;
delete this.options.autoHeight;
}
if (this.options.readOnly) {
this.options.readOnly = 'nocursor';
this.options.lineNumbers = false;
delete this.options.gutters;
}
}
get getShowToolbar() {
return this.args.showToolbar === false ? false : true;
}
@action
updateValue(...args) {
if (this.args.valueUpdated) {
onUpdate(...args) {
if (!this.args.readOnly) {
// catching a situation in which the user is not readOnly and has not provided a valueUpdated function to the instance
this.args.valueUpdated(...args);
}
}

View File

@ -0,0 +1,68 @@
import { action } from '@ember/object';
import { bind } from '@ember/runloop';
import codemirror from 'codemirror';
import Modifier from 'ember-modifier';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/selection/active-line';
import 'codemirror/addon/lint/lint.js';
import 'codemirror/addon/lint/json-lint.js';
// right now we only use the ruby and javascript, if you use another mode you'll need to import it.
// https://codemirror.net/mode/
import 'codemirror/mode/ruby/ruby';
import 'codemirror/mode/javascript/javascript';
export default class CodeMirrorModifier extends Modifier {
didInstall() {
this._setup();
}
didUpdateArguments() {
this._editor.setOption('readOnly', this.args.named.readOnly);
if (!this.args.named.content) {
return;
}
if (this._editor.getValue() !== this.args.named.content) {
this._editor.setValue(this.args.named.content);
}
}
@action
_onChange(editor) {
this.args.named.onUpdate(editor.getValue(), this._editor);
}
@action
_onFocus(editor) {
this.args.named.onFocus(editor.getValue());
}
_setup() {
if (!this.element) {
throw new Error('CodeMirror modifier has no element');
}
const editor = codemirror(this.element, {
// IMPORTANT: `gutters` must come before `lint` since the presence of
// `gutters` is cached internally when `lint` is toggled
gutters: this.args.named.gutters || ['CodeMirror-lint-markers'],
matchBrackets: true,
lint: { lintOnChange: true },
showCursorWhenSelecting: true,
styleActiveLine: true,
tabSize: 2,
// all values we can pass into the JsonEditor
extraKeys: this.args.named.extraKeys || '',
lineNumbers: this.args.named.lineNumbers,
mode: this.args.named.mode || 'application/json',
readOnly: this.args.named.readOnly || false,
theme: this.args.named.theme || 'hashi',
value: this.args.named.content || '',
viewportMargin: this.args.named.viewportMargin || '',
});
editor.on('change', bind(this, this._onChange));
editor.on('focus', bind(this, this._onFocus));
this._editor = editor;
}
}

View File

@ -51,7 +51,7 @@ $gutter-grey: #2a2f36;
.cm-s-hashi {
&.CodeMirror {
background-color: $black !important;
resize: vertical;
resize: vertical;
color: #cfd2d1 !important;
border: none;
font-family: $family-monospace;
@ -168,34 +168,14 @@ $gutter-grey: #2a2f36;
}
.readonly-codemirror {
.CodeMirror-cursors {
.CodeMirror-code {
cursor: default;
}
.CodeMirror-cursor {
// https://github.com/codemirror/CodeMirror/issues/1099
display: none;
}
.cm-s-hashi {
span {
color: $light-grey;
}
span.cm-string,
span.cm-string-2 {
color: $faded-gray;
}
span.cm-number {
color: lighten($dark-gray, 30%);
}
span.cm-property {
color: white;
}
span.cm-variable-2 {
color: $light-grey-blue;
}
}
}
.cm-s-auto-height.CodeMirror {
height: auto;
}

View File

@ -2,7 +2,10 @@
<JsonEditor
@showToolbar={{false}}
@value={{stringify this.content}}
@options={{hash readOnly=true lineNumbers=false autoHeight=true gutters=false theme="hashi auto-height"}}
@readOnly={{true}}
@viewportMargin="Infinity"
@gutters={{false}}
@theme="hashi auto-height"
/>
<HoverCopyButton @copyValue={{stringify this.content}} />
</div>

View File

@ -17,7 +17,10 @@
data-test-json-viewer
@showToolbar={{false}}
@value={{stringify this.unwrapData}}
@options={{hash readOnly=true lineNumbers=false autoHeight=true gutters=false theme="hashi-read-only auto-height"}}
@readOnly={{true}}
@viewportMargin="Infinity"
@gutters={{false}}
@theme="hashi-read-only auto-height"
/>
<HoverCopyButton @copyValue={{stringify this.unwrapData}} />
</div>

View File

@ -23,14 +23,22 @@
</Toolbar>
</div>
{{/if}}
<IvyCodemirror
@data-test-component="json-editor"
@value={{@value}}
@options={{this.options}}
@valueUpdated={{action "updateValue"}}
@onFocusOut={{action "onFocus"}}
/>
<div
{{code-mirror
content=@value
extraKeys=@extraKeys
gutters=@gutters
lineNumbers=(if @readOnly false true)
mode=@mode
readOnly=@readOnly
theme=@theme
viewportMarg=@viewportMargin
onUpdate=this.onUpdate
onFocus=this.onFocus
}}
class={{if @readOnly "readonly-codemirror"}}
data-test-component="code-mirror-modifier"
></div>
{{#if @helpText}}
<div class="box is-shadowless is-fullwidth has-short-padding">

View File

@ -33,7 +33,7 @@
<JsonEditor
@title={{if @isV2 "Version Data" "Secret Data"}}
@value={{@modelForData.dataAsJSONString}}
@options={{hash readOnly=true}}
@readOnly={{true}}
/>
</div>
{{else}}

View File

@ -45,7 +45,7 @@
{{#if (eq @unwrapActiveTab "data")}}
<div class="field">
<div class="control">
<JsonEditor @title="Unwrapped Data" @value={{stringify @unwrap_data}} @options={{hash readOnly=true}} />
<JsonEditor @title="Unwrapped Data" @value={{stringify @unwrap_data}} @readOnly={{true}} />
</div>
</div>
{{else}}

View File

@ -8,7 +8,7 @@
<JsonEditor
@title="Ciphertext"
@valueUpdated={{action (mut @ciphertext)}}
@options={{hash mode="ruby"}}
@mode="ruby"
@data-test-transit-input="ciphertext"
/>
</div>

View File

@ -16,7 +16,7 @@
@title="Plaintext"
@value={{@plaintext}}
@valueUpdated={{action (mut @plaintext)}}
@options={{hash mode="ruby"}}
@mode="ruby"
@data-test-transit-input="plaintext"
/>
</div>

View File

@ -11,12 +11,7 @@
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
<div class="field">
<div id="input-control" class="control is-relative">
<JsonEditor
@title="Input"
@valueUpdated={{action (mut @input)}}
@options={{hash mode="ruby"}}
@data-test-transit-input="input"
/>
<JsonEditor @title="Input" @valueUpdated={{action (mut @input)}} @mode="ruby" @data-test-transit-input="input" />
</div>
</div>
<div class="field">

View File

@ -11,7 +11,7 @@
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
<div class="field">
<div class="control is-expanded">
<JsonEditor @title="Ciphertext" @valueUpdated={{action (mut @ciphertext)}} @options={{hash mode="ruby"}} />
<JsonEditor @title="Ciphertext" @valueUpdated={{action (mut @ciphertext)}} @mode="ruby" />
</div>
</div>
{{#if @key.derived}}

View File

@ -28,7 +28,7 @@
@title="Input"
@value={{@input}}
@valueUpdated={{action (mut @input)}}
@options={{hash mode="ruby"}}
@mode="ruby"
@data-test-transit-input="input"
/>
</div>

View File

@ -23,7 +23,7 @@
@title="Input"
@value={{@input}}
@valueUpdated={{action (mut @input)}}
@options={{hash mode="ruby"}}
@mode="ruby"
@data-test-transit-input="input"
/>
</div>
@ -140,12 +140,7 @@
{{#if (or (and @verification (eq @verification "HMAC")) @hmac)}}
<div class="field is-flex-column is-flex-1">
<div class="control is-flex-column is-flex-1">
<JsonEditor
@title="HMAC"
@value={{@hmac}}
@valueUpdated={{action (mut @hmac)}}
@options={{hash mode="ruby"}}
/>
<JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{action (mut @hmac)}} @mode="ruby" />
</div>
</div>
{{else}}
@ -155,7 +150,7 @@
@title="Signature"
@value={{@signature}}
@valueUpdated={{action (mut @signature)}}
@options={{hash mode="ruby"}}
@mode="ruby"
/>
</div>
</div>
@ -165,7 +160,7 @@
{{else}}
<div class="field">
<div class="control">
<JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{action (mut @hmac)}} @options={{hash mode="ruby"}} />
<JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{action (mut @hmac)}} @mode="ruby" />
</div>
</div>
<div class="field">

View File

@ -62,7 +62,8 @@
@showToolbar={{false}}
@value={{this.model.policy}}
@valueUpdated={{action (mut this.model.policy)}}
@options={{hash mode="ruby" extraKeys=(hash Shift-Enter=(action "savePolicy" this.model))}}
@mode="ruby"
@extraKeys={{hash Shift-Enter=(action "savePolicy" this.model)}}
/>
{{/if}}
</div>

View File

@ -50,7 +50,8 @@
@title="Policy"
@value={{this.model.policy}}
@valueUpdated={{action (mut this.model.policy)}}
@options={{hash mode="ruby" extraKeys=(hash Shift-Enter=(action "savePolicy" this.model))}}
@mode="ruby"
@extraKeys={{hash Shift-Enter=(action "savePolicy" this.model)}}
/>
<div class="box is-shadowless is-fullwidth has-short-padding">
<p class="help-text has-text-grey-dark is-size-7">

View File

@ -42,7 +42,8 @@
@title="Policy"
@subTitle={{if (eq this.policyType "acl") (concat this.uppercase this.model.format " format")}}
@value={{this.model.policy}}
@options={{hash readOnly=true mode="ruby"}}
@readOnly={{true}}
@mode="ruby"
/>
</div>
{{#if this.model.paths}}

View File

@ -26,10 +26,6 @@ const appConfig = {
return `${config.rootURL.replace(/\/$/, '')}${filePath}`;
},
},
codemirror: {
modes: ['javascript', 'ruby'],
keyMaps: ['sublime'],
},
babel: {
plugins: ['@babel/plugin-proposal-object-rest-spread', ['inline-json-import', {}]],
},
@ -74,8 +70,7 @@ module.exports = function (defaults) {
app.import('node_modules/jsonlint/lib/jsonlint.js');
app.import('node_modules/codemirror/addon/lint/lint.css');
app.import('node_modules/codemirror/addon/lint/lint.js');
app.import('node_modules/codemirror/addon/lint/json-lint.js');
app.import('node_modules/codemirror/lib/codemirror.css');
app.import('node_modules/text-encoder-lite/text-encoder-lite.js');
app.import('node_modules/jsondiffpatch/dist/jsondiffpatch.umd.js');
app.import('node_modules/jsondiffpatch/dist/formatters-styles/html.css');

View File

@ -233,7 +233,7 @@
this.attr.options.defaultValue
}}
@valueUpdated={{action "codemirrorUpdated" this.attr.name "string"}}
@options={{hash theme=(or this.attr.options.theme "hashi")}}
@theme={{or this.attr.options.theme "hashi"}}
@helpText={{this.attr.options.helpText}}
>
{{#if this.attr.options.allowReset}}

View File

@ -129,6 +129,7 @@
"ember-maybe-import-regenerator": "^0.1.6",
"ember-maybe-in-element": "^2.0.3",
"ember-modal-dialog": "^3.0.3",
"ember-modifier": "^3.1.0",
"ember-page-title": "^6.0.3",
"ember-power-select": "^5.0.3",
"ember-promise-helpers": "^1.0.9",

View File

@ -374,13 +374,15 @@ module('Acceptance | secrets/secret/create', function (hooks) {
await listPage.visitRoot({ backend: 'secret' });
await listPage.create();
await editPage.path(secretPath).toggleJSON();
await editPage.editor.fillIn(this, content);
let instance = document.querySelector('.CodeMirror').CodeMirror;
instance.setValue(content);
await editPage.save();
assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.show', 'redirects to the show page');
assert.ok(showPage.editIsPresent, 'shows the edit button');
let savedInstance = document.querySelector('.CodeMirror').CodeMirror;
assert.equal(
showPage.editor.content(this),
savedInstance.options.value,
JSON.stringify({ bar: 'boo', foo: 'fa' }, null, 2),
'saves the content'
);

View File

@ -19,8 +19,7 @@ module('Integration | Component | console/log json', function (hooks) {
this.set('content', objectContent);
await render(hbs`{{console/log-json content=content}}`);
const instance = this.codeMirror.instanceFor(find('[data-test-component=json-editor]').id);
assert.equal(instance.getValue(), expectedText);
const instance = find('[data-test-component=code-mirror-modifier]').innerText;
assert.equal(instance, expectedText);
});
});

View File

@ -1,44 +1,76 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { create } from 'ember-cli-page-object';
import { render } from '@ember/test-helpers';
import { render, fillIn, find, waitUntil } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import jsonEditor from '../../pages/components/json-editor';
import sinon from 'sinon';
const component = create(jsonEditor);
module('Integration | Component | json-editor', function (hooks) {
setupRenderingTest(hooks);
const setup = async function (context, title, value, options, showToolbar = true) {
context.set('value', JSON.stringify(value));
context.set('options', options);
context.set('title', title);
context.set('showToolbar', showToolbar);
await render(hbs`{{json-editor title=title value=value options=options showToolbar=showToolbar}}`);
};
const JSON_BLOB = `{
"test": "test"
}`;
const BAD_JSON_BLOB = `{
"test": test
}`;
hooks.beforeEach(function () {
this.set('valueUpdated', sinon.spy());
this.set('onFocusOut', sinon.spy());
this.set('json_blob', JSON_BLOB);
this.set('bad_json_blob', BAD_JSON_BLOB);
this.set('hashi-read-only-theme', 'hashi-read-only auto-height');
});
test('it renders', async function (assert) {
let value = '';
await setup(this, 'Test title', value, null);
await render(hbs`<JsonEditor
@value={{"{}"}}
@title={{"Test title"}}
@showToolbar={{true}}
@readOnly={{true}}
/>`);
assert.equal(component.title, 'Test title', 'renders the provided title');
assert.equal(component.hasToolbar, true, 'renders the toolbar');
assert.equal(component.hasJSONEditor, true, 'renders the ivy code mirror component');
assert.equal(component.canEdit, true, 'json editor is in read only mode');
assert.equal(component.hasJSONEditor, true, 'renders the code mirror modifier');
assert.ok(component.canEdit, 'json editor can be edited');
});
test('it renders in read only mode', async function (assert) {
let value = '';
let options = {
readOnly: true,
};
await setup(this, 'Test title', value, options);
assert.equal(component.canEdit, false, 'editor is in read only mode');
test('it handles editing and linting and styles to json', async function (assert) {
await render(hbs`<JsonEditor
@value={{this.json_blob}}
@readOnly={{false}}
@valueUpdated={{this.valueUpdated}}
@onFocusOut={{this.onFocusOut}}
/>`);
// check for json styling
assert.dom('.cm-property').hasStyle({
color: 'rgb(158, 132, 197)',
});
assert.dom('.cm-string:nth-child(2)').hasStyle({
color: 'rgb(29, 219, 163)',
});
await fillIn('textarea', this.bad_json_blob);
await waitUntil(() => find('.CodeMirror-lint-marker-error'));
assert.dom('.CodeMirror-lint-marker-error').exists('throws linting error');
assert.dom('.CodeMirror-linenumber').exists('shows line numbers');
});
test('it renders the editor without toolbar', async function (assert) {
let value = '';
await setup(this, 'Test title', value, null, false);
assert.equal(component.hasToolbar, false, 'toolbar is not rendered');
test('it renders the correct theme and expected styling', async function (assert) {
await render(hbs`<JsonEditor
@value={{this.json_blob}}
@theme={{this.hashi-read-only-theme}}
@readOnly={{true}}
/>`);
assert.dom('.cm-s-hashi-read-only').hasStyle({
background: 'rgb(247, 248, 250) none repeat scroll 0% 0% / auto padding-box border-box',
});
assert.dom('.CodeMirror-linenumber').doesNotExist('on readOnly does not show line numbers');
});
});

View File

@ -1,6 +1,6 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, find, settled } from '@ember/test-helpers';
import { render, settled } from '@ember/test-helpers';
import { resolve } from 'rsvp';
import { run } from '@ember/runloop';
import Service from '@ember/service';
@ -55,15 +55,12 @@ module('Integration | Component | secret edit', function (hooks) {
test('it shows an error when creating and data is not an object', async function (assert) {
this.set('mode', 'create');
this.set('model', {
secretData: {
int: '2',
null: 'null',
float: '1.234',
},
secretData: null,
});
await render(hbs`{{secret-edit mode=mode model=model preferAdvancedEdit=true }}`);
let instance = this.codeMirror.instanceFor(find('[data-test-component=json-editor]').id);
let instance = document.querySelector('.CodeMirror').CodeMirror;
instance.setValue(JSON.stringify([{ foo: 'bar' }]));
await settled();
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');
@ -99,7 +96,7 @@ module('Integration | Component | secret edit', function (hooks) {
await render(hbs`{{secret-edit mode=mode model=model preferAdvancedEdit=true }}`);
let instance = this.codeMirror.instanceFor(find('[data-test-component=json-editor]').id);
let instance = document.querySelector('.CodeMirror').CodeMirror;
instance.setValue(JSON.stringify([{ foo: 'bar' }]));
await settled();
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');

View File

@ -15,7 +15,7 @@ export default {
hasSearchSelect: isPresent('[data-test-component=search-select]'),
hasTextFile: isPresent('[data-test-component=text-file]'),
hasTTLPicker: isPresent('[data-test-toggle-input="Foo"]'),
hasJSONEditor: isPresent('[data-test-component=json-editor]'),
hasJSONEditor: isPresent('[data-test-component="code-mirror-modifier"]'),
hasJSONClearButton: isPresent('[data-test-json-clear-button]'),
hasSelect: isPresent('select'),
hasInput: isPresent('input'),

View File

@ -1,8 +1,8 @@
import { isPresent, isVisible, text } from 'ember-cli-page-object';
import { isPresent, notHasClass, text } from 'ember-cli-page-object';
export default {
title: text('[data-test-component=json-editor-title]'),
hasToolbar: isPresent('[data-test-component=json-editor-toolbar]'),
hasJSONEditor: isPresent('[data-test-component=json-editor]'),
canEdit: isVisible('div.CodeMirror-gutters'),
hasJSONEditor: isPresent('[data-test-component="code-mirror-modifier"]'),
canEdit: notHasClass('readonly-codemirror'),
};

View File

@ -1,34 +0,0 @@
import getCodeMirrorInstance from 'vault/tests/helpers/codemirror';
// Like fillable, but for the CodeMirror editor
//
// Usage: fillIn: codeFillable('[data-test-editor]')
// Page.fillIn(code);
export function codeFillable(selector) {
return {
isDescriptor: true,
get() {
return function (context, code) {
const cm = getCodeMirrorInstance(context, selector);
cm.setValue(code);
return this;
};
},
};
}
// Like text, but for the CodeMirror editor
//
// Usage: content: code('[data-test-editor]')
// Page.code(); // some = [ 'string', 'of', 'code' ]
export function code(selector) {
return {
isDescriptor: true,
get() {
return function (context) {
const cm = getCodeMirrorInstance(context, selector);
return cm.getValue();
};
},
};
}

View File

@ -1,6 +1,6 @@
import { Base } from '../create';
import { isPresent, clickable, visitable, create, fillable } from 'ember-cli-page-object';
import { codeFillable } from 'vault/tests/pages/helpers/codemirror';
export default create({
...Base,
path: fillable('[data-test-secret-path="true"]'),
@ -17,9 +17,6 @@ export default create({
hasMetadataFields: isPresent('[data-test-metadata-fields]'),
maxVersion: fillable('[data-test-input="maxVersions"]'),
startCreateSecret: clickable('[data-test-secret-create]'),
editor: {
fillIn: codeFillable('[data-test-component="json-editor"]'),
},
deleteSecret() {
return this.deleteBtn().confirmBtn();
},

View File

@ -1,6 +1,5 @@
import { Base } from '../show';
import { create, clickable, collection, isPresent, text } from 'ember-cli-page-object';
import { code } from 'vault/tests/pages/helpers/codemirror';
export default create({
...Base,
@ -18,9 +17,7 @@ export default create({
editIsPresent: isPresent('[data-test-secret-edit]'),
noReadIsPresent: isPresent('[data-test-write-without-read-empty-message]'),
noReadMessage: text('data-test-empty-state-message'),
editor: {
content: code('[data-test-component="json-editor"]'),
},
deleteSecret() {
return this.deleteBtn().confirmBtn();
},

View File

@ -7974,7 +7974,12 @@ 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.58.2:
codemirror@^5.47.0:
version "5.65.2"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.2.tgz#5799a70cb3d706e10f60e267245e3a75205d3dd9"
integrity sha512-SZM4Zq7XEC8Fhroqe3LxbEEX1zUPWH1wMr5zxiBuiUF64iYOUH/JI88v4tBag8MiBS8B8gRv8O1pPXGYXQ4ErA==
codemirror@^5.58.2:
version "5.61.0"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.61.0.tgz#318e5b034a707207948b92ffc2862195e8fdb08e"
integrity sha512-D3wYH90tYY1BsKlUe0oNj2JAhQ9TepkD51auk3N7q+4uz7A/cgJ5JsWHreT0PqieW1QhOuqxQ2reCXV1YXzecg==
@ -10222,7 +10227,7 @@ ember-cli-typescript@^3.0.0, ember-cli-typescript@^3.1.3, ember-cli-typescript@^
stagehand "^1.0.0"
walk-sync "^2.0.0"
ember-cli-typescript@^4.1.0, ember-cli-typescript@^4.2.0:
ember-cli-typescript@^4.1.0, ember-cli-typescript@^4.2.0, ember-cli-typescript@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/ember-cli-typescript/-/ember-cli-typescript-4.2.1.tgz#54d08fc90318cc986f3ea562f93ce58a6cc4c24d"
integrity sha512-0iKTZ+/wH6UB/VTWKvGuXlmwiE8HSIGcxHamwNhEC5x1mN3z8RfvsFZdQWYUzIWFN2Tek0gmepGRPTwWdBYl/A==
@ -10396,6 +10401,17 @@ ember-compatibility-helpers@^1.2.0, ember-compatibility-helpers@^1.2.1, ember-co
fs-extra "^9.1.0"
semver "^5.4.1"
ember-compatibility-helpers@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.6.tgz#603579ab2fb14be567ef944da3fc2d355f779cd8"
integrity sha512-2UBUa5SAuPg8/kRVaiOfTwlXdeVweal1zdNPibwItrhR0IvPrXpaqwJDlEZnWKEoB+h33V0JIfiWleSG6hGkkA==
dependencies:
babel-plugin-debug-macros "^0.2.0"
ember-cli-version-checker "^5.1.1"
find-up "^5.0.0"
fs-extra "^9.1.0"
semver "^5.4.1"
ember-composable-helpers@^4.3.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/ember-composable-helpers/-/ember-composable-helpers-4.5.0.tgz#94febbdf4348e64f45f7a6f993f326e32540a61e"
@ -10656,6 +10672,17 @@ ember-modifier@^2.1.0:
ember-destroyable-polyfill "^2.0.2"
ember-modifier-manager-polyfill "^1.2.0"
ember-modifier@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/ember-modifier/-/ember-modifier-3.1.0.tgz#ba5b0941302accd787ed3dcfc8d20400b77ffc41"
integrity sha512-G5Lj9jVFsD2sVJcRNQfaGKG1p81wT4LGfClBhCuB4TgwP1NGJKdqI+Q8BW2MptONxQt/71UjjUH0YK7Gm9eahg==
dependencies:
ember-cli-babel "^7.26.6"
ember-cli-normalize-entity-name "^1.0.0"
ember-cli-string-utils "^1.1.0"
ember-cli-typescript "^4.2.1"
ember-compatibility-helpers "^1.2.5"
ember-native-dom-helpers@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/ember-native-dom-helpers/-/ember-native-dom-helpers-0.7.0.tgz#98a87c11a391cec5c12382a4857e59ea2fb4b00a"