typescript
Yanick Champoux 2021-10-15 12:41:58 -04:00
parent 3394a00419
commit 440c76d408
32 changed files with 203 additions and 223 deletions

6
babel.config.cjs Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
'@babel/preset-typescript',
],
};

0
docs/API/assets/main.d.ts vendored Normal file
View File

0
docs/API/assets/search.d.ts vendored Normal file
View File

33
docs/assets/js/search.d.ts vendored Normal file
View File

@ -0,0 +1,33 @@
declare namespace typedoc {
namespace search {
namespace data {
const kinds: {
"32": string;
"64": string;
"128": string;
"256": string;
"512": string;
"1024": string;
"2048": string;
"65536": string;
"262144": string;
"4194304": string;
};
const rows: ({
id: number;
kind: number;
name: string;
url: string;
classes: string;
parent?: undefined;
} | {
id: number;
kind: number;
name: string;
url: string;
classes: string;
parent: string;
})[];
}
}
}

0
docs/scripts/linenumber.d.ts vendored Normal file
View File

0
docs/scripts/prettify/lang-css.d.ts vendored Normal file
View File

1
docs/scripts/prettify/prettify.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare var q: any;

0
docs/scripts/search.d.ts vendored Normal file
View File

3
jest.config.ts Normal file
View File

@ -0,0 +1,3 @@
export default {
roots: [ './src' ]
}

0
out/scripts/linenumber.d.ts vendored Normal file
View File

0
out/scripts/prettify/lang-css.d.ts vendored Normal file
View File

1
out/scripts/prettify/prettify.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare var q: any;

View File

