Skip to content

Commit c007ffa

Browse files
committed
Basic functions
1 parent 25b3c0e commit c007ffa

10 files changed

+3813
-85
lines changed

.gitignore

+10-85
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,29 @@
1+
#builds
2+
src/**/*.js
3+
src/**/*.map
4+
dist/
5+
lib/
6+
lib.es2015/
7+
8+
# IDEs
9+
.idea/
10+
111
# Logs
212
logs
313
*.log
414
npm-debug.log*
515
yarn-debug.log*
616
yarn-error.log*
7-
lerna-debug.log*
8-
9-
# Diagnostic reports (https://nodejs.org/api/report.html)
10-
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11-
12-
# Runtime data
13-
pids
14-
*.pid
15-
*.seed
16-
*.pid.lock
17-
18-
# Directory for instrumented libs generated by jscoverage/JSCover
19-
lib-cov
20-
21-
# Coverage directory used by tools like istanbul
22-
coverage
23-
*.lcov
24-
25-
# nyc test coverage
26-
.nyc_output
27-
28-
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29-
.grunt
30-
31-
# Bower dependency directory (https://bower.io/)
32-
bower_components
33-
34-
# node-waf configuration
35-
.lock-wscript
36-
37-
# Compiled binary addons (https://nodejs.org/api/addons.html)
38-
build/Release
3917

4018
# Dependency directories
4119
node_modules/
4220
jspm_packages/
4321

44-
# TypeScript v1 declaration files
45-
typings/
46-
4722
# TypeScript cache
4823
*.tsbuildinfo
4924

5025
# Optional npm cache directory
5126
.npm
5227

53-
# Optional eslint cache
54-
.eslintcache
55-
56-
# Microbundle cache
57-
.rpt2_cache/
58-
.rts2_cache_cjs/
59-
.rts2_cache_es/
60-
.rts2_cache_umd/
61-
62-
# Optional REPL history
63-
.node_repl_history
64-
65-
# Output of 'npm pack'
66-
*.tgz
67-
6828
# Yarn Integrity file
6929
.yarn-integrity
70-
71-
# dotenv environment variables file
72-
.env
73-
.env.test
74-
75-
# parcel-bundler cache (https://parceljs.org/)
76-
.cache
77-
78-
# Next.js build output
79-
.next
80-
81-
# Nuxt.js build / generate output
82-
.nuxt
83-
dist
84-
85-
# Gatsby files
86-
.cache/
87-
# Comment in the public line in if your project uses Gatsby and *not* Next.js
88-
# https://nextjs.org/blog/next-9-1#public-directory-support
89-
# public
90-
91-
# vuepress build output
92-
.vuepress/dist
93-
94-
# Serverless directories
95-
.serverless/
96-
97-
# FuseBox cache
98-
.fusebox/
99-
100-
# DynamoDB Local files
101-
.dynamodb/
102-
103-
# TernJS port file
104-
.tern-port

cfg/tsconfig.commonjs.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"extends": "../tsconfig.json",
3+
"compilerOptions": {
4+
"declaration": true,
5+
"baseUrl": "../",
6+
"sourceMap": true,
7+
"target": "es5",
8+
"module": "commonjs",
9+
"outDir": "../lib"
10+
},
11+
"exclude": [
12+
"../**/*.test.ts"
13+
]
14+
}

cfg/tsconfig.es2015.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"extends": "../tsconfig.json",
3+
"compilerOptions": {
4+
"declaration": true,
5+
"baseUrl": "../",
6+
"sourceMap": true,
7+
"target": "es2015",
8+
"module": "es2015",
9+
"outDir": "../lib.es2015"
10+
},
11+
"exclude": [
12+
"../**/*.test.ts"
13+
]
14+
}

jest.config.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
preset: 'ts-jest',
3+
testEnvironment: 'node',
4+
};

package.json

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"name": "be-good",
3+
"version": "0.1.0",
4+
"description": "Simple and flexible data decoding and transformation",
5+
"author": "hoichi <[email protected]>",
6+
"license": "MIT",
7+
"keywords": [
8+
"data",
9+
"decoder",
10+
"decoding",
11+
"typechecking",
12+
"typescript"
13+
],
14+
"repository": {
15+
"type": "git",
16+
"url": "git+https://github.com/hoichi/be-good.git"
17+
},
18+
"homepage": "https://github.com/hoichi/be-good.git#readme",
19+
"bugs": {
20+
"url": "https://github.com/hoichi/be-good.git#readme"
21+
},
22+
"main": "lib/index.js",
23+
"module": "lib.es2015/index.js",
24+
"jsnext:main": "lib.es2015/index.js",
25+
"typings": "lib/index.d.ts",
26+
"files": [
27+
"dist",
28+
"lib"
29+
],
30+
"scripts": {
31+
"build": "npm run build:commonjs && npm run build:es2015 && npm run build:umd",
32+
"build:commonjs": "./node_modules/.bin/tsc -P cfg/tsconfig.commonjs.json",
33+
"build:es2015": "./node_modules/.bin/tsc -P cfg/tsconfig.es2015.json",
34+
"build:umd": "mkdir -p dist && rollup -c --name 'be-good' && uglifyjs dist/be-good.js -o dist/be-good.min.js",
35+
"prepublish": "npm run build",
36+
"preversion": "npm run build",
37+
"test": "tsc && jest --watch"
38+
},
39+
"devDependencies": {
40+
"@rollup/plugin-node-resolve": "^7.1.0",
41+
"@rollup/plugin-typescript": "^3.0.0",
42+
"@types/check-types": "^7.3.1",
43+
"@types/jest": "^25.1.1",
44+
"@types/lodash": "^4.14.149",
45+
"check-types": "^11.1.2",
46+
"jest": "^25.1.0",
47+
"lodash": "^4.17.15",
48+
"prettier": "^1.19.1",
49+
"rollup": "^1.31.0",
50+
"ts-jest": "^25.1.0",
51+
"tslib": "^1.10.0",
52+
"typescript": "^3.7.5",
53+
"uglify-js": "^3.7.6"
54+
}
55+
}

