@diplodoc/transform 4.76.10 → 4.77.0
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/dist/css/_yfm-only.css.map +1 -1
- package/dist/css/_yfm-only.min.css.map +1 -1
- package/dist/css/base.css +7 -0
- package/dist/css/base.css.map +3 -3
- package/dist/css/base.min.css +1 -1
- package/dist/css/base.min.css.map +3 -3
- package/dist/css/print.css.map +1 -1
- package/dist/css/yfm.css +7 -0
- package/dist/css/yfm.css.map +3 -3
- package/dist/css/yfm.min.css +1 -1
- package/dist/css/yfm.min.css.map +3 -3
- package/dist/js/base.js +19 -6
- package/dist/js/base.js.map +2 -2
- package/dist/js/base.min.js +3 -1
- package/dist/js/base.min.js.map +3 -3
- package/dist/js/yfm.js +19 -6
- package/dist/js/yfm.js.map +2 -2
- package/dist/js/yfm.min.js +3 -1
- package/dist/js/yfm.min.js.map +3 -3
- package/dist/scss/_code.scss +6 -0
- package/lib/plugins/code.js +110 -17
- package/lib/plugins/code.js.map +1 -1
- package/lib/utils.js +4 -1
- package/lib/utils.js.map +1 -1
- package/package.json +2 -1
- package/src/js/code.ts +28 -11
- package/src/scss/_code.scss +6 -0
- package/src/transform/plugins/code.ts +140 -17
- package/src/transform/utils.ts +4 -1
package/lib/plugins/code.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/* eslint-disable max-len */
|
|
3
|
-
const utils_1 = require("
|
|
3
|
+
const utils_1 = require("markdown-it/lib/common/utils");
|
|
4
|
+
const utils_2 = require("./utils");
|
|
4
5
|
const wrapInFloatingContainer = (element, id, lineWrapping) => {
|
|
5
6
|
const wrappingButton = lineWrapping
|
|
6
7
|
? `<button role="button" class="g-button g-button_view_flat g-button_size_m g-button_pin_round-round g-md-viewer-code-button yfm-code-button yfm-wrapping-button" tabindex="-1" type="button" aria-label="Toggle line wrapping" aria-pressed="false" data-tabindex="0">
|
|
@@ -102,7 +103,38 @@ function balanceSpansPerLine(lines) {
|
|
|
102
103
|
return prefix + line + suffix;
|
|
103
104
|
});
|
|
104
105
|
}
|
|
105
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Strips the prompt prefix from the raw fence content BEFORE it reaches the highlighter.
|
|
108
|
+
*
|
|
109
|
+
* @param content - Raw fence content.
|
|
110
|
+
* @param prompt - Raw prompt string (e.g. `$`, `#`, `>>>`).
|
|
111
|
+
* @returns Stripped content (same number of lines) and a per-line prompt flag array.
|
|
112
|
+
*/
|
|
113
|
+
function stripPrompt(content, prompt) {
|
|
114
|
+
const hasTrailingNewline = content.endsWith('\n');
|
|
115
|
+
const lines = content.split('\n');
|
|
116
|
+
const linesToProcess = hasTrailingNewline ? lines.slice(0, -1) : lines;
|
|
117
|
+
const flags = [];
|
|
118
|
+
const stripped = linesToProcess.map((line) => {
|
|
119
|
+
const trimmed = line.trimStart();
|
|
120
|
+
const leadingWs = line.slice(0, line.length - trimmed.length);
|
|
121
|
+
if (trimmed.startsWith(prompt + ' ')) {
|
|
122
|
+
flags.push(true);
|
|
123
|
+
return leadingWs + trimmed.slice(prompt.length + 1);
|
|
124
|
+
}
|
|
125
|
+
if (trimmed === prompt) {
|
|
126
|
+
flags.push(true);
|
|
127
|
+
return leadingWs;
|
|
128
|
+
}
|
|
129
|
+
flags.push(false);
|
|
130
|
+
return line;
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
content: stripped.join('\n') + (hasTrailingNewline ? '\n' : ''),
|
|
134
|
+
flags,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function postProcessCode(code, { showLineNumbers, lineWrapping, promptFlags, promptEscaped, }) {
|
|
106
138
|
const hasTrailingNewline = code.endsWith('\n');
|
|
107
139
|
const lines = code.split('\n');
|
|
108
140
|
const linesToProcess = hasTrailingNewline ? lines.slice(0, -1) : lines;
|
|
@@ -110,41 +142,102 @@ function addLineNumbers(code, { lineWrapping }) {
|
|
|
110
142
|
const maxDigits = String(normalized.length).length;
|
|
111
143
|
return (normalized
|
|
112
144
|
.map((line, index) => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
145
|
+
// The prompt was already stripped from the raw content before
|
|
146
|
+
// highlighting, so `line` here is the highlighted command only.
|
|
147
|
+
// Re-insert the prompt as a dedicated, non-selectable span after
|
|
148
|
+
// any leading whitespace. `promptFlags` marks which lines carried
|
|
149
|
+
// a prompt in the original source.
|
|
150
|
+
if ((promptFlags === null || promptFlags === void 0 ? void 0 : promptFlags[index]) && promptEscaped !== undefined) {
|
|
151
|
+
const trimmed = line.trimStart();
|
|
152
|
+
const leadingWs = line.slice(0, line.length - trimmed.length);
|
|
153
|
+
line =
|
|
154
|
+
`${leadingWs}` +
|
|
155
|
+
`<span class="yfm-code-prompt" aria-hidden="true">${promptEscaped} </span>` +
|
|
156
|
+
`${trimmed}`;
|
|
157
|
+
}
|
|
158
|
+
if (showLineNumbers) {
|
|
159
|
+
const lineNumber = String(index + 1).padStart(maxDigits, ' ');
|
|
160
|
+
line = lineWrapping
|
|
161
|
+
? `<span class="yfm-line-number">${lineNumber}</span><span class="yfm-line">${line}</span>`
|
|
162
|
+
: `<span class="yfm-line-number">${lineNumber}</span>${line}`;
|
|
163
|
+
}
|
|
164
|
+
return line;
|
|
117
165
|
})
|
|
118
166
|
.join('\n') + (hasTrailingNewline ? '\n' : ''));
|
|
119
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* @param info - Raw fence info string (e.g. `js showLineNumbers prompt="$"`).
|
|
170
|
+
* @returns Parsed flags and the optional prompt value.
|
|
171
|
+
*/
|
|
172
|
+
function parseFenceInfo(info) {
|
|
173
|
+
var _a;
|
|
174
|
+
const promptMatch = /(?:^|\s)prompt=(?:"([^"]*)"|'([^']*)')/.exec(info);
|
|
175
|
+
const prompt = (_a = promptMatch === null || promptMatch === void 0 ? void 0 : promptMatch[1]) !== null && _a !== void 0 ? _a : promptMatch === null || promptMatch === void 0 ? void 0 : promptMatch[2];
|
|
176
|
+
const rest = promptMatch ? info.replace(promptMatch[0], ' ') : info;
|
|
177
|
+
return {
|
|
178
|
+
showLineNumbers: /\bshowLineNumbers\b/.test(rest),
|
|
179
|
+
wrapLines: /\bwrap\b/.test(rest),
|
|
180
|
+
prompt,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
120
183
|
const code = (md, opts) => {
|
|
121
184
|
var _a;
|
|
122
185
|
const lineWrapping = (opts === null || opts === void 0 ? void 0 : opts.codeLineWrapping) || false;
|
|
123
|
-
const generateID = (_a = opts.generateID) !== null && _a !== void 0 ? _a :
|
|
186
|
+
const generateID = (_a = opts === null || opts === void 0 ? void 0 : opts.generateID) !== null && _a !== void 0 ? _a : utils_2.generateID;
|
|
124
187
|
const superCodeRenderer = md.renderer.rules.fence;
|
|
125
188
|
md.renderer.rules.fence = function (tokens, idx, options, env, self) {
|
|
126
189
|
const token = tokens[idx];
|
|
127
|
-
const showLineNumbers = token.info
|
|
128
|
-
const wrapLines = token.info.includes('wrap');
|
|
190
|
+
const { showLineNumbers, prompt, wrapLines } = parseFenceInfo(token.info);
|
|
129
191
|
const shouldWrap = wrapLines;
|
|
192
|
+
// Strip the prompt prefix from the raw content BEFORE handing it
|
|
193
|
+
// to the highlighter, so language grammars never misinterpret prompt
|
|
194
|
+
// symbols (e.g. `#` as a shell comment, `$` as a variable). The prompt is
|
|
195
|
+
// re-inserted as a span in postProcessCode, driven by `promptFlags`.
|
|
196
|
+
let promptFlags;
|
|
197
|
+
let promptEscaped;
|
|
198
|
+
const originalContent = token.content;
|
|
199
|
+
if (prompt) {
|
|
200
|
+
const stripped = stripPrompt(token.content, prompt);
|
|
201
|
+
token.content = stripped.content;
|
|
202
|
+
promptFlags = stripped.flags;
|
|
203
|
+
promptEscaped = (0, utils_1.escapeHtml)(prompt);
|
|
204
|
+
}
|
|
130
205
|
let superCode = superCodeRenderer === null || superCodeRenderer === void 0 ? void 0 : superCodeRenderer(tokens, idx, options, env, self);
|
|
131
|
-
|
|
206
|
+
// Restore the original content so the mutation does not leak to other
|
|
207
|
+
// consumers of the token.
|
|
208
|
+
token.content = originalContent;
|
|
209
|
+
if (superCode && (showLineNumbers || prompt)) {
|
|
132
210
|
// Extract the code content from the pre/code tags
|
|
133
211
|
const codeMatch = superCode.match(/<pre[^>]*><code[^>]*>([\s\S]*?)<\/code><\/pre>/);
|
|
134
212
|
if (codeMatch) {
|
|
135
213
|
const codeContent = codeMatch[1];
|
|
136
|
-
const
|
|
214
|
+
const processedCode = postProcessCode(codeContent, {
|
|
215
|
+
showLineNumbers,
|
|
216
|
+
lineWrapping,
|
|
217
|
+
promptFlags,
|
|
218
|
+
promptEscaped,
|
|
219
|
+
});
|
|
137
220
|
// Escape $ in replacement string: $$ becomes a literal $ in String.replace()
|
|
138
|
-
const escapedReplacement =
|
|
221
|
+
const escapedReplacement = processedCode.replace(/\$/g, '$$$$');
|
|
139
222
|
superCode = superCode.replace(codeContent, escapedReplacement);
|
|
140
223
|
}
|
|
141
224
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
225
|
+
// Attach the raw prompt value as a data attribute on <code> so that the
|
|
226
|
+
// copy widget can strip it from the plain-text content without relying on
|
|
227
|
+
// any class names or DOM structure introduced by the plugin.
|
|
228
|
+
if (superCode && (prompt || shouldWrap)) {
|
|
229
|
+
superCode = superCode.replace(/(<pre[^>]*><code)([^>]*)>/, (_match, open, attrs) => {
|
|
230
|
+
// Add the wrap class
|
|
231
|
+
if (shouldWrap) {
|
|
232
|
+
attrs = /class="[^"]*"/.test(attrs)
|
|
233
|
+
? attrs.replace(/class="([^"]*)"/, 'class="$1 wrap"')
|
|
234
|
+
: ` class="wrap"${attrs}`;
|
|
235
|
+
}
|
|
236
|
+
// Add the data-prompt attribute
|
|
237
|
+
if (prompt) {
|
|
238
|
+
attrs = ` data-prompt="${(0, utils_1.escapeHtml)(prompt)}"${attrs}`;
|
|
146
239
|
}
|
|
147
|
-
return
|
|
240
|
+
return `${open}${attrs}>`;
|
|
148
241
|
});
|
|
149
242
|
}
|
|
150
243
|
const superCodeWithTerms = superCode && (env === null || env === void 0 ? void 0 : env.terms)
|
package/lib/plugins/code.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../src/transform/plugins/code.ts"],"names":[],"mappings":";AAAA,4BAA4B;AAK5B,mCAAuD;AAEvD,MAAM,uBAAuB,GAAG,CAC5B,OAA2B,EAC3B,EAAU,EACV,YAAqB,EACvB,EAAE;IACA,MAAM,cAAc,GAAG,YAAY;QAC/B,CAAC,CAAC;;;;;;;;;kBASQ;QACV,CAAC,CAAC,EAAE,CAAC;IAET,OAAO;;UAED,OAAO;;cAEH,cAAc;;;;2HAI+F,EAAE;;;;;;;;;;;;;oDAazE,EAAE;;;;;;;;;gDASN,EAAE;;;;;uDAKK,EAAE;;;;;;;;;;CAUxD,CAAC;AACF,CAAC,CAAC;AAQF,SAAS,WAAW,CAChB,GAAW,EACX,GAAY,EACZ,MAA+B,EAC/B,UAAuB;IAEvB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;SAClC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SACxB,GAAG,CAAC,MAAM,CAAC;SACX,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,MAAM,OAAO,GAAG,yBAAyB,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC/D,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAErC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CACxB,GAAG,EACH,CAAC,MAAc,EAAE,EAAU,EAAE,GAAW,EAAE,EAAU,EAAE,EAAE,CACpD,4CAA4C,EAAE,SAAS,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CACzF,CAAC;IAEF,OAAO,QAAQ,IAAI,GAAG,CAAC;AAC3B,CAAC;AAED,MAAM,WAAW,GAAG,uBAAuB,CAAC;AAE5C;;;;;;;;;;;GAWG;AACH,SAAS,mBAAmB,CAAC,KAAe;IACxC,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACtB,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAErC,WAAW,CAAC,SAAS,GAAG,CAAC,CAAC;QAC1B,IAAI,KAA6B,CAAC;QAClC,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/C,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;gBACzB,YAAY,CAAC,GAAG,EAAE,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACJ,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAChC,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACrD,OAAO,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;IAClC,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../src/transform/plugins/code.ts"],"names":[],"mappings":"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"}
|
package/lib/utils.js
CHANGED
|
@@ -12,7 +12,10 @@ exports.defaultTransformLink = defaultTransformLink;
|
|
|
12
12
|
exports.filterTokens = filterTokens;
|
|
13
13
|
const utils_1 = require("@diplodoc/utils");
|
|
14
14
|
function isLocalUrl(url) {
|
|
15
|
-
|
|
15
|
+
// A URL is local unless it has an external scheme: either `scheme://...`
|
|
16
|
+
// / `//host...` (protocol-relative), or an opaque scheme URI without
|
|
17
|
+
// a slash-slash part (e.g. `mailto:`, `tel:`, `custom:`).
|
|
18
|
+
return !isExternalHref(url);
|
|
16
19
|
}
|
|
17
20
|
function findBlockTokens(tokens, id) {
|
|
18
21
|
let blockTokens = [];
|
package/lib/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/transform/utils.ts"],"names":[],"mappings":";;;AAIA,
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/transform/utils.ts"],"names":[],"mappings":";;;AAIA,gCAKC;AAED,0CAwCC;AAED,kCAuBC;AAED,wCAEC;AAED,wEAIC;AAED,4CAOC;AAED,0CAQC;AAID,oDAQC;AAOD,oCA8BC;AAxJD,2CAAsD;AAEtD,SAAgB,UAAU,CAAC,GAAW;IAClC,yEAAyE;IACzE,qEAAqE;IACrE,0DAA0D;IAC1D,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED,SAAgB,eAAe,CAAC,MAAe,EAAE,EAAU;IACvD,IAAI,WAAW,GAAY,EAAE,CAAC;IAC9B,IAAI,CAAC,GAAG,CAAC,EACL,UAAU,EACV,KAAK,EACL,GAAG,CAAC;IACR,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAExB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,UAAU,EAAE,CAAC;YAC1C,IAAI,UAAU,CAAC,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBAC3E,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;gBACZ,MAAM;YACV,CAAC;iBAAM,IAAI,UAAU,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBAC5C,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,GAAG,KAAK,UAAU,CAAC,GAAG,EAAE,CAAC;oBAChE,GAAG,GAAG,CAAC,CAAC;oBACR,MAAM;gBACV,CAAC;qBAAM,IAAI,CAAC,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACjC,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;gBACxB,CAAC;YACL,CAAC;QACL,CAAC;QAED,IACI,CAAC,KAAK,CAAC,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,CAAC;YAClE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE;YAC1B,OAAO,KAAK,KAAK,WAAW,EAC9B,CAAC;YACC,UAAU,GAAG,KAAK,CAAC;YACnB,KAAK,GAAG,CAAC,CAAC;QACd,CAAC;QAED,CAAC,EAAE,CAAC;IACR,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACvD,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,WAAW,CAAC;AACvB,CAAC;AAED,SAAgB,WAAW,CAAC,MAAe,EAAE,GAAW;IACpD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAEpC,IAAI,KAAK,GAAG,EAAE,EACV,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,WAAW,CAAC,QAAQ,IAAI,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC7D,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAEtC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACzD,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC;QAC3B,CAAC;QAED,CAAC,EAAE,CAAC;IACR,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1D,KAAK,KAAL,KAAK,GAAK,WAAW,CAAC,OAAO,EAAC;IAE9B,OAAO;QACH,KAAK;QACL,KAAK;KACR,CAAC;AACN,CAAC;AAED,SAAgB,cAAc,CAAC,IAAY;IACvC,OAAO,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1E,CAAC;AAED,SAAgB,8BAA8B,CAAC,IAAyC;IACpF,MAAM,EAAC,IAAI,EAAE,WAAW,EAAC,GAAG,IAAI,CAAC;IAEjC,OAAO,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;AAC5E,CAAC;AAED,SAAgB,gBAAgB,CAAC,KAAY;IACzC,IAAI,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACvC,IAAI,CAAC;QACD,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,WAAM,CAAC,CAAA,CAAC;IAEV,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAgB,eAAe,CAAC,KAAY;IACxC,IAAI,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,CAAC;QACD,yHAAyH;QACzH,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,WAAM,CAAC,CAAA,CAAC;IAEV,OAAO,GAAG,CAAC;AACf,CAAC;AAEY,QAAA,gBAAgB,GAAG,gBAAgB,CAAC;AAEjD,SAAgB,oBAAoB,CAAC,IAAY;;IAC7C,MAAM,MAAM,GAAG,IAAA,iBAAS,EAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,GAAG,IAAA,kBAAU,EAAC;QACd,GAAG,MAAM;QACT,QAAQ,EAAE,MAAA,MAAM,CAAC,QAAQ,0CAAE,OAAO,CAAC,wBAAgB,EAAE,OAAO,CAAC;KAChE,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AAChB,CAAC;AAOD,SAAgB,YAAY,CAAC,MAAe,EAAE,IAAY,EAAE,OAAoB;IAC5E,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAC5B,OAAO;IACX,CAAC;IAED,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAE5B,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC9B,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACjD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAE9C,IAAI,YAAY,IAAI,CAAC,UAAU,EAAE,CAAC;gBAC9B,SAAS,GAAG,IAAI,CAAC;YACrB,CAAC;YAED,IAAI,CAAC,YAAY,IAAI,UAAU,EAAE,CAAC;gBAC9B,SAAS,GAAG,KAAK,CAAC;YACtB,CAAC;QACL,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,EAAC,SAAS,EAAE,KAAK,EAAC,CAAC,CAAC;YAClD,IAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,IAAI,EAAE,CAAC;gBACf,KAAK,IAAI,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,IAAI,IAAG,CAAC,CAAC;YAC9B,CAAC;QACL,CAAC;IACL,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diplodoc/transform",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.77.0",
|
|
4
4
|
"description": "A simple transformer of text in YFM (Yandex Flavored Markdown) to HTML",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -94,6 +94,7 @@
|
|
|
94
94
|
"esbuild-sass-plugin": "^3.3.1",
|
|
95
95
|
"highlight.js": "^11.8.0",
|
|
96
96
|
"jest-serializer-html": "^7.1.0",
|
|
97
|
+
"jsdom": "^25.0.1",
|
|
97
98
|
"markdown-it-testgen": "^0.1.6",
|
|
98
99
|
"npm-run-all": "^4.1.5",
|
|
99
100
|
"postcss": "^8.4.27",
|
package/src/js/code.ts
CHANGED
|
@@ -31,17 +31,34 @@ function buttonCopyFn(target: HTMLElement) {
|
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
// Strip line-number decorations (class-based, no semantic alternative).
|
|
35
|
+
const clone = code.cloneNode(true) as HTMLElement;
|
|
36
|
+
|
|
37
|
+
clone.querySelectorAll('.yfm-line-number').forEach((el) => el.remove());
|
|
38
|
+
let textContent = clone.textContent ?? '';
|
|
39
|
+
|
|
40
|
+
// Strip prompt prefix from each line using the raw value stored in data-prompt.
|
|
41
|
+
// This avoids any dependency on how (or whether) the prompt is wrapped in the DOM.
|
|
42
|
+
// Leading whitespace before the prompt is preserved so indented commands keep
|
|
43
|
+
// their indentation level in the copied text.
|
|
44
|
+
const promptValue = code.dataset.prompt;
|
|
45
|
+
if (promptValue) {
|
|
46
|
+
const prefix = promptValue + ' ';
|
|
47
|
+
textContent = textContent
|
|
48
|
+
.split('\n')
|
|
49
|
+
.map((line) => {
|
|
50
|
+
const trimmed = line.trimStart();
|
|
51
|
+
const leadingWs = line.slice(0, line.length - trimmed.length);
|
|
52
|
+
if (trimmed.startsWith(prefix)) {
|
|
53
|
+
return leadingWs + trimmed.slice(prefix.length);
|
|
54
|
+
}
|
|
55
|
+
if (trimmed === promptValue) {
|
|
56
|
+
return leadingWs;
|
|
57
|
+
}
|
|
58
|
+
return line;
|
|
59
|
+
})
|
|
60
|
+
.join('\n');
|
|
61
|
+
}
|
|
45
62
|
|
|
46
63
|
copyToClipboard(textContent.trim()).then(() => {
|
|
47
64
|
notifySuccess(container.querySelector('.yfm-clipboard-icon'));
|
package/src/scss/_code.scss
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import type {MarkdownItPluginCb} from './typings';
|
|
4
4
|
import type {IDGenerator} from './utils';
|
|
5
5
|
|
|
6
|
+
import {escapeHtml} from 'markdown-it/lib/common/utils';
|
|
7
|
+
|
|
6
8
|
import {generateID as globalGenerateID} from './utils';
|
|
7
9
|
|
|
8
10
|
const wrapInFloatingContainer = (
|
|
@@ -134,7 +136,57 @@ function balanceSpansPerLine(lines: string[]): string[] {
|
|
|
134
136
|
});
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Strips the prompt prefix from the raw fence content BEFORE it reaches the highlighter.
|
|
141
|
+
*
|
|
142
|
+
* @param content - Raw fence content.
|
|
143
|
+
* @param prompt - Raw prompt string (e.g. `$`, `#`, `>>>`).
|
|
144
|
+
* @returns Stripped content (same number of lines) and a per-line prompt flag array.
|
|
145
|
+
*/
|
|
146
|
+
function stripPrompt(content: string, prompt: string): {content: string; flags: boolean[]} {
|
|
147
|
+
const hasTrailingNewline = content.endsWith('\n');
|
|
148
|
+
const lines = content.split('\n');
|
|
149
|
+
const linesToProcess = hasTrailingNewline ? lines.slice(0, -1) : lines;
|
|
150
|
+
|
|
151
|
+
const flags: boolean[] = [];
|
|
152
|
+
const stripped = linesToProcess.map((line) => {
|
|
153
|
+
const trimmed = line.trimStart();
|
|
154
|
+
const leadingWs = line.slice(0, line.length - trimmed.length);
|
|
155
|
+
|
|
156
|
+
if (trimmed.startsWith(prompt + ' ')) {
|
|
157
|
+
flags.push(true);
|
|
158
|
+
return leadingWs + trimmed.slice(prompt.length + 1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (trimmed === prompt) {
|
|
162
|
+
flags.push(true);
|
|
163
|
+
return leadingWs;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
flags.push(false);
|
|
167
|
+
return line;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
content: stripped.join('\n') + (hasTrailingNewline ? '\n' : ''),
|
|
172
|
+
flags,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function postProcessCode(
|
|
177
|
+
code: string,
|
|
178
|
+
{
|
|
179
|
+
showLineNumbers,
|
|
180
|
+
lineWrapping,
|
|
181
|
+
promptFlags,
|
|
182
|
+
promptEscaped,
|
|
183
|
+
}: {
|
|
184
|
+
showLineNumbers: boolean;
|
|
185
|
+
lineWrapping: boolean;
|
|
186
|
+
promptFlags?: boolean[];
|
|
187
|
+
promptEscaped?: string;
|
|
188
|
+
},
|
|
189
|
+
): string {
|
|
138
190
|
const hasTrailingNewline = code.endsWith('\n');
|
|
139
191
|
const lines = code.split('\n');
|
|
140
192
|
const linesToProcess = hasTrailingNewline ? lines.slice(0, -1) : lines;
|
|
@@ -144,15 +196,53 @@ function addLineNumbers(code: string, {lineWrapping}: {lineWrapping: boolean}):
|
|
|
144
196
|
return (
|
|
145
197
|
normalized
|
|
146
198
|
.map((line, index) => {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
199
|
+
// The prompt was already stripped from the raw content before
|
|
200
|
+
// highlighting, so `line` here is the highlighted command only.
|
|
201
|
+
// Re-insert the prompt as a dedicated, non-selectable span after
|
|
202
|
+
// any leading whitespace. `promptFlags` marks which lines carried
|
|
203
|
+
// a prompt in the original source.
|
|
204
|
+
if (promptFlags?.[index] && promptEscaped !== undefined) {
|
|
205
|
+
const trimmed = line.trimStart();
|
|
206
|
+
const leadingWs = line.slice(0, line.length - trimmed.length);
|
|
207
|
+
line =
|
|
208
|
+
`${leadingWs}` +
|
|
209
|
+
`<span class="yfm-code-prompt" aria-hidden="true">${promptEscaped} </span>` +
|
|
210
|
+
`${trimmed}`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (showLineNumbers) {
|
|
214
|
+
const lineNumber = String(index + 1).padStart(maxDigits, ' ');
|
|
215
|
+
line = lineWrapping
|
|
216
|
+
? `<span class="yfm-line-number">${lineNumber}</span><span class="yfm-line">${line}</span>`
|
|
217
|
+
: `<span class="yfm-line-number">${lineNumber}</span>${line}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return line;
|
|
151
221
|
})
|
|
152
222
|
.join('\n') + (hasTrailingNewline ? '\n' : '')
|
|
153
223
|
);
|
|
154
224
|
}
|
|
155
225
|
|
|
226
|
+
/**
|
|
227
|
+
* @param info - Raw fence info string (e.g. `js showLineNumbers prompt="$"`).
|
|
228
|
+
* @returns Parsed flags and the optional prompt value.
|
|
229
|
+
*/
|
|
230
|
+
function parseFenceInfo(info: string): {
|
|
231
|
+
showLineNumbers: boolean;
|
|
232
|
+
wrapLines: boolean;
|
|
233
|
+
prompt?: string;
|
|
234
|
+
} {
|
|
235
|
+
const promptMatch = /(?:^|\s)prompt=(?:"([^"]*)"|'([^']*)')/.exec(info);
|
|
236
|
+
const prompt = promptMatch?.[1] ?? promptMatch?.[2];
|
|
237
|
+
const rest = promptMatch ? info.replace(promptMatch[0], ' ') : info;
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
showLineNumbers: /\bshowLineNumbers\b/.test(rest),
|
|
241
|
+
wrapLines: /\bwrap\b/.test(rest),
|
|
242
|
+
prompt,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
156
246
|
type CodeOptions = {
|
|
157
247
|
/**
|
|
158
248
|
* Show button to toggle line wrapping of code.
|
|
@@ -164,36 +254,69 @@ type CodeOptions = {
|
|
|
164
254
|
|
|
165
255
|
const code: MarkdownItPluginCb<CodeOptions> = (md, opts) => {
|
|
166
256
|
const lineWrapping = opts?.codeLineWrapping || false;
|
|
167
|
-
const generateID = opts
|
|
257
|
+
const generateID = opts?.generateID ?? globalGenerateID;
|
|
168
258
|
|
|
169
259
|
const superCodeRenderer = md.renderer.rules.fence;
|
|
170
260
|
md.renderer.rules.fence = function (tokens, idx, options, env, self) {
|
|
171
261
|
const token = tokens[idx];
|
|
172
|
-
const showLineNumbers = token.info
|
|
173
|
-
const wrapLines = token.info.includes('wrap');
|
|
174
|
-
|
|
262
|
+
const {showLineNumbers, prompt, wrapLines} = parseFenceInfo(token.info);
|
|
175
263
|
const shouldWrap = wrapLines;
|
|
176
264
|
|
|
265
|
+
// Strip the prompt prefix from the raw content BEFORE handing it
|
|
266
|
+
// to the highlighter, so language grammars never misinterpret prompt
|
|
267
|
+
// symbols (e.g. `#` as a shell comment, `$` as a variable). The prompt is
|
|
268
|
+
// re-inserted as a span in postProcessCode, driven by `promptFlags`.
|
|
269
|
+
let promptFlags: boolean[] | undefined;
|
|
270
|
+
let promptEscaped: string | undefined;
|
|
271
|
+
const originalContent = token.content;
|
|
272
|
+
if (prompt) {
|
|
273
|
+
const stripped = stripPrompt(token.content, prompt);
|
|
274
|
+
token.content = stripped.content;
|
|
275
|
+
promptFlags = stripped.flags;
|
|
276
|
+
promptEscaped = escapeHtml(prompt);
|
|
277
|
+
}
|
|
278
|
+
|
|
177
279
|
let superCode = superCodeRenderer?.(tokens, idx, options, env, self);
|
|
178
280
|
|
|
179
|
-
|
|
281
|
+
// Restore the original content so the mutation does not leak to other
|
|
282
|
+
// consumers of the token.
|
|
283
|
+
token.content = originalContent;
|
|
284
|
+
|
|
285
|
+
if (superCode && (showLineNumbers || prompt)) {
|
|
180
286
|
// Extract the code content from the pre/code tags
|
|
181
287
|
const codeMatch = superCode.match(/<pre[^>]*><code[^>]*>([\s\S]*?)<\/code><\/pre>/);
|
|
182
288
|
if (codeMatch) {
|
|
183
289
|
const codeContent = codeMatch[1];
|
|
184
|
-
const
|
|
290
|
+
const processedCode = postProcessCode(codeContent, {
|
|
291
|
+
showLineNumbers,
|
|
292
|
+
lineWrapping,
|
|
293
|
+
promptFlags,
|
|
294
|
+
promptEscaped,
|
|
295
|
+
});
|
|
185
296
|
// Escape $ in replacement string: $$ becomes a literal $ in String.replace()
|
|
186
|
-
const escapedReplacement =
|
|
297
|
+
const escapedReplacement = processedCode.replace(/\$/g, '$$$$');
|
|
187
298
|
superCode = superCode.replace(codeContent, escapedReplacement);
|
|
188
299
|
}
|
|
189
300
|
}
|
|
190
301
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
302
|
+
// Attach the raw prompt value as a data attribute on <code> so that the
|
|
303
|
+
// copy widget can strip it from the plain-text content without relying on
|
|
304
|
+
// any class names or DOM structure introduced by the plugin.
|
|
305
|
+
if (superCode && (prompt || shouldWrap)) {
|
|
306
|
+
superCode = superCode.replace(/(<pre[^>]*><code)([^>]*)>/, (_match, open, attrs) => {
|
|
307
|
+
// Add the wrap class
|
|
308
|
+
if (shouldWrap) {
|
|
309
|
+
attrs = /class="[^"]*"/.test(attrs)
|
|
310
|
+
? attrs.replace(/class="([^"]*)"/, 'class="$1 wrap"')
|
|
311
|
+
: ` class="wrap"${attrs}`;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Add the data-prompt attribute
|
|
315
|
+
if (prompt) {
|
|
316
|
+
attrs = ` data-prompt="${escapeHtml(prompt)}"${attrs}`;
|
|
195
317
|
}
|
|
196
|
-
|
|
318
|
+
|
|
319
|
+
return `${open}${attrs}>`;
|
|
197
320
|
});
|
|
198
321
|
}
|
|
199
322
|
|
package/src/transform/utils.ts
CHANGED
|
@@ -3,7 +3,10 @@ import type Token from 'markdown-it/lib/token';
|
|
|
3
3
|
import {formatHref, parseHref} from '@diplodoc/utils';
|
|
4
4
|
|
|
5
5
|
export function isLocalUrl(url: string) {
|
|
6
|
-
|
|
6
|
+
// A URL is local unless it has an external scheme: either `scheme://...`
|
|
7
|
+
// / `//host...` (protocol-relative), or an opaque scheme URI without
|
|
8
|
+
// a slash-slash part (e.g. `mailto:`, `tel:`, `custom:`).
|
|
9
|
+
return !isExternalHref(url);
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
export function findBlockTokens(tokens: Token[], id: string) {
|