@ -1,5 +1,4 @@
{
"type": "module",
"dependencies": {
"@yanick/updeep": "link:../updeep",
"lodash": "^4.17.15",
@ -7,7 +6,6 @@
"moize": "^6.1.0",
"redux": "^4.0.5",
"ts-action": "^11.0.0",
"ts-node": "^8.6.2",
"updeep": "^1.2.1"
},
"devDependencies": {
@ -15,6 +13,8 @@
"@babel/core": "^7.15.5",
"@babel/plugin-transform-modules-commonjs": "^7.15.4",
"@babel/preset-env": "^7.8.7",
"@babel/preset-typescript": "^7.15.0",
"@types/jest": "^27.0.2",
"@types/lodash": "^4.14.149",
"@types/sinon": "^7.5.2",
"@typescript-eslint/eslint-plugin": "^2.23.0",
@ -29,12 +29,14 @@
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-prettier": "^3.1.2",
"glob": "^7.1.6",
"jest": "^27.2.5",
"jsdoc": "^3.6.7",
"prettier": "^2.4.1",
"promake": "^3.1.3",
"sinon": "^9.0.1",
"standard-version": "^8.0.0",
"tap": "15",
"ts-node": "^8.10.2",
"tsd": "^0.17.0",
"typedoc": "0.22.5",
"typescript": "^4.4.3"

View File

@ -2,17 +2,73 @@
import moize from 'moize';
import u from '@yanick/updeep';
import { createStore as reduxCreateStore, applyMiddleware } from 'redux';
import { get, map, mapValues, merge, difference } from 'lodash-es';
import { get, map, mapValues, merge, difference } from 'lodash';
import { buildInitial } from './buildInitial/index.js';
import { buildActions } from './buildActions/index.js';
import { buildSelectors } from './buildSelectors/index.js';
import { action } from './actions.js';
import { buildUpreducer } from './buildUpreducer.js';
import { buildInitial } from './buildInitial';
import { buildActions } from './buildActions';
import { buildSelectors } from './buildSelectors';
import { action } from './actions';
import { buildUpreducer } from './buildUpreducer';
import {
buildMiddleware,
augmentMiddlewareApi,
} from './buildMiddleware/index.js';
} from './buildMiddleware';
import { Dict } from './types';
/**
* Configuration object typically passed to the constructor of the class Updux.
*/
export interface UpduxConfig<TState = unknown> {
/**
* Local initial state.
* @default {}
*/
initial?: TState;
/**
* Subduxes to be merged to this dux.
*/
subduxes?: Dict<Updux | UpduxConfig>;
/**
* Local actions.
*/
actions?: Record<string, any>;
/**
* Local selectors.
*/
selectors?: Record<string, Function>;
/**
* Local mutations
*/
mutations?: Record<string, Function>;
/**
* Selectors to apply to the mapped subduxes. Only
* applicable if the dux is a mapping dux.
*/
mappedSelectors?: Record<string, Function>;
/**
* Local effects.
*/
effects?: Record<string, Function>;
/**
* Local reactions.
*/
reactions?: Function[];
/**
* If true, enables mapped reactions. Additionally, it can be
* a reaction function, which will treated as a regular
* reaction for the mapped dux.
*/
mappedReaction?: Function | boolean;
}
export class Updux {
/** @type { unknown } */
@ -24,11 +80,11 @@ export class Updux {
#selectors = {};
#mutations = {};
#effects = [];
#subscriptions = [];
#splatSelector = undefined;
#splatReaction = undefined;
#reactions = [];
#mappedSelectors = undefined;
#mappedReaction = undefined;
constructor(config) {
constructor(config: UpduxConfig) {
this.#initial = config.initial ?? {};
this.#subduxes = config.subduxes ?? {};
@ -49,7 +105,7 @@ export class Updux {
}
this.#selectors = config.selectors ?? {};
this.#splatSelector = config.splatSelector;
this.#mappedSelectors = config.mappedSelectors;
this.#mutations = config.mutations ?? {};
@ -64,9 +120,9 @@ export class Updux {
this.#effects = Object.entries(config.effects);
}
this.#subscriptions = config.subscriptions ?? [];
this.#reactions = config.reactions ?? [];
this.#splatReaction = config.splatReaction;
this.#mappedReaction = config.mappedReaction;
}
#memoInitial = moize(buildInitial);
@ -75,12 +131,11 @@ export class Updux {
#memoUpreducer = moize(buildUpreducer);
#memoMiddleware = moize(buildMiddleware);
get subscriptions() {
return this.#subscriptions;
}
setSplatSelector(name, f) {
this.#splatSelector = [name, f];
setMappedSelector(name, f) {
this.#mappedSelectors = {
...this.#mappedSelectors,
[name]: f,
}
}
get middleware() {
@ -107,7 +162,7 @@ export class Updux {
get selectors() {
return this.#memoSelectors(
this.#selectors,
this.#splatSelector,
this.#mappedSelectors,
this.#subduxes
);
}
@ -125,7 +180,7 @@ export class Updux {
}
addSubscription(subscription) {
this.#subscriptions = [...this.#subscriptions, subscription];
this.#reactions = [...this.#reactions, subscription];
}
setAction(type, payloadFunc) {
@ -235,7 +290,7 @@ export class Updux {
}
subscribeAll(store) {
let results = this.#subscriptions.map((sub) =>
let results = this.#reactions.map((sub) =>
this.subscribeTo(store, sub)
);
@ -249,14 +304,14 @@ export class Updux {
}
}
if (this.#splatReaction) {
if (this.#mappedReaction) {
results.push(
this.subscribeTo(
store,
this.splatSubscriber(
store,
this.#subduxes['*'],
this.#splatReaction
this.#mappedReaction
)
)
);
@ -271,11 +326,16 @@ export class Updux {
}
createStore(initial) {
const store = reduxCreateStore(
const store : {
getState: Function,
dispatch: Function,
selectors: Record<string,Function>,
actions: Record<string,Function>,
} = reduxCreateStore(
this.reducer,
initial ?? this.initial,
applyMiddleware(this.middleware)
);
) as any;
store.actions = this.actions;

View File

@ -1,5 +1,5 @@
import { isPlainObject, mapValues } from 'lodash-es';
import u from '@yanick/updeep';
import { isPlainObject, mapValues } from 'lodash';
import u from 'updeep';
export function buildInitial(initial, subduxes = {}) {
if (!isPlainObject(initial) && Object.keys(subduxes).length > 0)

View File

@ -1,6 +1,6 @@
import { test } from 'tap';
import { buildInitial } from './index.js';
import { buildInitial } from './index.ts';
test('basic', async (t) => {
t.same(buildInitial({ a: 1 }, { b: { initial: { c: 2 } } }), {

10
src/buildInitial/test.ts Normal file
View File

@ -0,0 +1,10 @@
import { buildInitial } from '.';
test('basic', () => {
expect(
buildInitial({ a: 1 }, { b: { initial: { c: 2 } } })
).toMatchObject({
a: 1,
b: { c: 2 },
});
});

View File

@ -1,5 +1,5 @@
import u from '@yanick/updeep';
import { mapValues, map, get } from 'lodash-es';
import u from 'updeep';
import { mapValues, map, get } from 'lodash';
import { Updux } from '../Updux.js';
const middlewareFor = (type, middleware) => (api) => (next) => (action) => {

View File

@ -1,4 +1,4 @@
import { map, mapValues, merge } from 'lodash-es';
import { map, mapValues, merge } from 'lodash';
export function buildSelectors(localSelectors, splatSelector, subduxes) {
const subSelectors = map(subduxes, ({ selectors }, slice) => {

View File

@ -1,5 +1,5 @@
import u from '@yanick/updeep';
import { mapValues } from 'lodash-es';
import u from 'updeep';
import { mapValues } from 'lodash';
export function buildUpreducer(initial, mutations, subduxes = {}) {
const subReducers =

View File

@ -1,50 +0,0 @@
import { test } from 'tap';
import u from 'updeep';
import add from 'lodash/fp/add.js';
import { Updux } from './index.js';
test('README.md', async (t) => {
const otherDux = new Updux({});
const dux = new Updux({
initial: {
counter: 0,
},
actions: {
inc: null,
},
subduxes: {
otherDux,
},
});
dux.setMutation('inc', (increment) => u({ counter: add(increment) }));
dux.addEffect('*', (api) => (next) => (action) => {
next(action);
});
const store = dux.createStore();
store.dispatch.inc(1);
t.equal(store.getState().counter, 1);
});
test('tutorial', async (t) => {
const todosDux = new Updux({
initial: {
next_id: 1,
todos: [],
}
});
todosDux.setAction( 'addTodo' );
todosDux.setAction( 'todoDone' );
t.same( todosDux.actions.addTodo('write tutorial') , { type: 'addTodo', payload: 'write tutorial' });
})

1
src/index.d.ts vendored
View File

@ -1 +0,0 @@
export { Updux };

View File

@ -1,2 +1,2 @@
export { Updux } from './Updux.js';
export { action } from './actions.js';
export { Updux } from './Updux';
export { action } from './actions';

View File

@ -1,25 +1,24 @@
import { test } from 'tap';
import u from '@yanick/updeep';
import u from 'updeep';
import { Updux } from './Updux.js';
import { Updux } from './Updux';
test('basic reducer', async (t) => {
test('basic reducer', () => {
const dux = new Updux({});
t.type(dux.reducer, 'function');
expect(typeof dux.reducer).toBe('function');
t.same(dux.reducer({ a: 1 }, { type: 'foo' }), { a: 1 }, 'noop');
expect(dux.reducer({ a: 1 }, { type: 'foo' })).toMatchObject({a:1}); // noop
});
test('basic upreducer', async (t) => {
test('basic upreducer', () => {
const dux = new Updux({});
t.type(dux.upreducer, 'function');
expect(typeof dux.upreducer).toBe('function');
t.same(dux.upreducer({ type: 'foo' })({ a: 1 }), { a: 1 }, 'noop');
expect(dux.upreducer({type:'foo'})({ a: 1 })).toMatchObject({a:1}); // noop
});
test('reducer with action', async (t) => {
test('reducer with action', () => {
const dux = new Updux({
actions: {
inc: null,
@ -29,5 +28,5 @@ test('reducer with action', async (t) => {
},
});
t.same(dux.reducer({ a: 1 }, { type: 'inc' }), { a: 2 });
expect(dux.reducer({ a: 1 }, { type: 'inc' })).toMatchObject({a:2});
});

2
src/types.ts Normal file
View File

@ -0,0 +1,2 @@
export type Dict<T> = Record<string, T>;

View File

@ -1,74 +1,19 @@
{
"include": ["./types/*.d.ts"],
"exclude": [ "types/index.test-d.ts" ],
"include": [ "./src" ],
"exclude": [ "./docs", "./dist" ],
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
"incremental": false, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
"declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./ts-out", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"rootDirs": ["./types"], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": ["./types"], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "./types", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
"rootDir": "src",
"outDir": "dist",
"target": "es2020",
"lib": [
"es2020"
],
"module": "ES2020",
"moduleResolution": "Node",
"strict": false,
"sourceMap": true,
"allowSyntheticDefaultImports": true,
"declaration": true,
"allowJs": true
}
}

52
types/index.d.ts vendored
View File

@ -7,58 +7,6 @@ type Mutation<TState = unknown> = (
export * from './actions';
/**
* Configuration object typically passed to the constructor of the class Updux.
*/
export interface UpduxConfig<TState = unknown> {
/**
* Local initial state.
* @default {}
*/
initial?: TState;
/**
* Subduxes to be merged to this dux.
*/
subduxes?: Dict<Updux | UpduxConfig>;
/**
* Local actions.
*/
actions?: Record<string, any>;
/**
* Local selectors.
*/
selectors?: Record<string, Function>;
/**
* Local mutations
*/
mutations?: Record<string, Function>;
/**
* Selectors to apply to the mapped subduxes. Only
* applicable if the dux is a mapping dux.
*/
mappedSelectors?: Record<string, Function>;
/**
* Local effects.
*/
effects?: Record<string, Function>;
/**
* Local reactions.
*/
reactions?: Record<string, Function>;
/**
* If true, enables mapped reactions. Additionally, it can be
* a reaction function, which will treated as a regular
* reaction for the mapped dux.
*/
mappedReaction?: Function | boolean;
}
export class Updux<TState = unknown> {
constructor(config: Partial<UpduxConfig<TState>>);

1
types/index.test-d.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export {};

18
types/index.test-d.js Normal file
View File

@ -0,0 +1,18 @@
import { expectAssignable, expectType } from 'tsd';
import { Updux, action } from '.';
const dux = new Updux({});
expectType(dux.initial);
() => {
const dux = new Updux({});
expectAssignable(dux);
};
// ActionGenerator
() => {
let a = action('a');
expectAssignable(a);
expectAssignable(a);
let b = action('b', (() => ({})));
const c = b("foo");
expectAssignable(c);
};
//# sourceMappingURL=index.test-d.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.test-d.js","sourceRoot":"","sources":["index.test-d.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,gBAAgB,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAE9D,OAAO,EAAE,KAAK,EAAmB,MAAM,EAAE,MAAM,GAAG,CAAC;AAEnD,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC;AAC1B,UAAU,CAAW,GAAG,CAAC,OAAO,CAAE,CAAC;AAGnC,GAAG,EAAE;IAED,MAAM,GAAG,GAAG,IAAI,KAAK,CAAc,EAAE,CAAC,CAAC;IAEvC,gBAAgB,CAAwB,GAAG,CAAC,CAAC;AACjD,CAAC,CAAA;AAED,kBAAkB;AAClB,GAAG,EAAE;IACD,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACpB,gBAAgB,CAAiB,CAAC,CAAC,CAAC;IACpC,gBAAgB,CAA4B,CAAC,CAAC,CAAC;IAE/C,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAkD,CAAE,CAAC;IACpF,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACnB,gBAAgB,CAA+B,CAAC,CAAC,CAAC;AAGtD,CAAC,CAAA"}

1
types/test.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export var __esModule: boolean;