-
Notifications
You must be signed in to change notification settings - Fork 127
Expand file tree
/
Copy pathremark-smartypants.js
More file actions
87 lines (75 loc) · 2.45 KB
/
remark-smartypants.js
File metadata and controls
87 lines (75 loc) · 2.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*!
* Based on 'silvenon/remark-smartypants'
* https://github.com/silvenon/remark-smartypants/pull/80
*/
const visit = require('unist-util-visit');
const retext = require('retext');
const smartypants = require('retext-smartypants');
function check(node, parent) {
if (node.data?.skipSmartyPants) return false;
if (parent.tagName === 'script') return false;
if (parent.tagName === 'style') return false;
return true;
}
function markSkip(node) {
if (!node) return;
node.data ??= {};
node.data.skipSmartyPants = true;
if (Array.isArray(node.children)) {
for (const child of node.children) {
markSkip(child);
}
}
}
module.exports = function (options) {
const processor = retext().use(smartypants, {
...options,
// Do not replace ellipses, dashes, backticks because they change string
// length, and we couldn't guarantee right splice of text in second visit of
// tree
ellipses: false,
dashes: false,
backticks: false,
});
const processor2 = retext().use(smartypants, {
...options,
// Do not replace quotes because they are already replaced in the first
// processor
quotes: false,
});
function transformer(tree) {
let allText = '';
let startIndex = 0;
const textOrInlineCodeNodes = [];
visit(tree, 'mdxJsxFlowElement', (node) => {
if (['TerminalBlock'].includes(node.name)) {
markSkip(node); // Mark all children to skip smarty pants
}
});
visit(tree, ['text', 'inlineCode'], (node, _, parent) => {
if (check(node, parent)) {
if (node.type === 'text') allText += node.value;
// for the case when inlineCode contains just one part of quote: `foo'bar`
else allText += 'A'.repeat(node.value.length);
textOrInlineCodeNodes.push(node);
}
});
// Concat all text into one string, to properly replace quotes around non-"text" nodes
allText = String(processor.processSync(allText));
for (const node of textOrInlineCodeNodes) {
const endIndex = startIndex + node.value.length;
if (node.type === 'text') {
const processedText = allText.slice(startIndex, endIndex);
node.value = String(processor2.processSync(processedText));
}
startIndex = endIndex;
}
}
return transformer;
};