Skip to content

Commit

Permalink
Revive zen-observable-ts wrapper.
Browse files Browse the repository at this point in the history
Apollo Client currently uses the Observable implementation provided by the
zen-observable npm package, since it is small and works well for our
needs. Although zen-observable itself is not written in TypeScript, we
use the companion package @types/zen-observable to provide types.

We are actively considering switching from zen-observable to RxJS (#5749),
though it remains to be seen how much of a breaking change that will be
(and thus whether it makes sense for a minor or major version update).

In the meantime, several issues (most recently #5961) have been opened
pointing out that zen-observable effectively does not support importing
its code as ECMAScript modules, even though it has a separate entry point
(zen-observable/esm.js) that uses ESM syntax.

This is a problem the zen-observable package could easily fix by adding a
"module" field to its package.json, but @abdonrd tried to propose that
change in a PR (as I requested), and has not heard back since June 2020:
zenparsing/zen-observable#74

Fortunately, Apollo maintains an npm package called zen-observable-ts,
which was originally created to provide TypeScript types, before
@types/zen-observable was introduced. This commit revives that wrapper
package, so we can make sure both CommonJS and ESM exports are supported,
with full TypeScript types, until the zen-observable maintainer gets
around to merging that PR.

I considered forking zen-observable, but I'm happy with this wrapping
strategy, since reusing zen-observable makes it easier to take advantage
of any future updates to the zen-observable package.

I also considered using https://www.npmjs.com/package/patch-package to
apply changes to node_modules/zen-observable/package.json upon
installation, but that doesn't really work unless @apollo/client bundles
the zen-observable implementation into itself, since otherwise the
original (unpatched) zen-observable package will continue to be installed
when developers npm install @apollo/client. The patch-package tool is
great, but I don't think it's meant for libraries to use.

Thankfully, this time around we do not need to hand-write the TypeScript
types, since they can be re-exported from @types/zen-observable.

I bumped the major version of zen-observable-ts, since the older versions
(used by apollo-link) still get more than two million downloads per week.

The source code for the zen-observable-ts package can now be found at
https://github.com/apollographql/zen-observable-ts, rather than the old
https://github.com/apollographql/apollo-link monorepo.
  • Loading branch information
benjamn committed Jan 25, 2021
1 parent 333ff48 commit 848fca2
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 14 deletions.
27 changes: 18 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@
},
"dependencies": {
"@graphql-typed-document-node/core": "^3.0.0",
"@types/zen-observable": "^0.8.0",
"@wry/context": "^0.5.2",
"@wry/equality": "^0.3.0",
"@wry/trie": "^0.2.1",
Expand All @@ -86,7 +85,7 @@
"symbol-observable": "^2.0.0",
"ts-invariant": "^0.6.0",
"tslib": "^1.10.0",
"zen-observable": "^0.8.14"
"zen-observable-ts": "^1.0.0-beta.4"
},
"devDependencies": {
"@babel/parser": "7.12.11",
Expand Down
13 changes: 10 additions & 3 deletions src/utilities/observables/Observable.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import Observable from 'zen-observable';
import {
Observable,
Observer,
Subscription as ObservableSubscription,
} from 'zen-observable-ts';

// This simplified polyfill attempts to follow the ECMAScript Observable
// proposal (https://github.com/zenparsing/es-observable)
import 'symbol-observable';

export type ObservableSubscription = ZenObservable.Subscription;
export type Observer<T> = ZenObservable.Observer<T>;
export type {
Observer,
ObservableSubscription,
};

// Use global module augmentation to add RxJS interop functionality. By
// using this approach (instead of subclassing `Observable` and adding an
Expand All @@ -18,4 +24,5 @@ declare global {
}
}
(Observable.prototype as any)['@@observable'] = function () { return this; };

export { Observable };

0 comments on commit 848fca2

Please sign in to comment.