diff --git a/.gitignore b/.gitignore index 735f4af..c977c85 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +*.d.ts *.log coverage/ node_modules/ diff --git a/index.js b/index.js index 6f42cf3..8107bdf 100644 --- a/index.js +++ b/index.js @@ -1,34 +1,58 @@ +/** + * @typedef {import('unist').Node} Node + * @typedef {import('unist').Parent} Parent + * + * @typedef {import('unist-util-is').Type} Type + * @typedef {import('unist-util-is').Props} Props + * @typedef {import('unist-util-is').TestFunctionAnything} TestFunctionAnything + */ + import {convert} from 'unist-util-is' -export function findBefore(parent, index, test) { - var is = convert(test) +export var findBefore = + /** + * @type {( + * ((node: Parent, index: Node|number, test: T['type']|Partial|import('unist-util-is').TestFunctionPredicate|Array.|import('unist-util-is').TestFunctionPredicate>) => T|null) & + * ((node: Parent, index: Node|number, test?: null|undefined|Type|Props|TestFunctionAnything|Array) => Node|null) + * )} + */ + ( + /** + * @param {Parent} parent Parent node + * @param {Node|number} index Child of `parent`, or it’s index + * @param {null|undefined|Type|Props|TestFunctionAnything|Array} [test] is-compatible test (such as a type) + * @returns {Node|null} + */ + function (parent, index, test) { + var is = convert(test) - if (!parent || !parent.type || !parent.children) { - throw new Error('Expected parent node') - } + if (!parent || !parent.type || !parent.children) { + throw new Error('Expected parent node') + } - if (typeof index === 'number') { - if (index < 0 || index === Number.POSITIVE_INFINITY) { - throw new Error('Expected positive finite number as index') - } - } else { - index = parent.children.indexOf(index) + if (typeof index === 'number') { + if (index < 0 || index === Number.POSITIVE_INFINITY) { + throw new Error('Expected positive finite number as index') + } + } else { + index = parent.children.indexOf(index) - if (index < 0) { - throw new Error('Expected child node or index') - } - } + if (index < 0) { + throw new Error('Expected child node or index') + } + } - // Performance. - if (index > parent.children.length) { - index = parent.children.length - } + // Performance. + if (index > parent.children.length) { + index = parent.children.length + } - while (index--) { - if (is(parent.children[index], index, parent)) { - return parent.children[index] - } - } + while (index--) { + if (is(parent.children[index], index, parent)) { + return parent.children[index] + } + } - return null -} + return null + } + ) diff --git a/package.json b/package.json index a2e8118..d86ca83 100644 --- a/package.json +++ b/package.json @@ -25,26 +25,35 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" }, "devDependencies": { + "@types/tape": "^4.0.0", "c8": "^7.0.0", "prettier": "^2.0.0", "remark": "^13.0.0", "remark-cli": "^9.0.0", "remark-preset-wooorm": "^8.0.0", + "rimraf": "^3.0.0", "tape": "^5.0.0", + "type-coverage": "^2.0.0", + "typescript": "^4.0.0", "xo": "^0.38.0" }, "scripts": { + "prepack": "npm run build && npm run format", + "build": "rimraf \"*.d.ts\" && tsc && type-coverage", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", "test-api": "node test.js", "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test.js", - "test": "npm run format && npm run test-coverage" + "test": "npm run build && npm run format && npm run test-coverage" }, "prettier": { "tabWidth": 2, @@ -57,6 +66,7 @@ "xo": { "prettier": true, "rules": { + "import/no-mutable-exports": "off", "no-var": "off", "prefer-arrow-callback": "off" } @@ -65,5 +75,10 @@ "plugins": [ "preset-wooorm" ] + }, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true } } diff --git a/test.js b/test.js index bc4c24c..3af1755 100644 --- a/test.js +++ b/test.js @@ -1,3 +1,7 @@ +/** + * @typedef {import('unist').Node} Node + */ + import test from 'tape' import remark from 'remark' import {findBefore} from './index.js' @@ -9,6 +13,7 @@ var children = paragraph.children test('unist-util-find-before', function (t) { t.throws( function () { + // @ts-ignore runtime findBefore() }, /Expected parent node/, @@ -17,9 +22,8 @@ test('unist-util-find-before', function (t) { t.throws( function () { - findBefore({ - type: 'foo' - }) + // @ts-ignore runtime + findBefore({type: 'foo'}) }, /Expected parent node/, 'should fail without parent node' @@ -27,6 +31,7 @@ test('unist-util-find-before', function (t) { t.throws( function () { + // @ts-ignore runtime findBefore({type: 'foo', children: []}) }, /Expected child node or index/, @@ -51,6 +56,7 @@ test('unist-util-find-before', function (t) { t.throws( function () { + // @ts-ignore runtime findBefore({type: 'foo', children: [{type: 'bar'}]}, 1, false) }, /Expected function, string, or object as test/, @@ -59,6 +65,7 @@ test('unist-util-find-before', function (t) { t.throws( function () { + // @ts-ignore runtime findBefore({type: 'foo', children: [{type: 'bar'}]}, 1, true) }, /Expected function, string, or object as test/, @@ -154,7 +161,11 @@ test('unist-util-find-before', function (t) { 'should return a child when given a `test` and existing (#4)' ) - function test(node, n) { + /** + * @param {Node} _ + * @param {number} n + */ + function test(_, n) { return n === 3 } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..be08abe --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "include": ["*.js"], + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020"], + "module": "ES2020", + "moduleResolution": "node", + "allowJs": true, + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true + } +}