Skip to content

Commit d4833f8

Browse files
committedOct 20, 2021
Add JSDoc based types
1 parent 646929e commit d4833f8

File tree

8 files changed

+275
-179
lines changed

8 files changed

+275
-179
lines changed
 

Diff for: ‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
coverage/
22
node_modules/
33
.DS_Store
4+
*.d.ts
45
*.log
56
yarn.lock

Diff for: ‎cli.js

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env node
22
import fs from 'node:fs'
33
import process from 'node:process'
4+
import {URL} from 'node:url'
45
import notifier from 'update-notifier'
56
import supportsColor from 'supports-color'
67
import meow from 'meow'
@@ -20,8 +21,9 @@ import retextProfanities from 'retext-profanities'
2021
import unifiedDiff from 'unified-diff'
2122
import {filter} from './filter.js'
2223

24+
/** @type {import('type-fest').PackageJson} */
2325
const pack = JSON.parse(
24-
fs.readFileSync(new URL('./package.json', import.meta.url))
26+
String(fs.readFileSync(new URL('./package.json', import.meta.url)))
2527
)
2628

2729
const textExtensions = [
@@ -38,6 +40,7 @@ const htmlExtensions = ['htm', 'html']
3840
const mdxExtensions = ['mdx']
3941

4042
// Update messages.
43+
/** @ts-expect-error: `package.json` is fine. */
4144
notifier({pkg: pack}).notify()
4245

4346
// Set-up meow.
@@ -88,7 +91,9 @@ const extensions = cli.flags.html
8891
? mdxExtensions
8992
: textExtensions
9093
const defaultGlobs = ['{docs/**/,doc/**/,}*.{' + extensions.join(',') + '}']
94+
/** @type {boolean|undefined} */
9195
let silentlyIgnore
96+
/** @type {string[]|undefined} */
9297
let globs
9398

9499
if (cli.flags.stdin) {
@@ -112,7 +117,7 @@ engine(
112117
output: false,
113118
rcName: '.alexrc',
114119
packageField: 'alex',
115-
color: supportsColor.stderr,
120+
color: Boolean(supportsColor.stderr),
116121
reporter: cli.flags.reporter || vfileReporter,
117122
reporterOptions: {
118123
verbose: cli.flags.why
@@ -121,36 +126,43 @@ engine(
121126
ignoreName: '.alexignore',
122127
silentlyIgnore,
123128
frail: true,
124-
defaultConfig: transform()
129+
defaultConfig: transform({})
125130
},
126131
function (error, code) {
127132
if (error) console.error(error.message)
128133
process.exit(code)
129134
}
130135
)
131136

132-
function transform(options) {
133-
const settings = options || {}
137+
/**
138+
* @type {import('unified-engine').ConfigTransform}
139+
* @param {import('./index.js').OptionsObject} [options]
140+
*/
141+
function transform(options = {}) {
142+
/** @type {import('unified').PluggableList} */
134143
let plugins = [
135144
retextEnglish,
136-
[retextProfanities, {sureness: settings.profanitySureness}],
137-
[retextEquality, {noBinary: settings.noBinary}]
145+
[retextProfanities, {sureness: options.profanitySureness}],
146+
[retextEquality, {noBinary: options.noBinary}]
138147
]
139148

140149
if (cli.flags.html) {
150+
// @ts-expect-error: types are having a hard time for bridges.
141151
plugins = [rehypeParse, [rehypeRetext, unified().use({plugins})]]
142152
} else if (cli.flags.mdx) {
153+
// @ts-expect-error: types are having a hard time for bridges.
143154
plugins = [remarkParse, remarkMdx, [remarkRetext, unified().use({plugins})]]
144155
} else if (!cli.flags.text) {
145156
plugins = [
146157
remarkParse,
147158
remarkGfm,
148159
[remarkFrontmatter, ['yaml', 'toml']],
160+
// @ts-expect-error: types are having a hard time for bridges.
149161
[remarkRetext, unified().use({plugins})]
150162
]
151163
}
152164

153-
plugins.push([filter, {allow: settings.allow, deny: settings.deny}])
165+
plugins.push([filter, {allow: options.allow, deny: options.deny}])
154166

155167
// Hard to check.
156168
/* c8 ignore next 3 */

Diff for: ‎filter.js

+23-8
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
1-
import remarkMessageControl from 'remark-message-control'
1+
/**
2+
* @typedef {import('mdast').Root} Root
3+
*
4+
* @typedef Options
5+
* Configuration.
6+
* @property {string[]} [deny]
7+
* The `deny` field should be an array of rules or `undefined` (the default is
8+
* `undefined`).
9+
* When provided, *only* the rules specified are reported.
10+
* You cannot use both `allow` and `deny` at the same time.
11+
* @property {string[]} [allow]
12+
* The `allow` field should be an array of rules or `undefined` (the default
13+
* is `undefined`).
14+
* When provided, the rules specified are skipped and not reported.
15+
* You cannot use both `allow` and `deny` at the same time.
16+
*/
217

3-
export function filter(options) {
4-
/* c8 ignore next */
5-
const settings = options || {}
18+
import remarkMessageControl from 'remark-message-control'
619

7-
if (settings.allow && settings.deny) {
20+
/** @type {import('unified').Plugin<[Options?]|[], Root>} */
21+
export function filter(options = {}) {
22+
if (options.allow && options.deny) {
823
throw new Error(
924
'Do not provide both allow and deny configuration parameters'
1025
)
1126
}
1227

1328
return remarkMessageControl({
1429
name: 'alex',
15-
reset: Boolean(settings.deny),
16-
enable: settings.deny,
17-
disable: settings.allow,
30+
reset: Boolean(options.deny),
31+
enable: options.deny,
32+
disable: options.allow,
1833
source: ['retext-equality', 'retext-profanities']
1934
})
2035
}

Diff for: ‎index.js

+87-31
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
/**
2+
* @typedef {import('nlcst').Root} Root
3+
* @typedef {import('./filter.js').Options} FilterOptions
4+
*
5+
* @typedef {boolean|undefined} NoBinaryOption
6+
* @typedef {0|1|2|undefined} SurenessOption
7+
*
8+
* @typedef {{noBinary: NoBinaryOption, sureness: SurenessOption}} TextOptions
9+
*
10+
* @typedef {{noBinary?: NoBinaryOption, profanitySureness?: SurenessOption} & FilterOptions} OptionsObject
11+
* @typedef {import('vfile').VFileCompatible} Input
12+
* @typedef {OptionsObject|string[]|undefined} Options
13+
*/
14+
115
import {VFile} from 'vfile'
216
import {unified} from 'unified'
317
import remarkParse from 'remark-parse'
@@ -13,31 +27,24 @@ import rehypeRetext from 'rehype-retext'
1327
import {sort} from 'vfile-sort'
1428
import {filter} from './filter.js'
1529

16-
function makeText(config) {
30+
/** @param {TextOptions} options */
31+
function makeText(options) {
1732
return unified()
1833
.use(retextEnglish)
19-
.use(retextEquality, {
20-
noBinary: config && config.noBinary
21-
})
22-
.use(retextProfanities, {
23-
sureness: config && config.profanitySureness
24-
})
34+
.use(retextEquality, options)
35+
.use(retextProfanities, options)
2536
}
2637

27-
// Alex’s core.
28-
function core(value, config, processor) {
29-
let allow
30-
let deny
31-
32-
if (Array.isArray(config)) {
33-
allow = config
34-
} else if (config) {
35-
allow = config.allow
36-
deny = config.deny
37-
}
38-
38+
/**
39+
* Alex’s core.
40+
*
41+
* @param {Input} value
42+
* @param {FilterOptions} options
43+
* @param {import('unified').Processor<void, Root>} processor
44+
*/
45+
function core(value, options, processor) {
3946
const file = new VFile(value)
40-
const tree = processor.use(filter, {allow, deny}).parse(file)
47+
const tree = processor.use(filter, options).parse(file)
4148

4249
processor.runSync(tree, file)
4350

@@ -48,41 +55,90 @@ function core(value, config, processor) {
4855

4956
export default markdown
5057

51-
// Alex.
58+
/**
59+
* Alex (markdown).
60+
*
61+
* @param {Input} value
62+
* @param {Options} [config]
63+
*/
5264
export function markdown(value, config) {
65+
const options = splitOptions(config)
5366
return core(
5467
value,
55-
config,
68+
options.filter,
5669
unified()
5770
.use(remarkParse)
5871
.use(remarkGfm)
5972
.use(remarkFrontmatter, ['yaml', 'toml'])
60-
.use(remarkRetext, makeText(config))
73+
.use(remarkRetext, makeText(options.text))
6174
)
6275
}
6376

64-
// Alex, for MDX.
77+
/**
78+
* Alex (MDX).
79+
*
80+
* @param {Input} value
81+
* @param {Options} [config]
82+
*/
6583
export function mdx(value, config) {
84+
const options = splitOptions(config)
6685
return core(
6786
value,
68-
config,
87+
options.filter,
6988
unified()
7089
.use(remarkParse)
7190
.use(remarkMdx)
72-
.use(remarkRetext, makeText(config))
91+
.use(remarkRetext, makeText(options.text))
7392
)
7493
}
7594

76-
// Alex, for HTML.
95+
/**
96+
* Alex (HTML).
97+
*
98+
* @param {Input} value
99+
* @param {Options} [config]
100+
*/
77101
export function html(value, config) {
102+
const options = splitOptions(config)
78103
return core(
79104
value,
80-
config,
81-
unified().use(rehypeParse).use(rehypeRetext, makeText(config))
105+
options.filter,
106+
unified().use(rehypeParse).use(rehypeRetext, makeText(options.text))
82107
)
83108
}
84109

85-
// Alex, without the markdown.
110+
/**
111+
* Alex (plain text).
112+
*
113+
* @param {Input} value
114+
* @param {Options} [config]
115+
*/
86116
export function text(value, config) {
87-
return core(value, config, makeText(config))
117+
const options = splitOptions(config)
118+
return core(value, options.filter, makeText(options.text))
119+
}
120+
121+
/**
122+
* @param {Options} options
123+
*/
124+
function splitOptions(options) {
125+
/** @type {string[]|undefined} */
126+
let allow
127+
/** @type {string[]|undefined} */
128+
let deny
129+
/** @type {boolean|undefined} */
130+
let noBinary
131+
/** @type {SurenessOption} */
132+
let sureness
133+
134+
if (Array.isArray(options)) {
135+
allow = options
136+
} else if (options) {
137+
allow = options.allow
138+
deny = options.deny
139+
noBinary = options.noBinary
140+
sureness = options.profanitySureness
141+
}
142+
143+
return {filter: {allow, deny}, text: {noBinary, sureness}}
88144
}

Diff for: ‎package.json

+19-1
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,17 @@
5858
"type": "module",
5959
"sideEffects": false,
6060
"bin": "cli.js",
61+
"types": "index.d.ts",
6162
"files": [
63+
"index.d.ts",
6264
"index.js",
65+
"filter.d.ts",
6366
"filter.js",
6467
"cli.js"
6568
],
6669
"dependencies": {
70+
"@types/mdast": "^3.0.0",
71+
"@types/nlcst": "^1.0.0",
6772
"meow": "^10.0.0",
6873
"rehype-parse": "^8.0.0",
6974
"rehype-retext": "^3.0.0",
@@ -85,19 +90,26 @@
8590
"vfile-sort": "^3.0.0"
8691
},
8792
"devDependencies": {
93+
"@types/tape": "^4.0.0",
94+
"@types/update-notifier": "^5.0.0",
8895
"c8": "^7.10.0",
8996
"prettier": "^2.0.0",
9097
"remark-cli": "^10.0.0",
9198
"remark-preset-wooorm": "^9.0.0",
99+
"rimraf": "^3.0.0",
92100
"tape": "^5.0.0",
101+
"type-coverage": "^2.0.0",
102+
"type-fest": "^2.0.0",
103+
"typescript": "^4.0.0",
93104
"vfile-reporter-json": "^3.0.0",
94105
"xo": "^0.45.0"
95106
},
96107
"scripts": {
108+
"build": "rimraf \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage",
97109
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
98110
"test-api": "node test/index.js",
99111
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api",
100-
"test": "npm run format && npm run test-coverage"
112+
"test": "npm run build && npm run format && npm run test-coverage"
101113
},
102114
"alex": {
103115
"allow": [
@@ -129,5 +141,11 @@
129141
}
130142
]
131143
]
144+
},
145+
"typeCoverage": {
146+
"atLeast": 100,
147+
"detail": true,
148+
"strict": true,
149+
"ignoreCatch": true
132150
}
133151
}

Diff for: ‎test/api.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import fs from 'node:fs'
2+
import {URL} from 'node:url'
23
import test from 'tape'
34
import alex, {markdown, mdx, text, html} from '../index.js'
45

Diff for: ‎test/cli.js

+108-131
Large diffs are not rendered by default.

Diff for: ‎tsconfig.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"include": ["test/**/*.js", "*.js"],
3+
"compilerOptions": {
4+
"target": "ES2020",
5+
"lib": ["ES2020"],
6+
"module": "ES2020",
7+
"moduleResolution": "node",
8+
"allowJs": true,
9+
"checkJs": true,
10+
"declaration": true,
11+
"emitDeclarationOnly": true,
12+
"allowSyntheticDefaultImports": true,
13+
"skipLibCheck": true,
14+
"strict": true
15+
}
16+
}

0 commit comments

Comments
 (0)
Please sign in to comment.