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
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module.exports = {
testEnvironment: "jsdom",
preset: "ts-jest",
globals: {
"ts-jest": {
Expand Down
21 changes: 11 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,23 @@
"release:publish:ci": "./scripts/release/publish.js",
"release:version": "./scripts/release/version.js"
},
"//": "Currently can't upgrade TypeScript to v4.4.2 because it breaks Rollup: https://git.io/JuCcs",
"devDependencies": {
"@rollup/plugin-replace": "^2.4.1",
"@rollup/plugin-typescript": "^8.2.0",
"@types/jest": "^26.0.20",
"@types/node": "^14.14.32",
"@rollup/plugin-replace": "^3.0.0",
"@rollup/plugin-typescript": "^8.2.5",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"conventional-changelog-cli": "^2.1.1",
"execa": "^5.0.0",
"execa": "^5.1.1",
"is-ci": "^3.0.0",
"jest": "^26.6.3",
"rollup": "^2.40.0",
"jest": "^27.1.0",
"rollup": "^2.56.3",
"rollup-plugin-terser": "^7.0.2",
"semver": "^7.3.5",
"ts-jest": "^26.5.3",
"tslib": "^2.1.0",
"ts-jest": "^27.0.5",
"tslib": "^2.3.1",
"tslint": "^6.1.3",
"typescript": "^4.2.3"
"typescript": "4.3.5"
},
"resolutions": {
"**/optimist/minimist": "1.2.5"
Expand Down
8 changes: 4 additions & 4 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ function rollupConfig({ formats, prod }) {
tsconfig: false,
target: 'es2016',
exclude: ['test/*'],
typescript: require('typescript')
}),
prod !== undefined && replace({ 'process.env.NODE_ENV': replaceToken }),
prod !== undefined && replace({ 'process.env.NODE_ENV': replaceToken, preventAssignment: true }),
prod && terser()
].filter(Boolean);
];

const output = formats.map(format => {
const targetDirectory = format === 'umd' ? umdDir : format === 'cjs' ? cjsDir : modulesDir;
Expand All @@ -40,7 +39,8 @@ function rollupConfig({ formats, prod }) {
format,
banner,
footer,
file: path.join(targetDirectory, targetName)
file: path.join(targetDirectory, targetName),
exports: 'auto'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not seem to change the output file format, and suppresses a warning from Rollup.

};
});

Expand Down
22 changes: 11 additions & 11 deletions src/base-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
ObjectDefineProperty,
preventExtensions,
ArrayPush,
ObjectCreate,
ObjectCreate, ProxyPropertyKey,
} from './shared';
import { ReactiveMembrane } from './reactive-membrane';

