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 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
+ }