Browse Source

Rewrite in TypeScript (#1)

iss58
Sam Verschueren 8 years ago
committed by Sindre Sorhus
parent
commit
5d24a9db22
  1. 3
      .gitignore
  2. 2
      .travis.yml
  3. 2
      example.js
  4. 66
      index.js
  5. 16
      package.json
  6. 3
      source/index.ts
  7. 8
      source/lib/argument-error.ts
  8. 20
      source/lib/predicates/predicate.ts
  9. 36
      source/lib/predicates/string.ts
  10. 24
      source/ow.ts
  11. 18
      source/test/string.ts
  12. 6
      test.js
  13. 20
      tsconfig.json

3
.gitignore

@ -1,2 +1,5 @@
node_modules
yarn.lock
.nyc_output
coverage
dist

2
.travis.yml

@ -3,3 +3,5 @@ node_js:
- '8'
- '6'
- '4'
after_script:
- './node_modules/.bin/nyc report --reporter=text-lcov > coverage.lcov && ./node_modules/.bin/codecov'

2
example.js

@ -1,5 +1,5 @@
'use strict';
const ow = require('.');
const ow = require('./dist');
const fn = (input, options) => {
ow(input, ow.string.minLength(10));

66
index.js

@ -1,66 +0,0 @@
'use strict';
const is = require('@sindresorhus/is');
class ArgumentError extends Error {
constructor(message, context) {
super(message);
// TODO: Node does not preserve the error name in output when using the below, why?
//Error.captureStackTrace(this, context);
this.name = 'ArgumentError';
}
}
const ow = (value, predicate) => {
for (const validator of predicate.context.validators) {
const result = validator(value);
if (result) {
// TODO: Modify the stack output to show the original `ow()` call instead of this `throw` statement
throw new ArgumentError(result, ow);
}
}
};
const newInstance = (fn, context) => {
const instance = fn(context);
instance.context = context;
return instance;
};
const stringPredicates = context => ({
minLength: number => {
context.validators.push(value => {
if (value.length < number) {
return `Expected string length to be minimum ${number}`;
}
});
return newInstance(stringPredicates, context);
},
get alphanumeric() {
context.validators.push(value => {
if (/[a-z\d]/gi.test(value)) {
return `Expected string to contain only alphanumeric characters but received \`${value}\``;
}
});
return newInstance(stringPredicates, context);
}
});
Object.defineProperty(ow, 'string', {
enumerable: true,
get() {
const instance = newInstance(stringPredicates, {
validators: []
});
instance.context.validators.push(value => {
// TODO: Create a generic function for all types that can be used here
if (!is.string(value)) {
return `Expected argument to be of type \`string\` but received type \`${is(value)}\``;
}
});
return instance;
}
});
module.exports = ow;

16
package.json

@ -9,15 +9,19 @@
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"engines": {
"node": ">=4"
},
"scripts": {
"test": "xo && ava"
"prerelease": "npm run build",
"pretest": "npm run build -- --sourceMap",
"test": "nyc ava dist/test",
"build": "del dist && tsc -p tsconfig.json --declaration"
},
"files": [
"index.js",
"example.js"
"dist"
],
"keywords": [
"type",
@ -43,7 +47,11 @@
"@sindresorhus/is": "^0.2.0"
},
"devDependencies": {
"@types/node": "^8.0.31",
"ava": "*",
"xo": "*"
"codecov": "^2.3.0",
"del-cli": "^1.1.0",
"nyc": "^11.2.1",
"typescript": "^2.5.3"
}
}

3
source/index.ts

@ -0,0 +1,3 @@
import { ow } from './ow';
export = ow;

8
source/lib/argument-error.ts

@ -0,0 +1,8 @@
export class ArgumentError extends Error {
constructor(message, context) {
super(message);
// TODO: Node does not preserve the error name in output when using the below, why?
//Error.captureStackTrace(this, context);
this.name = 'ArgumentError';
}
}

20
source/lib/predicates/predicate.ts

@ -0,0 +1,20 @@
import * as is from '@sindresorhus/is';
export type Validator = (value: any) => string | undefined;
export interface Context {
validators: Validator[];
}
export class Predicate {
constructor(
type: string,
public context: Context = { validators: [] }
) {
this.context.validators.push(value => {
if (!is[type](value)) {
return `Expected argument to be of type \`${type}\` but received type \`${is(value)}\``;
}
});
}
}

36
source/lib/predicates/string.ts

@ -0,0 +1,36 @@
import { Predicate, Context } from './predicate';
export class StringPredicate extends Predicate {
constructor(context?: Context) {
super('string', context);
}
/**
* Test a string to have a minimum length.
*
* @param number The minimum length of the string.
*/
minLength(number: number) {
this.context.validators.push(value => {
if (value.length < number) {
return `Expected string length to be minimum ${number}`;
}
});
return this;
}
/**
* Test a string to be alphanumeric.
*/
get alphanumeric() {
this.context.validators.push(value => {
if (!/^[a-z\d]+$/i.test(value)) {
return `Expected string to contain only alphanumeric characters but received \`${value}\``;
}
});
return this;
}
}

24
source/ow.ts

@ -0,0 +1,24 @@
import * as is from '@sindresorhus/is';
import { ArgumentError } from './lib/argument-error';
import { Predicate } from './lib/predicates/predicate';
import { StringPredicate } from './lib/predicates/string';
export interface Ow {
(value: any, predicate: Predicate): void;
/**
* Test the value to be a string.
*/
string?: StringPredicate;
}
export const ow: Ow = (value: any, predicate: Predicate) => {
for (const validator of predicate.context.validators) {
const result = validator(value);
if (result) {
// TODO: Modify the stack output to show the original `ow()` call instead of this `throw` statement
throw new ArgumentError(result, ow);
}
}
};
ow.string = new StringPredicate();

18
source/test/string.ts

@ -0,0 +1,18 @@
import test from 'ava';
import * as m from '..';
test('string', t => {
t.notThrows(() => m('foo', m.string));
t.throws(() => m(12, m.string), 'Expected argument to be of type `string` but received type `number`');
});
test('string.minLength', t => {
t.notThrows(() => m('foo', m.string.minLength(2)));
t.notThrows(() => m('foo', m.string.minLength(3)));
t.throws(() => m('foo', m.string.minLength(4)), 'Expected string length to be minimum 4');
});
test('string.alphanumeric', t => {
t.notThrows(() => m('Foo123', m.string.alphanumeric));
t.throws(() => m('Foo123!', m.string.alphanumeric), 'Expected string to contain only alphanumeric characters but received `Foo123!`');
});

6
test.js

@ -1,6 +0,0 @@
import test from 'ava';
import m from '.';
test('', t => {
});

20
tsconfig.json

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"declaration": false,
"removeComments": false,
"pretty": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"noImplicitAny": false,
"noImplicitUseStrict": false,
"noFallthroughCasesInSwitch": true,
"allowSyntheticDefaultImports": true,
"outDir": "dist"
},
"exclude": [
"node_modules",
"dist"
]
}
Loading…
Cancel
Save