Skip to content

Commit b6ea1f3

Browse files
committed
Add JSDoc based types
1 parent 5573ee1 commit b6ea1f3

File tree

5 files changed

+114
-18
lines changed

5 files changed

+114
-18
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
2+
*.d.ts
23
*.log
34
coverage/
45
node_modules/

index.js

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,101 @@
1+
/**
2+
* @typedef {import('css-selector-parser').RuleAttr} CssRuleAttr
3+
* @typedef {import('css-selector-parser').RulePseudo} CssRulePseudo
4+
* @typedef {import('css-selector-parser').Selectors} CssSelectors
5+
* @typedef {import('css-selector-parser').RuleSet} CssRuleSet
6+
* @typedef {import('css-selector-parser').Rule} CssRule
7+
*
8+
* @typedef {import('hast').Element} HastElement
9+
* @typedef {import('hast').Properties} HastProperties
10+
*
11+
* @typedef {'html'|'svg'} Space
12+
*
13+
* @typedef Options
14+
* @property {Space} [space]
15+
*
16+
* @typedef Context
17+
* @property {Space} space
18+
* @property {boolean} root
19+
*/
20+
121
import {h, s} from 'hastscript'
222
import {zwitch} from 'zwitch'
323
import {CssSelectorParser} from 'css-selector-parser'
424

5-
var compile = zwitch('type', {
6-
handlers: {
7-
selectors,
8-
ruleSet,
9-
rule
10-
}
11-
})
25+
var compile = zwitch('type', {handlers: {selectors, ruleSet, rule}})
1226

1327
var parser = new CssSelectorParser()
1428

1529
parser.registerNestingOperators('>', '+', '~')
1630
// Register these so we can throw nicer errors.
1731
parser.registerAttrEqualityMods('~', '|', '^', '$', '*')
1832

33+
/**
34+
* @param {string} [selector='']
35+
* @param {Space|Options} [space='html']
36+
* @returns {HastElement}
37+
*/
1938
export function fromSelector(selector, space) {
20-
var config = {space: (space && space.space) || space || 'html', root: true}
39+
/** @type {Context} */
40+
var config = {
41+
space:
42+
(space && typeof space === 'object' && space.space) ||
43+
(typeof space === 'string' && space) ||
44+
'html',
45+
root: true
46+
}
2147

2248
return (
49+
// @ts-ignore Assume one element is returned.
2350
compile(parser.parse(selector || ''), config) || build(config.space)('')
2451
)
2552
}
2653

