diff --git a/packages/map2tree/.babelrc b/packages/map2tree/.babelrc index 1320b9a3..5259cd24 100755 --- a/packages/map2tree/.babelrc +++ b/packages/map2tree/.babelrc @@ -1,3 +1,4 @@ { - "presets": ["@babel/preset-env"] + "presets": ["@babel/preset-env", "@babel/preset-typescript"], + "plugins": ["@babel/plugin-proposal-class-properties"] } diff --git a/packages/map2tree/.eslintignore b/packages/map2tree/.eslintignore new file mode 100644 index 00000000..1d149abd --- /dev/null +++ b/packages/map2tree/.eslintignore @@ -0,0 +1,3 @@ +examples +lib +dist diff --git a/packages/map2tree/.eslintrc.js b/packages/map2tree/.eslintrc.js new file mode 100644 index 00000000..88327738 --- /dev/null +++ b/packages/map2tree/.eslintrc.js @@ -0,0 +1,29 @@ +module.exports = { + extends: '../../.eslintrc', + overrides: [ + { + files: ['*.ts'], + extends: '../../eslintrc.ts.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + }, + { + files: ['test/*.ts'], + extends: '../../eslintrc.ts.jest.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./test/tsconfig.json'], + }, + }, + { + files: ['webpack.config.umd.ts'], + extends: '../../eslintrc.ts.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.webpack.json'], + }, + }, + ], +}; diff --git a/packages/map2tree/jest.config.js b/packages/map2tree/jest.config.js new file mode 100644 index 00000000..8824c114 --- /dev/null +++ b/packages/map2tree/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + preset: 'ts-jest', +}; diff --git a/packages/map2tree/package.json b/packages/map2tree/package.json index ee8d4cf3..04d1eb48 100755 --- a/packages/map2tree/package.json +++ b/packages/map2tree/package.json @@ -2,20 +2,6 @@ "name": "map2tree", "version": "1.4.2", "description": "Utility for mapping maps to trees", - "main": "lib/index.js", - "scripts": { - "clean": "rimraf lib dist", - "build": "babel src --out-dir lib", - "build:umd": "webpack --progress --config webpack.config.umd.js", - "build:umd:min": "webpack --env.production --progress --config webpack.config.umd.js", - "test": "jest", - "prepare": "npm run build && npm run build:umd", - "prepublishOnly": "npm run test && npm run clean && npm run build && npm run build:umd && npm run build:umd:min" - }, - "repository": { - "type": "git", - "url": "https://github.com/reduxjs/redux-devtools.git" - }, "keywords": [ "map2tree", "map-to-tree", @@ -23,38 +9,43 @@ "map", "tree" ], - "author": "romseguy", - "license": "MIT", + "homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/map2tree", "bugs": { "url": "https://github.com/reduxjs/redux-devtools/issues" }, - "homepage": "https://github.com/reduxjs/redux-devtools", - "devDependencies": { - "@babel/cli": "^7.10.5", - "@babel/core": "^7.11.1", - "@babel/preset-env": "^7.11.0", - "babel-loader": "^8.1.0", - "immutable": "^4.0.0-rc.12", - "jest": "^26.2.2", - "rimraf": "^3.0.2", - "webpack": "^4.44.1", - "webpack-cli": "^3.3.12" - }, - "dependencies": { - "lodash": "^4.17.19" - }, - "npmName": "map2tree", + "license": "MIT", + "author": "romseguy", "files": [ "lib", "dist", "src" ], - "npmFileMap": [ - { - "basePath": "/dist/", - "files": [ - "*.js" - ] - } - ] + "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/reduxjs/redux-devtools.git" + }, + "scripts": { + "build": "npm run build:types && npm run build:js && npm run build:umd && npm run build:umd:min", + "build:types": "tsc --emitDeclarationOnly", + "build:js": "babel src --out-dir lib --extensions \".ts\" --source-maps inline", + "build:umd": "webpack --progress --config webpack.config.umd.ts", + "build:umd:min": "webpack --env.production --progress --config webpack.config.umd.ts", + "clean": "rimraf lib dist", + "test": "jest", + "lint": "eslint . --ext .ts", + "lint:fix": "eslint . --ext .ts --fix", + "type-check": "tsc --noEmit", + "type-check:watch": "npm run type-check -- --watch", + "preversion": "npm run type-check && npm run lint && npm run test", + "prepublishOnly": "npm run clean && npm run build" + }, + "dependencies": { + "lodash": "^4.17.19" + }, + "devDependencies": { + "@types/lodash": "^4.14.159", + "immutable": "^4.0.0-rc.12" + } } diff --git a/packages/map2tree/src/index.js b/packages/map2tree/src/index.js deleted file mode 100755 index 4e43da70..00000000 --- a/packages/map2tree/src/index.js +++ /dev/null @@ -1,79 +0,0 @@ -import isArray from 'lodash/isArray'; -import isPlainObject from 'lodash/isPlainObject'; -import mapValues from 'lodash/mapValues'; - -function visit(parent, visitFn, childrenFn) { - if (!parent) return; - - visitFn(parent); - - const children = childrenFn(parent); - if (children) { - const count = children.length; - for (let i = 0; i < count; i++) { - visit(children[i], visitFn, childrenFn); - } - } -} - -function getNode(tree, key) { - let node = null; - - visit( - tree, - (d) => { - if (d.name === key) { - node = d; - } - }, - (d) => d.children - ); - - return node; -} - -export default function map2tree( - root, - options = {}, - tree = { name: options.key || 'state', children: [] } -) { - if (!isPlainObject(root) && root && !root.toJS) { - return {}; - } - - const { key: rootNodeKey = 'state', pushMethod = 'push' } = options; - const currentNode = getNode(tree, rootNodeKey); - - if (currentNode === null) { - return {}; - } - - mapValues(root && root.toJS ? root.toJS() : root, (maybeImmutable, key) => { - const value = - maybeImmutable && maybeImmutable.toJS - ? maybeImmutable.toJS() - : maybeImmutable; - let newNode = { name: key }; - - if (isArray(value)) { - newNode.children = []; - - for (let i = 0; i < value.length; i++) { - newNode.children[pushMethod]({ - name: `${key}[${i}]`, - [isPlainObject(value[i]) ? 'object' : 'value']: value[i], - }); - } - } else if (isPlainObject(value)) { - newNode.children = []; - } else { - newNode.value = value; - } - - currentNode.children[pushMethod](newNode); - - map2tree(value, { key, pushMethod }, tree); - }); - - return tree; -} diff --git a/packages/map2tree/src/index.ts b/packages/map2tree/src/index.ts new file mode 100644 index 00000000..958eef3d --- /dev/null +++ b/packages/map2tree/src/index.ts @@ -0,0 +1,99 @@ +import isArray from 'lodash/isArray'; +import isPlainObject from 'lodash/isPlainObject'; +import mapValues from 'lodash/mapValues'; + +interface Node { + name: string; + children?: Node[]; + value?: unknown; +} + +function visit( + parent: Node, + visitFn: (parent: Node) => void, + childrenFn: (parent: Node) => Node[] | undefined +) { + if (!parent) return; + + visitFn(parent); + + const children = childrenFn(parent); + if (children) { + const count = children.length; + for (let i = 0; i < count; i++) { + visit(children[i], visitFn, childrenFn); + } + } +} + +function getNode(tree: Node, key: string): Node | null { + let node = null; + + visit( + tree, + (d) => { + if (d.name === key) { + node = d; + } + }, + (d) => d.children + ); + + return node; +} + +export default function map2tree( + // eslint-disable-next-line @typescript-eslint/ban-types + root: {}, + options: { key?: string; pushMethod?: 'push' | 'unshift' } = {}, + tree: Node = { name: options.key || 'state', children: [] } +): Node { + // eslint-disable-next-line @typescript-eslint/ban-types + if (!isPlainObject(root) && root && !(root as { toJS: () => {} }).toJS) { + return {} as Node; + } + + const { key: rootNodeKey = 'state', pushMethod = 'push' } = options; + const currentNode = getNode(tree, rootNodeKey); + + if (currentNode === null) { + return {} as Node; + } + + mapValues( + // eslint-disable-next-line @typescript-eslint/ban-types + root && (root as { toJS: () => {} }).toJS + ? // eslint-disable-next-line @typescript-eslint/ban-types + (root as { toJS: () => {} }).toJS() + : root, + // eslint-disable-next-line @typescript-eslint/ban-types + (maybeImmutable: { toJS?: () => {} }, key) => { + const value = + maybeImmutable && maybeImmutable.toJS + ? maybeImmutable.toJS() + : maybeImmutable; + const newNode: Node = { name: key }; + + if (isArray(value)) { + newNode.children = []; + + for (let i = 0; i < value.length; i++) { + newNode.children[pushMethod]({ + name: `${key}[${i}]`, + [isPlainObject(value[i]) ? 'object' : 'value']: value[i], + }); + } + } else if (isPlainObject(value)) { + newNode.children = []; + } else { + newNode.value = value; + } + + currentNode.children![pushMethod](newNode); + + map2tree(value, { key, pushMethod }, tree); + } + ); + + return tree; +} diff --git a/packages/map2tree/test/map2tree.spec.js b/packages/map2tree/test/map2tree.spec.ts old mode 100755 new mode 100644 similarity index 97% rename from packages/map2tree/test/map2tree.spec.js rename to packages/map2tree/test/map2tree.spec.ts index 7114a16e..e1cc3787 --- a/packages/map2tree/test/map2tree.spec.js +++ b/packages/map2tree/test/map2tree.spec.ts @@ -1,5 +1,5 @@ import map2tree from '../src'; -import immutable from 'immutable'; +import * as immutable from 'immutable'; test('# rootNodeKey', () => { const map = {}; @@ -151,7 +151,7 @@ describe('# array map', () => { }); test('## unshift', () => { - const options = { pushMethod: 'unshift' }; + const options = { pushMethod: 'unshift' as const }; const expected = { name: 'state', children: [ diff --git a/packages/map2tree/test/tsconfig.json b/packages/map2tree/test/tsconfig.json new file mode 100644 index 00000000..b55532d2 --- /dev/null +++ b/packages/map2tree/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../tsconfig.base.json", + "include": ["../src", "."] +} diff --git a/packages/map2tree/tsconfig.json b/packages/map2tree/tsconfig.json new file mode 100644 index 00000000..7b7d1492 --- /dev/null +++ b/packages/map2tree/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.react.base.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src"] +} diff --git a/packages/map2tree/tsconfig.webpack.json b/packages/map2tree/tsconfig.webpack.json new file mode 100644 index 00000000..655c4644 --- /dev/null +++ b/packages/map2tree/tsconfig.webpack.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["webpack.config.umd.ts"] +} diff --git a/packages/map2tree/webpack.config.umd.js b/packages/map2tree/webpack.config.umd.ts similarity index 63% rename from packages/map2tree/webpack.config.umd.js rename to packages/map2tree/webpack.config.umd.ts index fe011338..6bb5dabc 100644 --- a/packages/map2tree/webpack.config.umd.js +++ b/packages/map2tree/webpack.config.umd.ts @@ -1,9 +1,9 @@ -const path = require('path'); +import * as path from 'path'; -module.exports = (env = {}) => ({ +module.exports = (env: { production?: boolean } = {}) => ({ mode: env.production ? 'production' : 'development', entry: { - app: ['./src/index.js'], + app: ['./src/index'], }, output: { library: 'd3tooltip', @@ -14,10 +14,13 @@ module.exports = (env = {}) => ({ module: { rules: [ { - test: /\.js$/, + test: /\.(js|ts)$/, loader: 'babel-loader', exclude: /node_modules/, }, ], }, + resolve: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, });