diff --git a/.config b/.config index 1a615b4..44ef701 100644 --- a/.config +++ b/.config @@ -2,4 +2,3 @@ repo-owner = sanctuary-js repo-name = sanctuary-show contributing-file = .github/CONTRIBUTING.md heading-level = 3 -module-type = commonjs diff --git a/.eslintrc.json b/.eslintrc.json index 252ce22..7a81095 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,7 @@ { "root": true, "extends": ["./node_modules/sanctuary-style/eslint.json"], + "parserOptions": {"sourceType": "module"}, "overrides": [ { "files": ["*.md"], diff --git a/.gitignore b/.gitignore index bd53bbe..cba87a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -/.nyc_output/ /coverage/ /node_modules/ diff --git a/index.js b/index.js index 6f37c86..dd5ab37 100644 --- a/index.js +++ b/index.js @@ -30,171 +30,152 @@ //. }; //. ``` -(f => { +export {show as default}; - 'use strict'; +// $$show :: String +const $$show = '@@show'; - /* istanbul ignore else */ - if (typeof module === 'object' && typeof module.exports === 'object') { - module.exports = f (); - } else if (typeof define === 'function' && define.amd != null) { - define ([], f); - } else { - self.sanctuaryShow = f (); - } +// seen :: Array Any +const seen = []; + +// entry :: Object -> String -> String +const entry = o => k => show (k) + ': ' + show (o[k]); + +// sortedKeys :: Object -> Array String +const sortedKeys = o => (Object.keys (o)).sort (); + +//# show :: Showable a => a -> String +//. +//. Returns a useful string representation of the given value. +//. +//. Dispatches to the value's `@@show` method if present. +//. +//. Where practical, `show (eval ('(' + show (x) + ')')) = show (x)`. +//. +//. ```javascript +//. > show (null) +//. 'null' +//. +//. > show (undefined) +//. 'undefined' +//. +//. > show (true) +//. 'true' +//. +//. > show (new Boolean (false)) +//. 'new Boolean (false)' +//. +//. > show (-0) +//. '-0' +//. +//. > show (NaN) +//. 'NaN' +//. +//. > show (new Number (Infinity)) +//. 'new Number (Infinity)' +//. +//. > show ('foo\n"bar"\nbaz\n') +//. '"foo\\n\\"bar\\"\\nbaz\\n"' +//. +//. > show (new String ('')) +//. 'new String ("")' +//. +//. > show (['foo', 'bar', 'baz']) +//. '["foo", "bar", "baz"]' +//. +//. > show ([[[[[0]]]]]) +//. '[[[[[0]]]]]' +//. +//. > show ({x: [1, 2], y: [3, 4], z: [5, 6]}) +//. '{"x": [1, 2], "y": [3, 4], "z": [5, 6]}' +//. ``` +const show = x => { + if (seen.indexOf (x) >= 0) return ''; + + const repr = Object.prototype.toString.call (x); + + switch (repr) { + + case '[object Null]': + return 'null'; + + case '[object Undefined]': + return 'undefined'; + + case '[object Boolean]': + return typeof x === 'object' ? + 'new Boolean (' + show (x.valueOf ()) + ')' : + x.toString (); + + case '[object Number]': + return typeof x === 'object' ? + 'new Number (' + show (x.valueOf ()) + ')' : + 1 / x === -Infinity ? '-0' : x.toString (10); + + case '[object String]': + return typeof x === 'object' ? + 'new String (' + show (x.valueOf ()) + ')' : + JSON.stringify (x); + + case '[object RegExp]': + return x.toString (); + + case '[object Date]': + return 'new Date (' + + show (isNaN (x.valueOf ()) ? NaN : x.toISOString ()) + + ')'; + + case '[object Error]': + return 'new ' + x.name + ' (' + show (x.message) + ')'; + + case '[object Arguments]': + return 'function () { return arguments; } (' + + (Array.prototype.map.call (x, show)).join (', ') + + ')'; + + case '[object Array]': + seen.push (x); + try { + return '[' + ((x.map (show)).concat ( + sortedKeys (x) + .filter (k => !(/^\d+$/.test (k))) + .map (entry (x)) + )).join (', ') + ']'; + } finally { + seen.pop (); + } + + case '[object Object]': + seen.push (x); + try { + return ( + $$show in x && + (x.constructor == null || x.constructor.prototype !== x) ? + x[$$show] () : + '{' + ((sortedKeys (x)).map (entry (x))).join (', ') + '}' + ); + } finally { + seen.pop (); + } + + case '[object Set]': + seen.push (x); + try { + return 'new Set (' + show (Array.from (x.values ())) + ')'; + } finally { + seen.pop (); + } + + case '[object Map]': + seen.push (x); + try { + return 'new Map (' + show (Array.from (x.entries ())) + ')'; + } finally { + seen.pop (); + } + + default: + return repr.replace (/^\[(.*)\]$/, '<$1>'); -}) (() => { - - 'use strict'; - - // $$show :: String - const $$show = '@@show'; - - // seen :: Array Any - const seen = []; - - // entry :: Object -> String -> String - const entry = o => k => show (k) + ': ' + show (o[k]); - - // sortedKeys :: Object -> Array String - const sortedKeys = o => (Object.keys (o)).sort (); - - //# show :: Showable a => a -> String - //. - //. Returns a useful string representation of the given value. - //. - //. Dispatches to the value's `@@show` method if present. - //. - //. Where practical, `show (eval ('(' + show (x) + ')')) = show (x)`. - //. - //. ```javascript - //. > show (null) - //. 'null' - //. - //. > show (undefined) - //. 'undefined' - //. - //. > show (true) - //. 'true' - //. - //. > show (new Boolean (false)) - //. 'new Boolean (false)' - //. - //. > show (-0) - //. '-0' - //. - //. > show (NaN) - //. 'NaN' - //. - //. > show (new Number (Infinity)) - //. 'new Number (Infinity)' - //. - //. > show ('foo\n"bar"\nbaz\n') - //. '"foo\\n\\"bar\\"\\nbaz\\n"' - //. - //. > show (new String ('')) - //. 'new String ("")' - //. - //. > show (['foo', 'bar', 'baz']) - //. '["foo", "bar", "baz"]' - //. - //. > show ([[[[[0]]]]]) - //. '[[[[[0]]]]]' - //. - //. > show ({x: [1, 2], y: [3, 4], z: [5, 6]}) - //. '{"x": [1, 2], "y": [3, 4], "z": [5, 6]}' - //. ``` - const show = x => { - if (seen.indexOf (x) >= 0) return ''; - - const repr = Object.prototype.toString.call (x); - - switch (repr) { - - case '[object Null]': - return 'null'; - - case '[object Undefined]': - return 'undefined'; - - case '[object Boolean]': - return typeof x === 'object' ? - 'new Boolean (' + show (x.valueOf ()) + ')' : - x.toString (); - - case '[object Number]': - return typeof x === 'object' ? - 'new Number (' + show (x.valueOf ()) + ')' : - 1 / x === -Infinity ? '-0' : x.toString (10); - - case '[object String]': - return typeof x === 'object' ? - 'new String (' + show (x.valueOf ()) + ')' : - JSON.stringify (x); - - case '[object RegExp]': - return x.toString (); - - case '[object Date]': - return 'new Date (' + - show (isNaN (x.valueOf ()) ? NaN : x.toISOString ()) + - ')'; - - case '[object Error]': - return 'new ' + x.name + ' (' + show (x.message) + ')'; - - case '[object Arguments]': - return 'function () { return arguments; } (' + - (Array.prototype.map.call (x, show)).join (', ') + - ')'; - - case '[object Array]': - seen.push (x); - try { - return '[' + ((x.map (show)).concat ( - sortedKeys (x) - .filter (k => !(/^\d+$/.test (k))) - .map (entry (x)) - )).join (', ') + ']'; - } finally { - seen.pop (); - } - - case '[object Object]': - seen.push (x); - try { - return ( - $$show in x && - (x.constructor == null || x.constructor.prototype !== x) ? - x[$$show] () : - '{' + ((sortedKeys (x)).map (entry (x))).join (', ') + '}' - ); - } finally { - seen.pop (); - } - - case '[object Set]': - seen.push (x); - try { - return 'new Set (' + show (Array.from (x.values ())) + ')'; - } finally { - seen.pop (); - } - - case '[object Map]': - seen.push (x); - try { - return 'new Map (' + show (Array.from (x.entries ())) + ')'; - } finally { - seen.pop (); - } - - default: - return repr.replace (/^\[(.*)\]$/, '<$1>'); - - } - }; - - return show; - -}); + } +}; diff --git a/package.json b/package.json index 01f35a6..c94813c 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,11 @@ "type": "git", "url": "git://github.com/sanctuary-js/sanctuary-show.git" }, + "type": "module", + "exports": { + ".": "./index.js", + "./package.json": "./package.json" + }, "files": [ "/LICENSE", "/README.md", @@ -15,6 +20,8 @@ ], "dependencies": {}, "devDependencies": { + "c8": "8.0.x", + "oletus": "4.0.x", "sanctuary-scripts": "6.0.x" }, "scripts": { @@ -22,8 +29,5 @@ "lint": "sanctuary-lint", "release": "sanctuary-release", "test": "npm run lint && sanctuary-test && npm run doctest" - }, - "mocha": { - "ui": "tdd" } } diff --git a/scripts/test b/scripts/test new file mode 100755 index 0000000..5ccef32 --- /dev/null +++ b/scripts/test @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euf -o pipefail + +node_modules/.bin/c8 \ + --check-coverage \ + --100 \ + --reporter text \ + --reporter html \ + --include index.js \ + node_modules/.bin/oletus -- test/index.js diff --git a/test/index.js b/test/index.js index 97f4b91..235a729 100644 --- a/test/index.js +++ b/test/index.js @@ -1,13 +1,10 @@ -'use strict'; +import assert from 'node:assert'; -const assert = require ('assert'); +import test from 'oletus'; -const show = require ('..'); +import show from 'sanctuary-show'; -// NODE_VERSION :: Integer -const NODE_VERSION = Number (process.version.replace (/^v|[.].*$/g, '')); - // eq :: a -> b -> Undefined ! function eq(actual) { assert.strictEqual (arguments.length, eq.length); @@ -86,10 +83,7 @@ test ('arrays', () => { eq (show ([])) ('[]'); eq (show (['foo'])) ('["foo"]'); eq (show (['foo', 'bar'])) ('["foo", "bar"]'); - eq (show (/x/.exec ('xyz'))) - (NODE_VERSION < 10 ? - '["x", "index": 0, "input": "xyz"]' : - '["x", "groups": undefined, "index": 0, "input": "xyz"]'); + eq (show (/x/.exec ('xyz'))) ('["x", "groups": undefined, "index": 0, "input": "xyz"]'); eq (show ((() => { const xs = []; xs.z = true; xs.a = true; return xs; }) ())) ('["a": true, "z": true]'); });