Skip to content

Commit ed1ad8e

Browse files
committed
Refactor to move implementation to lib/
1 parent 850b53c commit ed1ad8e

File tree

3 files changed

+146
-144
lines changed

3 files changed

+146
-144
lines changed

index.js

Lines changed: 1 addition & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,143 +1 @@
1-
/**
2-
* @typedef {import('unist').Parent} UnistParent
3-
* @typedef {import('nlcst').Root} Root
4-
* @typedef {import('nlcst').Content} Content
5-
* @typedef {Root|Content} Node
6-
* @typedef {Extract<Node, UnistParent>} Parent
7-
*/
8-
9-
import {toString} from 'nlcst-to-string'
10-
11-
const single = [
12-
'-', // Hyphen-minus
13-
'–', // En dash
14-
'—', // Em dash
15-
':', // Colon
16-
';' // Semi-colon
17-
]
18-
19-
/**
20-
* Pair delimiters.
21-
* From common sense, and WikiPedia:
22-
* <https://en.wikipedia.org/wiki/Quotation_mark>.
23-
*
24-
* @type {Record<string, Array<string>>}
25-
*/
26-
const pairs = {
27-
',': [','],
28-
'-': ['-'],
29-
'–': ['–'],
30-
'—': ['—'],
31-
'"': ['"'],
32-
"'": ["'"],
33-
'‘': ['’'],
34-
'‚': ['’'],
35-
'’': ['’', '‚'],
36-
'“': ['”'],
37-
'”': ['”'],
38-
'„': ['”', '“'],
39-
'«': ['»'],
40-
'»': ['«'],
41-
'‹': ['›'],
42-
'›': ['‹'],
43-
'(': [')'],
44-
'[': [']'],
45-
'{': ['}'],
46-
'⟨': ['⟩'],
47-
'「': ['」']
48-
}
49-
50-
const open = Object.keys(pairs)
51-
52-
/**
53-
* Check if the node in `parent` at `position` is enclosed by matching
54-
* delimiters.
55-
*
56-
* @param {Parent} parent
57-
* @param {number} index
58-
* @returns {boolean}
59-
*/
60-
export function isLiteral(parent, index) {
61-
if (!(parent && parent.children)) {
62-
throw new Error('Parent must be a node')
63-
}
64-
65-
if (index !== null && typeof index === 'object' && 'type' in index) {
66-
index = parent.children.indexOf(index)
67-
68-
if (index === -1) {
69-
throw new Error('Node must be a child of `parent`')
70-
}
71-
}
72-
73-
if (typeof index !== 'number' || Number.isNaN(index)) {
74-
throw new TypeError('Index must be a number')
75-
}
76-
77-
return Boolean(
78-
(!containsWord(parent, -1, index) &&
79-
siblingDelimiter(parent, index, 1, single)) ||
80-
(!containsWord(parent, index, parent.children.length) &&
81-
siblingDelimiter(parent, index, -1, single)) ||
82-
isWrapped(parent, index)
83-
)
84-
}
85-
86-
/**
87-
* Check if the node in `parent` at `position` is enclosed by matching
88-
* delimiters.
89-
* @param {Parent} parent
90-
* @param {number} position
91-
* @returns {Node|void}
92-
*/
93-
function isWrapped(parent, position) {
94-
const previous = siblingDelimiter(parent, position, -1, open)
95-
96-
if (previous) {
97-
return siblingDelimiter(parent, position, 1, pairs[toString(previous)])
98-
}
99-
}
100-
101-
/**
102-
* Find the previous or next delimiter before or after `position` in `parent`.
103-
* Returns the delimiter node when found.
104-
*
105-
* @param {Parent} parent
106-
* @param {number} position
107-
* @param {number} step
108-
* @param {Array<string>} delimiters
109-
* @returns {Node|void}
110-
*/
111-
function siblingDelimiter(parent, position, step, delimiters) {
112-
let index = position + step
113-
114-
while (index > -1 && index < parent.children.length) {
115-
const sibling = parent.children[index]
116-
117-
if (sibling.type === 'WordNode' || sibling.type === 'SourceNode') {
118-
return
119-
}
120-
121-
if (sibling.type !== 'WhiteSpaceNode') {
122-
return delimiters.includes(toString(sibling)) ? sibling : undefined
123-
}
124-
125-
index += step
126-
}
127-
}
128-
129-
/**
130-
* Check if parent contains word-nodes between `start` and `end` (both
131-
* excluding).
132-
* @param {Parent} parent
133-
* @param {number} start
134-
* @param {number} end
135-
* @returns {boolean|void}
136-
*/
137-
function containsWord(parent, start, end) {
138-
while (++start < end) {
139-
if (parent.children[start].type === 'WordNode') {
140-
return true
141-
}
142-
}
143-
}
1+
export {isLiteral} from './lib/index.js'