Expand Down Expand Up @@ -47,7 +47,7 @@ export abstract class BaseProxyHandler {
}
return descriptor;
}
copyDescriptorIntoShadowTarget(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey) {
copyDescriptorIntoShadowTarget(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey) {
const { originalTarget } = this;
// Note: a property might get defined multiple times in the shadowTarget
// but it will always be compatible with the previous descriptor
Expand All @@ -60,8 +60,8 @@ export abstract class BaseProxyHandler {
}
lockShadowTarget(shadowTarget: ReactiveMembraneShadowTarget): void {
const { originalTarget } = this;
const targetKeys: PropertyKey[] = ArrayConcat.call(getOwnPropertyNames(originalTarget), getOwnPropertySymbols(originalTarget));
targetKeys.forEach((key: PropertyKey) => {
const targetKeys: ProxyPropertyKey[] = ArrayConcat.call(getOwnPropertyNames(originalTarget), getOwnPropertySymbols(originalTarget));
targetKeys.forEach((key: ProxyPropertyKey) => {
this.copyDescriptorIntoShadowTarget(shadowTarget, key);
});
const { membrane: { tagPropertyKey } } = this;
Expand All @@ -73,11 +73,11 @@ export abstract class BaseProxyHandler {

// Abstract Traps

abstract set(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey, value: any): boolean;
abstract deleteProperty(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey): boolean;
abstract set(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey, value: any): boolean;
abstract deleteProperty(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey): boolean;
abstract setPrototypeOf(shadowTarget: ReactiveMembraneShadowTarget, prototype: any): any;
abstract preventExtensions(shadowTarget: ReactiveMembraneShadowTarget): boolean;
abstract defineProperty(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey, descriptor: PropertyDescriptor): boolean;
abstract defineProperty(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey, descriptor: PropertyDescriptor): boolean;

// Shared Traps

Expand All @@ -87,20 +87,20 @@ export abstract class BaseProxyHandler {
construct(shadowTarget: ReactiveMembraneShadowTarget, argArray: any, newTarget?: any): any {
/* No op */
}
get(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey): any {
get(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey): any {
const { originalTarget, membrane: { valueObserved } } = this;
const value = originalTarget[key];
valueObserved(originalTarget, key);
return this.wrapValue(value);
}
has(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey): boolean {
has(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey): boolean {
const { originalTarget, membrane: { tagPropertyKey, valueObserved } } = this;
valueObserved(originalTarget, key);
// since key is never going to be undefined, and tagPropertyKey might be undefined
// we can simply compare them as the second part of the condition.
return key in originalTarget || key === tagPropertyKey;
}
ownKeys(shadowTarget: ReactiveMembraneShadowTarget): PropertyKey[] {
ownKeys(shadowTarget: ReactiveMembraneShadowTarget): ProxyPropertyKey[] {
const { originalTarget, membrane: { tagPropertyKey } } = this;
// if the membrane tag key exists and it is not in the original target, we add it to the keys.
const keys = isUndefined(tagPropertyKey) || hasOwnProperty.call(originalTarget, tagPropertyKey) ? [] : [tagPropertyKey];
Expand All @@ -125,7 +125,7 @@ export abstract class BaseProxyHandler {
const { originalTarget } = this;
return getPrototypeOf(originalTarget);
}
getOwnPropertyDescriptor(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey): PropertyDescriptor | undefined {
getOwnPropertyDescriptor(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey): PropertyDescriptor | undefined {
const { originalTarget, membrane: { valueObserved, tagPropertyKey } } = this;

// keys looked up via getOwnPropertyDescriptor need to be reactive
Expand Down
4 changes: 2 additions & 2 deletions src/reactive-dev-formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
getPrototypeOf,
getOwnPropertyNames,
getOwnPropertySymbols,
unwrap
unwrap, ProxyPropertyKey
} from './shared';

// Define globalThis since it's not current defined in by typescript.
Expand All @@ -33,7 +33,7 @@ function extract(objectOrArray: any): any {
const obj = ObjectCreate(getPrototypeOf(objectOrArray));
const names = getOwnPropertyNames(objectOrArray);
return ArrayConcat.call(names, getOwnPropertySymbols(objectOrArray))
.reduce((seed: any, key: PropertyKey) => {
.reduce((seed: any, key: ProxyPropertyKey) => {
const item = objectOrArray[key];
const original = unwrap(item);
if (original !== item) {
Expand Down
18 changes: 14 additions & 4 deletions src/reactive-handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import { toString, isArray, unwrap, isExtensible, preventExtensions, ObjectDefineProperty, hasOwnProperty, isUndefined } from './shared';
import {
toString,
isArray,
unwrap,
isExtensible,
preventExtensions,
ObjectDefineProperty,
hasOwnProperty,
isUndefined,
ProxyPropertyKey
} from './shared';
import { BaseProxyHandler, ReactiveMembraneShadowTarget } from './base-handler';

const getterMap = new WeakMap<() => any, () => any>();
Expand Down Expand Up @@ -80,7 +90,7 @@ export class ReactiveProxyHandler extends BaseProxyHandler {
reverseSetterMap.set(redSet, set);;
return set;
}
set(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey, value: any): boolean {
set(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey, value: any): boolean {
const { originalTarget, membrane: { valueMutated } } = this;
const oldValue = originalTarget[key];
if (oldValue !== value) {
Expand All @@ -95,7 +105,7 @@ export class ReactiveProxyHandler extends BaseProxyHandler {
}
return true;
}
deleteProperty(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey): boolean {
deleteProperty(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey): boolean {
const { originalTarget, membrane: { valueMutated } } = this;
delete originalTarget[key];
valueMutated(originalTarget, key);
Expand All @@ -120,7 +130,7 @@ export class ReactiveProxyHandler extends BaseProxyHandler {
}
return true;
}
defineProperty(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey, descriptor: PropertyDescriptor): boolean {
defineProperty(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey, descriptor: PropertyDescriptor): boolean {
const { originalTarget, membrane: { valueMutated, tagPropertyKey } } = this;
if (key === tagPropertyKey && !hasOwnProperty.call(originalTarget, key)) {
// To avoid leaking the membrane tag property into the original target, we must
Expand Down
14 changes: 7 additions & 7 deletions src/reactive-membrane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
isUndefined,
getPrototypeOf,
isFunction,
registerProxy,
registerProxy, ProxyPropertyKey,
} from './shared';
import { ReactiveProxyHandler } from './reactive-handler';
import { ReadOnlyHandler } from './read-only-handler';
Expand All @@ -20,8 +20,8 @@ interface ReactiveState {
reactive: any;
}

export type ReactiveMembraneAccessCallback = (obj: any, key: PropertyKey) => void;
export type ReactiveMembraneMutationCallback = (obj: any, key: PropertyKey) => void;
export type ReactiveMembraneAccessCallback = (obj: any, key: ProxyPropertyKey) => void;
export type ReactiveMembraneMutationCallback = (obj: any, key: ProxyPropertyKey) => void;
export type ReactiveMembraneDistortionCallback = (value: any) => any;
export type ReactiveMembraneObservableCallback = (value: any) => boolean;

Expand All @@ -30,7 +30,7 @@ export interface ObservableMembraneInit {
valueObserved?: ReactiveMembraneAccessCallback;
valueDistortion?: ReactiveMembraneDistortionCallback;
valueIsObservable?: ReactiveMembraneObservableCallback;
tagPropertyKey?: PropertyKey;
tagPropertyKey?: ProxyPropertyKey;
}

const ObjectDotPrototype = Object.prototype;
Expand All @@ -54,10 +54,10 @@ function defaultValueIsObservable(value: any): boolean {
return (proto === ObjectDotPrototype || proto === null || getPrototypeOf(proto) === null);
}

const defaultValueObserved: ReactiveMembraneAccessCallback = (obj: any, key: PropertyKey) => {
const defaultValueObserved: ReactiveMembraneAccessCallback = (obj: any, key: ProxyPropertyKey) => {
/* do nothing */
};
const defaultValueMutated: ReactiveMembraneMutationCallback = (obj: any, key: PropertyKey) => {
const defaultValueMutated: ReactiveMembraneMutationCallback = (obj: any, key: ProxyPropertyKey) => {
/* do nothing */
};
const defaultValueDistortion: ReactiveMembraneDistortionCallback = (value: any) => value;
Expand All @@ -71,7 +71,7 @@ export class ReactiveMembrane {
valueMutated: ReactiveMembraneMutationCallback = defaultValueMutated;
valueObserved: ReactiveMembraneAccessCallback = defaultValueObserved;
valueIsObservable: ReactiveMembraneObservableCallback = defaultValueIsObservable;
tagPropertyKey: PropertyKey | undefined;
tagPropertyKey: ProxyPropertyKey | undefined;
private objectGraph: WeakMap<any, ReactiveState> = new WeakMap();

constructor(options?: ObservableMembraneInit) {
Expand Down
8 changes: 4 additions & 4 deletions src/read-only-handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { unwrap, isArray, isUndefined } from './shared';
import {unwrap, isArray, isUndefined, ProxyPropertyKey} from './shared';
import { BaseProxyHandler, ReactiveMembraneShadowTarget } from './base-handler';

const getterMap = new WeakMap<() => any, () => any>();
Expand Down Expand Up @@ -36,7 +36,7 @@ export class ReadOnlyHandler extends BaseProxyHandler {
setterMap.set(originalSet, set);
return set;
}
set(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey, value: any): boolean {
set(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey, value: any): boolean {
if (process.env.NODE_ENV !== 'production') {
const { originalTarget } = this;
const msg = isArray(originalTarget) ?
Expand All @@ -46,7 +46,7 @@ export class ReadOnlyHandler extends BaseProxyHandler {
}
return false;
}
deleteProperty(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey): boolean {
deleteProperty(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey): boolean {
if (process.env.NODE_ENV !== 'production') {
const { originalTarget } = this;
throw new Error(`Invalid mutation: Cannot delete "${key.toString()}" on "${originalTarget}". "${originalTarget}" is read-only.`);
Expand All @@ -66,7 +66,7 @@ export class ReadOnlyHandler extends BaseProxyHandler {
}
return false;
}
defineProperty(shadowTarget: ReactiveMembraneShadowTarget, key: PropertyKey, descriptor: PropertyDescriptor): boolean {
defineProperty(shadowTarget: ReactiveMembraneShadowTarget, key: ProxyPropertyKey, descriptor: PropertyDescriptor): boolean {
if (process.env.NODE_ENV !== 'production') {
const { originalTarget } = this;
throw new Error(`Invalid mutation: Cannot defineProperty "${key.toString()}" on "${originalTarget}". "${originalTarget}" is read-only.`);
Expand Down
4 changes: 4 additions & 0 deletions src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,7 @@ export function registerProxy(proxy: object, value: any) {
}

export const unwrap = (replicaOrAny: any): any => proxyToValueMap.get(replicaOrAny) || replicaOrAny;

// In the specification for Proxy, the keys are defined not as PropertyKeys (i.e. `string | symbol | number`)
// but as `string | symbol`. See: https://github.com/microsoft/TypeScript/pull/35594
export type ProxyPropertyKey = string | symbol;
Loading