patroon 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -518,12 +518,12 @@ const oneIsTwo = patroon(1, 2)
518
518
  oneIsTwo(3)
519
519
  ```
520
520
  ```
521
- [ 3 ]
522
- /home/ant/projects/patroon/src/index.js:93
521
+ /home/ant/projects/patroon/src/index.js:96
523
522
  throw error
524
523
  ^
525
524
 
526
525
  NoMatchError: Not able to match any pattern for arguments
526
+ at /home/ant/projects/patroon/src/index.js:90:21
527
527
  ```
528
528
 
529
529
  #### UnevenArgumentCountError
@@ -534,12 +534,12 @@ Another error that occurs is when the patroon function is not used correctly.
534
534
  patroon(1)
535
535
  ```
536
536
  ```
537
- /home/ant/projects/patroon/src/index.js:80
537
+ /home/ant/projects/patroon/src/index.js:82
538
538
  if (!isEven(list.length)) { throw new UnevenArgumentCountError('Patroon should have an even amount of arguments.') }
539
539
  ^
540
540
 
541
541
  UnevenArgumentCountError: Patroon should have an even amount of arguments.
542
- at patroon (/home/ant/projects/patroon/src/index.js:80:37)
542
+ at patroon (/home/ant/projects/patroon/src/index.js:82:37)
543
543
  ```
544
544
 
545
545
  #### PatroonError
@@ -573,8 +573,8 @@ We also care about code coverage so we'll use [nyc][8] to generate a coverage
573
573
  report.
574
574
 
575
575
  ```bash bash -eo pipefail
576
- # Install and prune dependencies
577
- { npm i && npm prune; } &> /dev/null
576
+ # Clean install dependencies.
577
+ npm ci &> /dev/null
578
578
 
579
579
  # Run tests and generate a coverage report
580
580
  npx nyc npm t | npx tap-nyc
@@ -583,7 +583,7 @@ npx nyc npm t | npx tap-nyc
583
583
  npx nyc check-coverage
584
584
  ```
585
585
  ```
586
- > patroon@1.1.2 test
586
+ > patroon@1.2.0 test
587
587
  > tape ./src/index.test.js
588
588
  -------------|---------|----------|---------|---------|-------------------
589
589
  File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
@@ -594,10 +594,10 @@ npx nyc check-coverage
594
594
  walkable.js | 100 | 100 | 100 | 100 |
595
595
  -------------|---------|----------|---------|---------|-------------------
596
596
 
597
- total: 19
598
- passing: 19
597
+ total: 28
598
+ passing: 28
599
599
 
600
- duration: 1.5s
600
+ duration: 8.8s
601
601
 
602
602
  ```
603
603
 
package/README.mz CHANGED
@@ -442,8 +442,8 @@ We also care about code coverage so we'll use [nyc][8] to generate a coverage
442
442
  report.
443
443
 
