Skip to content

Commit e0f582e

Browse files
committed
Refactor to move implementation to lib/
1 parent 39273c7 commit e0f582e

File tree

7 files changed

+381
-379
lines changed

7 files changed

+381
-379
lines changed

index.js

Lines changed: 2 additions & 369 deletions
Original file line numberDiff line numberDiff line change
@@ -1,372 +1,5 @@
11
/**
2-
* @typedef {import('assert').AssertionError} AssertionError
3-
* @typedef {import('unist').Node} Node
4-
* @typedef {import('unist').Parent} Parent
5-
* @typedef {import('unist').Literal} Literal
6-
* @typedef {import('unist').Position} Position
7-
* @typedef {import('unist').Point} Point
8-
* @typedef {Node & {children: never, value: never}} _Void
9-
*
10-
* @typedef SeenErrorFields
11-
* @property {true} [__unist__]
12-
*
13-
* @typedef {Error & SeenErrorFields} SeenError
2+
* @typedef {import('./lib/index.js').AssertionError} AssertionError
143
*/
154

16-
import nodeAssert from 'node:assert'
17-
import {inspect} from './inspect.js'
18-
19-
const own = {}.hasOwnProperty
20-
21-
/**
22-
* Assert that `tree` is a valid unist node.
23-
*
24-
* If `node` is a parent, all children will be asserted too.
25-
*
26-
* @param {unknown} [tree]
27-
* Thing to assert.
28-
* @param {Parent | null | undefined} [parent]
29-
* Optional, valid parent.
30-
* @returns {asserts tree is Node}
31-
* Whether `tree` (and its descendants) are valid nodes.
32-
* @throws {AssertionError}
33-
* When `tree` (or its descendants) is not a node.
34-
*/
35-
export function assert(tree, parent) {
36-
return wrap(assertNode)(tree, parent)
37-
}
38-
39-
/**
40-
* Assert that `tree` is a valid unist parent.
41-
*
42-
* All children will be asserted too.
43-
*
44-
* @param {unknown} [tree]
45-
* Thing to assert.
46-
* @param {Parent | null | undefined} [parent]
47-
* Optional, valid parent.
48-
* @returns {asserts tree is Parent}
49-
* Whether `tree` is a parent and its descendants are valid nodes.
50-
* @throws {AssertionError}
51-
* When `tree` is not a parent or its descendants are not nodes.
52-
*/
53-
export function parent(tree, parent) {
54-
return wrap(assertParent)(tree, parent)
55-
}
56-
57-
/**
58-
* Assert that `node` is a valid unist literal.
59-
*
60-
* @param {unknown} [node]
61-
* Thing to assert.
62-
* @param {Parent | null | undefined} [parent]
63-
* Optional, valid parent.
64-
* @returns {asserts node is Literal}
65-
* Whether `node` is a literal.
66-
* @throws {AssertionError}
67-
* When `node` is not a literal.
68-
*/
69-
export function literal(node, parent) {
70-
return wrap(assertLiteral)(node, parent)
71-
}
72-
73-
/**
74-
* Assert that `node` is a valid void node.
75-
*
76-
* @param {unknown} [node]
77-
* Thing to assert.
78-
* @param {Parent | null | undefined} [parent]
79-
* Optional, valid parent.
80-
* @returns {asserts node is _Void}
81-
* Whether `node` is a node but neither parent nor literal.
82-
* @throws {AssertionError}
83-
* When `node` is not a node, a parent, or a literal.
84-
*/
85-
export function _void(node, parent) {
86-
return wrap(assertVoid)(node, parent)
87-
}
88-
89-
// Identifier to check if a value is seen.
90-
const ID = '__unist__'
91-
92-
// List of specced properties.
93-
const defined = new Set(['type', 'value', 'children', 'position'])
94-
95-
/**
96-
* Wrapper that adds the current node (and parent, if available) to error
97-
* messages.
98-
*
99-
* @template {Node} T
100-
* Node type.
101-
* @param {(node?: any, parent?: Parent | null | undefined) => asserts node is T} fn
102-
* Custom assertion.
103-
* @returns {(node?: any, parent?: Parent | null | undefined) => asserts node is T}
104-
* Assertion.
105-
*/
106-
export function wrap(fn) {
107-
return wrapped
108-
109-
/**
110-
* @param {unknown} node
111-
* Thing to check.
112-
* @param {Parent | null | undefined} [parent]
113-
* Optional, valid parent.
114-
* @throws {AssertionError}
115-
* Whether `node` is a node but neither parent nor literal.
116-
* @returns {void}
117-
* Nothing.
118-
*/
119-
function wrapped(node, parent) {
120-
try {
121-
fn(node, parent)
122-
} catch (error) {
123-
const exception = /** @type {SeenError} */ (error)
124-
if (!own.call(exception, ID)) {
125-
exception[ID] = true
126-
exception.message += ': `' + view(node) + '`'
127-
if (parent) exception.message += ' in `' + view(parent) + '`'
128-
}
129-
130-
throw error
131-
}
132-
}
133-
}
134-
135-
/**
136-
* Assert that `node` is a valid unist parent.
137-
*
138-
* All children will be asserted too.
139-
*
140-
* @param {unknown} node
141-
* Thing to assert.
142-
* @returns {asserts node is Node}
143-
* Whether `node` (and its descendants) are valid nodes.
144-
* @throws {AssertionError}
145-
* When `node` (or its descendants) is not a node.
146-
*/
147-
function assertNode(node) {
148-
let index = -1
149-
150-
nodeAssert.ok(
151-
node && typeof node === 'object' && !Array.isArray(node),
152-
'node should be an object'
153-
)
154-
155-
nodeAssert.ok(own.call(node, 'type'), 'node should have a type')
156-
nodeAssert.strictEqual(
157-
// @ts-expect-error Looks like an indexed object.
158-
typeof node.type,
159-
'string',
160-
'`type` should be a string'
161-
)
162-
// @ts-expect-error Looks like an indexed object.
163-
nodeAssert.notStrictEqual(node.type, '', '`type` should not be empty')
164-
165-
// @ts-expect-error Looks like an indexed object.
166-
if (node.value !== null && node.value !== undefined) {
167-
nodeAssert.strictEqual(
168-
// @ts-expect-error Looks like an indexed object.
169-
typeof node.value,
170-
'string',
171-
'`value` should be a string'
172-
)
173-
}
174-
175-
// @ts-expect-error Looks like an indexed object.
176-
position(node.position)
177-
178-
/** @type {string} */
179-
let key
180-
181-
for (key in node) {
182-
if (!defined.has(key)) {
183-
/** @type {unknown} */
184-
// @ts-expect-error: hush.
185-
const value = node[key]
186-
vanilla(key, value)
187-
}
188-
}
189-
190-
// @ts-expect-error Looks like an indexed object.
191-
if (node.children !== null && node.children !== undefined) {
192-
/** @type {Parent} */
193-
// @ts-expect-error Looks like parent.
194-
const parent = node
195-
nodeAssert.ok(
196-
Array.isArray(parent.children),
197-
'`children` should be an array'
198-
)
199-
index = -1
200-
201-
while (++index < parent.children.length) {
202-
assert(parent.children[index], parent)
203-
}
204-
}
205-
}
206-
207-
/**
208-
* Assert `value` (which lives at `key`) can be stringified and re-parsed to the
209-
* same (deep) value.
210-
*
211-
* @param {string} key
212-
* Name of field.
213-
* @param {unknown} value
214-
* Value of field.
215-
*/
216-
function vanilla(key, value) {
217-
try {
218-
nodeAssert.deepStrictEqual(value, JSON.parse(JSON.stringify(value)))
219-
} catch {
220-
nodeAssert.fail('non-specced property `' + key + '` should be JSON')
221-
}
222-
}
223-
224-
/**
225-
* Stringify a value to inspect it.
226-
*
227-
* Tries `JSON.stringify()`, and if that fails uses `String()` instead.
228-
*
229-
* @param {unknown} value
230-
* Anything (should be JSON).
231-
* @returns {string}
232-
* User-visible preresentation.
233-
*/
234-
function view(value) {
235-
try {
236-
return inspect(value)
237-
/* c8 ignore next 3 */
238-
} catch {
239-
return String(value)
240-
}
241-
}
242-
243-
/**
244-
* Assert that `node` is a valid unist parent.
245-
*
246-
* All children will be asserted too.
247-
*
248-
* @param {Node} node
249-
* Thing to assert.
250-
* @returns {asserts node is Parent}
251-
* Whether `node` is a parent and its descendants are valid nodes.
252-
* @throws {AssertionError}
253-
* When `node` is not a parent or its descendants are not nodes.
254-
*/
255-
function assertParent(node) {
256-
assertNode(node)
257-
258-
nodeAssert.strictEqual(
259-
'value' in node,
260-
false,
261-
'parent should not have `value`'
262-
)
263-
nodeAssert.ok('children' in node, 'parent should have `children`')
264-
}
265-
266-
/**
267-
* Assert that `node` is a valid unist literal.
268-
*
269-
* @param {unknown} [node]
270-
* Thing to assert.
271-
* @returns {asserts node is Literal}
272-
* Whether `node` is a literal.
273-
* @throws {AssertionError}
274-
* When `node` is not a literal.
275-
*/
276-
function assertLiteral(node) {
277-
assertNode(node)
278-
279-
nodeAssert.strictEqual(
280-
'children' in node,
281-
false,
282-
'literal should not have `children`'
283-
)
284-
nodeAssert.ok('value' in node, 'literal should have `value`')
285-
}
286-
287-
/**
288-
* Assert that `node` is a valid void node.
289-
*
290-
* @param {unknown} [node]
291-
* Thing to assert.
292-
* @returns {asserts node is _Void}
293-
* Whether `node` is a node but neither parent nor literal.
294-
* @throws {AssertionError}
295-
* When `node` is not a node, a parent, or a literal.
296-
*/
297-
function assertVoid(node) {
298-
assertNode(node)
299-
300-
nodeAssert.strictEqual('value' in node, false, 'void should not have `value`')
301-
nodeAssert.strictEqual(
302-
'children' in node,
303-
false,
304-
'void should not have `children`'
305-
)
306-
}
307-
308-
/**
309-
* Assert that `position` is a unist position.
310-
*
311-
* @param {unknown} position
312-
* Thing to assert.
313-
* @returns {asserts position is Position}
314-
* Whether `position` is a unist position.
315-
* @throws {AssertionError}
316-
* When `position` is not a position.
317-
*/
318-
function position(position) {
319-
if (position !== null && position !== undefined) {
320-
nodeAssert.ok(
321-
typeof position === 'object' && position === Object(position),
322-
'`position` should be an object'
323-
)
324-
325-
// @ts-expect-error: indexable.
326-
point(position.start, 'position.start')
327-
// @ts-expect-error: indexable.
328-
point(position.end, 'position.end')
329-
}
330-
}
331-
332-
/**
333-
* Assert `point` is a unist point.
334-
*
335-
* @param {unknown} point
336-
* Thing to assert.
337-
* @param {string} label
338-
* Whether `point` is a unist point.
339-
* @returns {asserts point is Point}
340-
* When `point` is not a point.
341-
*/
342-
function point(point, label) {
343-
if (point !== null && point !== undefined) {
344-
nodeAssert.ok(
345-
typeof point === 'object' && point === Object(point),
346-
'`' + label + '` should be an object'
347-
)
348-
349-
if ('line' in point && point.line !== null && point.line !== undefined) {
350-
nodeAssert.ok(
351-
typeof point.line === 'number',
352-
'`' + label + '` should have numeric `line`'
353-
)
354-
nodeAssert.ok(point.line >= 1, '`' + label + '.line` should be gte `1`')
355-
}
356-
357-
if (
358-
'column' in point &&
359-
point.column !== null &&
360-
point.column !== undefined
361-
) {
362-
nodeAssert.ok(
363-
typeof point.column === 'number',
364-
'`' + label + '` should have numeric `column`'
365-
)
366-
nodeAssert.ok(
367-
point.column >= 1,
368-
'`' + label + '.column` should be gte `1`'
369-
)
370-
}
371-
}
372-
}
5+
export {_void, assert, literal, parent, wrap} from './lib/index.js'

index.test-d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {expectType, expectNotType} from 'tsd'
2-
import {type Node, type Parent} from 'unist'
2+
import type {Node, Parent} from 'unist'
33
import {assert, parent} from './index.js'
44

55
const emptyNode = {type: 'a'}

0 commit comments

Comments
 (0)