remark-break-line 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +235 -0
- package/package.json +69 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./lib/index.js";
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from './lib/index.js'
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn gemoji shortcodes (`:+1:`) into emoji (`👍`).
|
|
3
|
+
*
|
|
4
|
+
* @returns
|
|
5
|
+
* Transform.
|
|
6
|
+
*/
|
|
7
|
+
export default function remarkBreakLongLines({ maxLineLength }?: {
|
|
8
|
+
maxLineLength?: number | undefined;
|
|
9
|
+
}): (tree: Root) => undefined;
|
|
10
|
+
export type Root = import("mdast").Root;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('mdast').Root} Root
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { assert } from 'console'
|
|
6
|
+
import { visit } from 'unist-util-visit'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @param {string} string_
|
|
11
|
+
* @param {number} index
|
|
12
|
+
* @param {string} replacement
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
function replaceAt(string_, index, replacement) {
|
|
16
|
+
return (
|
|
17
|
+
string_.slice(0, Math.max(0, index)) +
|
|
18
|
+
replacement +
|
|
19
|
+
string_.slice(Math.max(0, index + replacement.length))
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const elementSize = {
|
|
24
|
+
text: 0,
|
|
25
|
+
strong: 2,
|
|
26
|
+
emphasis: 2,
|
|
27
|
+
delete: 2
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Turn gemoji shortcodes (`:+1:`) into emoji (`👍`).
|
|
32
|
+
*
|
|
33
|
+
* @returns
|
|
34
|
+
* Transform.
|
|
35
|
+
*/
|
|
36
|
+
export default function remarkBreakLongLines({ maxLineLength = 80 } = {}) {
|
|
37
|
+
/**
|
|
38
|
+
* Transform.
|
|
39
|
+
*
|
|
40
|
+
* @param {Root} tree
|
|
41
|
+
* Tree.
|
|
42
|
+
* @returns {undefined}
|
|
43
|
+
* Nothing.
|
|
44
|
+
*/
|
|
45
|
+
return function (tree) {
|
|
46
|
+
visit(tree, function (node) {
|
|
47
|
+
if (node.type === 'paragraph') {
|
|
48
|
+
let currentLine = 0
|
|
49
|
+
/**
|
|
50
|
+
*
|
|
51
|
+
* @param {typeof node.children[number]} n
|
|
52
|
+
* @returns {{type:'nothing'}|{type:'break-before'}|{type:'split', replaceBy:[typeof node.children[number],typeof node.children[number]]}}
|
|
53
|
+
*/
|
|
54
|
+
const walk = (n, /** @type {(typeof node.children[number]['type'])[]} */ path) => {
|
|
55
|
+
if (n.type === 'text' && n.value.length > 0) {
|
|
56
|
+
let lastIndex = n.value[0] === '\n' ? 0 : -1
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
{ // @ts-ignore
|
|
62
|
+
const additionalCharacters = path.map(p => (elementSize[p] ?? 0) * 2).reduce((p, c) => p + c, 0);
|
|
63
|
+
if (currentLine + additionalCharacters >= maxLineLength) {
|
|
64
|
+
if (path.some(x => x != 'text')) {
|
|
65
|
+
// we need to split the node
|
|
66
|
+
const current = path[path.length - 1];
|
|
67
|
+
assert(current === 'text');
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
type: 'break-before',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
} else {
|
|
76
|
+
n.value = '\n' + n.value
|
|
77
|
+
currentLine = 0
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i < n.value.length; i++) {
|
|
84
|
+
const element = n.value[i]
|
|
85
|
+
if (element === '\n') {
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
const additionalCharacters = path.map(p => (elementSize[p] ?? 0) * 2).reduce((p, c) => p + c, 0);
|
|
88
|
+
if (currentLine + additionalCharacters >= maxLineLength) {
|
|
89
|
+
if (path.some(x => x != 'text')) {
|
|
90
|
+
// we need to split the node
|
|
91
|
+
const current = path[path.length - 1];
|
|
92
|
+
assert(current === 'text');
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
type: 'split',
|
|
96
|
+
replaceBy: [{ type: 'text', value: n.value.substring(0, lastIndex) }, { type: 'text', value: n.value.substring(lastIndex + 1) }]
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
} else {
|
|
102
|
+
if (lastIndex === -1) {
|
|
103
|
+
// We need to increase i because we increase the text
|
|
104
|
+
i++
|
|
105
|
+
n.value = '\n' + n.value
|
|
106
|
+
} else {
|
|
107
|
+
n.value = replaceAt(n.value, lastIndex, '\n')
|
|
108
|
+
}
|
|
109
|
+
currentLine = i - lastIndex
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
currentLine = 0
|
|
115
|
+
} else if (element === ' ') {
|
|
116
|
+
// @ts-ignore
|
|
117
|
+
const additionalCharacters = path.map(p => (elementSize[p] ?? 0) * 2).reduce((p, c) => p + c, 0);
|
|
118
|
+
if (currentLine + additionalCharacters >= maxLineLength) {
|
|
119
|
+
if (path.some(x => x != 'text')) {
|
|
120
|
+
// we need to split the node
|
|
121
|
+
const current = path[path.length - 1];
|
|
122
|
+
assert(current === 'text');
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if (lastIndex === -1) {
|
|
126
|
+
return { type: 'break-before' };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
type: 'split',
|
|
131
|
+
replaceBy: [{ type: 'text', value: n.value.substring(0, lastIndex) }, { type: 'text', value: n.value.substring(lastIndex + 1) }]
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
} else {
|
|
137
|
+
if (lastIndex === -1) {
|
|
138
|
+
// We need to increase i because we increase the text
|
|
139
|
+
i++
|
|
140
|
+
n.value = '\n' + n.value
|
|
141
|
+
} else {
|
|
142
|
+
n.value = replaceAt(n.value, lastIndex, '\n')
|
|
143
|
+
}
|
|
144
|
+
currentLine = i - lastIndex
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
currentLine++
|
|
149
|
+
|
|
150
|
+
lastIndex = i
|
|
151
|
+
} else {
|
|
152
|
+
currentLine++
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
{
|
|
157
|
+
// @ts-ignore
|
|
158
|
+
const additionalCharacters = path.map(p => (elementSize[p] ?? 0) * 2).reduce((p, c) => p + c, 0);
|
|
159
|
+
if (currentLine + additionalCharacters >= maxLineLength) {
|
|
160
|
+
if (path.some(x => x != 'text')) {
|
|
161
|
+
// we need to split the node
|
|
162
|
+
const current = path[path.length - 1];
|
|
163
|
+
assert(current === 'text');
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
type: 'split',
|
|
167
|
+
replaceBy: [{ type: 'text', value: n.value.substring(0, lastIndex) }, { type: 'text', value: n.value.substring(lastIndex + 1) }]
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
} else {
|
|
173
|
+
if (lastIndex === -1) {
|
|
174
|
+
// We need to increase i because we increase the text
|
|
175
|
+
|
|
176
|
+
n.value = '\n' + n.value
|
|
177
|
+
} else {
|
|
178
|
+
n.value = replaceAt(n.value, lastIndex, '\n')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} else if ('children' in n) {
|
|
185
|
+
for (let i = 0; i < n.children.length; i++) {
|
|
186
|
+
const element = n.children[i]
|
|
187
|
+
if (element.type in elementSize) {
|
|
188
|
+
const result = walk(element, [...path, element.type]);
|
|
189
|
+
if (result.type === 'split') {
|
|
190
|
+
/**
|
|
191
|
+
* @type {any}
|
|
192
|
+
*/
|
|
193
|
+
const current = path[path.length - 1];
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
type: 'split',
|
|
197
|
+
replaceBy: [{ type: current, children: [...n.children.slice(0, i), result.replaceBy[0]] },
|
|
198
|
+
{ type: current, children: [result.replaceBy[1], ...n.children.slice(i + 1)] }]
|
|
199
|
+
};
|
|
200
|
+
} else if (result.type === 'break-before') {
|
|
201
|
+
return {
|
|
202
|
+
type: 'break-before',
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return { type: 'nothing' };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
213
|
+
const element = node.children[i]
|
|
214
|
+
const result = walk(element, [element.type])
|
|
215
|
+
|
|
216
|
+
if (result.type == 'split') {
|
|
217
|
+
currentLine = 0
|
|
218
|
+
node.children.splice(i, 1, result.replaceBy[0], { type: 'text', value: '\n' }, result.replaceBy[1])
|
|
219
|
+
} else if (result.type == 'break-before') {
|
|
220
|
+
currentLine = 0
|
|
221
|
+
node.children.splice(i, 0, { type: 'text', value: '\n' })
|
|
222
|
+
if (i > 0) {
|
|
223
|
+
const prev = node.children[i - 1];
|
|
224
|
+
if (prev.type === 'text') {
|
|
225
|
+
prev.value = prev.value.trimEnd();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
})
|
|
233
|
+
console.log(JSON.stringify(tree));
|
|
234
|
+
}
|
|
235
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "remark-break-line",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"markdown",
|
|
8
|
+
"plugin",
|
|
9
|
+
"remark",
|
|
10
|
+
"remark-plugin",
|
|
11
|
+
"unified"
|
|
12
|
+
],
|
|
13
|
+
"author": "",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"exports": "./index.js",
|
|
16
|
+
"files": [
|
|
17
|
+
"lib/",
|
|
18
|
+
"index.d.ts",
|
|
19
|
+
"index.js"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@types/mdast": "^4.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^20.0.0",
|
|
26
|
+
"c8": "^8.0.0",
|
|
27
|
+
"prettier": "^3.0.0",
|
|
28
|
+
"remark": "^15.0.0",
|
|
29
|
+
"remark-cli": "^12.0.0",
|
|
30
|
+
"type-coverage": "^2.0.0",
|
|
31
|
+
"typescript": "^5.0.0",
|
|
32
|
+
"xo": "^0.56.0"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc --build --clean && tsc --build && type-coverage",
|
|
36
|
+
"format": "remark . --frail --output --quiet && prettier . --log-level warn --write && xo --fix",
|
|
37
|
+
"prepack": "npm run build && npm run format",
|
|
38
|
+
"test": "npm run build && npm run format && npm run test-api",
|
|
39
|
+
"test-api": "node --conditions development test/index.js",
|
|
40
|
+
"test-coverage": "c8 --100 --reporter lcov npm run test-api"
|
|
41
|
+
},
|
|
42
|
+
"prettier": {
|
|
43
|
+
"bracketSpacing": false,
|
|
44
|
+
"singleQuote": true,
|
|
45
|
+
"semi": false,
|
|
46
|
+
"tabWidth": 2,
|
|
47
|
+
"trailingComma": "none",
|
|
48
|
+
"useTabs": false
|
|
49
|
+
},
|
|
50
|
+
"typeCoverage": {
|
|
51
|
+
"atLeast": 100,
|
|
52
|
+
"detail": true,
|
|
53
|
+
"ignoreCatch": true,
|
|
54
|
+
"strict": true
|
|
55
|
+
},
|
|
56
|
+
"xo": {
|
|
57
|
+
"overrides": [
|
|
58
|
+
{
|
|
59
|
+
"files": [
|
|
60
|
+
"test/**/*.js"
|
|
61
|
+
],
|
|
62
|
+
"rules": {
|
|
63
|
+
"no-await-in-loop": "off"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"prettier": true
|
|
68
|
+
}
|
|
69
|
+
}
|