Skip to content

Commit

Permalink
Merge pull request #2733 from sanderhahn/master
Browse files Browse the repository at this point in the history
typescript version of store
  • Loading branch information
Rich-Harris authored May 15, 2019
2 parents 0bf9910 + e45aa0f commit 8d539d8
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 94 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ node_modules
/compiler.js
/index.js
/internal.*
/store.js
/store.*
/easing.js
/motion.*
/transition.js
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@
"precodecov": "npm run coverage",
"lint": "eslint src test/*.js",
"build": "rollup -c",
"prepare": "npm run build",
"prepare": "npm run build && npm run tsd",
"dev": "rollup -cw",
"pretest": "npm run build",
"posttest": "agadoo src/internal/index.js",
"prepublishOnly": "export PUBLISH=true && npm run lint && npm test"
"prepublishOnly": "export PUBLISH=true && npm run lint && npm test",
"tsd": "tsc -d src/store.ts --outDir ."
},
"repository": {
"type": "git",
Expand Down
31 changes: 30 additions & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,37 @@ export default [
external: id => id.startsWith('svelte/')
},

/* store.mjs */
{
input: `src/store.ts`,
output: [
{
file: `store.mjs`,
format: 'esm',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
},
{
file: `store.js`,
format: 'cjs',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
}
],
plugins: [
is_publish
? typescript({
include: 'src/**',
exclude: 'src/internal/**',
typescript: require('typescript')
})
: sucrase({
transforms: ['typescript']
})
],
external: id => id.startsWith('svelte/')
},

// everything else
...['index', 'store', 'easing', 'transition', 'animate'].map(name => ({
...['index', 'easing', 'transition', 'animate'].map(name => ({
input: `${name}.mjs`,
output: {
file: `${name}.js`,
Expand Down
136 changes: 136 additions & 0 deletions src/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { run_all, noop, safe_not_equal } from './internal/utils';

type Subscriber<T> = (value: T) => void;

type Unsubscriber = () => void;

type Updater<T> = (value: T) => T;

type Invalidater<T> = (value?: T) => void;

type StartStopNotifier<T> = (set: Subscriber<T>) => Unsubscriber | void;

export interface Readable<T> {
subscribe(run: Subscriber<T>, invalidate?: Invalidater<T>): Unsubscriber;
}

export interface Writable<T> extends Readable<T> {
set(value: T): void;
update(updater: Updater<T>): void;
}

type SubscribeInvalidateTuple<T> = [Subscriber<T>, Invalidater<T>];

export function readable<T>(value: T, start: StartStopNotifier<T>): Readable<T> {
return {
subscribe: writable(value, start).subscribe,
};
}

export function writable<T>(value: T, start: StartStopNotifier<T> = noop): Writable<T> {
let stop: Unsubscriber;
const subscribers: Array<SubscribeInvalidateTuple<T>> = [];

function set(new_value: T): void {
if (safe_not_equal(value, new_value)) {
value = new_value;
if (!stop) {
return; // not ready
}
subscribers.forEach((s) => s[1]());
subscribers.forEach((s) => s[0](value));
}
}

function update(fn: Updater<T>): void {
set(fn(value));
}

function subscribe(run: Subscriber<T>, invalidate: Invalidater<T> = noop): Unsubscriber {
const subscriber: SubscribeInvalidateTuple<T> = [run, invalidate];
subscribers.push(subscriber);
if (subscribers.length === 1) {
stop = start(set) || noop;
}
run(value);

return () => {
const index = subscribers.indexOf(subscriber);
if (index !== -1) {
subscribers.splice(index, 1);
}
if (subscribers.length === 0) {
stop();
}
};
}

return { set, update, subscribe };
}

type Stores = Readable<any> | [Readable<any>, ...Array<Readable<any>>];

type StoresValues<T> = T extends Readable<infer U> ? U :
{ [K in keyof T]: T[K] extends Readable<infer U> ? U : never };

export function derived<T, S extends Stores>(
stores: S,
fn: (values: StoresValues<S>, set?: Subscriber<T>) => T | Unsubscriber | void,
initial_value?: T,
): Readable<T> {

const single = !Array.isArray(stores);
const stores_array: Array<Readable<any>> = single
? [stores as Readable<any>]
: stores as Array<Readable<any>>;

const auto = fn.length < 2;

return readable(initial_value, (set) => {
let inited = false;
const values: StoresValues<S> = [] as StoresValues<S>;

let pending = 0;
let cleanup = noop;

const sync = () => {
if (pending) {
return;
}
cleanup();
const result = fn(single ? values[0] : values, set);
if (auto) {
set(result as T);
} else {
cleanup = result as Unsubscriber || noop;
}
};

const unsubscribers = stores_array.map((store, i) => store.subscribe(
(value) => {
values[i] = value;
pending &= ~(1 << i);
if (inited) {
sync();
}
},
() => {
pending |= (1 << i);
}),
);

inited = true;
sync();

return function stop() {
run_all(unsubscribers);
cleanup();
};
});
}

export function get<T>(store: Readable<T>): T {
let value: T | undefined;
store.subscribe((_: T) => value = _)();
return value as T;
}
85 changes: 0 additions & 85 deletions store.mjs

This file was deleted.

22 changes: 17 additions & 5 deletions test/store/index.js → test/store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as assert from 'assert';
import { readable, writable, derived, get } from '../../store.js';
import { readable, writable, derived, get } from '../store';

describe('store', () => {
describe('writable', () => {
Expand Down Expand Up @@ -30,10 +30,10 @@ describe('store', () => {
return () => called -= 1;
});

const unsubscribe1 = store.subscribe(() => {});
const unsubscribe1 = store.subscribe(() => { });
assert.equal(called, 1);

const unsubscribe2 = store.subscribe(() => {});
const unsubscribe2 = store.subscribe(() => { });
assert.equal(called, 1);

unsubscribe1();
Expand Down Expand Up @@ -73,7 +73,7 @@ describe('store', () => {
set(0);

return () => {
tick = () => {};
tick = () => { };
running = false;
};
});
Expand Down Expand Up @@ -242,11 +242,23 @@ describe('store', () => {

assert.deepEqual(cleaned_up, [2, 3, 4]);
});

it('allows derived with different types', () => {
const a = writable('one');
const b = writable(1);
const c = derived([a, b], ([a, b]) => `${a} ${b}`);

assert.deepEqual(get(c), 'one 1');

a.set('two');
b.set(2);
assert.deepEqual(get(c), 'two 2');
});
});

describe('get', () => {
it('gets the current value of a store', () => {
const store = readable(42, () => {});
const store = readable(42, () => { });
assert.equal(get(store), 42);
});
});
Expand Down

0 comments on commit 8d539d8

Please sign in to comment.