diff --git a/ember-cli-build.js b/ember-cli-build.js index b0a91ef4e44..5f6e154d16a 100644 --- a/ember-cli-build.js +++ b/ember-cli-build.js @@ -18,6 +18,7 @@ const rename = require('./broccoli/rename'); const { routerES, jquery, + emberDecorators, internalLoader, qunit, handlebarsES, diff --git a/packages/@ember/-internals/metal/lib/computed.ts b/packages/@ember/-internals/metal/lib/computed.ts index 66b9f767518..16674217e8a 100644 --- a/packages/@ember/-internals/metal/lib/computed.ts +++ b/packages/@ember/-internals/metal/lib/computed.ts @@ -3,6 +3,9 @@ import { inspect } from '@ember/-internals/utils'; import { EMBER_METAL_TRACKED_PROPERTIES } from '@ember/canary-features'; import { assert, warn } from '@ember/debug'; import EmberError from '@ember/error'; + +import { computedDecoratorWithParams } from './decorator-utils/index'; + import { getCachedValueFor, getCacheFor, @@ -21,6 +24,9 @@ import { notifyPropertyChange } from './property_events'; import { set } from './property_set'; import { tagForProperty, update } from './tags'; import { getCurrentTracker, setCurrentTracker } from './tracked'; +import { decoratorWithParams } from './decorator-utils/decorator'; +import { getModifierMeta } from './decorator-utils/computed'; +import { isComputedDescriptor, computedDescriptorFor } from './decorator-utils/-private/descriptor'; export type ComputedPropertyGetter = (keyName: string) => any; export type ComputedPropertySetter = (leyName: string, value: any) => any; @@ -609,7 +615,7 @@ if (EMBER_METAL_TRACKED_PROPERTIES) { @return {ComputedProperty} property descriptor instance @public */ -export default function computed(...args: (string | ComputedPropertyConfig)[]): ComputedProperty { +export function computed_original(...args: (string | ComputedPropertyConfig)[]): ComputedProperty { let func = args.pop(); let cp = new ComputedProperty(func as ComputedPropertyConfig); @@ -620,7 +626,154 @@ export default function computed(...args: (string | ComputedPropertyConfig)[]): return cp; } + +/** + Decorator that turns a native getter/setter into a computed property. Note + that though they use getters and setters, you must still use the Ember `get`/ + `set` functions to get and set their values. + + ```js + import Component from '@ember/component'; + import { computed } from '@ember-decorators/object'; + + export default class UserProfileComponent extends Component { + first = 'John'; + last = 'Smith'; + + @computed('first', 'last') + get name() { + const first = this.get('first'); + const last = this.get('last'); + + return `${first} ${last}`; // => 'John Smith' + } + + set name(value) { + if (typeof value !== 'string' || !value.test(/^[a-z]+ [a-z]+$/i)) { + throw new TypeError('Invalid name'); + } + + const [first, last] = value.split(' '); + this.setProperties({ first, last }); + + return value; + } + } + ``` + + @function + @param {...string} propertyNames - List of property keys this computed is dependent on + @return {ComputedProperty} +*/ + +// https://tc39.github.io/proposal-decorators/#sec-elementdescriptor-specification-type +interface ElementDescriptor { + descriptor: PropertyDescriptor; + initializer?: () => any; // unknown + key: string; + kind: 'method' | 'field' | 'initializer'; + placement: 'own' | 'prototype' | 'static'; +} + +function computedDecoratorInner(fn: any) { + return (desc: ElementDescriptor, params = []) => { + // All computeds are methods + desc.kind = 'method'; + desc.placement = 'prototype'; + + desc.finisher = function initializeComputedProperty(target) { + let { prototype } = target; + let { key } = desc; + + assert( + `ES6 property getters/setters only need to be decorated once, '${key}' was decorated on both the getter and the setter`, + !computedDescriptorFor(prototype, key) + ); + + let computedDesc = fn(desc, params); + + assert( + `computed decorators must return an instance of an Ember ComputedProperty descriptor, received ${computedDesc}`, + isComputedDescriptor(computedDesc) + ); + + let modifierMeta = getModifierMeta(prototype); + + if (modifierMeta !== undefined && modifierMeta[key] !== undefined) { + computedDesc[modifierMeta[key]](); + } + + defineProperty(prototype, key, computedDesc); + + return target; + }; + }; +} + +// computed(function() { ... }) +// computed('dependentKey', { get: function() { ... }}) +// computed('dependentKey', {}) +// computed('dependentKey', function() { ...}) +function isLegacyComputed(...args) { + const lastArg = args[args.length - 1]; + + // only non-decorator computeds have a last arg as a function + if (typeof lastArg === 'function') { + return true; + } + + if (lastArg.hasOwnProperty('descriptor')) { + return false; + } + + // TODO: configuration + // lastArg could have readOnly: true + if (typeof lastArg === 'object') { + const keys = Object.keys(lastArg); + + if (keys.length === 2 && keys.includes('get') && keys.includes('set')) { + return true; + } + + if (keys.length === 1 && keys.includes('get')) { + return true; + } + + // else: configuration object or descriptor? + return !lastArg.hasOwnProperty('configurable'); + } + + // dependent key array + return false; +} + +/** + decorator? or original, non-decorator-computed + + non-decorator usage: + property = computed(function() { ... }) + property = computed('dependentKey', function() { ... }) + property = computed({ get: ..., set?: ... }) + + decorator usage: + @computed get property() { ... } + @computed('dependentKey') get property() { ... } + @computed('dependentKey', { readOnly: true }) get property() { ... } + +**/ +const computed = function(...args: any[]) { + if (isLegacyComputed(...args)) { + return computed_original(...args); + } + + // any decorator usage should be handled here, + // as all of the non-decorator usages are handled above + return decoratorWithParams(computedDecoratorInner(args)); +}; + +export default computed; + // used for the Ember.computed global only -export const _globalsComputed = computed.bind(null); +export const _globalsComputed = computed_original.bind(null); export { ComputedProperty, computed }; diff --git a/packages/@ember/-internals/metal/lib/decorator-utils/-private/descriptor.ts b/packages/@ember/-internals/metal/lib/decorator-utils/-private/descriptor.ts new file mode 100644 index 00000000000..b5bd34975e2 --- /dev/null +++ b/packages/@ember/-internals/metal/lib/decorator-utils/-private/descriptor.ts @@ -0,0 +1,29 @@ +import { DEBUG } from '@glimmer/env'; + +import { assert } from '@ember/debug'; + +const DESCRIPTOR = '__DESCRIPTOR__'; + +function isCPGetter(getter: any) { + // Hack for descriptor traps, we want to be able to tell if the function + // is a descriptor trap before we call it at all + return getter !== null && typeof getter === 'function' && getter.toString().indexOf('CPGETTER_FUNCTION') !== -1; +} + +function isDescriptorTrap(possibleDesc: any) { + throw new Error('Cannot call `isDescriptorTrap` in production'); +} + +export function isComputedDescriptor(possibleDesc: any) { + return possibleDesc !== null && typeof possibleDesc === 'object' && possibleDesc.isDescriptor; +} + +export function computedDescriptorFor(obj: any, keyName: any) { + assert('Cannot call `descriptorFor` on null', obj !== null); + assert('Cannot call `descriptorFor` on undefined', obj !== undefined); + assert(`Cannot call \`descriptorFor\` on ${typeof obj}`, typeof obj === 'object' || typeof obj === 'function'); + + let { value: possibleDesc, get: possibleCPGetter } = Object.getOwnPropertyDescriptor(obj, keyName); + + return isComputedDescriptor(possibleDesc) ? possibleDesc : undefined; +} diff --git a/packages/@ember/-internals/metal/lib/decorator-utils/-private/modifier-meta.ts b/packages/@ember/-internals/metal/lib/decorator-utils/-private/modifier-meta.ts new file mode 100644 index 00000000000..01f6d368a1f --- /dev/null +++ b/packages/@ember/-internals/metal/lib/decorator-utils/-private/modifier-meta.ts @@ -0,0 +1,13 @@ +const MODIFIER_META_MAP = new WeakMap(); + +export function getModifierMeta(target: any) { + return MODIFIER_META_MAP.get(target); +} + +export function getOrCreateModifierMeta(target: any) { + if (!MODIFIER_META_MAP.has(target)) { + MODIFIER_META_MAP.set(target, {}); + } + + return MODIFIER_META_MAP.get(target); +} diff --git a/packages/@ember/-internals/metal/lib/decorator-utils/collapse-proto.ts b/packages/@ember/-internals/metal/lib/decorator-utils/collapse-proto.ts new file mode 100644 index 00000000000..46a0df7ad52 --- /dev/null +++ b/packages/@ember/-internals/metal/lib/decorator-utils/collapse-proto.ts @@ -0,0 +1,8 @@ +export default function collapseProto(target: any) { + // We must collapse the superclass prototype to make sure that the `actions` + // object will exist. Since collapsing doesn't generally happen until a class is + // instantiated, we have to do it manually. + if (typeof target.constructor.proto === 'function') { + target.constructor.proto(); + } +} diff --git a/packages/@ember/-internals/metal/lib/decorator-utils/computed.ts b/packages/@ember/-internals/metal/lib/decorator-utils/computed.ts new file mode 100644 index 00000000000..e3331f7ba64 --- /dev/null +++ b/packages/@ember/-internals/metal/lib/decorator-utils/computed.ts @@ -0,0 +1,80 @@ +// import { defineProperty } from '@ember/object'; +import { decoratorWithParams, decoratorWithRequiredParams, decorator } from './decorator'; +// import { HAS_NATIVE_COMPUTED_GETTERS } from 'ember-compatibility-helpers'; +// import { NEEDS_STAGE_1_DECORATORS } from 'ember-decorators-flags'; + +import { computedDescriptorFor, isComputedDescriptor } from './-private/descriptor'; +import { getModifierMeta, getOrCreateModifierMeta } from './-private/modifier-meta'; + +import { assert } from '@ember/debug'; + +export { computedDescriptorFor, getModifierMeta, getOrCreateModifierMeta }; + +/** + * A macro that receives a decorator function which returns a ComputedProperty, + * and defines that property using `Ember.defineProperty`. Conceptually, CPs + * are custom property descriptors that require Ember's intervention to apply + * correctly. In the future, we will use finishers to define the CPs rather than + * directly defining them in the decorator function. + * + * @param {Function} fn - decorator function + */ +function computedDecoratorInner(fn) { + return (desc, params = []) => { + // All computeds are methods + desc.kind = 'method'; + desc.placement = 'prototype'; + + desc.finisher = function initializeComputedProperty(target) { + let { prototype } = target; + let { key } = desc; + + assert(`ES6 property getters/setters only need to be decorated once, '${key}' was decorated on both the getter and the setter`, !computedDescriptorFor(prototype, key)); + + let computedDesc = fn(desc, params); + + assert(`computed decorators must return an instance of an Ember ComputedProperty descriptor, received ${computedDesc}`, isComputedDescriptor(computedDesc)); + + let modifierMeta = getModifierMeta(prototype); + + if (modifierMeta !== undefined && modifierMeta[key] !== undefined) { + computedDesc[modifierMeta[key]](); + } + + // if (!HAS_NATIVE_COMPUTED_GETTERS) { + // // Until recent versions of Ember, computed properties would be defined + // // by just setting them. We need to blow away any predefined properties + // // (getters/setters, etc.) to allow Ember.defineProperty to work correctly. + // Object.defineProperty(prototype, key, { + // configurable: true, + // writable: true, + // enumerable: true, + // value: undefined + // }); + // } + + // defineProperty(prototype, key, computedDesc); + Object.defineProperty(prototype, key, computedDesc); + + // if (NEEDS_STAGE_1_DECORATORS) { + // // There's currently no way to disable redefining the property when decorators + // // are run, so return the property descriptor we just assigned + // desc.descriptor = Object.getOwnPropertyDescriptor(prototype, key); + // } + + return target; + } + } +} + +export function computedDecorator(fn) { + return decorator(computedDecoratorInner(fn)); +} + +export function computedDecoratorWithParams(fn) { + return decoratorWithParams(computedDecoratorInner(fn)); +} + +export function computedDecoratorWithRequiredParams(fn, name) { + return decoratorWithRequiredParams(computedDecoratorInner(fn), name); +} \ No newline at end of file diff --git a/packages/@ember/-internals/metal/lib/decorator-utils/decorator.ts b/packages/@ember/-internals/metal/lib/decorator-utils/decorator.ts new file mode 100644 index 00000000000..ef8f717300b --- /dev/null +++ b/packages/@ember/-internals/metal/lib/decorator-utils/decorator.ts @@ -0,0 +1,174 @@ +import { assert } from '@ember/debug'; + +// import { NEEDS_STAGE_1_DECORATORS } from 'ember-decorators-flags'; + +function isDescriptor(possibleDesc) { + let isDescriptor = isStage2Descriptor(possibleDesc); + + // if (NEEDS_STAGE_1_DECORATORS) { + // isDescriptor = isDescriptor || isStage1Descriptor(possibleDesc); + // } + + return isDescriptor; +} + +// function isStage1Descriptor(possibleDesc) { +// if (possibleDesc.length === 3) { +// let [target, key, desc] = possibleDesc; + +// return typeof target === 'object' +// && target !== null +// && typeof key === 'string' +// && ( +// ( +// typeof desc === 'object' +// && desc !== null +// && 'enumerable' in desc +// && 'configurable' in desc +// ) +// || desc === undefined // TS compatibility +// ); +// } else if (possibleDesc.length === 1) { +// let [target] = possibleDesc; + +// return typeof target === 'function' && 'prototype' in target; +// } + +// return false; +// } + +function isStage2Descriptor(possibleDesc) { + return possibleDesc && possibleDesc.toString() === '[object Descriptor]'; +} + +function kindForDesc(desc) { + if ('value' in desc && desc.enumerable === true) { + return 'field'; + } else { + return 'method'; + } +} + +function placementForKind(kind) { + return kind === 'method' ? 'prototype' : 'own'; +} + +// function convertStage1ToStage2(desc) { +// if (desc.length === 3) { +// // Class element decorator +// let [, key, descriptor] = desc; + +// let kind = kindForDesc(desc); +// let placement = placementForKind(kind); + +// let initializer = descriptor !== undefined ? descriptor.initializer : undefined; + +// return { +// descriptor, +// key, +// kind, +// placement, +// initializer, +// toString: () => '[object Descriptor]', +// }; +// } else { +// // Class decorator +// return { +// kind: 'class', +// elements: [], +// }; +// } +// } + +export function decorator(fn) { + // if (NEEDS_STAGE_1_DECORATORS) { + // return function(...params) { + // if (isStage2Descriptor(params)) { + // let desc = params[0]; + + // return fn(desc); + // } else { + // let desc = convertStage1ToStage2(params); + + // fn(desc); + + // if (typeof desc.finisher === 'function') { + // // Finishers are supposed to run at the end of class finalization, + // // but we don't get that with stage 1 transforms. We have to be careful + // // to make sure that we aren't doing any operations which would change + // // due to timing. + // let [target] = params; + + // desc.finisher(target.prototype ? target : target.constructor); + // } + + // if (typeof desc.initializer === 'function') { + // // Babel 6 / the legacy decorator transform needs the initializer back + // // on the property descriptor/ In case the user has set a new + // // initializer on the member descriptor, we transfer it back to + // // original descriptor. + // desc.descriptor.initializer = desc.initializer; + // } + + // return desc.descriptor; + // } + // } + // } else { + return fn; + // } +} + +/** + * A macro that takes a decorator function and allows it to optionally + * receive parameters + * + * ```js + * let foo = decoratorWithParams((target, desc, key, params) => { + * console.log(params); + * }); + * + * class { + * @foo bar; // undefined + * @foo('bar') baz; // ['bar'] + * } + * ``` + * + * @param {Function} fn - decorator function + */ +export function decoratorWithParams(fn) { + return function(...params) { + // determine if user called as @computed('blah', 'blah') or @computed + if (isDescriptor(params)) { + return decorator(fn)(...params); + } else { + return decorator(desc => fn(desc, params)); + } + }; +} + +/** + * A macro that takes a decorator function and requires it to receive + * parameters: + * + * ```js + * let foo = decoratorWithRequiredParams((target, desc, key, params) => { + * console.log(params); + * }); + * + * class { + * @foo('bar') baz; // ['bar'] + * @foo bar; // Error + * } + * ``` + * + * @param {Function} fn - decorator function + */ +export function decoratorWithRequiredParams(fn, name) { + return function(...params) { + assert(`The @${name || fn.name} decorator requires parameters`, !isDescriptor(params) && params.length > 0); + + return decorator(desc => { + return fn(desc, params); + }); + } +} \ No newline at end of file diff --git a/packages/@ember/-internals/metal/lib/decorator-utils/index.ts b/packages/@ember/-internals/metal/lib/decorator-utils/index.ts new file mode 100644 index 00000000000..35aa176f02b --- /dev/null +++ b/packages/@ember/-internals/metal/lib/decorator-utils/index.ts @@ -0,0 +1,5 @@ +export { + computedDecorator, + computedDecoratorWithParams, + computedDecoratorWithRequiredParams +} from './computed'; diff --git a/packages/@ember/-internals/metal/tests/computed_test.js b/packages/@ember/-internals/metal/tests/computed_test.js index 7168be447d2..77a3483ab3e 100644 --- a/packages/@ember/-internals/metal/tests/computed_test.js +++ b/packages/@ember/-internals/metal/tests/computed_test.js @@ -988,3 +988,196 @@ moduleFor( } } ); + +function addComputedTo(klass, descriptor, dependents) { + let proto = klass.prototype; + + Object.defineProperty(proto, name, descriptor); + + let propertyDescriptor = Object.getOwnPropertyDescriptor(proto, name); + + if (dependents) { + return computed(dependents)(proto, name, propertyDescriptor); + } + + return computed(proto, name, propertyDescriptor); +} + +moduleFor( + 'computed - decorator - cacheable', + class extends AbstractTestCase { + beforeEach() { + class TestObj {} + count = 0; + let func = function() { + count++; + return 'bar ' + count; + }; + addComputedTo(TestObj, { + key: 'foo', + descriptor: { + configurable: false, + enumerable: true, + get: func, + set: func, + }, + }); + + obj = new TestObj(); + } + + afterEach() { + obj = count = null; + } + ['@test cacheable should cache'](assert) { + assert.equal(get(obj, 'foo'), 'bar 1', 'first get'); + assert.equal(get(obj, 'foo'), 'bar 1', 'second get'); + assert.equal(count, 1, 'should only invoke once'); + } + + ['@test modifying a cacheable property should update cache'](assert) { + assert.equal(get(obj, 'foo'), 'bar 1', 'first get'); + assert.equal(get(obj, 'foo'), 'bar 1', 'second get'); + + assert.equal(set(obj, 'foo', 'baz'), 'baz', 'setting'); + assert.equal(get(obj, 'foo'), 'bar 2', 'third get'); + assert.equal(count, 2, 'should not invoke again'); + } + + ['@test inherited property should not pick up cache'](assert) { + let objB = Object.create(obj); + + assert.equal(get(obj, 'foo'), 'bar 1', 'obj first get'); + assert.equal(get(objB, 'foo'), 'bar 2', 'objB first get'); + + assert.equal(get(obj, 'foo'), 'bar 1', 'obj second get'); + assert.equal(get(objB, 'foo'), 'bar 2', 'objB second get'); + + set(obj, 'foo', 'baz'); // modify A + assert.equal(get(obj, 'foo'), 'bar 3', 'obj third get'); + assert.equal(get(objB, 'foo'), 'bar 2', 'objB third get'); + } + + ['@test getCachedValueFor should return the cached value'](assert) { + assert.equal(getCachedValueFor(obj, 'foo'), undefined, 'should not yet be a cached value'); + + get(obj, 'foo'); + + assert.equal(getCachedValueFor(obj, 'foo'), 'bar 1', 'should retrieve cached value'); + } + + ['@test getCachedValueFor should return falsy cached values'](assert) { + defineProperty( + obj, + 'falsy', + computed(function() { + return false; + }) + ); + + assert.equal(getCachedValueFor(obj, 'falsy'), undefined, 'should not yet be a cached value'); + + get(obj, 'falsy'); + + assert.equal(getCachedValueFor(obj, 'falsy'), false, 'should retrieve cached value'); + } + + ['@test setting a cached computed property passes the old value as the third argument']( + assert + ) { + let obj = { + foo: 0, + }; + + let receivedOldValue; + + defineProperty( + obj, + 'plusOne', + computed({ + get() {}, + set(key, value, oldValue) { + receivedOldValue = oldValue; + return value; + }, + }).property('foo') + ); + + set(obj, 'plusOne', 1); + assert.strictEqual(receivedOldValue, undefined, 'oldValue should be undefined'); + + set(obj, 'plusOne', 2); + assert.strictEqual(receivedOldValue, 1, 'oldValue should be 1'); + + set(obj, 'plusOne', 3); + assert.strictEqual(receivedOldValue, 2, 'oldValue should be 2'); + } + } +); + +moduleFor( + 'computed - decorator', + class extends AbstractTestCase { + ['@test computed property asserts the presence of a getter'](assert) { + class TestObj {} + + assert.throws(() => { + addComputedTo(TestObj, { + key: 'nonGetter', + descriptor: { + configurable: false, + enumerable: true, + writeable: true, + value: () => true, // method + } + }); + }, /not a native accessor/); + } + + ['@test computed property works with a getter'](assert) { + class TestObj {} + + addComputedTo(TestObj, { + key: 'someGetter', + descriptor: { + configurable: false, + get: () => true, + } + }); + + let instance = new TestObj(); + assert.ok(instance.someGetter); + } + + ['@test computed property with dependent key and getter'](assert) { + class TestObj {} + + addComputedTo(TestObj, { + key: 'other', + descriptor: { + value: true, + writable: true, + } + }); + addComputedTo( + TestObj, + { + key: 'someGetter', + descriptor: { + configurable: false, + get: function() { + return `${this.other}`; + }, + } + }, + 'other' + ); + + let instance = new TestObj(); + assert.equal(instance.someGetter, 'true'); + + set(instance, 'other', false); + assert.equal(instance.someGetter, 'false'); + } + } +); diff --git a/yarn.lock b/yarn.lock index 7ad53b7ec13..fdd7e8ab24b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1362,6 +1362,11 @@ babel-plugin-syntax-async-functions@^6.8.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" integrity sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU= +babel-plugin-syntax-decorators@^6.1.18: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" + integrity sha1-MSVjtNvePMgGzuPkFszurd0RrAs= + babel-plugin-syntax-exponentiation-operator@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" @@ -1381,6 +1386,15 @@ babel-plugin-transform-async-to-generator@^6.22.0: babel-plugin-syntax-async-functions "^6.8.0" babel-runtime "^6.22.0" +babel-plugin-transform-decorators-legacy@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.5.tgz#0e492dffa0edd70529072887f8aa86d4dd8b40a1" + integrity sha512-jYHwjzRXRelYQ1uGm353zNzf3QmtdCfvJbuYTZ4gKveK7M9H1fs3a5AKdY1JUDl0z97E30ukORW1dzhWvsabtA== + dependencies: + babel-plugin-syntax-decorators "^6.1.18" + babel-runtime "^6.2.0" + babel-template "^6.3.0" + babel-plugin-transform-es2015-arrow-functions@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" @@ -1653,7 +1667,7 @@ babel-register@^6.26.0: mkdirp "^0.5.1" source-map-support "^0.4.15" -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: +babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= @@ -1661,7 +1675,7 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: core-js "^2.4.0" regenerator-runtime "^0.11.0" -babel-template@^6.24.1, babel-template@^6.26.0: +babel-template@^6.24.1, babel-template@^6.26.0, babel-template@^6.3.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=