lib/index.js

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/**
2+
* @typedef {import('unist').Parent} UnistParent
3+
* @typedef {import('nlcst').Root} Root
4+
* @typedef {import('nlcst').Content} Content
5+
* @typedef {Root|Content} Node
6+
* @typedef {Extract<Node, UnistParent>} Parent
7+
*/
8+
9+
import {toString} from 'nlcst-to-string'
10+
11+
const single = [
12+
'-', // Hyphen-minus
13+
'–', // En dash
14+
'—', // Em dash
15+
':', // Colon
16+
';' // Semi-colon
17+
]
18+
19+
/**
20+
* Pair delimiters.
21+
* From common sense, and WikiPedia:
22+
* <https://en.wikipedia.org/wiki/Quotation_mark>.
23+
*
24+
* @type {Record<string, Array<string>>}
25+
*/
26+
const pairs = {
27+
',': [','],
28+
'-': ['-'],
29+
'–': ['–'],
30+
'—': ['—'],
31+
'"': ['"'],
32+
"'": ["'"],
33+
'‘': ['’'],
34+
'‚': ['’'],
35+
'’': ['’', '‚'],
36+
'“': ['”'],
37+
'”': ['”'],
38+
'„': ['”', '“'],
39+
'«': ['»'],
40+
'»': ['«'],
41+
'‹': ['›'],
42+
'›': ['‹'],
43+
'(': [')'],
44+
'[': [']'],
45+
'{': ['}'],
46+
'⟨': ['⟩'],
47+
'「': ['」']
48+
}
49+
50+
const open = Object.keys(pairs)
51+
52+
/**
53+
* Check if the node in `parent` at `position` is enclosed by matching
54+
* delimiters.
55+
*
56+
* @param {Parent} parent
57+
* @param {number} index
58+
* @returns {boolean}
59+
*/
60+
export function isLiteral(parent, index) {
61+
if (!(parent && parent.children)) {
62+
throw new Error('Parent must be a node')
63+
}
64+
65+
if (index !== null && typeof index === 'object' && 'type' in index) {
66+
index = parent.children.indexOf(index)
67+
68+
if (index === -1) {
69+
throw new Error('Node must be a child of `parent`')
70+
}
71+
}
72+
73+
if (typeof index !== 'number' || Number.isNaN(index)) {
74+
throw new TypeError('Index must be a number')
75+
}
76+
77+
return Boolean(
78+
(!containsWord(parent, -1, index) &&
79+
siblingDelimiter(parent, index, 1, single)) ||
80+
(!containsWord(parent, index, parent.children.length) &&
81+
siblingDelimiter(parent, index, -1, single)) ||
82+
isWrapped(parent, index)
83+
)
84+
}
85+
86+
/**
87+
* Check if the node in `parent` at `position` is enclosed by matching
88+
* delimiters.
89+
* @param {Parent} parent
90+
* @param {number} position
91+
* @returns {Node|void}
92+
*/
93+
function isWrapped(parent, position) {
94+
const previous = siblingDelimiter(parent, position, -1, open)
95+
96+
if (previous) {
97+
return siblingDelimiter(parent, position, 1, pairs[toString(previous)])
98+
}
99+
}
100+
101+
/**
102+
* Find the previous or next delimiter before or after `position` in `parent`.
103+
* Returns the delimiter node when found.
104+
*
105+
* @param {Parent} parent
106+
* @param {number} position
107+
* @param {number} step
108+
* @param {Array<string>} delimiters
109+
* @returns {Node|void}
110+
*/
111+
function siblingDelimiter(parent, position, step, delimiters) {
112+
let index = position + step
113+
114+
while (index > -1 && index < parent.children.length) {
115+
const sibling = parent.children[index]
116+
117+
if (sibling.type === 'WordNode' || sibling.type === 'SourceNode') {
118+
return
119+
}
120+
121+
if (sibling.type !== 'WhiteSpaceNode') {
122+
return delimiters.includes(toString(sibling)) ? sibling : undefined
123+
}
124+
125+
index += step
126+
}
127+
}
128+
129+
/**
130+
* Check if parent contains word-nodes between `start` and `end` (both
131+
* excluding).
132+
* @param {Parent} parent
133+
* @param {number} start
134+
* @param {number} end
135+
* @returns {boolean|void}
136+
*/
137+
function containsWord(parent, start, end) {
138+
while (++start < end) {
139+
if (parent.children[start].type === 'WordNode') {
140+
return true
141+
}
142+
}
143+
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"main": "index.js",
2828
"types": "index.d.ts",
2929
"files": [
30+
"lib/",
3031
"index.d.ts",
3132
"index.js"
3233
],
@@ -52,7 +53,7 @@
5253
"prepack": "npm run build && npm run format",
5354
"build": "tsc --build --clean && tsc --build && type-coverage",
5455
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
55-
"test-api": "node --conditions development test/index.js",
56+
"test-api": "node --conditions development test.js",
5657
"test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api",
5758
"test": "npm run build && npm run format && npm run test-coverage"
5859
},

0 commit comments

Comments
 (0)