From ff1415f5937fcfdacd755085676b0e86f3d7db07 Mon Sep 17 00:00:00 2001 From: Steven Farthing Date: Wed, 4 Mar 2020 12:01:39 -0500 Subject: [PATCH 1/3] Upgrade babel Update to babel 7's scoped packages and remove unneeded transform-object-rest-spread plugin --- .babelrc | 8 -------- babel.config.json | 3 +++ package.json | 30 +++++++++++++----------------- 3 files changed, 16 insertions(+), 25 deletions(-) delete mode 100644 .babelrc create mode 100644 babel.config.json diff --git a/.babelrc b/.babelrc deleted file mode 100644 index f91ebace..00000000 --- a/.babelrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "presets": [ - "env" - ], - "plugins": [ - "transform-object-rest-spread" - ] -} diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 00000000..1320b9a3 --- /dev/null +++ b/babel.config.json @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env"] +} diff --git a/package.json b/package.json index 1ec0f8e0..579e8a4d 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,13 @@ "scripts": { "build": "webpack", "danger": "duti", - "test": "babel-node ./node_modules/mocha/bin/_mocha --require ./test/init.js", + "test": "mocha --require @babel/register --require @babel/polyfill --require ./test/init.js", "test:watch": "chokidar 'src/*.js' 'test/*.js' -c 'npm t'", "browser": "TEST_ENV=browser karma start karma.conf.js", "browser:local": "karma start karma.conf.js", - "coverage": "nyc --require babel-core/register --require babel-polyfill --require ./test/init.js mocha test", - "cicoveralls": "nyc report --reporter=text-lcov --require babel-core/register --require babel-polyfill --require ./test/init.js mocha test | coveralls", - "cicoverage": "nyc --reporter=lcov --require babel-core/register --require babel-polyfill --require ./test/init.js mocha test --reporter json > test-results.json", + "coverage": "nyc --require @babel/register --require @babel/polyfill --require ./test/init.js mocha test", + "cicoveralls": "nyc report --reporter=text-lcov --require @babel/register --require @babel/polyfill --require ./test/init.js mocha test | coveralls", + "cicoverage": "nyc --reporter=lcov --require @babel/register --require @babel/polyfill --require ./test/init.js mocha test --reporter json > test-results.json", "lint": "eslint dangerfile.js src/*.js test/*.js", "lint:ci": "npm run lint -- -o lint-results.json -f json", "lint-fix": "eslint dangerfile.js src/*.js test/*.js --fix", @@ -27,26 +27,22 @@ }, "license": "MIT", "dependencies": { - "babel-polyfill": "^6.23.0", + "@babel/polyfill": "^7.8.3", "lodash": "^4.17.4" }, - "babel": { - "presets": [ - "env" - ] - }, "prettier": { "singleQuote": true, "semi": false, "trailingComma": "es5" }, "devDependencies": { - "babel": "^6.5.2", - "babel-cli": "^6.22.2", - "babel-core": "^6.22.1", - "babel-eslint": "^8.0.0", - "babel-loader": "^7.0.0", - "babel-plugin-transform-object-rest-spread": "^6.26.0", + "@babel/cli": "^7.8.4", + "@babel/core": "^7.8.6", + "@babel/node": "^7.8.4", + "@babel/preset-env": "^7.8.6", + "@babel/register": "^7.8.6", + "babel-eslint": "^10.1.0", + "babel-loader": "^8.0.6", "babel-preset-env": "^1.6.1", "bluebird": "^3.5.0", "chai": "^4.1.0", @@ -70,7 +66,7 @@ "karma-webpack": "^3.0.0", "mocha": "^6.2.2", "mocha-lcov-reporter": "^1.2.0", - "nyc": "^12.0.1", + "nyc": "^15.0.0", "prettier": "^1.7.4", "read-pkg": "^5.2.0", "sinon": "^7.5.0", From c969ef8ddb03106073f2762e7ddddb34d0a6fc5d Mon Sep 17 00:00:00 2001 From: Steven Farthing Date: Wed, 4 Mar 2020 12:10:14 -0500 Subject: [PATCH 2/3] old present was left-over --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 579e8a4d..c38b1500 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "@babel/register": "^7.8.6", "babel-eslint": "^10.1.0", "babel-loader": "^8.0.6", - "babel-preset-env": "^1.6.1", "bluebird": "^3.5.0", "chai": "^4.1.0", "chai-as-promised": "^7.1.1", From 1384574576c64ea6d1bd195d1101b1479cb220d5 Mon Sep 17 00:00:00 2001 From: Steven Farthing Date: Tue, 10 Mar 2020 14:57:25 -0400 Subject: [PATCH 3/3] Typescript POC --- babel.config.json | 2 +- package.json | 11 +++-- src/function.js | 49 ------------------- src/function.ts | 117 ++++++++++++++++++++++++++++++++++++++++++++++ test/init.js | 5 ++ tsconfig.json | 19 ++++++++ webpack.config.js | 7 ++- 7 files changed, 154 insertions(+), 56 deletions(-) delete mode 100644 src/function.js create mode 100644 src/function.ts create mode 100644 tsconfig.json diff --git a/babel.config.json b/babel.config.json index 1320b9a3..3313ff9e 100644 --- a/babel.config.json +++ b/babel.config.json @@ -1,3 +1,3 @@ { - "presets": ["@babel/preset-env"] + "presets": ["@babel/preset-env", "@babel/preset-typescript"] } diff --git a/package.json b/package.json index c38b1500..e9f9a070 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,13 @@ "scripts": { "build": "webpack", "danger": "duti", - "test": "mocha --require @babel/register --require @babel/polyfill --require ./test/init.js", + "test": "tsc && mocha --require @babel/polyfill --require ./test/init.js", "test:watch": "chokidar 'src/*.js' 'test/*.js' -c 'npm t'", "browser": "TEST_ENV=browser karma start karma.conf.js", "browser:local": "karma start karma.conf.js", - "coverage": "nyc --require @babel/register --require @babel/polyfill --require ./test/init.js mocha test", - "cicoveralls": "nyc report --reporter=text-lcov --require @babel/register --require @babel/polyfill --require ./test/init.js mocha test | coveralls", - "cicoverage": "nyc --reporter=lcov --require @babel/register --require @babel/polyfill --require ./test/init.js mocha test --reporter json > test-results.json", + "coverage": "nyc --require @babel/polyfill --require ./test/init.js mocha test", + "cicoveralls": "nyc report --reporter=text-lcov --require @babel/polyfill --require ./test/init.js mocha test | coveralls", + "cicoverage": "nyc --reporter=lcov --require @babel/polyfill --require ./test/init.js mocha test --reporter json > test-results.json", "lint": "eslint dangerfile.js src/*.js test/*.js", "lint:ci": "npm run lint -- -o lint-results.json -f json", "lint-fix": "eslint dangerfile.js src/*.js test/*.js --fix", @@ -40,7 +40,9 @@ "@babel/core": "^7.8.6", "@babel/node": "^7.8.4", "@babel/preset-env": "^7.8.6", + "@babel/preset-typescript": "^7.8.3", "@babel/register": "^7.8.6", + "@types/lodash": "^4.14.149", "babel-eslint": "^10.1.0", "babel-loader": "^8.0.6", "bluebird": "^3.5.0", @@ -70,6 +72,7 @@ "read-pkg": "^5.2.0", "sinon": "^7.5.0", "sinon-chai": "^3.0.0", + "typescript": "^3.8.3", "webpack": "^4.1.0", "webpack-cli": "^3.0.0", "write-pkg": "^4.0.0" diff --git a/src/function.js b/src/function.js deleted file mode 100644 index 88aa8ced..00000000 --- a/src/function.js +++ /dev/null @@ -1,49 +0,0 @@ -import _ from 'lodash/fp' - -// (fn, a, b) -> fn(a, b) -export let maybeCall = (fn, ...args) => _.isFunction(fn) && fn(...args) -// (fn, a, b) -> fn(a, b) -export let callOrReturn = (fn, ...args) => (_.isFunction(fn) ? fn(...args) : fn) -// (a, Monoid f) -> f[a] :: f a -export let boundMethod = (method, object) => object[method].bind(object) - -// http://ramdajs.com/docs/#converge -export let converge = (converger, branches) => (...args) => - converger(_.over(branches)(...args)) - -export let composeApply = (f, g) => x => f(g(x))(x) -export let comply = composeApply - -// Prettier version of `defer` the one from bluebird docs -export let defer = () => { - let resolve - let reject - let promise = new Promise((res, rej) => { - resolve = res - reject = rej - }) - return { - resolve, - reject, - promise, - } -} -// `_.debounce` for async functions, which require consistently returning a single promise for all queued calls -export let debounceAsync = (n, f) => { - let deferred = defer() - let debounced = _.debounce(n, (...args) => { - deferred.resolve(f(...args)) - deferred = defer() - }) - return (...args) => { - debounced(...args) - return deferred.promise - } -} - -let currier = f => (...fns) => _.curryN(fns[0].length, f(...fns)) -// (f1, f2, ...fn) -> f1Args1 -> f1Arg2 -> ...f1ArgN -> fn(f2(f1)) -export let flurry = currier(_.flow) - -// like _.overArgs, but on all args -export let mapArgs = _.curry((mapper, fn) => (...x) => fn(...x.map(mapper))) diff --git a/src/function.ts b/src/function.ts new file mode 100644 index 00000000..2fba7748 --- /dev/null +++ b/src/function.ts @@ -0,0 +1,117 @@ +import _ from 'lodash/fp' + +// ✓ maybeCall((a, b) => a + b, 1, 5) +// ✓ maybeCall(null, 1, 5) +// × maybeCall('d', 1, 5) +// × maybeCall(undefined, 1, 5) +// × maybeCall((a, b) => a + b, 1) +// × maybeCall((a, b) => a.concat(b), 3, [2, 3, 4]) +export let maybeCall = ( + fn: ((...args: I) => O) | null, + ...args: I +): O | false => (_.isFunction(fn) && fn ? fn(...args) : false) + +// (fn, a, b) -> fn(a, b) +// ✓ callOrReturn((a, b) => a + b, 1, 5) +// × callOrReturn(null, 1, 5) +// × callOrReturn('d', 1, 5) +// × callOrReturn(undefined, 1, 5) +// × callOrReturn((a, b) => a + b, 1) +// × callOrReturn((a, b) => a.concat(b), 3, [2, 3, 4]) +export let callOrReturn = ( + fn: (...args: I) => O, + ...args: I +): ((...args: I) => O) | O => (_.isFunction(fn) ? fn(...args) : fn) + +// (a, Monoid f) -> f[a] :: f a +export let boundMethod = (method: string, object: Object) => { + // NOTE its complex to define a type to ensure object has the key 'method'. Type + // can be made to be more strict in the future. For now we keep it loose. + // @ts-ignore implicit any + return object[method].bind(object) +} + +// http://ramdajs.com/docs/#converge +type Many = T | ReadonlyArray +export let converge = ( + converger: Function, + branches: Many<(...args: any[]) => any> +) => (...args: any[]) => converger(_.over(branches)(...args)) + +// Example +// type P = { name: string; height: number } +// const isTallerThan: F> = p1 => p2 => p1.height > p2.height +// const fatherOf: F = p => ({ name: 'Bob', height: 5.6 }) +// const isShorterThanFather = comply(isTallerThan, fatherOf) +// ^^^^^^^^^^^^^^^^^^^ +// inferred Boolean type +type F = (i: I) => O +export let composeApply = (f: F>, g: F) => ( + x: I +): O => f(g(x))(x) + +export let comply = composeApply + +// Prettier version of `defer` the one from bluebird docs +export let defer = (): { + resolve: F + reject: F + promise: Promise +} => { + let resolve + let reject + let promise: Promise = new Promise((res, rej) => { + resolve = res + reject = rej + }) + + return { + // NOTE We know resolve and reject will be defined here, + // but Typescript is unsure. Use ts-ignore for now. + // @ts-ignore possibly undefined. + resolve, + // @ts-ignore possibly undefined. + reject, + promise, + } +} +// `_.debounce` for async functions, which require consistently returning a single promise for all queued calls + +// const add1 = (a: number) => a + 1 +// const f = debounceAsync(1000, add1) +// const o = f(40) +// ^ +// inferred Promise +export let debounceAsync = ( + n: number, + f: (...A: I) => O +): ((...A: I) => Promise) => { + let deferred = defer() + let debounced = _.debounce(n, (...args) => { + deferred.resolve(f(...args)) + deferred = defer() + }) + return (...args: any) => { + debounced(...args) + return deferred.promise + } +} + +// TODO stronger types +let currier = (f: Function) => (...fns: any[]) => + _.curryN(fns[0].length, f(...fns)) + +// (f1, f2, ...fn) -> f1Args1 -> f1Arg2 -> ...f1ArgN -> fn(f2(f1)) +// let add = (x: number, y: number) => x + y +// let double = (x: number) => x * 2 +// const addAndDouble = flurry(add, double) +// ✓ addAndDouble(1)(4) +// × addAndDouble(1, 4) +export let flurry = currier(_.flow) + +// like _.overArgs, but on all args +export let mapArgs = _.curry( + (mapper: (value: any, index: number, array: any[]) => any, fn: Function) => ( + ...x: any[] + ) => fn(...x.map(mapper)) +) diff --git a/test/init.js b/test/init.js index b1efa571..76bce2d7 100644 --- a/test/init.js +++ b/test/init.js @@ -1 +1,6 @@ global.__VERSION__ = '1.0.0' + +/** + * @babel/register does not load .ts files by default + */ +require('@babel/register')({ extensions: ['.js', '.jsx', '.ts', '.tsx'] }) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..1c79f799 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + // Target latest version of ECMAScript. + "target": "esnext", + // Search under node_modules for non-relative imports. + "moduleResolution": "node", + // Process & infer types from .js files. + "allowJs": true, + // Don't emit; allow Babel to transform files. + "noEmit": true, + // Enable strictest settings like strictNullChecks & noImplicitAny. + "strict": true, + // Disallow features that require cross-file information for emit. + "isolatedModules": true, + // Import non-ES modules as default imports. + "esModuleInterop": true + }, + "include": ["src"] +} diff --git a/webpack.config.js b/webpack.config.js index bca13902..f7689c77 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,7 +3,7 @@ var webpack = require('webpack') var packageMetadata = require('./package.json') var libraryName = packageMetadata.name var libraryVersion = packageMetadata.version -var outputFile = libraryName + '.js' +var outputFile = `${libraryName}.js` module.exports = { devtool: 'source-map', @@ -16,10 +16,13 @@ module.exports = { libraryTarget: 'umd', globalObject: 'this', }, + resolve: { + extensions: ['.tsx', '.ts', '.js'], + }, module: { rules: [ { - test: /(\.jsx|\.js)$/, + test: /(\.jsx|\.js|\.ts)$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader',