diff --git a/addon-test-support/index.js b/addon-test-support/index.ts similarity index 71% rename from addon-test-support/index.js rename to addon-test-support/index.ts index 38a8fc1e..7a088c67 100644 --- a/addon-test-support/index.js +++ b/addon-test-support/index.ts @@ -1,9 +1,12 @@ import { getContext } from '@ember/test-helpers'; +import { Source } from '@orbit/data'; -export async function waitForSource(sourceOrSourceName) { +export async function waitForSource( + sourceOrSourceName: Source | string +): Promise { let source; if (typeof sourceOrSourceName === 'string') { - let { owner } = getContext(); + let { owner } = getContext() as any; source = owner.lookup(`data-source:${sourceOrSourceName}`); if (!source) { throw new Error( diff --git a/addon/-private/model-factory.ts b/addon/-private/model-factory.ts index f324a361..db0c3273 100644 --- a/addon/-private/model-factory.ts +++ b/addon/-private/model-factory.ts @@ -1,9 +1,12 @@ +import Orbit from '@orbit/core'; import { getOwner } from '@ember/application'; import { Dict } from '@orbit/utils'; import { RecordIdentity, cloneRecordIdentity } from '@orbit/records'; import Store from './store'; import Model, { ModelSettings } from './model'; +const { assert } = Orbit; + interface Factory { create(settings: ModelSettings): Model; } @@ -34,7 +37,16 @@ export default class ModelFactory { if (!modelFactory) { let owner = getOwner(this.#store); let orbitConfig = owner.lookup('ember-orbit:config'); - modelFactory = owner.factoryFor(`${orbitConfig.types.model}:${type}`); + + modelFactory = owner.factoryFor( + `${orbitConfig.types.model}:${type}` + ) as Factory; + + assert( + `An ember-orbit model for type ${type} has not been registered.`, + modelFactory !== undefined + ); + this.#modelFactoryMap[type] = modelFactory; } diff --git a/addon/-private/system/modules-of-type.ts b/addon/-private/system/modules-of-type.ts index 378f2bb1..e23c25b2 100644 --- a/addon/-private/system/modules-of-type.ts +++ b/addon/-private/system/modules-of-type.ts @@ -15,7 +15,7 @@ export default function (prefix: string, type: string): string[] { const matches = regex.exec(moduleName); if (matches && matches.length === 1) { const matchedName = moduleName.match(/[^\/]+\/?$/); - if (matchedName) { + if (matchedName?.[0]) { found.push(matchedName[0]); } } diff --git a/addon/-private/utils/normalize-record-properties.ts b/addon/-private/utils/normalize-record-properties.ts index cf0861f7..a8eae4e4 100644 --- a/addon/-private/utils/normalize-record-properties.ts +++ b/addon/-private/utils/normalize-record-properties.ts @@ -4,7 +4,8 @@ import { RecordRelationship, Record, ModelDefinition, - RecordIdentity + RecordIdentity, + RelationshipDefinition } from '@orbit/records'; import { deepSet, Dict } from '@orbit/utils'; @@ -30,10 +31,12 @@ function assignKeys( record: Record, properties: Dict ) { - const keys = modelDefinition.keys || {}; - for (let key of Object.keys(keys)) { - if (properties[key] !== undefined) { - deepSet(record, ['keys', key], properties[key]); + const keyDefs = modelDefinition.keys; + if (keyDefs) { + for (let key of Object.keys(keyDefs)) { + if (properties[key] !== undefined) { + deepSet(record, ['keys', key], properties[key]); + } } } } @@ -43,10 +46,12 @@ function assignAttributes( record: Record, properties: Dict ) { - const attributes = modelDefinition.attributes || {}; - for (let attribute of Object.keys(attributes)) { - if (properties[attribute] !== undefined) { - deepSet(record, ['attributes', attribute], properties[attribute]); + const attributeDefs = modelDefinition.attributes; + if (attributeDefs) { + for (let attribute of Object.keys(attributeDefs)) { + if (properties[attribute] !== undefined) { + deepSet(record, ['attributes', attribute], properties[attribute]); + } } } } @@ -56,23 +61,26 @@ function assignRelationships( record: Record, properties: Dict ) { - const relationships = modelDefinition.relationships || {}; - for (let relationship of Object.keys(relationships)) { - if (properties[relationship] !== undefined) { - let relationshipType = relationships[relationship].type as - | string - | string[]; - let relationshipProperties = properties[relationship] as - | RecordIdentity - | RecordIdentity[] - | string - | string[] - | null; - deepSet( - record, - ['relationships', relationship], - normalizeRelationship(relationshipType, relationshipProperties) - ); + const relationshipDefs = modelDefinition.relationships; + if (relationshipDefs) { + for (let relationship of Object.keys(relationshipDefs)) { + if (properties[relationship] !== undefined) { + let relationshipDef = relationshipDefs[ + relationship + ] as RelationshipDefinition; + let relationshipType = relationshipDef.type as string | string[]; + let relationshipProperties = properties[relationship] as + | RecordIdentity + | RecordIdentity[] + | string + | string[] + | null; + deepSet( + record, + ['relationships', relationship], + normalizeRelationship(relationshipType, relationshipProperties) + ); + } } } } diff --git a/addon/-private/utils/property-cache.ts b/addon/-private/utils/property-cache.ts index 9294cbeb..daefb9bc 100644 --- a/addon/-private/utils/property-cache.ts +++ b/addon/-private/utils/property-cache.ts @@ -44,90 +44,104 @@ export class PropertyCache { } export function notifyPropertyChange(record: Model, property: string) { - const cache = caches.get(record); - if (cache && cache[property]) { - cache[property].notifyPropertyChange(); - // TODO: there is an issue with glimmer cache and ember CP macros - // https://github.com/ember-polyfills/ember-cache-primitive-polyfill/issues/78 - // in order to fix it for now we are calling Ember.notifyPropertyChange(); - emberNotifyPropertyChange(record, property); - } + caches.get(record)?.[property]?.notifyPropertyChange(); + + // TODO: there is an issue with glimmer cache and ember CP macros + // https://github.com/ember-polyfills/ember-cache-primitive-polyfill/issues/78 + // in order to fix it for now we are calling Ember.notifyPropertyChange(); + emberNotifyPropertyChange(record, property); } export function getKeyCache( record: Model, property: string ): PropertyCache { - let cache = caches.get(record); + let recordCaches = caches.get(record); - if (!cache) { - cache = {}; - caches.set(record, cache); + if (recordCaches === undefined) { + recordCaches = {}; + caches.set(record, recordCaches); } - if (!cache[property]) { - cache[property] = new PropertyCache(() => record.getKey(property)); + + let propertyCache = recordCaches[property]; + + if (propertyCache === undefined) { + propertyCache = recordCaches[property] = new PropertyCache(() => + record.getKey(property) + ); } - return cache[property]; + return propertyCache; } export function getAttributeCache( record: Model, property: string ): PropertyCache { - let cache = caches.get(record); + let recordCaches = caches.get(record); - if (!cache) { - cache = {}; - caches.set(record, cache); + if (recordCaches === undefined) { + recordCaches = {}; + caches.set(record, recordCaches); } - if (!cache[property]) { - cache[property] = new PropertyCache(() => record.getAttribute(property)); + + let propertyCache = recordCaches[property]; + + if (propertyCache === undefined) { + propertyCache = recordCaches[property] = new PropertyCache(() => + record.getAttribute(property) + ); } - return cache[property]; + return propertyCache; } export function getHasOneCache( record: Model, property: string ): PropertyCache { - let cache = caches.get(record); + let recordCaches = caches.get(record); - if (!cache) { - cache = {}; - caches.set(record, cache); + if (recordCaches === undefined) { + recordCaches = {}; + caches.set(record, recordCaches); } - if (!cache[property]) { - cache[property] = new PropertyCache(() => + + let propertyCache = recordCaches[property]; + + if (propertyCache === undefined) { + propertyCache = recordCaches[property] = new PropertyCache(() => record.getRelatedRecord(property) ); } - return cache[property]; + return propertyCache; } export function getHasManyCache( record: Model, property: string ): PropertyCache { - let cache = caches.get(record); + let recordCaches = caches.get(record); - if (!cache) { - cache = {}; - caches.set(record, cache); + if (recordCaches === undefined) { + recordCaches = {}; + caches.set(record, recordCaches); } - if (!cache[property]) { - cache[property] = new PropertyCache(() => + + let propertyCache = recordCaches[property]; + + if (propertyCache === undefined) { + propertyCache = recordCaches[property] = new PropertyCache(() => addLegacyMutationMethods( record, property, - record.getRelatedRecords(property) || [] + record.getRelatedRecords(property) ?? [] ) ); } - return cache[property]; + return propertyCache; } function addLegacyMutationMethods( diff --git a/app/initializers/ember-orbit-config.js b/app/initializers/ember-orbit-config.ts similarity index 100% rename from app/initializers/ember-orbit-config.js rename to app/initializers/ember-orbit-config.ts diff --git a/app/initializers/ember-orbit-services.js b/app/initializers/ember-orbit-services.ts similarity index 100% rename from app/initializers/ember-orbit-services.js rename to app/initializers/ember-orbit-services.ts diff --git a/package.json b/package.json index 3150b312..51f121d3 100644 --- a/package.json +++ b/package.json @@ -53,9 +53,10 @@ "@ember/test-helpers": "^2.2.0", "@glimmer/component": "^1.0.3", "@types/ember": "^3.16.0", - "@types/ember-qunit": "^3.4.11", - "@types/ember-testing-helpers": "^0.0.4", - "@types/qunit": "^2.9.4", + "@types/ember-qunit": "^3.4.13", + "@types/ember__test": "^3.16.1", + "@types/ember__test-helpers": "^1.7.3", + "@types/qunit": "^2.11.1", "@types/rsvp": "^4.0.3", "@typescript-eslint/parser": "^4.2.0", "babel-eslint": "^10.1.0", @@ -90,7 +91,7 @@ "prettier": "^2.2.1", "qunit": "^2.14.0", "qunit-dom": "^1.6.0", - "typescript": "^3.9.7" + "typescript": "^4.1.5" }, "engines": { "node": "10.* || >= 12" @@ -102,7 +103,7 @@ "configPath": "tests/dummy/config" }, "peerDependencies": { - "ember-source": "~3.16.0" + "ember-source": "^3.16.0" }, "volta": { "node": "10.23.0", diff --git a/tsconfig.json b/tsconfig.json index 05e19cfa..793235b8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,12 @@ "inlineSourceMap": true, "inlineSources": true, "strict": true, + "alwaysStrict": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noUncheckedIndexedAccess": true, "baseUrl": ".", "paths": { "dummy/tests/*": ["tests/*"], @@ -23,7 +29,7 @@ "include": [ "app/**/*", "addon/**/*", - "tests/**/*", + "tests/**/*.ts", "types/**/*", "test-support/**/*", "addon-test-support/**/*" diff --git a/yarn.lock b/yarn.lock index 0752ccd1..83549be7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1388,7 +1388,7 @@ dependencies: "@types/node" "*" -"@types/ember-qunit@^3.4.11": +"@types/ember-qunit@^3.4.13": version "3.4.13" resolved "https://registry.yarnpkg.com/@types/ember-qunit/-/ember-qunit-3.4.13.tgz#58d359b62a6f6e7039d546f4c08b83ad269f7498" integrity sha512-T7Lq8ppyxueneQgXeo8VsV4OGMeVTMYuzFOc5yUAGZIkUDSi907CdVkCCO4zUqRGl9FkkgCQUxwPiZv+reerGQ== @@ -1407,14 +1407,6 @@ "@types/jquery" "*" "@types/rsvp" "*" -"@types/ember-testing-helpers@^0.0.4": - version "0.0.4" - resolved "https://registry.yarnpkg.com/@types/ember-testing-helpers/-/ember-testing-helpers-0.0.4.tgz#d305b418d477c6f84fcd4dcb851a3efadbc4a2bd" - integrity sha512-6EEY+kk4+HsKMzLkzZp0UU7TzUG1EB2mPyORrQXcudjJ0M7k67Z9cCBDn7kupDcu4NVgtG7HNRZTZgBljOjxoA== - dependencies: - "@types/jquery" "*" - "@types/rsvp" "*" - "@types/ember@*", "@types/ember@^3.16.0": version "3.16.3" resolved "https://registry.yarnpkg.com/@types/ember/-/ember-3.16.3.tgz#e2e9c24e56d8161ffcdbfa16d6a1e7e032bb557b" @@ -1546,7 +1538,17 @@ resolved "https://registry.yarnpkg.com/@types/ember__template/-/ember__template-3.16.1.tgz#30d7f50a49b190934db0f5a56dd76ad86c21efc6" integrity sha512-APQINizzizl2LHWGMFBCanRjKZQsdzqn7b+us17zbNhnx/R0IZAJq901x/i7eozCRwxsDKmGzNABSCIu6uc1Tg== -"@types/ember__test@*": +"@types/ember__test-helpers@^1.7.3": + version "1.7.3" + resolved "https://registry.yarnpkg.com/@types/ember__test-helpers/-/ember__test-helpers-1.7.3.tgz#a3553f5c3318032dcdf03b7b511254b9edf930eb" + integrity sha512-5LGWJHkdbKYrrA/l4QFeu2WNE0iaz5lBjc980VLzOqyfy2/r4CbZbuHtg+oL5jYQDnB2Udpz9b3EwwM3lg7hsA== + dependencies: + "@types/ember" "*" + "@types/ember__application" "*" + "@types/ember__error" "*" + "@types/htmlbars-inline-precompile" "*" + +"@types/ember__test@*", "@types/ember__test@^3.16.1": version "3.16.1" resolved "https://registry.yarnpkg.com/@types/ember__test/-/ember__test-3.16.1.tgz#8407e42b9835a13ef0c6ef7a7ce3aa3d7ebcb7ed" integrity sha512-0ICnkM4BDwOKhqmLQRpfvNuZlb6QOqE+FhP5fPaWXWy7bgcL9CY7kMRc7N+wZQbTvbSKqgEdfbvjd0bJsIrz5w== @@ -1636,7 +1638,7 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.5.tgz#434711bdd49eb5ee69d90c1d67c354a9a8ecb18b" integrity sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ== -"@types/qunit@*", "@types/qunit@^2.9.4": +"@types/qunit@*", "@types/qunit@^2.11.1": version "2.11.1" resolved "https://registry.yarnpkg.com/@types/qunit/-/qunit-2.11.1.tgz#3496d430d2bb0fa4761f00a27511f46020c6b410" integrity sha512-vcM5+9O8LZuu5DYseaV4J7ehkYrhkv+aMIuxnF/OqMYlVEdv+odpCH1/5OVztiqxbCqTpQKWuELkMvG7OPycUQ== @@ -11167,10 +11169,10 @@ typescript-memoize@^1.0.0-alpha.3: resolved "https://registry.yarnpkg.com/typescript-memoize/-/typescript-memoize-1.0.0-alpha.4.tgz#fd97ab63807c3392af5d0ac5f4754254a4fcd634" integrity sha512-woA2UUWSvx8ugkEjPN8DMuNjukBp8NQeLmz+LRXbEsQIvhLR8LSlD+8Qxdk7NmgE8xeJabJdU8zSrO4ozijGjg== -typescript@^3.9.7: - version "3.9.9" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674" - integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w== +typescript@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72" + integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6"