@diplodoc/transform 4.71.0 → 4.72.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/lib/typings.d.ts CHANGED
@@ -110,6 +110,15 @@ export interface MarkdownItPluginOpts {
110
110
  enabled: boolean;
111
111
  maxFileSize: number;
112
112
  };
113
+ /**
114
+ * When true, term definitions continue past blank lines until the next
115
+ * `[*key]:` or end of block. Designed for documents where all term
116
+ * definitions are placed at the end of the file.
117
+ *
118
+ * When false (default), a blank line terminates the definition unless
119
+ * the next non-blank line is an `{% include %}` directive.
120
+ */
121
+ multilineTermDefinitions?: boolean;
113
122
  }
114
123
  export type MarkdownItPluginCb<T extends {} = {}> = {
115
124
  (md: MarkdownIt, opts: T & MarkdownItPluginOpts): void;
@@ -129,4 +138,5 @@ export type CssWhiteList = {
129
138
  export type ImageOptions = {
130
139
  width: string | undefined | null;
131
140
  height: string | undefined | null;
141
+ title: string | undefined | null;
132
142
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diplodoc/transform",
3
- "version": "4.71.0",
3
+ "version": "4.72.0",
4
4
  "description": "A simple transformer of text in YFM (Yandex Flavored Markdown) to HTML",
5
5
  "keywords": [
6
6
  "markdown",
@@ -49,7 +49,7 @@
49
49
  "lint": "lint update && lint",
50
50
  "lint:fix": "lint update && lint fix",
51
51
  "pre-commit": "lint update && lint-staged",
52
- "prepare": "husky"
52
+ "prepare": "husky || exit 0"
53
53
  },
54
54
  "dependencies": {
55
55
  "@diplodoc/cut-extension": "^1.1.1",
@@ -147,6 +147,7 @@ const index: MarkdownItPluginCb<Opts> = (md, opts) => {
147
147
  const imageOpts = {
148
148
  width: image.attrGet('width'),
149
149
  height: image.attrGet('height'),
150
+ title: image.attrGet('title'),
150
151
  };
151
152
 
152
153
  const from = state.env.path || opts.path;
@@ -225,6 +226,13 @@ function replaceSvgContent(content: string | null, options: ImageOptions) {
225
226
  content = content.replace(/.*?<svg([^>]*)>/, `<svg${svgRoot}>`);
226
227
  }
227
228
 
229
+ // title
230
+ const hasTitle = /<title>.*?<\/title>/i.test(content);
231
+ if (!hasTitle && options.title) {
232
+ // Insert title tag after the opening svg tag
233
+ content = content.replace(/(<svg[^>]*>)/, `$1<title>${options.title}</title>`);
234
+ }
235
+
228
236
  // randomize ids
229
237
  content = optimize(content, {
230
238
  plugins: [
@@ -6,24 +6,38 @@ import {BASIC_TERM_REGEXP} from './constants';
6
6
 
7
7
  const INCLUDE_LINE_RE = /^{%\s*include\s/;
8
8
  const NEW_LINE_RE = /^(\r\n|\r|\n)/;
9
- const TERM_DEF_RE = /^\[\*(\w+)\]:/;
9
+ const TERM_DEF_RE = /^\[\*([^[\]]+)\]:/;
10
+
11
+ /**
12
+ * @param state - markdown-it block state
13
+ * @param line - line number to read
14
+ * @returns raw source text of the given line
15
+ */
16
+ function getNextLineContent(state: StateBlock, line: number): string {
17
+ const start = state.bMarks[line];
18
+ const end = state.eMarks[line];
19
+
20
+ return start === end ? state.src[start] : state.src.slice(start, end);
21
+ }
10
22
 
11
23
  /**
12
24
  * Checks whether the first non-blank line after {@link fromLine} is an
13
25
  * `{% include %}` directive. Used to allow blank-line gaps between
14
26
  * consecutive includes inside a single term definition.
15
27
  *
16
- * @param {StateBlock} state - The markdown-it state block containing parsing information
17
- * @param {number} fromLine - The line number from which to start searching for include directives
18
- * @param {number} endLine - The last line number to search within
19
- * @returns {boolean} Returns true if an include directive is found after blank lines, false otherwise
28
+ * @param state - markdown-it block state
29
+ * @param fromLine - line number from which to start searching
30
+ * @param endLine - last line number to search within
31
+ * @returns true if the first non-blank line is an include directive
20
32
  */
21
33
  function hasIncludeAfterBlanks(state: StateBlock, fromLine: number, endLine: number): boolean {
22
34
  for (let line = fromLine + 1; line <= endLine; line++) {
23
35
  const start = state.bMarks[line];
24
36
  const end = state.eMarks[line];
25
37
 
26
- if (start === end) continue;
38
+ if (start === end) {
39
+ continue;
40
+ }
27
41
 
28
42
  const content = state.src.slice(start, end);
29
43
  return INCLUDE_LINE_RE.test(content.trimStart());
@@ -31,7 +45,51 @@ function hasIncludeAfterBlanks(state: StateBlock, fromLine: number, endLine: num
31
45
  return false;
32
46
  }
33
47
 
48
+ /**
49
+ * Scans forward from {@link startLine} to find where the current term
50
+ * definition ends.
51
+ *
52
+ * When `multilineTermDefinitions` is enabled, the definition continues
53
+ * past blank lines and stops only at the next `[*key]:` or end of block.
54
+ * Otherwise blank lines terminate the definition unless followed by an
55
+ * `{% include %}` directive (legacy behaviour).
56
+ *
57
+ * @param state - markdown-it block state
58
+ * @param startLine - line where the current definition starts
59
+ * @param endLine - last line of the block
60
+ * @param multiline - whether multiline mode is enabled
61
+ * @returns line number where the definition ends
62
+ */
63
+ function findDefinitionEnd(
64
+ state: StateBlock,
65
+ startLine: number,
66
+ endLine: number,
67
+ multiline: boolean,
68
+ ): number {
69
+ let currentLine = startLine;
70
+
71
+ for (; currentLine < endLine; currentLine++) {
72
+ const nextLine = getNextLineContent(state, currentLine + 1);
73
+
74
+ if (TERM_DEF_RE.test(nextLine)) {
75
+ break;
76
+ }
77
+
78
+ if (!multiline && NEW_LINE_RE.test(nextLine)) {
79
+ if (!hasIncludeAfterBlanks(state, currentLine + 1, endLine)) {
80
+ break;
81
+ }
82
+ }
83
+
84
+ state.line = currentLine + 1;
85
+ }
86
+
87
+ return currentLine;
88
+ }
89
+
34
90
  export function termDefinitions(md: MarkdownIt, options: MarkdownItPluginOpts) {
91
+ const multiline = options.multilineTermDefinitions === true;
92
+
35
93
  return (state: StateBlock, startLine: number, endLine: number, silent: boolean) => {
36
94
  let ch;
37
95
  let labelEnd;
@@ -63,33 +121,7 @@ export function termDefinitions(md: MarkdownIt, options: MarkdownItPluginOpts) {
63
121
  }
64
122
  }
65
123
 
66
- let currentLine = startLine;
67
-
68
- // Allow multiline term definition.
69
- // Blank lines normally terminate the definition, but we look ahead
70
- // past them: if the next non-blank line is an {% include %} directive,
71
- // we keep scanning so that multiple includes can be part of one term.
72
- for (; currentLine < endLine; currentLine++) {
73
- const nextLineStart = state.bMarks[currentLine + 1];
74
- const nextLineEnd = state.eMarks[currentLine + 1];
75
-
76
- const nextLine =
77
- nextLineStart === nextLineEnd
78
- ? state.src[nextLineStart]
79
- : state.src.slice(nextLineStart, nextLineEnd);
80
-
81
- if (TERM_DEF_RE.test(nextLine)) {
82
- break;
83
- }
84
-
85
- if (NEW_LINE_RE.test(nextLine)) {
86
- if (!hasIncludeAfterBlanks(state, currentLine + 1, endLine)) {
87
- break;
88
- }
89
- }
90
-
91
- state.line = currentLine + 1;
92
- }
124
+ const currentLine = findDefinitionEnd(state, startLine, endLine, multiline);
93
125
 
94
126
  max = state.eMarks[currentLine];
95
127
 
@@ -165,6 +197,8 @@ function processTermDefinition(
165
197
  state.env.terms[':' + label] = title;
166
198
  }
167
199
 
200
+ const fromInclude = Array.isArray(state.env.includes) && state.env.includes.length > 0;
201
+
168
202
  token = new state.Token('dfn_open', 'dfn', 1);
169
203
  token.attrSet('class', 'yfm yfm-term_dfn');
170
204
  token.attrSet('id', ':' + label + '_element');
@@ -172,6 +206,10 @@ function processTermDefinition(
172
206
  token.attrSet('aria-live', 'polite');
173
207
  token.attrSet('aria-modal', 'true');
174
208
 
209
+ if (fromInclude) {
210
+ token.attrSet('from-include', 'true');
211
+ }
212
+
175
213
  state.tokens.push(token);
176
214
 
177
215
  const titleTokens = md.parse(title, state.env);
@@ -144,6 +144,15 @@ export interface MarkdownItPluginOpts {
144
144
  enabled: boolean;
145
145
  maxFileSize: number;
146
146
  };
147
+ /**
148
+ * When true, term definitions continue past blank lines until the next
149
+ * `[*key]:` or end of block. Designed for documents where all term
150
+ * definitions are placed at the end of the file.
151
+ *
152
+ * When false (default), a blank line terminates the definition unless
153
+ * the next non-blank line is an `{% include %}` directive.
154
+ */
155
+ multilineTermDefinitions?: boolean;
147
156
  }
148
157
 
149
158
  export type MarkdownItPluginCb<T extends {} = {}> = {
@@ -174,4 +183,5 @@ export type CssWhiteList = {[property: string]: boolean};
174
183
  export type ImageOptions = {
175
184
  width: string | undefined | null;
176
185
  height: string | undefined | null;
186
+ title: string | undefined | null;
177
187
  };