444
444
  ```bash bash -eo pipefail
445
- # Install and prune dependencies
446
- { npm i && npm prune; } &> /dev/null
445
+ # Clean install dependencies.
446
+ npm ci &> /dev/null
447
447
 
448
448
  # Run tests and generate a coverage report
449
449
  npx nyc npm t | npx tap-nyc
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patroon",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "Pattern matching library",
5
5
  "repository": "github:bas080/patroon",
6
6
  "main": "./src/index.js",
package/src/helpers.js CHANGED
@@ -17,7 +17,17 @@ const T = () => true
17
17
  const is = Ctor => instance => isDefined(instance) && (instance.constructor ===
18
18
  Ctor || instance instanceof Ctor)
19
19
 
20
+ function isPrimitive (x) {
21
+ return x !== Object(x)
22
+ }
23
+
24
+ function isEmpty (x) {
25
+ return !isPrimitive(x) && (Object.keys(x).length === 0)
26
+ }
27
+
20
28
  module.exports = {
29
+ isPrimitive,
30
+ isEmpty,
21
31
  isDefined,
22
32
  always: x => () => x,
23
33
  is,
package/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const { mapLeaves, path, PathError } = require('./walkable')()
2
- const { isFunction, equals, T, is, tryCatch, isEven, isNil, toPairs, always } = require('./helpers')
2
+ const { isFunction, equals, T, is, tryCatch, isEven, isNil, toPairs, always, isEmpty, isPrimitive } = require('./helpers')
3
3
  const { inspect } = require('util')
4
4
 
5
5
  const deprecated = (fn, message) => (...args) => {
@@ -44,6 +44,8 @@ const predicate = pattern => {
44
44
 
45
45
  if (isFunction(value)) { return arg => value(path(pth, arg)) }
46
46
 
47
+ if (isEmpty(value)) { return arg => !isPrimitive(path(pth, arg)) }
48
+
47
49
  return arg => equals(path(pth, arg), value)
48
50
  }
49
51
 
@@ -87,8 +89,9 @@ const patroon = (...list) => {
87
89
  if (isNil(found)) {
88
90
  const error = new NoMatchError('Not able to match any pattern for arguments')
89
91
  error.arguments = args
90
-
91
- console.error(inspect(args))
92
+ error.patterns = list
93
+ error.arguments_inspect = inspect(args)
94
+ error.patterns_inspect = inspect(list)
92
95
 
93
96
  throw error
94
97
  }
package/src/index.test.js CHANGED
@@ -1,5 +1,7 @@
1
1
  const { test } = require('tape')
2
+ const { isNil } = require('./helpers')
2
3
  const { check, gen } = require('tape-check')
4
+ const { version } = require('../package')
3
5
  const {
4
6
  reference,
5
7
  instanceOf,
@@ -56,16 +58,12 @@ test('Matches always when pattern equals value', check(gen.any, (t, val) => {
56
58
  }))
57
59
 
58
60
  test('Matches none of the patterns and throws', t => {
59
- try {
61
+ t.plan(1)
62
+ t.throws(() =>
60
63
  patroon(
61
64
  1, () => t.fail(),
62
65
  2, () => t.fail()
63
- )(3)
64
- t.fail()
65
- } catch (e) {
66
- t.ok(e instanceof NoMatchError)
67
- t.end()
68
- }
66
+ )(3), NoMatchError)
69
67
  })
70
68
 
71
69
  test('Does not match when a value does not exist', t => {
@@ -150,11 +148,10 @@ test('Matches always when pattern equals value', check(gen.any, (t, val) => {
150
148
  t.end()
151
149
  }))
152
150
 
153
- test('Matches when arguments match multi pattern', check(gen.any, (t, val) => {
151
+ test('Matches always when arguments match multi pattern', check(gen.array(gen.any, {}), (t, args) => {
154
152
  patroon(
155
- multi(0, 1, 2), () => t.fail(),
156
- multi(1, 2, 3), () => t.end()
157
- )(1, 2, 3)
153
+ multi(...args), () => t.end()
154
+ )(...args)
158
155
  }))
159
156
 
160
157
  test('Deprecated functions', ts => {
@@ -172,21 +169,58 @@ test('Deprecated functions', ts => {
172
169
  ts.end()
173
170
  })
174
171
 
172
+ test('Returns primitive values', check(gen.primitive, (t, primitive) => {
173
+ t.equals(patroon(true, primitive)(true), primitive)
174
+ t.end()
175
+ }))
176
+
177
+ test(
178
+ 'Does not match primitive with empty object or array',
179
+ check(gen.primitive, (t, primitive) => {
180
+ t.plan(2)
181
+ t.throws(() => patroon([], true)(primitive), NoMatchError)
182
+ t.throws(() => patroon({}, true)(primitive), NoMatchError)
183
+ }))
184
+
185
+ test('Matching on nil values', check(gen.any, v => !isNil(v), (t, any) => {
186
+ t.plan(2)
187
+ t.throws(() => patroon(any, true)(undefined), NoMatchError)
188
+ t.throws(() => patroon(any, true)(null), NoMatchError)
189
+ }))
190
+
175
191
  // Circular reference
176
192
  const circular = {}
177
193
  circular.a = circular
178
194
 
179
- test('At the moment recursive patterns throw and print', t => {
195
+ test('Allows recursive patterns', t => {
180
196
  t.plan(1)
181
- t.throws(() => patroon(circular, true)({}))
197
+ t.ok(patroon(circular, true)(circular))
182
198
  })
183
199
 
184
200
  test('Throws and prints because path is not defined', t => {
185
201
  t.plan(1)
186
- t.throws(() => patroon({ b: _ }, true)(circular))
202
+ t.throws(() => patroon({ b: _ }, true)(circular), NoMatchError)
187
203
  })
188
204
 
189
205
  test('Does not throw when value is recursive', t => {
190
206
  t.plan(1)
191
207
  t.ok(patroon({ a: _ }, true)(circular))
192
208
  })
209
+
210
+ test('Does not match on reference', t => {
211
+ t.plan(4)
212
+ t.ok(patroon({ a: {} }, true)({ a: {} }))
213
+ t.ok(patroon({ a: [] }, true)({ a: [] }))
214
+ t.ok(patroon({ a: [] }, true)({ a: {} }))
215
+ t.ok(patroon({ a: {} }, true)({ a: [] }))
216
+ t.end()
217
+ })
218
+
219
+ if (version.startsWith('2')) {
220
+ test('Deprecated functions in version 2', t => {
221
+ t.plan(3)
222
+ t.ok(isNil(require('./index').typed))
223
+ t.ok(isNil(require('./index').t))
224
+ t.ok(isNil(require('./index').ref))
225
+ })
226
+ }
package/src/walkable.js CHANGED
@@ -1,8 +1,10 @@
1
- const { isFunction, isNil } = require('./helpers')
1
+ const { isEmpty, isFunction, isNil } = require('./helpers')
2
2
 
3
3
  function walk (config, transform, item, path = []) {
4
4
  if (config.isLeafe(item)) { return transform(item, path) }
5
5
 
6
+ if (config.isCircular(item)) { return transform(item, path) }
7
+
6
8
  return config.map((v, i) => walk(config, transform, v, [...path, i]), item)
7
9
  }
8
10
 
@@ -24,8 +26,16 @@ function isObject (v) {
24
26
  return typeof v === 'object' && v !== null
25
27
  }
26
28
 
29
+ const isCircular = (visited = []) => x => {
30
+ const isCircular = visited.includes(x)
31
+
32
+ visited.push(x)
33
+
34
+ return isCircular
35
+ }
36
+
27
37
  function isLeafe (x) {
28
- return Number.isNaN(x) || isNil(x) || x instanceof RegExp || isFunction(x) || (!isObject(x) && !isArray(x))
38
+ return Number.isNaN(x) || isNil(x) || x instanceof RegExp || isFunction(x) || isEmpty(x) || (!isObject(x) && !isArray(x))
29
39
  }
30
40
 
31
41
  function mapLeaves (config, cb, item) {
@@ -42,7 +52,7 @@ function path (pth, v, errorData) {
42
52
 
43
53
  const [key, ...rest] = pth
44
54
 
45
- if (!(key in v)) {
55
+ if (isNil(v) || !(key in v)) {
46
56
  const error = new PathError(
47
57
  `Cannot read path ${pth}.`)
48
58
 
@@ -83,7 +93,10 @@ function walkable (config = {}) {
83
93
  reduce,
84
94
  mapLeaves
85
95
  ].reduce((api, fn) => {
86
- api[fn.name] = fn.bind(null, config)
96
+ api[fn.name] = (...args) => fn({
97
+ isCircular: isCircular(),
98
+ ...config
99
+ }, ...args)
87
100
 
88
101
  return api
89
102
  }, api)