27-
function selectors() {
54+
/**
55+
* @param {CssSelectors} _
56+
*/
57+
function selectors(_) {
2858
throw new Error('Cannot handle selector list')
2959
}
3060

61+
/**
62+
* @param {CssRuleSet} query
63+
* @param {Context} config
64+
* @returns {HastElement|Array.<HastElement>}
65+
*/
3166
function ruleSet(query, config) {
67+
// @ts-ignore Assume one or more elements is returned.
3268
return compile(query.rule, config)
3369
}
3470

71+
/**
72+
* @param {CssRule} query
73+
* @param {Context} config
74+
* @returns {HastElement|Array.<HastElement>}
75+
*/
3576
function rule(query, config) {
3677
var parentSpace = config.space
3778
var name = query.tagName === '*' ? '' : query.tagName || ''
3879
var space = parentSpace === 'html' && name === 'svg' ? 'svg' : parentSpace
80+
/** @type {boolean} */
3981
var sibling
40-
var operator
82+
/** @type {HastElement} */
4183
var node
4284

4385
if (query.rule) {
44-
operator = query.rule.nestingOperator
45-
sibling = operator === '+' || operator === '~'
86+
sibling =
87+
query.rule.nestingOperator === '+' || query.rule.nestingOperator === '~'
4688

4789
if (sibling && config.root) {
4890
throw new Error(
49-
'Cannot handle sibling combinator `' + operator + '` at root'
91+
'Cannot handle sibling combinator `' +
92+
query.rule.nestingOperator +
93+
'` at root'
5094
)
5195
}
5296
}
5397

98+
// @ts-ignore Assume one or more elements is returned.
5499
node = build(space)(
55100
name,
56101
Object.assign(
@@ -61,9 +106,14 @@ function rule(query, config) {
61106
!query.rule || sibling ? [] : compile(query.rule, {space})
62107
)
63108

109+
// @ts-ignore Assume one or more elements is returned.
64110
return sibling ? [node, compile(query.rule, {space: parentSpace})] : node
65111
}
66112

113+
/**
114+
* @param {Array.<CssRulePseudo>} pseudos
115+
* @returns {HastProperties}
116+
*/
67117
function pseudosToHast(pseudos) {
68118
var pseudo = pseudos[0]
69119

@@ -78,15 +128,21 @@ function pseudosToHast(pseudos) {
78128
return {}
79129
}
80130

131+
/**
132+
* @param {Array.<CssRuleAttr>} attrs
133+
* @returns {HastProperties}
134+
*/
81135
function attrsToHast(attrs) {
82-
var props = {}
83136
var index = -1
137+
/** @type {HastProperties} */
138+
var props = {}
139+
/** @type {CssRuleAttr} */
84140
var attr
85141

86142
while (++index < attrs.length) {
87143
attr = attrs[index]
88144

89-
if (attr.operator) {
145+
if ('operator' in attr) {
90146
if (attr.operator === '=') {
91147
props[attr.name] = attr.value
92148
} else {
@@ -102,6 +158,10 @@ function attrsToHast(attrs) {
102158
return props
103159
}
104160

161+
/**
162+
* @param {Space} space
163+
* @returns {typeof h}
164+
*/
105165
function build(space) {
106166
return space === 'html' ? h : s
107167
}

package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,33 @@
2929
"sideEffects": false,
3030
"type": "module",
3131
"main": "index.js",
32+
"types": "index.d.ts",
3233
"files": [
34+
"index.d.ts",
3335
"index.js"
3436
],
3537
"dependencies": {
38+
"@types/hast": "^2.0.0",
3639
"css-selector-parser": "^1.0.0",
3740
"hastscript": "^7.0.0",
3841
"zwitch": "^2.0.0"
3942
},
4043
"devDependencies": {
44+
"@types/tape": "^4.0.0",
4145
"c8": "^7.0.0",
4246
"prettier": "^2.0.0",
4347
"remark-cli": "^9.0.0",
4448
"remark-preset-wooorm": "^8.0.0",
49+
"rimraf": "^3.0.0",
4550
"tape": "^5.0.0",
51+
"type-coverage": "^2.0.0",
52+
"typescript": "^4.0.0",
4653
"unist-builder": "^3.0.0",
4754
"xo": "^0.39.0"
4855
},
4956
"scripts": {
57+
"prepack": "npm run build && npm run format",
58+
"build": "rimraf \"*.d.ts\" && tsc && type-coverage",
5059
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
5160
"test-api": "node test.js",
5261
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test.js",
@@ -71,5 +80,10 @@
7180
"plugins": [
7281
"preset-wooorm"
7382
]
83+
},
84+
"typeCoverage": {
85+
"atLeast": 100,
86+
"detail": true,
87+
"strict": true
7488
}
7589
}

test.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,21 @@ test('fromSelector()', function (t) {
139139
t.deepEqual(fromSelector('altGlyph', 'svg').tagName, 'altGlyph', 'space (#2)')
140140

141141
t.deepEqual(
142-
fromSelector('svg altGlyph').children[0].tagName,
142+
fromSelector('altGlyph', {space: 'svg'}).tagName,
143143
'altGlyph',
144144
'space (#3)'
145145
)
146146

147+
t.deepEqual(
148+
fromSelector('svg altGlyph').children[0].tagName,
149+
'altGlyph',
150+
'space (#4)'
151+
)
152+
147153
t.deepEqual(
148154
fromSelector('div svg + altGlyph').children[1].tagName,
149155
'altglyph',
150-
'space (#4)'
156+
'space (#5)'
151157
)
152158

153159
t.deepEqual(
@@ -159,7 +165,7 @@ test('fromSelector()', function (t) {
159165
s('circle', {cx: '10', cy: '10', r: '10'}, [s('altGlyph')])
160166
])
161167
]),
162-
'space (#5)'
168+
'space (#6)'
163169
)
164170

165171
t.end()

tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"include": ["*.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+
}
15+
}

0 commit comments

Comments
 (0)