From 199ab2ba3167fa9db99be3354e80e2d7461b6329 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 12:47:11 -0400 Subject: [PATCH 01/15] move buildInitial to ts --- index.d.ts | 1 + index.test-d.ts | 17 +++++++++++++++++ src/buildInitial/index.js | 8 -------- src/buildInitial/index.ts | 11 +++++++++++ src/buildInitial/test-d.js | 0 tsconfig.json | 15 +++++++++------ 6 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 index.d.ts create mode 100644 index.test-d.ts delete mode 100644 src/buildInitial/index.js create mode 100644 src/buildInitial/index.ts delete mode 100644 src/buildInitial/test-d.js diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/index.d.ts @@ -0,0 +1 @@ + diff --git a/index.test-d.ts b/index.test-d.ts new file mode 100644 index 0000000..6a31513 --- /dev/null +++ b/index.test-d.ts @@ -0,0 +1,17 @@ +import { expectType, expectError } from 'tsd'; + +import buildInitial from './src/buildInitial'; + +expectType<{}>(buildInitial()); + +type MyState = { + foo: { + bar: number + }, + baz: string, +} + +expectType(buildInitial()); + +expectError( buildInitial({ foo: { bar: "potato" } }) ); + diff --git a/src/buildInitial/index.js b/src/buildInitial/index.js deleted file mode 100644 index 7df345e..0000000 --- a/src/buildInitial/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import fp from 'lodash/fp'; - -export default function buildInitial( - initial= {}, - subduxes = {}, -) { - return fp.isPlainObject(initial) ? fp.mergeAll([subduxes, initial]) : initial; -} diff --git a/src/buildInitial/index.ts b/src/buildInitial/index.ts new file mode 100644 index 0000000..96e2878 --- /dev/null +++ b/src/buildInitial/index.ts @@ -0,0 +1,11 @@ +import fp from 'lodash/fp'; + +function buildInitial( initial?: Partial, subduxes?: Partial ): S; +function buildInitial( + initial = {}, + subduxes = {} , +) { + return fp.isPlainObject(initial) ? fp.mergeAll([subduxes, initial]) : initial; +} + +export default buildInitial; diff --git a/src/buildInitial/test-d.js b/src/buildInitial/test-d.js deleted file mode 100644 index e69de29..0000000 diff --git a/tsconfig.json b/tsconfig.json index 63ebd76..29bc38a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,20 +1,23 @@ { + "include": [ + "./src/**/*" + ], "compilerOptions": { /* Basic Options */ "incremental": true, /* Enable incremental compilation */ "target": "ES2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "lib": [ "dom", "es2019" ], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ + "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ - "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ + "declaration": false, /* Generates corresponding '.d.ts' file. */ + "declarationMap": false, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": false, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./dist", /* Redirect output structure to the directory. */ "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ + //"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. */ @@ -24,7 +27,7 @@ /* Strict Type-Checking Options */ "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ + "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. */ From 11ff89501dd7eba576bcd26a79f33fa07c3dad6d Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 12:56:12 -0400 Subject: [PATCH 02/15] make jest work with js and ts --- jest.config.js | 9 +++++++++ package.json | 30 ++++++++++++++++++------------ 2 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 jest.config.js diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..2d94fac --- /dev/null +++ b/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + "roots": [ + "./src" + ], + "transform": { + "^.+\\.ts$": "ts-jest", + "^.+\\.js$": "babel-jest", + }, +} diff --git a/package.json b/package.json index 6aa2228..9c44d41 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,12 @@ "@babel/cli": "^7.6.4", "@babel/core": "^7.6.4", "@babel/preset-env": "^7.6.3", + "@types/lodash": "^4.14.144", "babel-jest": "^24.9.0", "jest": "^24.9.0", + "ts-jest": "^24.1.0", + "tsd": "^0.10.0", + "typescript": "^3.6.4", "updeep": "^1.2.0" }, "license": "MIT", @@ -20,16 +24,18 @@ "test": "jest" }, "version": "0.1.0", - "repository": { - "type": "git", - "url": "git+https://github.com/yanick/updux.git" - }, - "keywords": [ - "redux", "updeep" - ], - "author": "Yanick Champoux (http://techblog.babyl.ca)", - "bugs": { - "url": "https://github.com/yanick/updux/issues" - }, - "homepage": "https://github.com/yanick/updux#readme" + "repository": { + "type": "git", + "url": "git+https://github.com/yanick/updux.git" + }, + "keywords": [ + "redux", + "updeep" + ], + "author": "Yanick Champoux (http://techblog.babyl.ca)", + "bugs": { + "url": "https://github.com/yanick/updux/issues" + }, + "homepage": "https://github.com/yanick/updux#readme", + "types": "./index.d.ts" } From f092526deda602720d057759843b7bc5723c84cf Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 13:25:39 -0400 Subject: [PATCH 03/15] buildUpreducer --- src/buildUpreducer/index.js | 15 --------------- src/buildUpreducer/index.ts | 19 +++++++++++++++++++ src/types.ts | 10 ++++++++++ 3 files changed, 29 insertions(+), 15 deletions(-) delete mode 100644 src/buildUpreducer/index.js create mode 100644 src/buildUpreducer/index.ts create mode 100644 src/types.ts diff --git a/src/buildUpreducer/index.js b/src/buildUpreducer/index.js deleted file mode 100644 index 52724f6..0000000 --- a/src/buildUpreducer/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import fp from 'lodash/fp'; - -export default function buildUpreducer(initial, mutations) { - return (action = {}) => (state) => { - if (state === null) state = initial; - - const a = - mutations[(action).type] || - mutations['*']; - - if(!a) return state; - - return a((action).payload, action)(state); - }; -} diff --git a/src/buildUpreducer/index.ts b/src/buildUpreducer/index.ts new file mode 100644 index 0000000..bedc8b2 --- /dev/null +++ b/src/buildUpreducer/index.ts @@ -0,0 +1,19 @@ +import fp from 'lodash/fp'; + +import { Dictionary, Mutation, UpduxAction } from '../types'; + +function buildUpreducer(initial: S, mutations: Dictionary> ) { + return (action :UpduxAction) => (state: S) => { + if (state === null) state = initial; + + const a = + mutations[action.type] || + mutations['*']; + + if(!a) return state; + + return a(action.payload, action)(state); + }; +} + +export default buildUpreducer; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..414d299 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,10 @@ +import { Action } from 'redux'; + +export type UpduxAction = Action & Partial<{ + payload: any, + meta: any, +}> + +export type Dictionary = { [key: string]: T }; + +export type Mutation = (payload: any, action: Action) => (state: S) => S ; From d9bac9dd7d637dfd6e580dc981abf8a8dbdb54f4 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 13:28:13 -0400 Subject: [PATCH 04/15] index.js => ts --- src/{index.js => index.ts} | 4 +++- src/types.ts | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) rename src/{index.js => index.ts} (54%) diff --git a/src/index.js b/src/index.ts similarity index 54% rename from src/index.js rename to src/index.ts index ffdb8d5..d7458cb 100644 --- a/src/index.js +++ b/src/index.ts @@ -3,6 +3,8 @@ import u from 'updeep'; import Updux from './updux'; -export default function updux(config) { +import { UpduxConfig } from './types'; + +export default function updux(config: UpduxConfig) { return new Updux(config); } diff --git a/src/types.ts b/src/types.ts index 414d299..afdf4d1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -8,3 +8,6 @@ export type UpduxAction = Action & Partial<{ export type Dictionary = { [key: string]: T }; export type Mutation = (payload: any, action: Action) => (state: S) => S ; + +export type UpduxConfig = Partial<{ +}>; From ca89c53c0b622d9842e99a4ac6c134b602fdd5de Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 13:57:40 -0400 Subject: [PATCH 05/15] buildActions => ts --- src/buildActions/{index.js => index.ts} | 16 +++++++++++----- src/buildUpreducer/index.ts | 4 ++-- src/types.ts | 10 +++++----- 3 files changed, 18 insertions(+), 12 deletions(-) rename src/buildActions/{index.js => index.ts} (60%) diff --git a/src/buildActions/index.js b/src/buildActions/index.ts similarity index 60% rename from src/buildActions/index.js rename to src/buildActions/index.ts index c3b4848..0feaa10 100644 --- a/src/buildActions/index.js +++ b/src/buildActions/index.ts @@ -1,8 +1,14 @@ import fp from 'lodash/fp'; +import { Action } from '../types'; -function actionFor(type) { - const creator = ( (payload = undefined, meta = undefined) => - fp.pickBy(v => v !== undefined)({type, payload, meta}) +interface ActionCreator { + ( ...args: any[] ): Action; + _genericAction?: boolean +} + +function actionFor(type:string) { + const creator : ActionCreator = ( (payload = undefined, meta = undefined) => + fp.pickBy(v => v !== undefined)({type, payload, meta}) as Action ); creator._genericAction = true; @@ -11,7 +17,7 @@ function actionFor(type) { } export default function buildActions( - creators = {}, + creators : { [action: string]: Function } = {}, mutations = {}, effects = {}, subActions = [], @@ -31,7 +37,7 @@ export default function buildActions( ...generic, ...crafted, ...Object.entries(creators).map( - ([type, payload]) => [type, (...args) => ({ type, payload: payload(...args) })] + ([type, payload]: [ string, Function ]) => [type, (...args: any) => ({ type, payload: payload(...args) })] ), ]; diff --git a/src/buildUpreducer/index.ts b/src/buildUpreducer/index.ts index bedc8b2..5209d76 100644 --- a/src/buildUpreducer/index.ts +++ b/src/buildUpreducer/index.ts @@ -1,9 +1,9 @@ import fp from 'lodash/fp'; -import { Dictionary, Mutation, UpduxAction } from '../types'; +import { Dictionary, Mutation, Action } from '../types'; function buildUpreducer(initial: S, mutations: Dictionary> ) { - return (action :UpduxAction) => (state: S) => { + return (action :Action) => (state: S) => { if (state === null) state = initial; const a = diff --git a/src/types.ts b/src/types.ts index afdf4d1..0aa5fe9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,9 +1,9 @@ -import { Action } from 'redux'; -export type UpduxAction = Action & Partial<{ - payload: any, - meta: any, -}> +export type Action = { + type: string, + payload?: any, + meta?: any, +} export type Dictionary = { [key: string]: T }; From beae30d0911a3a55710234fd62f5200411637074 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 15:55:12 -0400 Subject: [PATCH 06/15] buildMiddleware => ts --- src/buildMiddleware.js | 30 -------------------------- src/buildMiddleware/index.ts | 42 ++++++++++++++++++++++++++++++++++++ src/types.ts | 2 ++ 3 files changed, 44 insertions(+), 30 deletions(-) delete mode 100644 src/buildMiddleware.js create mode 100644 src/buildMiddleware/index.ts diff --git a/src/buildMiddleware.js b/src/buildMiddleware.js deleted file mode 100644 index bad859d..0000000 --- a/src/buildMiddleware.js +++ /dev/null @@ -1,30 +0,0 @@ -import fp from 'lodash/fp'; - -const MiddlewareFor = (type,mw) => api => next => action => { - if (type !== '*' && action.type !== type) return next(action); - - return mw(api)(next)(action); -}; - -export default function buildMiddleware( - effects = {}, - actions = {}, - subduxes = {}, -) { - return api => { - for (let type in actions) { - api.dispatch[type] = (...args) => api.dispatch(actions[type](...args)); - } - - return original_next => { - return [ - ...fp.toPairs(effects).map(([type, effect]) => - MiddlewareFor(type,effect) - ), - ...fp.map('middleware', subduxes), - ] - .filter(x => x) - .reduceRight((next, mw) => mw(api)(next), original_next); - }; - }; -} diff --git a/src/buildMiddleware/index.ts b/src/buildMiddleware/index.ts new file mode 100644 index 0000000..519c62d --- /dev/null +++ b/src/buildMiddleware/index.ts @@ -0,0 +1,42 @@ +import fp from 'lodash/fp'; + +import { Middleware } from 'redux'; +import { Dictionary, ActionCreator, Action } from '../types'; + +const MiddlewareFor = (type: any, mw: Middleware ): Middleware => api => next => action => { + if (type !== '*' && action.type !== type) return next(action); + + return mw(api)(next)(action); +}; + +type Next = (action: Action) => any; + +function buildMiddleware( + effects: Dictionary, + actions: Dictionary, + subMiddlewares: Middleware[], +): Middleware +function buildMiddleware( + effects = {}, + actions = {}, + subduxes = {}, +) { + return (api: any) => { + for (let type in actions) { + api.dispatch[type] = (...args:any[]) => api.dispatch(((actions as any)[type] as any)(...args)); + } + + return (original_next: Next)=> { + return [ + ...fp.toPairs(effects).map(([type, effect]) => + MiddlewareFor(type,effect as Middleware) + ), + ...fp.map('middleware', subduxes), + ] + .filter(x => x) + .reduceRight((next, mw) => mw(api)(next), original_next); + }; + }; +} + +export default buildMiddleware; diff --git a/src/types.ts b/src/types.ts index 0aa5fe9..78701e6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,5 +9,7 @@ export type Dictionary = { [key: string]: T }; export type Mutation = (payload: any, action: Action) => (state: S) => S ; +export type ActionCreator = (...args: any[] ) => Action; + export type UpduxConfig = Partial<{ }>; From 4754fb2377bdad0982359ad940d3e6c4636b99b1 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 16:02:42 -0400 Subject: [PATCH 07/15] buildCreateStore => ts --- src/buildCreateStore.js | 17 ----------------- src/buildCreateStore/index.ts | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 17 deletions(-) delete mode 100644 src/buildCreateStore.js create mode 100644 src/buildCreateStore/index.ts diff --git a/src/buildCreateStore.js b/src/buildCreateStore.js deleted file mode 100644 index 76654ba..0000000 --- a/src/buildCreateStore.js +++ /dev/null @@ -1,17 +0,0 @@ -import { createStore as reduxCreateStore, applyMiddleware } from 'redux'; - -export default function buildCreateStore( reducer, initial, middleware, - actions ) { - return () => { - const store = reduxCreateStore( reducer, initial, - applyMiddleware( middleware) - ); - for ( let a in actions ) { - store.dispatch[a] = (...args) => { - store.dispatch(actions[a](...args)) - }; - } - - return store; - } -}; diff --git a/src/buildCreateStore/index.ts b/src/buildCreateStore/index.ts new file mode 100644 index 0000000..650c448 --- /dev/null +++ b/src/buildCreateStore/index.ts @@ -0,0 +1,31 @@ +import { + createStore as reduxCreateStore, + applyMiddleware, + Middleware, + Reducer, +} from 'redux'; +import { ActionCreator, Dictionary } from '../types'; + +function buildCreateStore( + reducer: Reducer, + initial: S, + middleware: Middleware, + actions: Dictionary, +) { + return () => { + const store = reduxCreateStore( + reducer, + initial, + applyMiddleware(middleware), + ); + for (let a in actions) { + ( store.dispatch as any)[a] = (...args: any[]) => { + store.dispatch(actions[a](...args)); + }; + } + + return store; + }; +} + +export default buildCreateStore; From 125800a8b97170a45abc3d8eb9619b293e14ff45 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 17:01:48 -0400 Subject: [PATCH 08/15] buildMutations => ts --- src/buildMutations/index.js | 53 ----------------------------- src/buildMutations/index.ts | 66 +++++++++++++++++++++++++++++++++++++ src/types.ts | 2 +- 3 files changed, 67 insertions(+), 54 deletions(-) delete mode 100644 src/buildMutations/index.js create mode 100644 src/buildMutations/index.ts diff --git a/src/buildMutations/index.js b/src/buildMutations/index.js deleted file mode 100644 index f7dab81..0000000 --- a/src/buildMutations/index.js +++ /dev/null @@ -1,53 +0,0 @@ -import fp from 'lodash/fp'; -import u from 'updeep'; - -const composeMutations = (mutations) => - mutations.reduce( (m1,m2) => - (payload=null,action={}) => state => m2(payload,action)( - m1(payload,action)(state) )); - -export default function buildMutations(mutations = {}, subduxes= {}) { - // we have to differentiate the subduxes with '*' than those - // without, as the root '*' is not the same as any sub-'*' - - const actions = fp.uniq( Object.keys(mutations).concat( - ...Object.values( subduxes ).map( ({mutations = {}}) => Object.keys(mutations) ) - ) ); - - let mergedMutations = {}; - - let [ globby, nonGlobby ] = fp.partition( - ([_,{mutations={}}]) => mutations['*'], - Object.entries(subduxes) - ); - - globby = - fp.flow([ - fp.fromPairs, - fp.mapValues( - ({reducer}) => (_,action={}) => state => - reducer(state,action) ), - ])(globby); - - const globbyMutation = (payload,action) => u( - fp.mapValues( (mut) => mut(payload,action) )(globby) - ); - - actions.forEach( action => { - mergedMutations[action] = [ globbyMutation ] - }); - - nonGlobby.forEach( ([slice, {mutations={},reducer={}}]) => { - Object.entries(mutations).forEach(([type,mutation]) => { - const localized = (payload=null,action={}) => u.updateIn( slice )( (mutation)(payload,action) ); - - mergedMutations[type].push(localized); - }) - }); - - Object.entries(mutations).forEach(([type,mutation]) => { - mergedMutations[type].push(mutation); - }); - - return fp.mapValues( composeMutations )(mergedMutations); -} diff --git a/src/buildMutations/index.ts b/src/buildMutations/index.ts new file mode 100644 index 0000000..472a1a0 --- /dev/null +++ b/src/buildMutations/index.ts @@ -0,0 +1,66 @@ +import fp from 'lodash/fp'; +import u from 'updeep'; +import {Mutation, Action, Dictionary} from '../types'; + +const composeMutations = (mutations: Mutation[]) => + mutations.reduce((m1, m2) => (payload: any = null, action: Action) => state => + m2(payload, action)(m1(payload, action)(state)), + ); + +type SubMutations = { + [ slice: string ]: Dictionary +} + +function buildMutations( + mutations :Dictionary = {}, + subduxes = {} +) { + // we have to differentiate the subduxes with '*' than those + // without, as the root '*' is not the same as any sub-'*' + + const actions = fp.uniq( + Object.keys(mutations).concat( + ...Object.values(subduxes).map(({mutations = {}}:any) => + Object.keys(mutations), + ), + ), + ); + + let mergedMutations :Dictionary = {}; + + let [globby, nonGlobby] = fp.partition( + ([_, {mutations = {}}]:any) => mutations['*'], + Object.entries(subduxes), + ); + + globby = fp.flow([ + fp.fromPairs, + fp.mapValues(({reducer}) => (_:any, action :Action) => ( state: any ) => + reducer(state, action), + ), + ])(globby); + + const globbyMutation = (payload:any, action:Action) => + u(fp.mapValues((mut:any) => mut(payload, action))(globby)); + + actions.forEach(action => { + mergedMutations[action] = [globbyMutation]; + }); + + nonGlobby.forEach(([slice, {mutations = {}, reducer = {}}]:any[]) => { + Object.entries(mutations).forEach(([type, mutation]) => { + const localized = (payload = null, action :Action) => + u.updateIn(slice)((mutation as Mutation)(payload, action)); + + mergedMutations[type].push(localized); + }); + }); + + Object.entries(mutations).forEach(([type, mutation]) => { + mergedMutations[type].push(mutation); + }); + + return fp.mapValues(composeMutations)(mergedMutations); +} + +export default buildMutations; diff --git a/src/types.ts b/src/types.ts index 78701e6..5bacf08 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,7 +7,7 @@ export type Action = { export type Dictionary = { [key: string]: T }; -export type Mutation = (payload: any, action: Action) => (state: S) => S ; +export type Mutation = (payload: any, action: Action) => (state: S) => S ; export type ActionCreator = (...args: any[] ) => Action; From 0949200bac26cd34a451cc49a5ee8ce4a27fd135 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 13:48:19 -0400 Subject: [PATCH 09/15] index wip --- src/types.ts | 1 + src/{updux.js => updux.ts} | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) rename src/{updux.js => updux.ts} (81%) diff --git a/src/types.ts b/src/types.ts index 5bacf08..782d9a7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -12,4 +12,5 @@ export type Mutation = (payload: any, action: Action) => (state: S) => S export type ActionCreator = (...args: any[] ) => Action; export type UpduxConfig = Partial<{ + subduxes: {} }>; diff --git a/src/updux.js b/src/updux.ts similarity index 81% rename from src/updux.js rename to src/updux.ts index 44d6303..1af27a5 100644 --- a/src/updux.js +++ b/src/updux.ts @@ -6,14 +6,17 @@ import buildMutations from './buildMutations'; import buildCreateStore from './buildCreateStore'; import buildMiddleware from './buildMiddleware'; import buildUpreducer from './buildUpreducer'; +import { UpduxConfig, Dictionary } from './types'; export class Updux { - constructor(config) { + subduxes: Dictionary; + + constructor(config: UpduxConfig) { this.subduxes = fp.mapValues( - value => fp.isPlainObject(value) ? new Updux(value ) : value )(fp.getOr({},'subduxes',config) - ); + (value:UpduxConfig|Updux) => fp.isPlainObject(value) ? new Updux(value) : value )(fp.getOr({},'subduxes',config) + ) as Dictionary; this.actions = buildActions( From 76a75c91202876a5ca0a2c6001f9e40b5aadf408 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 14:44:12 -0400 Subject: [PATCH 10/15] wip --- src/types.ts | 9 ++++++++- src/updux.ts | 5 ++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/types.ts b/src/types.ts index 782d9a7..b1df809 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,8 +9,15 @@ export type Dictionary = { [key: string]: T }; export type Mutation = (payload: any, action: Action) => (state: S) => S ; +type ActionPayloadGenerator = (...args:any[]) => any; + export type ActionCreator = (...args: any[] ) => Action; export type UpduxConfig = Partial<{ - subduxes: {} + subduxes: {}, + actions: { + [ type: string ]: ActionPayloadGenerator + }, + mutations: any, + effects: any, }>; diff --git a/src/updux.ts b/src/updux.ts index 1af27a5..cbe237b 100644 --- a/src/updux.ts +++ b/src/updux.ts @@ -6,12 +6,15 @@ import buildMutations from './buildMutations'; import buildCreateStore from './buildCreateStore'; import buildMiddleware from './buildMiddleware'; import buildUpreducer from './buildUpreducer'; -import { UpduxConfig, Dictionary } from './types'; +import { UpduxConfig, Dictionary, Action, ActionCreator } from './types'; + export class Updux { subduxes: Dictionary; + actions: Dictionary + constructor(config: UpduxConfig) { this.subduxes = fp.mapValues( From f0e3b15fa43bab0d8f9783c4d8c0b10793629bd8 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 17:47:43 -0400 Subject: [PATCH 11/15] updux => ts --- src/buildActions/index.ts | 26 +++++++++++++------------- src/buildInitial/index.ts | 8 +++++--- src/buildMiddleware/index.ts | 13 ++++--------- src/buildUpreducer/index.ts | 4 ++-- src/types.ts | 7 +++++-- src/updux.ts | 30 +++++++++++++++++++++--------- 6 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/buildActions/index.ts b/src/buildActions/index.ts index 0feaa10..0455716 100644 --- a/src/buildActions/index.ts +++ b/src/buildActions/index.ts @@ -1,5 +1,5 @@ import fp from 'lodash/fp'; -import { Action } from '../types'; +import { Action, ActionPayloadGenerator, Dictionary } from '../types'; interface ActionCreator { ( ...args: any[] ): Action; @@ -16,27 +16,25 @@ function actionFor(type:string) { return creator; } -export default function buildActions( - creators : { [action: string]: Function } = {}, - mutations = {}, - effects = {}, - subActions = [], -) { +type ActionPair = [ string, ActionCreator ]; + +function buildActions( + generators : Dictionary = {}, + actionNames: string[] = [], + subActions : ActionPair[] = [], +):Dictionary { // priority => generics => generic subs => craft subs => creators const [ crafted, generic ] = fp.partition( ([type,f]) => !f._genericAction - )( fp.flatten( subActions.map( x => Object.entries(x) ) ).filter( - ([_,f]) => f - ) ) + )( subActions ); const actions = [ - ...([ ...Object.keys(mutations), ...Object.keys(effects) ] - .map( type => [ type, actionFor(type) ] )), + ...(actionNames.map( type => [ type, actionFor(type) ] )), ...generic, ...crafted, - ...Object.entries(creators).map( + ...Object.entries(generators).map( ([type, payload]: [ string, Function ]) => [type, (...args: any) => ({ type, payload: payload(...args) })] ), ]; @@ -44,3 +42,5 @@ export default function buildActions( return fp.fromPairs(actions); } + +export default buildActions; diff --git a/src/buildInitial/index.ts b/src/buildInitial/index.ts index 96e2878..a32431d 100644 --- a/src/buildInitial/index.ts +++ b/src/buildInitial/index.ts @@ -1,9 +1,11 @@ import fp from 'lodash/fp'; +import { Dictionary } from '../types'; -function buildInitial( initial?: Partial, subduxes?: Partial ): S; +function buildInitial( initial: S, subduxes?: Dictionary ): S; +function buildInitial( initial?: Partial, subduxes?: Partial ): S extends object ? S : never; function buildInitial( - initial = {}, - subduxes = {} , + initial : any = {}, + subduxes : any = {} , ) { return fp.isPlainObject(initial) ? fp.mergeAll([subduxes, initial]) : initial; } diff --git a/src/buildMiddleware/index.ts b/src/buildMiddleware/index.ts index 519c62d..bb5d61d 100644 --- a/src/buildMiddleware/index.ts +++ b/src/buildMiddleware/index.ts @@ -12,14 +12,9 @@ const MiddlewareFor = (type: any, mw: Middleware ): Middleware => api => next => type Next = (action: Action) => any; function buildMiddleware( - effects: Dictionary, - actions: Dictionary, - subMiddlewares: Middleware[], -): Middleware -function buildMiddleware( - effects = {}, - actions = {}, - subduxes = {}, + effects : Dictionary= {}, + actions : Dictionary= {}, + subMiddlewares :Middleware[] = [], ) { return (api: any) => { for (let type in actions) { @@ -31,7 +26,7 @@ function buildMiddleware( ...fp.toPairs(effects).map(([type, effect]) => MiddlewareFor(type,effect as Middleware) ), - ...fp.map('middleware', subduxes), + ...subMiddlewares ] .filter(x => x) .reduceRight((next, mw) => mw(api)(next), original_next); diff --git a/src/buildUpreducer/index.ts b/src/buildUpreducer/index.ts index 5209d76..98fac0e 100644 --- a/src/buildUpreducer/index.ts +++ b/src/buildUpreducer/index.ts @@ -1,8 +1,8 @@ import fp from 'lodash/fp'; -import { Dictionary, Mutation, Action } from '../types'; +import { Dictionary, Mutation, Action, Upreducer } from '../types'; -function buildUpreducer(initial: S, mutations: Dictionary> ) { +function buildUpreducer(initial: S, mutations: Dictionary> ): Upreducer { return (action :Action) => (state: S) => { if (state === null) state = initial; diff --git a/src/types.ts b/src/types.ts index b1df809..a303474 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,11 +9,12 @@ export type Dictionary = { [key: string]: T }; export type Mutation = (payload: any, action: Action) => (state: S) => S ; -type ActionPayloadGenerator = (...args:any[]) => any; +export type ActionPayloadGenerator = (...args:any[]) => any; export type ActionCreator = (...args: any[] ) => Action; -export type UpduxConfig = Partial<{ +export type UpduxConfig = Partial<{ + initial: S, subduxes: {}, actions: { [ type: string ]: ActionPayloadGenerator @@ -21,3 +22,5 @@ export type UpduxConfig = Partial<{ mutations: any, effects: any, }>; + +export type Upreducer = (action:Action) => (state:S) => S; diff --git a/src/updux.ts b/src/updux.ts index cbe237b..f734082 100644 --- a/src/updux.ts +++ b/src/updux.ts @@ -6,15 +6,28 @@ import buildMutations from './buildMutations'; import buildCreateStore from './buildCreateStore'; import buildMiddleware from './buildMiddleware'; import buildUpreducer from './buildUpreducer'; -import { UpduxConfig, Dictionary, Action, ActionCreator } from './types'; +import { UpduxConfig, Dictionary, Action, ActionCreator, Mutation, Upreducer } from './types'; +import { Middleware } from 'redux'; -export class Updux { +export class Updux { subduxes: Dictionary; actions: Dictionary + initial: S; + + mutations: Dictionary; + + upreducer: Upreducer; + + reducer: (state:S|undefined,action:Action) => S; + + middleware: Middleware; + + createStore: Function; + constructor(config: UpduxConfig) { this.subduxes = fp.mapValues( @@ -24,12 +37,11 @@ export class Updux { this.actions = buildActions( config.actions, - config.mutations, - config.effects, - Object.values( this.subduxes ).map( ({actions}) => actions ), + [ ...Object.keys(config.mutations||{}), ...Object.keys(config.effects||{} ) ], + fp.flatten( Object.values( this.subduxes ).map( ({actions}:Updux) => Object.entries(actions) ) ), ) - this.initial = buildInitial( + this.initial = buildInitial( config.initial, fp.mapValues( ({initial}) => initial )(this.subduxes) ); @@ -42,16 +54,16 @@ export class Updux { ); this.reducer = (state,action) => { - return this.upreducer(action)(state); + return this.upreducer(action)(state as S); } this.middleware = buildMiddleware( config.effects, this.actions, - config.subduxes, + Object.values(this.subduxes).map( sd => sd.middleware ) ); - this.createStore = buildCreateStore(this.reducer,this.initial,this.middleware,this.actions); + this.createStore = buildCreateStore(this.reducer,this.initial,this.middleware,this.actions); } } From 07eaf93e4809308ec121a38128080ca90ecc899b Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 17:51:47 -0400 Subject: [PATCH 12/15] splat.test => ts --- package.json | 1 + src/{splat.test.js => splat.test.ts} | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) rename src/{splat.test.js => splat.test.ts} (81%) diff --git a/package.json b/package.json index 9c44d41..47c5081 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@babel/cli": "^7.6.4", "@babel/core": "^7.6.4", "@babel/preset-env": "^7.6.3", + "@types/jest": "^24.0.19", "@types/lodash": "^4.14.144", "babel-jest": "^24.9.0", "jest": "^24.9.0", diff --git a/src/splat.test.js b/src/splat.test.ts similarity index 81% rename from src/splat.test.js rename to src/splat.test.ts index 57ac5bf..abbd335 100644 --- a/src/splat.test.js +++ b/src/splat.test.ts @@ -1,13 +1,13 @@ import updux from '.'; import u from 'updeep'; -const tracer = chr => u({ tracer: s => (s||'') + chr }); +const tracer = (chr:string) => u({ tracer: (s='') => s + chr }); test( 'mutations, simple', () => { const dux = updux({ mutations: { foo: () => tracer('a'), - '*': (p,a) => tracer('b'), + '*': () => tracer('b'), }, }); @@ -28,14 +28,14 @@ test( 'with subduxes', () => { const dux = updux({ mutations: { foo: () => tracer('a'), - '*': (dummy,a) => tracer('b'), - bar: () => ({bar}) => ({ bar, tracer: bar.tracer }) + '*': () => tracer('b'), + bar: () => ({bar}:any) => ({ bar, tracer: bar.tracer }) }, subduxes: { bar: updux({ mutations: { foo: () => tracer('d'), - '*': (dummy,a) => tracer('e'), + '*': () => tracer('e'), }, }), }, From e63f844bdc7cd2d14f7b45167b600fc1546bb72d Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 17:54:37 -0400 Subject: [PATCH 13/15] actions.test => ts --- src/actions.test.js | 38 -------------------------------------- src/actions.test.ts | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 38 deletions(-) delete mode 100644 src/actions.test.js create mode 100644 src/actions.test.ts diff --git a/src/actions.test.js b/src/actions.test.js deleted file mode 100644 index 8c35f98..0000000 --- a/src/actions.test.js +++ /dev/null @@ -1,38 +0,0 @@ -import updux from '.'; -import u from 'updeep'; - -test( 'actions defined in effects and mutations, multi-level', () => { - - const { actions } = updux({ - effects: { - foo: api => next => action => { }, - }, - mutations: { bar: () => () => null }, - subduxes: { - mysub: { - effects: { baz: api => next => action => { }, }, - mutations: { quux: () => () => null }, - actions: { - foo: (limit) => ({limit}), - }, - }, - myothersub: { - effects: { - foo: () => () => () => {}, - }, - } - }, - }); - - const types = Object.keys(actions); - types.sort(); - - expect( types).toEqual([ 'bar', 'baz', 'foo', 'quux', ]); - - expect( actions.bar() ).toEqual({ type: 'bar' }); - expect( actions.bar('xxx') ).toEqual({ type: 'bar', payload: 'xxx' }); - expect( actions.bar(undefined,'yyy') ).toEqual({ type: 'bar',meta: 'yyy' }); - - expect(actions.foo(12)).toEqual({type: 'foo', payload: { limit: 12 }}); - -}); diff --git a/src/actions.test.ts b/src/actions.test.ts new file mode 100644 index 0000000..4ec6492 --- /dev/null +++ b/src/actions.test.ts @@ -0,0 +1,38 @@ +import updux from '.'; +import u from 'updeep'; + +const noopEffect = () => () => () => {}; + +test('actions defined in effects and mutations, multi-level', () => { + const {actions} = updux({ + effects: { + foo: noopEffect, + }, + mutations: {bar: () => () => null}, + subduxes: { + mysub: { + effects: {baz: noopEffect }, + mutations: {quux: () => () => null}, + actions: { + foo: (limit:number) => ({limit}), + }, + }, + myothersub: { + effects: { + foo: noopEffect, + }, + }, + }, + }); + + const types = Object.keys(actions); + types.sort(); + + expect(types).toEqual(['bar', 'baz', 'foo', 'quux']); + + expect(actions.bar()).toEqual({type: 'bar'}); + expect(actions.bar('xxx')).toEqual({type: 'bar', payload: 'xxx'}); + expect(actions.bar(undefined, 'yyy')).toEqual({type: 'bar', meta: 'yyy'}); + + expect(actions.foo(12)).toEqual({type: 'foo', payload: {limit: 12}}); +}); From 366bd91cf66ee91c4517c03a955be2c622e16d99 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 23 Oct 2019 17:58:18 -0400 Subject: [PATCH 14/15] mw.test => ts --- src/{middleware.test.js => middleware.test.ts} | 6 +++--- src/types.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) rename src/{middleware.test.js => middleware.test.ts} (91%) diff --git a/src/middleware.test.js b/src/middleware.test.ts similarity index 91% rename from src/middleware.test.js rename to src/middleware.test.ts index c8be79b..67b3095 100644 --- a/src/middleware.test.js +++ b/src/middleware.test.ts @@ -7,7 +7,7 @@ test( 'simple effect', () => { const store = updux({ effects: { - foo: api => next => action => { + foo: (api:any) => (next:any) => (action:any) => { tracer(); next(action); }, @@ -30,7 +30,7 @@ test( 'effect and sub-effect', () => { const tracer = jest.fn(); - const tracerEffect = signature => api => next => action => { + const tracerEffect = ( signature: string ) => ( api:any ) => (next:any) => ( action: any ) => { tracer(signature); next(action); }; @@ -83,7 +83,7 @@ test( '"*" effect', () => { test( 'async effect', async () => { - function timeout(ms) { + function timeout(ms:number) { return new Promise(resolve => setTimeout(resolve, ms)); } diff --git a/src/types.ts b/src/types.ts index a303474..ae28d61 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,4 @@ +import { Middleware } from 'redux'; export type Action = { type: string, @@ -20,7 +21,7 @@ export type UpduxConfig = Partial<{ [ type: string ]: ActionPayloadGenerator }, mutations: any, - effects: any, + effects: Dictionary, }>; export type Upreducer = (action:Action) => (state:S) => S; From 44a897ac5a3bf2a6a8527f555531166fc9a9e357 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Thu, 24 Oct 2019 11:17:57 -0400 Subject: [PATCH 15/15] all is converted! --- src/buildInitial/index.test-d.js | 0 src/buildMiddleware/index.ts | 15 ++++++++------- src/{test.js => test.ts} | 26 +++++++++++++------------- src/types.ts | 6 ++++-- src/updux.ts | 16 +++++++++++----- tsconfig.json | 8 ++++---- 6 files changed, 40 insertions(+), 31 deletions(-) delete mode 100644 src/buildInitial/index.test-d.js rename src/{test.js => test.ts} (82%) diff --git a/src/buildInitial/index.test-d.js b/src/buildInitial/index.test-d.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/buildMiddleware/index.ts b/src/buildMiddleware/index.ts index bb5d61d..241a74c 100644 --- a/src/buildMiddleware/index.ts +++ b/src/buildMiddleware/index.ts @@ -1,7 +1,7 @@ import fp from 'lodash/fp'; -import { Middleware } from 'redux'; -import { Dictionary, ActionCreator, Action } from '../types'; +import { Middleware, MiddlewareAPI, Dispatch } from 'redux'; +import { Dictionary, ActionCreator, Action, UpduxDispatch } from '../types'; const MiddlewareFor = (type: any, mw: Middleware ): Middleware => api => next => action => { if (type !== '*' && action.type !== type) return next(action); @@ -11,12 +11,13 @@ const MiddlewareFor = (type: any, mw: Middleware ): Middleware => api => next => type Next = (action: Action) => any; -function buildMiddleware( - effects : Dictionary= {}, +function buildMiddleware( + effects : Dictionary>= {}, actions : Dictionary= {}, - subMiddlewares :Middleware[] = [], -) { - return (api: any) => { + subMiddlewares :Middleware<{},S,UpduxDispatch>[] = [], +): Middleware<{},S,UpduxDispatch> + { + return (api: MiddlewareAPI) => { for (let type in actions) { api.dispatch[type] = (...args:any[]) => api.dispatch(((actions as any)[type] as any)(...args)); } diff --git a/src/test.js b/src/test.ts similarity index 82% rename from src/test.js rename to src/test.ts index 32fb945..4468d57 100644 --- a/src/test.js +++ b/src/test.ts @@ -5,7 +5,7 @@ test('actions from mutations', () => { actions: {foo, bar}, } = updux({ mutations: { - foo: () => x => x, + foo: () => (x:any) => x, }, }); @@ -24,11 +24,11 @@ test('reducer', () => { const {actions, reducer} = updux({ initial: {counter: 1}, mutations: { - inc: () => ({counter}) => ({counter: counter + 1}), + inc: () => ({counter}:{counter:number}) => ({counter: counter + 1}), }, }); - let state = reducer(null, {}); + let state = reducer(null, {type:'noop'}); expect(state).toEqual({counter: 1}); @@ -41,16 +41,16 @@ test( 'sub reducers', () => { const foo = updux({ initial: 1, mutations: { - doFoo: () => (x) => x + 1, - doAll: () => x => x + 10, + doFoo: () => (x:number) => x + 1, + doAll: () => (x:number) => x + 10, }, }); const bar = updux({ initial: 'a', mutations: { - doBar: () => x => x + 'a', - doAll: () => x => x + 'b', + doBar: () => (x:string) => x + 'a', + doAll: () => (x:string) => x + 'b', } }); @@ -64,7 +64,7 @@ test( 'sub reducers', () => { expect(Object.keys(actions)).toHaveLength(3); - let state = reducer(null,{}); + let state = reducer(null,{type:'noop'}); expect(state).toEqual({ foo: 1, bar: 'a' }); @@ -92,7 +92,7 @@ test('precedence between root and sub-reducers', () => { foo: { bar: 4 }, }, mutations: { - inc: () => state => { + inc: () => (state:any) => { return { ...state, surprise: state.foo.bar @@ -106,7 +106,7 @@ test('precedence between root and sub-reducers', () => { quux: 3, }, mutations: { - inc: () => state => ({...state, bar: state.bar + 1 }) + inc: () => (state:any) => ({...state, bar: state.bar + 1 }) }, }), } @@ -122,7 +122,7 @@ test('precedence between root and sub-reducers', () => { }); -function timeout(ms) { +function timeout(ms:number) { return new Promise(resolve => setTimeout(resolve, ms)); } @@ -133,8 +133,8 @@ test( 'middleware', async () => { } = updux({ initial: "", mutations: { - inc: (addition) => state => state + addition, - doEeet: () => state => { + inc: (addition:number) => (state:number) => state + addition, + doEeet: () => (state:number) => { return state + 'Z'; }, }, diff --git a/src/types.ts b/src/types.ts index ae28d61..db8308d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import { Middleware } from 'redux'; +import { Dispatch, Middleware } from 'redux'; export type Action = { type: string, @@ -14,6 +14,8 @@ export type ActionPayloadGenerator = (...args:any[]) => any; export type ActionCreator = (...args: any[] ) => Action; +export type UpduxDispatch = Dispatch & Dictionary; + export type UpduxConfig = Partial<{ initial: S, subduxes: {}, @@ -21,7 +23,7 @@ export type UpduxConfig = Partial<{ [ type: string ]: ActionPayloadGenerator }, mutations: any, - effects: Dictionary, + effects: Dictionary>, }>; export type Upreducer = (action:Action) => (state:S) => S; diff --git a/src/updux.ts b/src/updux.ts index f734082..5d506fb 100644 --- a/src/updux.ts +++ b/src/updux.ts @@ -6,9 +6,13 @@ import buildMutations from './buildMutations'; import buildCreateStore from './buildCreateStore'; import buildMiddleware from './buildMiddleware'; import buildUpreducer from './buildUpreducer'; -import { UpduxConfig, Dictionary, Action, ActionCreator, Mutation, Upreducer } from './types'; +import { UpduxConfig, Dictionary, Action, ActionCreator, Mutation, Upreducer, UpduxDispatch } from './types'; -import { Middleware } from 'redux'; +import { Middleware, Store } from 'redux'; + +type StoreWithDispatchActions Action }> = Store & { + dispatch: { [ type in keyof Actions ]: (...args:any) => void } +}; export class Updux { @@ -24,9 +28,9 @@ export class Updux { reducer: (state:S|undefined,action:Action) => S; - middleware: Middleware; + middleware: Middleware<{},S,UpduxDispatch>; - createStore: Function; + createStore: () => StoreWithDispatchActions; constructor(config: UpduxConfig) { @@ -63,7 +67,9 @@ export class Updux { Object.values(this.subduxes).map( sd => sd.middleware ) ); - this.createStore = buildCreateStore(this.reducer,this.initial,this.middleware,this.actions); + const actions = this.actions; + this.createStore = buildCreateStore(this.reducer,this.initial,this.middleware as Middleware,this.actions) as + () => StoreWithDispatchActions< S, typeof actions >; } } diff --git a/tsconfig.json b/tsconfig.json index 29bc38a..a26462a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,12 +8,12 @@ "target": "ES2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "lib": [ "dom", "es2019" ], /* Specify library files to be included in the compilation. */ - "allowJs": true, /* Allow javascript files to be compiled. */ + "allowJs": false, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "declaration": false, /* Generates corresponding '.d.ts' file. */ - "declarationMap": false, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": false, /* Generates corresponding '.map' file. */ + "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": "./dist", /* Redirect output structure to the directory. */ "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */