diff --git a/packages/tree-select/package.json b/packages/tree-select/package.json index 3a435ee99cc3c..8cdd1afcac4b1 100644 --- a/packages/tree-select/package.json +++ b/packages/tree-select/package.json @@ -11,7 +11,7 @@ "license": "GPL-2.0-or-later", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", - "calypso:src": "src/index.js", + "calypso:src": "src/index.ts", "sideEffects": false, "repository": { "type": "git", @@ -28,9 +28,14 @@ "dist", "src" ], + "types": "dist/types", "scripts": { - "clean": "npx rimraf dist", - "build": "transpile", - "prepack": "yarn run clean && yarn run build" + "clean": "tsc --build ./tsconfig.json ./tsconfig-cjs.json --clean", + "build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json", + "prepack": "yarn run clean && yarn run build", + "watch": "tsc --build ./tsconfig.json --watch" + }, + "dependencies": { + "tslib": "^1.10.0" } } diff --git a/packages/tree-select/src/index.js b/packages/tree-select/src/index.ts similarity index 50% rename from packages/tree-select/src/index.js rename to packages/tree-select/src/index.ts index cf4c864471348..7c468d0712cb7 100644 --- a/packages/tree-select/src/index.js +++ b/packages/tree-select/src/index.ts @@ -1,25 +1,56 @@ -const defaultGetCacheKey = ( ...args ) => args.join(); +declare const process: { + env: { + NODE_ENV: unknown; + }; +}; + +interface GenerateCacheKey< A extends unknown[] > { + ( ...args: A ): string; +} + +const defaultGetCacheKey: GenerateCacheKey< unknown[] > = ( ...args: unknown[] ): string => + args.join(); -const isFunction = ( fn ) => { - return fn && typeof fn === 'function'; +const isFunction = ( fn: unknown ): fn is ( ...args: unknown[] ) => unknown => { + return !! fn && typeof fn === 'function'; }; -const isObject = ( o ) => { - return o && typeof o === 'object'; +const isObject = ( o: unknown ): o is Record< string, unknown > => { + return !! o && typeof o === 'object'; }; +type WeakMapKey = object; // eslint-disable-line @typescript-eslint/ban-types + +interface Options< A extends unknown[] > { + /** Custom way to compute the cache key from the `args` list */ + getCacheKey?: GenerateCacheKey< A >; +} + +interface CachedSelector< S, A extends unknown[], R = unknown > { + ( state: S, ...args: A ): R; + clearCache: () => void; +} + /** * Returns a selector that caches values. * - * @param {Function} getDependents A Function describing the dependent(s) of the selector. - * Must return an array which gets passed as the first arg to the selector - * @param {Function} selector A standard selector for calculating cached result - * @param {object} options Options bag with additional arguments - * @param {Function} options.getCacheKey - * Custom way to compute the cache key from the `args` list - * @returns {Function} Cached selector + * @param getDependents A Function describing the dependent(s) of the selector. + * Must return an array which gets passed as the first arg to the selector. + * @param selector A standard selector for calculating cached result. + * @param options Options bag with additional arguments. + * + * @returns Cached selector */ -export default function treeSelect( getDependents, selector, options = {} ) { +export default function treeSelect< + State = unknown, + Args extends unknown[] = unknown[], + Deps extends WeakMapKey[] = any[], // eslint-disable-line @typescript-eslint/no-explicit-any + Result = unknown +>( + getDependents: ( state: State, ...args: Args ) => Deps, + selector: ( deps: Deps, ...args: Args ) => Result, + options: Options< Args > = {} +): CachedSelector< State, Args, Result > { if ( process.env.NODE_ENV !== 'production' ) { if ( ! isFunction( getDependents ) || ! isFunction( selector ) ) { throw new TypeError( @@ -32,7 +63,10 @@ export default function treeSelect( getDependents, selector, options = {} ) { const { getCacheKey = defaultGetCacheKey } = options; - const cachedSelector = function ( state, ...args ) { + const cachedSelector: CachedSelector< State, Args, Result > = function ( + state: State, + ...args: Args + ) { const dependents = getDependents( state, ...args ); if ( process.env.NODE_ENV !== 'production' ) { @@ -44,11 +78,11 @@ export default function treeSelect( getDependents, selector, options = {} ) { // create a dependency tree for caching selector results. // this is beneficial over standard memoization techniques so that we can // garbage collect any values that are based on outdated dependents - const leafCache = dependents.reduce( insertDependentKey, cache ); + const leafCache: Map< string, Result > = dependents.reduce( insertDependentKey, cache ); const key = getCacheKey( ...args ); if ( leafCache.has( key ) ) { - return leafCache.get( key ); + return leafCache.get( key ) as Result; } const value = selector( dependents, ...args ); @@ -76,7 +110,8 @@ const NULLISH_KEY = {}; * Note: Inserts WeakMaps except for the last map which will be a regular Map. * The last map is a regular one because the the key for the last map is the string results of args.join(). */ -function insertDependentKey( map, key, currentIndex, arr ) { +function insertDependentKey( map: any, key: unknown, currentIndex: number, arr: unknown[] ) { + // eslint-disable-line @typescript-eslint/no-explicit-any if ( key != null && Object( key ) !== key ) { throw new TypeError( 'key must be an object, `null`, or `undefined`' ); } diff --git a/packages/tree-select/tsconfig-cjs.json b/packages/tree-select/tsconfig-cjs.json new file mode 100644 index 0000000000000..bd64442da990d --- /dev/null +++ b/packages/tree-select/tsconfig-cjs.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "module": "commonjs", + "declaration": false, + "declarationMap": false, + "declarationDir": null, + "outDir": "dist/cjs", + "composite": false, + "incremental": true + } +} diff --git a/packages/tree-select/tsconfig.json b/packages/tree-select/tsconfig.json index cc8b859680a4f..405a965e3d7bf 100644 --- a/packages/tree-select/tsconfig.json +++ b/packages/tree-select/tsconfig.json @@ -1,7 +1,36 @@ { - "extends": "@automattic/calypso-build/tsconfig", - "exclude": [ "node_modules" ], "compilerOptions": { - "allowJs": true - } + "target": "ES5", + "lib": [ "ES2015" ], + "module": "ESNEXT", + "allowJs": false, + "declaration": true, + "declarationMap": true, + "declarationDir": "dist/types", + "rootDir": "src", + "outDir": "dist/esm", + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + + "moduleResolution": "node", + "esModuleInterop": true, + "downlevelIteration": true, + + "forceConsistentCasingInFileNames": true, + + "importsNotUsedAsValues": "error", + + "typeRoots": [ "../../node_modules/@types" ], + "types": [], + + "noEmitHelpers": true, + "importHelpers": true, + + "composite": true + }, + "files": [ "src/index.ts" ] } diff --git a/packages/tsconfig.json b/packages/tsconfig.json index 7751cf2fa3ee5..b0c98904b3a25 100644 --- a/packages/tsconfig.json +++ b/packages/tsconfig.json @@ -28,6 +28,7 @@ { "path": "./react-i18n" }, { "path": "./search" }, { "path": "./shopping-cart" }, + { "path": "./tree-select" }, { "path": "./wpcom-checkout" } ] }