@diplodoc/transform 4.31.0 → 4.31.2

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.
@@ -0,0 +1,252 @@
1
+ import {bold} from 'chalk';
2
+
3
+ import {log} from '../log';
4
+
5
+ import evalExp from './evaluation';
6
+ import {tagLine} from './lexical';
7
+ import {getPreparedLeftContent, removeIndentBlock} from './utils';
8
+ import {createSourceMapApi, getLineNumber} from './sourceMap';
9
+
10
+ type Options = {
11
+ firstLineNumber: number;
12
+ lastLineNumber: number;
13
+ resFirstLineNumber: number;
14
+ resLastLineNumber: number;
15
+ linesTotal: number;
16
+ sourceMap: Record<number, number>;
17
+ };
18
+
19
+ function changeSourceMap({
20
+ firstLineNumber,
21
+ lastLineNumber,
22
+ resFirstLineNumber,
23
+ resLastLineNumber,
24
+ linesTotal,
25
+ sourceMap,
26
+ }: Options) {
27
+ if (!sourceMap) {
28
+ return;
29
+ }
30
+
31
+ const {getSourceMapValue, moveLines, removeLines} = createSourceMapApi(sourceMap);
32
+
33
+ let offsetRestLines;
34
+ if (resFirstLineNumber) {
35
+ // Move condition's content to the top
36
+ const offsetContentLines = firstLineNumber - resFirstLineNumber;
37
+ moveLines({
38
+ start: resFirstLineNumber,
39
+ end: resLastLineNumber - 1,
40
+ offset: offsetContentLines,
41
+ withReplace: true,
42
+ });
43
+
44
+ // Remove the rest lines of the condition block
45
+ removeLines({start: firstLineNumber, end: resFirstLineNumber - 1});
46
+ removeLines({start: resLastLineNumber, end: lastLineNumber});
47
+
48
+ // Calculate an offset of the rest lines
49
+ offsetRestLines = getSourceMapValue(resLastLineNumber - 1) - lastLineNumber;
50
+ } else {
51
+ // Remove the whole condition block
52
+ removeLines({start: firstLineNumber, end: lastLineNumber});
53
+
54
+ // Calculate offset of the rest lines
55
+ offsetRestLines = firstLineNumber - lastLineNumber - 1;
56
+ }
57
+
58
+ // Offset the rest lines
59
+ moveLines({start: lastLineNumber + 1, end: linesTotal, offset: offsetRestLines});
60
+ }
61
+
62
+ function getElseProp<B extends keyof Elses>({elses}: {elses: Elses[]}, propName: B, index = 0) {
63
+ if (!elses.length || index >= elses.length) {
64
+ return undefined;
65
+ }
66
+
67
+ return elses[index][propName];
68
+ }
69
+
70
+ type Opts = {
71
+ ifTag: Tag;
72
+ vars: Record<string, unknown>;
73
+ content: string;
74
+ match: RegExpExecArray;
75
+ lastIndex: number;
76
+ sourceMap: Record<number, number>;
77
+ linesTotal: number;
78
+ };
79
+
80
+ function inlineConditions({ifTag, vars, content, match, lastIndex, sourceMap, linesTotal}: Opts) {
81
+ let res = '';
82
+ const firstLineNumber = getLineNumber(content, ifTag.startPos);
83
+ const lastLineNumber = getLineNumber(content, lastIndex);
84
+ let resFirstLineNumber = 0;
85
+ let resLastLineNumber = 0;
86
+
87
+ if (evalExp(ifTag.condition, vars)) {
88
+ const ifRawLastIndex = ifTag.startPos + ifTag.ifRaw.length;
89
+ const contentLastIndex = getElseProp(ifTag, 'startPos') || match.index;
90
+
91
+ res = content.substring(ifRawLastIndex, contentLastIndex);
92
+ resFirstLineNumber = getLineNumber(content, ifRawLastIndex + 1);
93
+ resLastLineNumber = getLineNumber(content, contentLastIndex + 1);
94
+ } else {
95
+ ifTag.elses.some(({condition, startPos, raw}, index) => {
96
+ const isTruthy = !condition || evalExp(condition, vars);
97
+
98
+ if (isTruthy) {
99
+ const elseRawLastIndex = startPos + raw.length;
100
+ const contentLastIndex = getElseProp(ifTag, 'startPos', index + 1) || match.index;
101
+
102
+ res = content.substring(elseRawLastIndex, contentLastIndex);
103
+ resFirstLineNumber = getLineNumber(content, elseRawLastIndex + 1);
104
+ resLastLineNumber = getLineNumber(content, contentLastIndex + 1);
105
+
106
+ return true;
107
+ }
108
+
109
+ return false;
110
+ });
111
+ }
112
+
113
+ changeSourceMap({
114
+ firstLineNumber,
115
+ lastLineNumber,
116
+ resFirstLineNumber,
117
+ resLastLineNumber,
118
+ linesTotal,
119
+ sourceMap,
120
+ });
121
+
122
+ const preparedLeftContent = getPreparedLeftContent({
123
+ content,
124
+ tagStartPos: ifTag.startPos,
125
+ tagContent: res,
126
+ });
127
+
128
+ let shift = 0;
129
+ if (
130
+ res === '' &&
131
+ preparedLeftContent[preparedLeftContent.length - 1] === '\n' &&
132
+ content[lastIndex] === '\n'
133
+ ) {
134
+ shift = 1;
135
+ }
136
+
137
+ if (res !== '') {
138
+ if (res[0] === '\n') {
139
+ res = res.substring(1);
140
+ }
141
+
142
+ res = removeIndentBlock(res);
143
+
144
+ if (res[res.length - 1] === '\n') {
145
+ res = res.slice(0, -1);
146
+ }
147
+ }
148
+
149
+ const leftPart = preparedLeftContent + res;
150
+
151
+ return {
152
+ result: leftPart + content.substring(lastIndex + shift),
153
+ idx: leftPart.length,
154
+ };
155
+ }
156
+
157
+ type Elses = {startPos: number; raw: string; condition?: string};
158
+
159
+ type Tag = {
160
+ isOpen: Boolean;
161
+ condition: string;
162
+ startPos: number;
163
+ ifRaw: string;
164
+ elses: Elses[];
165
+ };
166
+
167
+ export = function legacyConditions(
168
+ originInput: string,
169
+ vars: Record<string, unknown>,
170
+ path?: string,
171
+ settings?: {
172
+ sourceMap: Record<number, number>;
173
+ },
174
+ ) {
175
+ const sourceMap = settings?.sourceMap || {};
176
+
177
+ const R_LIQUID = /({%-?([\s\S]*?)-?%})/g;
178
+
179
+ let match;
180
+ const tagStack: Tag[] = [];
181
+ let input = originInput;
182
+ let linesTotal = originInput.split('\n').length;
183
+
184
+ while ((match = R_LIQUID.exec(input)) !== null) {
185
+ if (!match[1]) {
186
+ continue;
187
+ }
188
+
189
+ const tagMatch = match[2].trim().match(tagLine);
190
+ if (!tagMatch) {
191
+ continue;
192
+ }
193
+
194
+ const [type, args] = tagMatch.slice(1);
195
+
196
+ switch (type) {
197
+ case 'if':
198
+ tagStack.push({
199
+ isOpen: true,
200
+ condition: args,
201
+ startPos: match.index,
202
+ ifRaw: match[1],
203
+ elses: [],
204
+ });
205
+ break;
206
+ case 'else':
207
+ tagStack[tagStack.length - 1].elses.push({
208
+ startPos: match.index,
209
+ raw: match[1],
210
+ });
211
+ break;
212
+ case 'elsif':
213
+ tagStack[tagStack.length - 1].elses.push({
214
+ condition: args,
215
+ startPos: match.index,
216
+ raw: match[1],
217
+ });
218
+ break;
219
+ case 'endif': {
220
+ const ifTag = tagStack.pop();
221
+
222
+ if (!ifTag) {
223
+ log.error(
224
+ `If block must be opened before close${path ? ` in ${bold(path)}` : ''}`,
225
+ );
226
+ break;
227
+ }
228
+
229
+ const {idx, result} = inlineConditions({
230
+ ifTag,
231
+ vars,
232
+ content: input,
233
+ match,
234
+ lastIndex: R_LIQUID.lastIndex,
235
+ sourceMap,
236
+ linesTotal,
237
+ });
238
+ R_LIQUID.lastIndex = idx;
239
+ input = result;
240
+ linesTotal = result.split('\n').length;
241
+
242
+ break;
243
+ }
244
+ }
245
+ }
246
+
247
+ if (tagStack.length !== 0) {
248
+ log.error(`Condition block must be closed${path ? ` in ${bold(path)}` : ''}`);
249
+ }
250
+
251
+ return input;
252
+ };
@@ -5,6 +5,7 @@ export type ArgvSettings = {
5
5
  substitutions?: boolean;
6
6
  keepNotVar?: boolean;
7
7
  withSourceMap?: boolean;
8
+ useLegacyConditions?: boolean;
8
9
  };
