Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"test:watch": "run-s clean:* karma:watch"
},
"dependencies": {
"assert": "^1.4.1",
"lodash": "^4.17.0",
"tslib": "^1.9.1"
},
Expand All @@ -29,7 +28,6 @@
"@types/enzyme-adapter-react-16": "1.0.3",
"@types/jasmine": "2.8.8",
"@types/lodash": "4.14.116",
"@types/node": "10.9.4",
"@types/react": "16.4.14",
"@types/react-dom": "16.0.7",
"enzyme": "3.6.0",
Expand Down
46 changes: 20 additions & 26 deletions src/AutoSubscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,10 @@
// super.render, the descriptor's logic only applies until the end of that method, not the end of yours. This is why that functionality is
// exposes as a function instead of a decorator.

import * as assert from 'assert';

import * as _ from './lodashMini';
import * as Decorator from './Decorator';
import Options from './Options';
import { assert } from './utils';
import { StoreBase } from './StoreBase';

type MetadataIndex = {
Expand Down Expand Up @@ -161,10 +160,9 @@ export function enableAutoSubscribe(handler: AutoSubscribeHandler): MethodDecora
return <T>(target: InstanceTarget, propertyKey: string|symbol, descriptor: TypedPropertyDescriptor<T>) => {
// Note: T might have other properties (e.g. T = { (): void; bar: number; }). We don't support that and need a cast/assert.
const existingMethod = <Function><any>descriptor.value;
assert.ok(_.isFunction(existingMethod), 'Can only use @enableAutoSubscribe on methods');
assert(_.isFunction(existingMethod), 'Can only use @enableAutoSubscribe on methods');

descriptor.value = <T><any>enableAutoSubscribeWrapper(handler, existingMethod, undefined);

return descriptor;
};
}
Expand Down Expand Up @@ -204,9 +202,7 @@ export const AutoSubscribeStore: ClassDecorator = <TFunction extends Function>(f
if (_.isFunction(target[property]) && property !== 'constructor') {
const metaForMethod = target.__resubMetadata[property];
if (!metaForMethod || !metaForMethod.hasAutoSubscribeDecorator) {
Decorator.decorate([
warnIfAutoSubscribeEnabled
], target, property, null);
Decorator.decorate([warnIfAutoSubscribeEnabled], target, property, null);
}
}
});
Expand All @@ -228,12 +224,11 @@ function makeAutoSubscribeDecorator(shallow = false, defaultKeyValues: string[])
// Save the method being decorated. Note this might not be the original method if already decorated.
// Note: T might have other properties (e.g. T = { (): void; bar: number; }). We don't support that and need a cast/assert.
const existingMethod = <Function><any>descriptor.value;
assert.ok(_.isFunction(existingMethod), 'Can only use @autoSubscribe on methods');
assert(_.isFunction(existingMethod), 'Can only use @autoSubscribe on methods');

// Note: we need to be given 'this', so cannot use '=>' syntax.
descriptor.value = <T><any>function AutoSubscribe(this: any, ...args: any[]) {
assert.ok(targetWithMetadata.__resubMetadata.__decorated,
'Missing @AutoSubscribeStore class decorator: "' + methodNameString + '"');
assert(targetWithMetadata.__resubMetadata.__decorated, `Missing @AutoSubscribeStore class decorator: "${ methodNameString }"`);

// Just call the method if no handler is setup.
const scopedHandleWrapper = handlerWrapper;
Expand All @@ -243,8 +238,9 @@ function makeAutoSubscribeDecorator(shallow = false, defaultKeyValues: string[])

// If this is forbidding auto-subscribe then do not go through the auto-subscribe path below.
if (scopedHandleWrapper.useAutoSubscriptions === AutoOptions.Forbid) {
assert.ok(false, 'Only Store methods WITHOUT the @autoSubscribe decorator can be called right now (e.g. in render): "'
+ methodNameString + '"');
assert(false, `Only Store methods WITHOUT the ` +
`@autoSubscribe decorator can be called right now (e.g. in render): "${ methodNameString }"`);

return existingMethod.apply(this, args);
}

Expand All @@ -261,10 +257,11 @@ function makeAutoSubscribeDecorator(shallow = false, defaultKeyValues: string[])
keyArg = keyArg.toString();
}

assert.ok(keyArg, '@key parameter must be given a non-empty string or number: "' + methodNameString + '"@'
+ metaForMethod.index + ' was given ' + JSON.stringify(keyArg));
assert.ok(_.isString(keyArg), '@key parameter must be given a string or number: "' + methodNameString + '"@'
+ metaForMethod.index);
assert(keyArg, `@key parameter must be given a non-empty string or number: ` +
`"${ methodNameString }"@${ metaForMethod.index } was given ${ JSON.stringify(keyArg) }`);

assert(_.isString(keyArg), `@key parameter must be given a string or number: ` +
`"${ methodNameString }"@${ metaForMethod.index }`);

specificKeyValues = [keyArg];
}
Expand Down Expand Up @@ -299,7 +296,7 @@ function makeAutoSubscribeDecorator(shallow = false, defaultKeyValues: string[])