rollup.config.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import typescript from '@rollup/plugin-node-resolve'
2+
import resolve from '@rollup/plugin-typescript'
3+
4+
export default {
5+
input: 'src/index.ts',
6+
output: {
7+
file: 'dist/be-good.js',
8+
name: 'BeGood',
9+
sourceMap: true,
10+
format: 'umd',
11+
},
12+
plugins: [
13+
typescript(),
14+
resolve(),
15+
]
16+
}

src/index.test.ts

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { isNumber, isString } from 'lodash';
2+
3+
import { be, beTrue, orBe } from './index'
4+
5+
test('be', () => {
6+
const numError = 'Not a number!'
7+
8+
expect(be(20, isNumber, numError)).toBe(20)
9+
expect(() => be("20", isNumber, numError)).toThrow(numError)
10+
});
11+
12+
test('beTrue', () => {
13+
const fiverError = 'Not equals to 5.5!'
14+
const eq = (m: number) => (n: number) => n === m
15+
16+
expect(beTrue(5.5, eq(5.5), fiverError)).toBe(5.5)
17+
expect(() => beTrue(5.7, eq(5.5), fiverError)).toThrow(fiverError)
18+
})
19+
20+
test('orBe: validation ok', () => {
21+
expect(
22+
orBe(() => ({
23+
foo: be('foo', isString, 'Failed'),
24+
bar: beTrue( 'bar', s => s === 'bar', 'Failed')
25+
}),
26+
null
27+
)).toStrictEqual({
28+
foo: 'foo',
29+
bar: 'bar'
30+
})
31+
})
32+
33+
test('orBe: falling back', () => {
34+
const logger = jest.fn()
35+
36+
expect(
37+
orBe(() => ({
38+
foo: be('foo', isString, 'Failed'),
39+
bar: beTrue('bar', s => s === 'foo', 'Failed')
40+
}),
41+
null,
42+
{ logger }
43+
)).toBe(null)
44+
45+
expect(logger)
46+
.toBeCalledWith(expect.objectContaining({ message: 'Failed' }))
47+
})

src/index.ts

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import isNumber from 'lodash/isNumber';
2+
3+
/**
4+
* The base for the whole library. Run predicate on the value.
5+
* If predicate returns true, returns the value as is. Otherwise, throws
6+
* with the given message.
7+
* If you just want a condition, not a type assertion, use `beTrue`
8+
* @param val {any} The value itself.
9+
* @param predicate {(a: any) => a is T}. As you can see, the predicate should
10+
* assert a type
11+
* @param errorMessage {string}
12+
*/
13+
export function be<T>(
14+
val: any,
15+
predicate: (a: any) => a is T,
16+
errorMessage: string,
17+
): T {
18+
if (predicate(val)) return val;
19+
20+
throw new TypeError(errorMessage);
21+
}
22+
23+
/**
24+
* Checks for a condition that is not a type assertion. Ergo cannot
25+
* narrow done the value type and returns the same type it gets.
26+
* If you want to assert and narrow down types, use `be`.
27+
* @param val {T} The value itself.
28+
* @param predicate: {(a: T) => boolean}.
29+
* @param errorMessage {string}
30+
*/
31+
export function beTrue<T>(
32+
val: T,
33+
predicate: (a: T) => boolean,
34+
errorMessage: string,
35+
): T {
36+
if (predicate(val)) return val;
37+
38+
throw new Error(errorMessage);
39+
}
40+
41+
type CatchOptions = {
42+
logger?: (e: Error) => void;
43+
}
44+
45+
const noop = () => {}
46+
47+
/**
48+
* Runs a decoding function, and if all the validations succeed, returns
49+
* the value. If something fails, falls back to another value (and optionally
50+
* logs the error).
51+
* @param make {() => T} The function that generates the expected type (and throws on invalid data)
52+
* @param fallback {U} The fallback value (probably different from T)
53+
* @param options {CatchOptions}
54+
*/
55+
export function orBe<T, U>(
56+
make: () => T,
57+
fallback: U,
58+
{ logger = noop }: CatchOptions = {}
59+
): T | U {
60+
try {
61+
return make()
62+
} catch(e) {
63+
/*
64+
If we ever add a custom error, we should replace all the
65+
`new TypeError` invocations. But frankly, why not catch all
66+
exceptions indiscriminately, even those not foreseen
67+
by a programmer.
68+
*/
69+
logger(e)
70+
return fallback
71+
}
72+
}

tsconfig.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"compilerOptions": {
3+
"allowSyntheticDefaultImports": true,
4+
"moduleResolution": "node",
5+
"target": "es5",
6+
"lib": [
7+
"es5",
8+
"es2015"
9+
],
10+
"noImplicitAny": true,
11+
"sourceMap": true,
12+
"noUnusedParameters": true,
13+
"strictNullChecks": true,
14+
"strictFunctionTypes": true,
15+
"strict": true,
16+
"types": [
17+
"jest",
18+
"node"
19+
]
20+
},
21+
"include": [
22+
"src/*.ts"
23+
],
24+
"exclude": [
25+
"node_modules",
26+
"**/*.test.ts"
27+
]
28+
}

0 commit comments

Comments
 (0)