9
10
 
10
11
  let _argv: ArgvSettings;
@@ -1,5 +1,6 @@
1
1
  import {relative} from 'path';
2
2
  import {bold} from 'chalk';
3
+ import {readFileSync} from 'fs';
3
4
 
4
5
  import {getRelativePath, isFileExists, resolveRelativePath} from '../../utilsFS';
5
6
 
@@ -23,30 +24,34 @@ function processRecursive(
23
24
  };
24
25
 
25
26
  try {
26
- const content = copyFile(includePath, targetDestPath, includeOptions);
27
+ const contentProcessed = copyFile(includePath, targetDestPath, includeOptions);
27
28
 
28
29
  // To reduce file reading we can include the file content into the generated content
29
- if (included && content) {
30
- const includedRelativePath = getRelativePath(includedParentPath, includePath);
31
-
32
- // The appendix is the map that protects from multiple include files
33
- if (!appendix.has(includedRelativePath)) {
34
- // Recursive function to include the depth structure
35
- const includeContent = collectRecursive(
36
- content,
37
- {
38
- ...options,
39
- path: includePath,
40
- includedParentPath,
41
- },
42
- appendix,
43
- );
44
-
45
- // Add to appendix set structure
46
- appendix.set(
47
- includedRelativePath,
48
- `{% included (${includedRelativePath}) %}\n${includeContent}\n{% endincluded %}`,
49
- );
30
+ if (included) {
31
+ const content = contentProcessed ?? readFileSync(targetDestPath, 'utf8');
32
+
33
+ if (content) {
34
+ const includedRelativePath = getRelativePath(includedParentPath, includePath);
35
+
36
+ // The appendix is the map that protects from multiple include files
37
+ if (!appendix.has(includedRelativePath)) {
38
+ // Recursive function to include the depth structure
39
+ const includeContent = collectRecursive(
40
+ content,
41
+ {
42
+ ...options,
43
+ path: includePath,
44
+ includedParentPath,
45
+ },
46
+ appendix,
47
+ );
48
+
49
+ // Add to appendix set structure
50
+ appendix.set(
51
+ includedRelativePath,
52
+ `{% included (${includedRelativePath}) %}\n${includeContent}\n{% endincluded %}`,
53
+ );
54
+ }
50
55
  }
51
56
  }
52
57
  } catch (e) {
@@ -560,7 +560,9 @@ function sanitizeStyleTags(dom: cheerio.CheerioAPI, cssWhiteList: CssWhiteList)
560
560
  });
561
561
 
562
562
  dom(element).text(css.stringify(parsedCSS));
563
- } catch {}
563
+ } catch {
564
+ dom(element).remove();
565
+ }
564
566
  });
565
567
  }
566
568