export const autoSubscribe = makeAutoSubscribeDecorator(true, [StoreBase.Key_All]);
export function autoSubscribeWithKey(keyOrKeys: string|number|(string|number)[]) {
assert.ok(keyOrKeys || _.isNumber(keyOrKeys), 'Must specify a key when using autoSubscribeWithKey');
assert(keyOrKeys || _.isNumber(keyOrKeys), 'Must specify a key when using "autoSubscribeWithKey"');
const keys = _.map(Array.isArray(keyOrKeys) ? keyOrKeys : [keyOrKeys], key => _.isNumber(key) ? key.toString() : key);
return makeAutoSubscribeDecorator(true, keys);
}
Expand All @@ -312,8 +309,7 @@ export function key(target: InstanceTarget, methodName: string, index: number) {
// Shorthand.
const metaForMethod = getMethodMetadata(targetWithMetadata, methodName);

assert.ok(!metaForMethod.hasIndex, 'Can only apply @key once per method: only the first will be used: "'
+ methodName + '"@' + index);
assert(!metaForMethod.hasIndex, `Can only apply @key once per method: only the first will be used "${ methodName }"@${ index }`);

// Save this parameter's index into the target's metadata.
metaForMethod.index = index;
Expand All @@ -338,7 +334,7 @@ export function disableWarnings<T extends Function>(target: InstanceTarget, meth
// Note: we need to be given 'this', so cannot use '=>' syntax.
// Note: T might have other properties (e.g. T = { (): void; bar: number; }). We don't support that and need a cast.
descriptor.value = <T><any>function DisableWarnings(this: any, ...args: any[]) {
assert.ok(targetWithMetadata.__resubMetadata.__decorated, 'Missing @AutoSubscribeStore class decorator: "' + methodName + '"');
assert(targetWithMetadata.__resubMetadata.__decorated, `Missing @AutoSubscribeStore class decorator: "${ methodName }"`);

// Just call the method if no handler is setup.
const scopedHandleWrapper = handlerWrapper;
Expand Down Expand Up @@ -393,13 +389,11 @@ export function warnIfAutoSubscribeEnabled<T extends Function>(target: InstanceT
// Note: we need to be given 'this', so cannot use '=>' syntax.
// Note: T might have other properties (e.g. T = { (): void; bar: number; }). We don't support that and need a cast.
descriptor.value = <T><any>function WarnIfAutoSubscribeEnabled(this: any, ...args: any[]) {
assert.ok(targetWithMetadata.__resubMetadata.__decorated, 'Missing @AutoSubscribeStore class decorator: "' + methodName + '"');

assert.ok(!handlerWrapper || handlerWrapper.useAutoSubscriptions !== AutoOptions.Enabled || handlerWrapper.inAutoSubscribe,
'Only Store methods with the @autoSubscribe decorator can be called right now (e.g. in _buildState): "' + methodName + '"');
assert(targetWithMetadata.__resubMetadata.__decorated, `Missing @AutoSubscribeStore class decorator: "${ methodName }"`);
assert(!handlerWrapper || handlerWrapper.useAutoSubscriptions !== AutoOptions.Enabled || handlerWrapper.inAutoSubscribe,
`Only Store methods with the @autoSubscribe decorator can be called right now (e.g. in _buildState): "${ methodName }"`);

const result = originalMethod.apply(this, args);
return result;
return originalMethod.apply(this, args);
};

return descriptor;
Expand Down
17 changes: 7 additions & 10 deletions src/ComponentBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
*/

import * as React from 'react';
import * as assert from 'assert';

import * as _ from './lodashMini';
import Options from './Options';
import Instrumentation from './Instrumentation';
import { SubscriptionCallbackBuildStateFunction, SubscriptionCallbackFunction, StoreSubscription } from './Types';
import { forbidAutoSubscribeWrapper, enableAutoSubscribeWrapper, enableAutoSubscribe } from './AutoSubscriptions';
import { assert } from './utils';
import { AutoSubscription, StoreBase } from './StoreBase';

// Subscriptions without a key need some way to be identified in the SubscriptionLookup.
Expand Down Expand Up @@ -178,8 +178,7 @@ export abstract class ComponentBase<P extends React.Props<any>, S extends Object
}

protected _addSubscription(subscription: StoreSubscription<P, S>): StoreSubscription<P, S>|undefined {
assert.ok(subscription.store instanceof StoreBase,
'Subscription added with store that\'s not an StoreBase');
assert(subscription.store instanceof StoreBase, `Subscription added with store that\'s not an StoreBase`);

const { enablePropertyName } = subscription;

Expand Down Expand Up @@ -236,12 +235,10 @@ export abstract class ComponentBase<P extends React.Props<any>, S extends Object
}

private _registerSubscription(subscription: StoreSubscriptionInternal<P, S>, key: string|number = StoreBase.Key_All) {
assert.ok(!subscription._subscriptionToken,
'Subscription already subscribed!');
assert.ok(!subscription.keyPropertyName || key !== StoreBase.Key_All,
'Subscription created with key of all when it has a key property name');
assert.notDeepEqual(subscription.specificKeyValue, StoreBase.Key_All,
'Subscription created with specific key of all');
assert(!subscription._subscriptionToken, 'Subscription already subscribed!');
assert(!subscription.keyPropertyName || key !== StoreBase.Key_All,
'Subscription created with key of all when it has a key property name');
assert(!_.isEqual(subscription.specificKeyValue, StoreBase.Key_All), 'Subscription created with specific key of all');

if (key) {
if (_.isNumber(key)) {
Expand Down Expand Up @@ -400,7 +397,7 @@ export abstract class ComponentBase<P extends React.Props<any>, S extends Object
private _findKeyFromPropertyName(props: Readonly<P>, keyPropertyName: keyof P): string {
const key = _.get(props, keyPropertyName);
if (!_.isString(key)) {
assert.ok(false, 'Subscription key property value ' + keyPropertyName + ' must be a string');
assert(false, `Subscription key property value ${ keyPropertyName } must be a string`);
// Fallback to subscribing to all values
return StoreBase.Key_All;
}
Expand Down
20 changes: 10 additions & 10 deletions src/StoreBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
* Stores can mark themselves as opt-out of the trigger-block logic for critical stores that must flow under all conditions.
*/

import * as assert from 'assert';

import * as _ from './lodashMini';
import Options from './Options';
import Instrumentation from './Instrumentation';
import { assert } from './utils';
import { SubscriptionCallbackFunction } from './Types';

export interface AutoSubscription {
Expand Down Expand Up @@ -53,7 +52,7 @@ export abstract class StoreBase {

static popTriggerBlock() {
this._triggerBlockCount--;
assert.ok(this._triggerBlockCount >= 0, 'Over-popped trigger blocks!');
assert(this._triggerBlockCount >= 0, 'Over-popped trigger blocks!');

if (this._triggerBlockCount === 0) {
StoreBase._resolveCallbacks();
Expand Down Expand Up @@ -245,7 +244,7 @@ export abstract class StoreBase {
const key = _.isNumber(rawKey) ? rawKey.toString() : rawKey;

// Adding extra type-checks since the key is often the result of following a string path, which is not type-safe.
assert.ok(key && _.isString(key), 'Trying to subscribe to invalid key: "' + key + '"');
assert(key && _.isString(key), `Trying to subscribe to invalid key: "${ key }"`);

let callbacks = this._subscriptions[key];
if (!callbacks) {
Expand All @@ -265,7 +264,7 @@ export abstract class StoreBase {

// Unsubscribe from a previous subscription. Pass in the token the subscribe function handed you.
unsubscribe(subToken: number) {
assert.ok(this._subsByNum[subToken], 'No subscriptions found for token ' + subToken);
assert(this._subsByNum[subToken], `No subscriptions found for token ${ subToken }`);

let key = this._subsByNum[subToken].key;
let callback = this._subsByNum[subToken].callback;
Expand All @@ -275,7 +274,7 @@ export abstract class StoreBase {
StoreBase._pendingCallbacks.delete(callback);

let callbacks = this._subscriptions[key];
assert.ok(callbacks, 'No subscriptions under key ' + key);
assert(callbacks, `No subscriptions under key ${ key }`);

const index = _.indexOf(callbacks, callback);
if (index !== -1) {
Expand All @@ -289,7 +288,7 @@ export abstract class StoreBase {
}
}
} else {
assert.ok(false, 'Subscription not found during unsubscribe...');
assert(false, 'Subscription not found during unsubscribe...');
}
}

Expand All @@ -309,13 +308,14 @@ export abstract class StoreBase {

removeAutoSubscription(subscription: AutoSubscription) {
const key = subscription.key;

let subs = this._autoSubscriptions[key];
assert.ok(subs, 'No subscriptions under key ' + key);

assert(subs, `No subscriptions under key ${ key }`);

const oldLength = subs.length;
_.pull(subs, subscription);
assert.equal(subs.length, oldLength - 1, 'Subscription not found during unsubscribe...');

assert(subs.length === oldLength - 1, 'Subscription not found during unsubscribe...');

StoreBase._pendingCallbacks.delete(subscription.callback);

Expand Down
12 changes: 12 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* utils
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
*/
export const assert = (cond: any, message?: string | undefined) => {
if (!cond) {
throw new Error(`[resub] ${ message || 'Assertion Failed' }`);
}
};
2 changes: 1 addition & 1 deletion test/AutoSubscribeWarnings.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { shallow } from 'enzyme';
import { SimpleStore } from './SimpleStore';

const WARN_IN_BUILD_STATE = 'build';
const WARNING_MESSAGE = `Only Store methods with the @autoSubscribe decorator \
const WARNING_MESSAGE = `[resub] Only Store methods with the @autoSubscribe decorator \
can be called right now (e.g. in _buildState): "setStoreData"`;

describe('AutoSubscribeWarnings', () => {
Expand Down
6 changes: 6 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,11 @@

"include": [
"src/*"
],

"exclude": [
"dist-es2015",
"dist-types",
"dist"
]
}