From d10bbc0cc35a8e4b38ac6490c0d7df6118f2861a Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Wed, 21 Aug 2019 17:16:45 -0700 Subject: [PATCH] [FEAT serializers] clean up packages organization (#6086) --- packages/-ember-data/addon/setup-container.js | 57 +++------ .../-ember-data/app/serializers/-default.ts | 1 + .../-ember-data/app/serializers/-json-api.ts | 1 + packages/-ember-data/app/serializers/-rest.ts | 1 + .../-ember-data/app/transforms/boolean.ts | 1 + packages/-ember-data/app/transforms/date.ts | 1 + packages/-ember-data/app/transforms/number.ts | 1 + packages/-ember-data/app/transforms/string.ts | 1 + packages/-ember-data/tests/helpers/store.js | 11 ++ .../integration/store/serializer-for-test.js | 14 ++- .../tests/unit/store/adapter-interop-test.js | 11 +- packages/adapter/types/require/index.d.ts | 1 + packages/serializer/tsconfig.json | 16 +-- .../store/addon/-private/system/core-store.ts | 111 +++++++++++++++++- .../-private/system/store/serializers.js | 8 -- packages/store/index.js | 1 + packages/store/types/require/index.d.ts | 3 + 17 files changed, 176 insertions(+), 64 deletions(-) create mode 100644 packages/-ember-data/app/serializers/-default.ts create mode 100644 packages/-ember-data/app/serializers/-json-api.ts create mode 100644 packages/-ember-data/app/serializers/-rest.ts create mode 100644 packages/-ember-data/app/transforms/boolean.ts create mode 100644 packages/-ember-data/app/transforms/date.ts create mode 100644 packages/-ember-data/app/transforms/number.ts create mode 100644 packages/-ember-data/app/transforms/string.ts create mode 100644 packages/adapter/types/require/index.d.ts create mode 100644 packages/store/types/require/index.d.ts diff --git a/packages/-ember-data/addon/setup-container.js b/packages/-ember-data/addon/setup-container.js index 5d26a1d3cf8..53598665488 100644 --- a/packages/-ember-data/addon/setup-container.js +++ b/packages/-ember-data/addon/setup-container.js @@ -1,22 +1,19 @@ import { DebugAdapter } from './-private'; import Store from '@ember-data/store'; -import JSONAPISerializer from '@ember-data/serializer/json-api'; -import JSONSerializer from '@ember-data/serializer/json'; -import RESTSerializer from '@ember-data/serializer/rest'; import JSONAPIAdapter from '@ember-data/adapter/json-api'; -import { BooleanTransform, DateTransform, NumberTransform, StringTransform } from '@ember-data/serializer/-private'; - -function has(applicationOrRegistry, fullName) { - if (applicationOrRegistry.has) { - // < 2.1.0 - return applicationOrRegistry.has(fullName); - } else { - // 2.1.0+ - return applicationOrRegistry.hasRegistration(fullName); +function hasRegistration(application, registrationName) { + // fallback our ember-data tests necessary + // until we kill-off setupStore + // see https://github.com/emberjs/data/issues/6357 + // or @ember/test-helpers kills off it's + // legacy support that calls our initializer with registry + // instead of application + if (typeof application.hasRegistration !== 'function') { + return application.has(registrationName); } + return application.hasRegistration(registrationName); } - /* Configures a registry for use with an Ember-Data store. Accepts an optional namespace argument. @@ -24,18 +21,17 @@ function has(applicationOrRegistry, fullName) { @method initializeStore @param {Ember.Registry} registry */ -function initializeStore(registry) { - let registerOptionsForType = registry.registerOptionsForType || registry.optionsForType; - registerOptionsForType.call(registry, 'serializer', { singleton: false }); - registerOptionsForType.call(registry, 'adapter', { singleton: false }); - registry.register('serializer:-default', JSONSerializer); - registry.register('serializer:-rest', RESTSerializer); +function initializeStore(application) { + // we can just use registerOptionsForType when setupStore is killed + // see https://github.com/emberjs/data/issues/6357 + let registerOptionsForType = application.registerOptionsForType || application.optionsForType; + registerOptionsForType.call(application, 'serializer', { singleton: false }); + registerOptionsForType.call(application, 'adapter', { singleton: false }); - registry.register('adapter:-json-api', JSONAPIAdapter); - registry.register('serializer:-json-api', JSONAPISerializer); + application.register('adapter:-json-api', JSONAPIAdapter); - if (!has(registry, 'service:store')) { - registry.register('service:store', Store); + if (!hasRegistration(application, 'service:store')) { + application.register('service:store', Store); } } @@ -66,23 +62,8 @@ function initializeStoreInjections(registry) { inject.call(registry, 'data-adapter', 'store', 'service:store'); } -/* - Configures a registry for use with Ember-Data - transforms. - - @method initializeTransforms - @param {Ember.Registry} registry - */ -function initializeTransforms(registry) { - registry.register('transform:boolean', BooleanTransform); - registry.register('transform:date', DateTransform); - registry.register('transform:number', NumberTransform); - registry.register('transform:string', StringTransform); -} - export default function setupContainer(application) { initializeDataAdapter(application); - initializeTransforms(application); initializeStoreInjections(application); initializeStore(application); } diff --git a/packages/-ember-data/app/serializers/-default.ts b/packages/-ember-data/app/serializers/-default.ts new file mode 100644 index 00000000000..d617bfb1824 --- /dev/null +++ b/packages/-ember-data/app/serializers/-default.ts @@ -0,0 +1 @@ +export { default } from '@ember-data/serializer/json'; diff --git a/packages/-ember-data/app/serializers/-json-api.ts b/packages/-ember-data/app/serializers/-json-api.ts new file mode 100644 index 00000000000..59723c5ab2a --- /dev/null +++ b/packages/-ember-data/app/serializers/-json-api.ts @@ -0,0 +1 @@ +export { default } from '@ember-data/serializer/json-api'; diff --git a/packages/-ember-data/app/serializers/-rest.ts b/packages/-ember-data/app/serializers/-rest.ts new file mode 100644 index 00000000000..d6878ba3c3e --- /dev/null +++ b/packages/-ember-data/app/serializers/-rest.ts @@ -0,0 +1 @@ +export { default } from '@ember-data/serializer/rest'; diff --git a/packages/-ember-data/app/transforms/boolean.ts b/packages/-ember-data/app/transforms/boolean.ts new file mode 100644 index 00000000000..f32800a7157 --- /dev/null +++ b/packages/-ember-data/app/transforms/boolean.ts @@ -0,0 +1 @@ +export { BooleanTransform as default } from '@ember-data/serializer/-private'; diff --git a/packages/-ember-data/app/transforms/date.ts b/packages/-ember-data/app/transforms/date.ts new file mode 100644 index 00000000000..d5a3eff02da --- /dev/null +++ b/packages/-ember-data/app/transforms/date.ts @@ -0,0 +1 @@ +export { DateTransform as default } from '@ember-data/serializer/-private'; diff --git a/packages/-ember-data/app/transforms/number.ts b/packages/-ember-data/app/transforms/number.ts new file mode 100644 index 00000000000..dedc67a0ed6 --- /dev/null +++ b/packages/-ember-data/app/transforms/number.ts @@ -0,0 +1 @@ +export { NumberTransform as default } from '@ember-data/serializer/-private'; diff --git a/packages/-ember-data/app/transforms/string.ts b/packages/-ember-data/app/transforms/string.ts new file mode 100644 index 00000000000..974d19693c9 --- /dev/null +++ b/packages/-ember-data/app/transforms/string.ts @@ -0,0 +1 @@ +export { StringTransform as default } from '@ember-data/serializer/-private'; diff --git a/packages/-ember-data/tests/helpers/store.js b/packages/-ember-data/tests/helpers/store.js index 872da9f4882..78e3e79da82 100644 --- a/packages/-ember-data/tests/helpers/store.js +++ b/packages/-ember-data/tests/helpers/store.js @@ -8,6 +8,7 @@ import JSONAPISerializer from '@ember-data/serializer/json-api'; import RESTSerializer from '@ember-data/serializer/rest'; import config from '../../config/environment'; import Resolver from '../../resolver'; +import { StringTransform, DateTransform, NumberTransform, BooleanTransform } from '@ember-data/serializer/-private'; const { _RegistryProxyMixin, _ContainerProxyMixin, Registry } = Ember; @@ -68,6 +69,12 @@ export default function setupStore(options) { registry.register('model:' + dasherize(prop), options[prop]); } + // avoid the deprecation for auto-registration of transforms for our helper + registry.register('transform:string', StringTransform); + registry.register('transform:date', DateTransform); + registry.register('transform:number', NumberTransform); + registry.register('transform:boolean', BooleanTransform); + registry.optionsForType('serializer', { singleton: false }); registry.optionsForType('adapter', { singleton: false }); @@ -86,6 +93,10 @@ export default function setupStore(options) { env.registry.register('serializer:application', options.serializer); env.serializer = store.serializerFor('application'); } else { + // avoid deprecations for -json-api serializer in our tests + // uncomment to find locations to refactor to explicit registration + owner.register('serializer:-json-api', JSONAPISerializer); + // Many tests rely on falling back to this serializer // they should refactor to register this as the application serializer owner.register('serializer:-default', JSONAPISerializer); diff --git a/packages/-ember-data/tests/integration/store/serializer-for-test.js b/packages/-ember-data/tests/integration/store/serializer-for-test.js index 58e01dde207..dbcb6f2b806 100644 --- a/packages/-ember-data/tests/integration/store/serializer-for-test.js +++ b/packages/-ember-data/tests/integration/store/serializer-for-test.js @@ -42,10 +42,16 @@ module('integration/store - serializerFor', function(hooks) { let { owner } = this; /* serializer:-default is the "last chance" fallback and is - registered automatically as the json-api serializer. - unregistering it will cause serializerFor to return `undefined`. + the json-api serializer which is re-exported as app/serializers/-default. + here we override to ensure serializerFor will return `undefined`. */ - owner.unregister('serializer:-default'); + const lookup = owner.lookup; + owner.lookup = registrationName => { + if (registrationName === 'serializer:-default') { + return undefined; + } + return lookup.call(owner, registrationName); + }; /* we fallback to -json-api adapter by default when no other adapter is present. This adapter specifies a defaultSerializer. We register our own to ensure @@ -64,7 +70,7 @@ module('integration/store - serializerFor', function(hooks) { assert.expectAssertion(() => { store.serializerFor('person'); - }, /No serializer was found for 'person' and no 'application', Adapter\.defaultSerializer, or '-default' serializer were found as fallbacks\./); + }, /Assertion Failed: No serializer was found for 'person' and no 'application' serializer was found as a fallback/); }); test('we find and instantiate the application serializer', async function(assert) { diff --git a/packages/-ember-data/tests/unit/store/adapter-interop-test.js b/packages/-ember-data/tests/unit/store/adapter-interop-test.js index 5d98824d859..c7f311a7aec 100644 --- a/packages/-ember-data/tests/unit/store/adapter-interop-test.js +++ b/packages/-ember-data/tests/unit/store/adapter-interop-test.js @@ -7,12 +7,14 @@ import setupStore from 'dummy/tests/helpers/store'; import testInDebug from 'dummy/tests/helpers/test-in-debug'; import { module, test } from 'qunit'; - import DS from 'ember-data'; +import { setupTest } from 'ember-qunit'; let TestAdapter, store; module('unit/store/adapter-interop - DS.Store working with a DS.Adapter', function(hooks) { + setupTest(hooks); + hooks.beforeEach(function() { TestAdapter = DS.Adapter.extend(); }); @@ -43,9 +45,14 @@ module('unit/store/adapter-interop - DS.Store working with a DS.Adapter', functi testInDebug('Adapter can not be set as an instance', function(assert) { assert.expect(1); - store = DS.Store.create({ + const BadStore = DS.Store.extend({ adapter: DS.Adapter.create(), }); + const { owner } = this; + + owner.unregister('service:store'); + owner.register('service:store', BadStore); + const store = owner.lookup('service:store'); assert.expectAssertion(() => store.get('defaultAdapter')); }); diff --git a/packages/adapter/types/require/index.d.ts b/packages/adapter/types/require/index.d.ts new file mode 100644 index 00000000000..604a4399b44 --- /dev/null +++ b/packages/adapter/types/require/index.d.ts @@ -0,0 +1 @@ +export default function(moduleName: string): any; diff --git a/packages/serializer/tsconfig.json b/packages/serializer/tsconfig.json index d426199a9de..cd1e2d61179 100644 --- a/packages/serializer/tsconfig.json +++ b/packages/serializer/tsconfig.json @@ -19,20 +19,16 @@ "dummy/*": ["tests/dummy/app/*", "app/*"], "@ember-data/serializer": ["addon"], "@ember-data/serializer/*": ["addon/*"], + "@ember-data/store": ["../store/addon"], + "@ember-data/store/*": ["../store/addon/*"], + "@ember-data/canary-features": ["../canary-features/addon"], + "@ember-data/adapter": ["../adapter/addon"], + "@ember-data/adapter/*": ["../adapter/addon/*"], "@ember-data/serializer/test-support": ["addon-test-support"], "@ember-data/serializer/test-support/*": ["addon-test-support/*"], - "ember-data": ["../-ember-data/addon"], - "ember-data/*": ["../-ember-data/addon/*"], "*": ["types/*"] } }, - "include": [ - "app/**/*", - "addon/**/*", - "tests/**/*", - "types/**/*", - "test-support/**/*", - "addon-test-support/**/*" - ], + "include": ["app/**/*", "addon/**/*", "tests/**/*", "types/**/*", "test-support/**/*", "addon-test-support/**/*"], "exclude": ["node_modules"] } diff --git a/packages/store/addon/-private/system/core-store.ts b/packages/store/addon/-private/system/core-store.ts index 298437d1593..e922acb6a75 100644 --- a/packages/store/addon/-private/system/core-store.ts +++ b/packages/store/addon/-private/system/core-store.ts @@ -14,6 +14,7 @@ import { default as RSVP, all, resolve, Promise, defer } from 'rsvp'; import Service from '@ember/service'; import { typeOf, isPresent, isNone } from '@ember/utils'; +import require, { has } from 'require'; import Ember from 'ember'; import { InvalidError } from '@ember-data/adapter/error'; import { assert, warn, inspect } from '@ember/debug'; @@ -98,6 +99,21 @@ type PendingSaveItem = { let globalClientIdCounter = 1; +const HAS_SERIALIZER_PACKAGE = has('@ember-data/serializer'); + +function deprecateTestRegistration(factoryType: 'serializer', factoryName: '-json-api' | '-rest' | '-default'): void; +// TODO add adapter here and deprecate those registrations as well after refactoring them to re-exports +function deprecateTestRegistration(factoryType: 'serializer', factoryName: '-json-api' | '-rest' | '-default'): void { + deprecate( + `You looked up the ${factoryType} "${factoryName}" but it was not found. Likely this means you are using a legacy ember-qunit moduleFor helper. Add "needs: ['${factoryType}:${factoryName}']", "integration: true", or refactor to modern syntax to resolve this deprecation.`, + false, + { + id: 'ember-data:-legacy-test-registrations', + until: '3.17', + } + ); +} + // Implementors Note: // // The variables in this file are consistently named according to the following @@ -285,6 +301,46 @@ abstract class CoreStore extends Service { } if (DEBUG) { + if (HAS_SERIALIZER_PACKAGE) { + // support for legacy moduleFor style unit tests + // that did not include transforms in "needs" + // or which were not set to integration:true + // that were relying on ember-test-helpers + // doing an auto-registration of the transform + // or us doing one + const Mapping = { + date: 'DateTransform', + boolean: 'BooleanTransform', + number: 'NumberTransform', + string: 'StringTransform', + }; + let shouldWarn = false; + + Object.keys(Mapping).forEach((attributeType: keyof typeof Mapping) => { + const transform = getOwner(this).lookup(`transform:${attributeType}`); + + if (!transform) { + // we don't deprecate this because the moduleFor style tests with the closed + // resolver will be deprecated on their own. When that deprecation completes + // we can drop this. + const Transform = require(`@ember-data/serializer/-private`)[Mapping[attributeType]]; + getOwner(this).register(`transform:${attributeType}`, Transform); + shouldWarn = true; + } + }); + + if (shouldWarn) { + deprecate( + `You are relying on the automatic registration of the transforms "date", "number", "boolean", and "string". Likely this means you are using a legacy ember-qunit moduleFor helper. Add "needs: ['transform:date', 'transform:boolean', 'transform:number', 'transform:string']", "integration: true", or refactor to modern syntax to resolve this deprecation.`, + false, + { + id: 'ember-data:-legacy-test-registrations', + until: '3.17', + } + ); + } + } + this.shouldAssertMethodCallsOnDestroyedStore = this.shouldAssertMethodCallsOnDestroyedStore || false; if (this.shouldTrackAsyncRequests === undefined) { this.shouldTrackAsyncRequests = false; @@ -3237,6 +3293,27 @@ abstract class CoreStore extends Service { let owner = getOwner(this); serializer = owner.lookup(`serializer:${normalizedModelName}`); + + // in production this is handled by the re-export + if (DEBUG && HAS_SERIALIZER_PACKAGE && serializer === undefined) { + if (normalizedModelName === '-json-api') { + const Serializer = require('@ember-data/serializer/json-api').default; + owner.register(`serializer:-json-api`, Serializer); + serializer = owner.lookup(`serializer:-json-api`); + deprecateTestRegistration('serializer', '-json-api'); + } else if (normalizedModelName === '-rest') { + const Serializer = require('@ember-data/serializer/rest').default; + owner.register(`serializer:-rest`, Serializer); + serializer = owner.lookup(`serializer:-rest`); + deprecateTestRegistration('serializer', '-rest'); + } else if (normalizedModelName === '-default') { + const Serializer = require('@ember-data/serializer/json').default; + owner.register(`serializer:-default`, Serializer); + serializer = owner.lookup(`serializer:-default`); + serializer && deprecateTestRegistration('serializer', '-default'); + } + } + if (serializer !== undefined) { set(serializer, 'store', this); _serializerCache[normalizedModelName] = serializer; @@ -3259,6 +3336,27 @@ abstract class CoreStore extends Service { serializer = serializerName ? _serializerCache[serializerName] || owner.lookup(`serializer:${serializerName}`) : undefined; + + // in production this is handled by the re-export + if (DEBUG && HAS_SERIALIZER_PACKAGE && serializer === undefined) { + if (serializerName === '-json-api') { + const Serializer = require('@ember-data/serializer/json-api').default; + owner.register(`serializer:-json-api`, Serializer); + serializer = owner.lookup(`serializer:-json-api`); + deprecateTestRegistration('serializer', '-json-api'); + } else if (serializerName === '-rest') { + const Serializer = require('@ember-data/serializer/rest').default; + owner.register(`serializer:-rest`, Serializer); + serializer = owner.lookup(`serializer:-rest`); + deprecateTestRegistration('serializer', '-rest'); + } else if (serializerName === '-default') { + const Serializer = require('@ember-data/serializer/json').default; + owner.register(`serializer:-default`, Serializer); + serializer = owner.lookup(`serializer:-default`); + serializer && deprecateTestRegistration('serializer', '-default'); + } + } + if (serializer !== undefined) { set(serializer, 'store', this); _serializerCache[normalizedModelName] = serializer; @@ -3267,12 +3365,21 @@ abstract class CoreStore extends Service { } // final fallback, no model specific serializer, no application serializer, no - // `serializer` property on store: use json-api serializer + // `serializer` property on store: use JSON serializer serializer = _serializerCache['-default'] || owner.lookup('serializer:-default'); + if (DEBUG && HAS_SERIALIZER_PACKAGE && serializer === undefined) { + const JSONSerializer = require('@ember-data/serializer/json').default; + owner.register('serializer:-default', JSONSerializer); + serializer = owner.lookup('serializer:-default'); + + serializer && deprecateTestRegistration('serializer', '-default'); + } + assert( - `No serializer was found for '${modelName}' and no 'application', Adapter.defaultSerializer, or '-default' serializer were found as fallbacks.`, + `No serializer was found for '${modelName}' and no 'application' serializer was found as a fallback`, serializer !== undefined ); + set(serializer, 'store', this); _serializerCache[normalizedModelName] = serializer; _serializerCache['-default'] = serializer; diff --git a/packages/store/addon/-private/system/store/serializers.js b/packages/store/addon/-private/system/store/serializers.js index 806af591224..d6269870e81 100644 --- a/packages/store/addon/-private/system/store/serializers.js +++ b/packages/store/addon/-private/system/store/serializers.js @@ -9,13 +9,5 @@ export function serializerForAdapter(store, adapter, modelName) { serializer = store.serializerFor(modelName); } - if (serializer === null || serializer === undefined) { - serializer = { - extract(store, type, payload) { - return payload; - }, - }; - } - return serializer; } diff --git a/packages/store/index.js b/packages/store/index.js index f61aa24a496..63ac9ff5a7a 100644 --- a/packages/store/index.js +++ b/packages/store/index.js @@ -13,6 +13,7 @@ module.exports = Object.assign(addonBaseConfig, { 'ember-inflector', '@ember/ordered-set', 'ember-data/-debug', + 'require', ]; }, }); diff --git a/packages/store/types/require/index.d.ts b/packages/store/types/require/index.d.ts new file mode 100644 index 00000000000..a2082859ad6 --- /dev/null +++ b/packages/store/types/require/index.d.ts @@ -0,0 +1,3 @@ +export default function(moduleName: string): any; + +export function has(moduleName: string): boolean;