@diplodoc/cli-tests 5.35.3 → 5.36.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/e2e/__snapshots__/bundles.spec.ts.snap +26 -26
- package/e2e/__snapshots__/files.spec.ts.snap +1 -2
- package/e2e/__snapshots__/merge-includes.spec.ts.snap +241 -30
- package/e2e/__snapshots__/pdf-page.spec.ts.snap +157 -6
- package/e2e/__snapshots__/preprocess.test.ts.snap +78 -78
- package/e2e/__snapshots__/regression.test.ts.snap +216 -55
- package/e2e/merge-includes.spec.ts +45 -1
- package/e2e/pdf-page.spec.ts +27 -0
- package/e2e/preprocess.test.ts +2 -2
- package/e2e/regression.test.ts +3 -3
- package/fixtures/utils/test.ts +112 -22
- package/mocks/merge-includes/hash-section-html/input/_includes/mixed.md +11 -0
- package/mocks/merge-includes/hash-section-html/input/index.md +3 -0
- package/mocks/merge-includes/hash-section-html/input/main.md +13 -0
- package/mocks/merge-includes/hash-section-html/input/toc.yaml +5 -0
- package/mocks/merge-includes/html-in-list/input/_includes/styles.md +7 -0
- package/mocks/merge-includes/html-in-list/input/index.md +3 -0
- package/mocks/merge-includes/html-in-list/input/main.md +11 -0
- package/mocks/merge-includes/html-in-list/input/toc.yaml +5 -0
- package/mocks/merge-includes/term-extract/input/_includes/chapter.md +7 -0
- package/mocks/merge-includes/term-extract/input/index.md +3 -0
- package/mocks/merge-includes/term-extract/input/main.md +7 -0
- package/mocks/merge-includes/term-extract/input/toc.yaml +5 -0
- package/mocks/merge-includes/yfm-table/input/_includes/cell-content.md +1 -0
- package/mocks/merge-includes/yfm-table/input/index.md +3 -0
- package/mocks/merge-includes/yfm-table/input/main.md +12 -0
- package/mocks/merge-includes/yfm-table/input/toc.yaml +5 -0
- package/mocks/pdf-page/custom-pdf-icon/input/.yfm +6 -0
- package/mocks/pdf-page/custom-pdf-icon/input/_assets/custom-pdf-icon.svg +1 -0
- package/mocks/pdf-page/custom-pdf-icon/input/index.md +1 -0
- package/mocks/pdf-page/custom-pdf-icon/input/pdf/output.pdf +0 -0
- package/mocks/pdf-page/custom-pdf-icon/input/toc.yaml +2 -0
- package/mocks/regression/input/autotitle.md +33 -1
- package/mocks/regression/input/includes/fragments.md +24 -0
- package/mocks/regression/input/includes/styles.md +8 -0
- package/mocks/regression/input/includes.md +6 -0
- package/mocks/regression/input/toc.yaml +2 -0
- package/package.json +1 -1
|
@@ -53,7 +53,7 @@ describe('Merge includes (md2md)', () => {
|
|
|
53
53
|
await TestAdapter.testBuildPass(inputPath, outputPath, {
|
|
54
54
|
md2md: true,
|
|
55
55
|
md2html: false,
|
|
56
|
-
args: '--merge-includes',
|
|
56
|
+
args: '--merge-includes --multiline-term-definitions',
|
|
57
57
|
});
|
|
58
58
|
await compareDirectories(outputPath);
|
|
59
59
|
});
|
|
@@ -69,6 +69,50 @@ describe('Merge includes (md2md)', () => {
|
|
|
69
69
|
await compareDirectories(outputPath);
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
test('term-extract: dep with terms → terms extracted, content inlined (Step 4)', async () => {
|
|
73
|
+
const {inputPath, outputPath} = getTestPaths('mocks/merge-includes/term-extract');
|
|
74
|
+
|
|
75
|
+
await TestAdapter.testBuildPass(inputPath, outputPath, {
|
|
76
|
+
md2md: true,
|
|
77
|
+
md2html: false,
|
|
78
|
+
args: '--merge-includes --multiline-term-definitions',
|
|
79
|
+
});
|
|
80
|
+
await compareDirectories(outputPath);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('yfm-table: include followed by || separator is inlined', async () => {
|
|
84
|
+
const {inputPath, outputPath} = getTestPaths('mocks/merge-includes/yfm-table');
|
|
85
|
+
|
|
86
|
+
await TestAdapter.testBuildPass(inputPath, outputPath, {
|
|
87
|
+
md2md: true,
|
|
88
|
+
md2html: false,
|
|
89
|
+
args: '--merge-includes',
|
|
90
|
+
});
|
|
91
|
+
await compareDirectories(outputPath);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('html-in-list: include with <style> in list (any indent) uses fallback', async () => {
|
|
95
|
+
const {inputPath, outputPath} = getTestPaths('mocks/merge-includes/html-in-list');
|
|
96
|
+
|
|
97
|
+
await TestAdapter.testBuildPass(inputPath, outputPath, {
|
|
98
|
+
md2md: true,
|
|
99
|
+
md2html: false,
|
|
100
|
+
args: '--merge-includes',
|
|
101
|
+
});
|
|
102
|
+
await compareDirectories(outputPath);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('hash-section-html: hash-section include in list is inlined when only other section has <style>', async () => {
|
|
106
|
+
const {inputPath, outputPath} = getTestPaths('mocks/merge-includes/hash-section-html');
|
|
107
|
+
|
|
108
|
+
await TestAdapter.testBuildPass(inputPath, outputPath, {
|
|
109
|
+
md2md: true,
|
|
110
|
+
md2html: false,
|
|
111
|
+
args: '--merge-includes',
|
|
112
|
+
});
|
|
113
|
+
await compareDirectories(outputPath);
|
|
114
|
+
});
|
|
115
|
+
|
|
72
116
|
test('without flag: includes are NOT merged', async () => {
|
|
73
117
|
const {inputPath, outputPath} = getTestPaths('mocks/merge-includes/basic');
|
|
74
118
|
|
package/e2e/pdf-page.spec.ts
CHANGED
|
@@ -98,3 +98,30 @@ describe('Pdf generation with md2md phase, only files structure', () => {
|
|
|
98
98
|
'mocks/pdf-page/title-pages',
|
|
99
99
|
);
|
|
100
100
|
});
|
|
101
|
+
|
|
102
|
+
describe('Pdf page with custom icon', () => {
|
|
103
|
+
test('md2md copies custom pdf icon asset to output', async () => {
|
|
104
|
+
const {inputPath, outputPath} = getTestPaths('mocks/pdf-page/custom-pdf-icon');
|
|
105
|
+
|
|
106
|
+
await TestAdapter.testBuildPass(inputPath, outputPath, {
|
|
107
|
+
md2md: true,
|
|
108
|
+
md2html: false,
|
|
109
|
+
args: '--allow-custom-resources',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await compareDirectories(outputPath, true);
|
|
113
|
+
await cleanupDirectory(outputPath);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('md2html copies all files and produces correct pdfIconConfig in __DATA__', async () => {
|
|
117
|
+
const {inputPath, outputPath} = getTestPaths('mocks/pdf-page/custom-pdf-icon');
|
|
118
|
+
|
|
119
|
+
await TestAdapter.testBuildPass(inputPath, outputPath, {
|
|
120
|
+
md2md: false,
|
|
121
|
+
md2html: true,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
await compareDirectories(outputPath);
|
|
125
|
+
await cleanupDirectory(outputPath);
|
|
126
|
+
});
|
|
127
|
+
});
|
package/e2e/preprocess.test.ts
CHANGED
|
@@ -12,12 +12,12 @@ const generateFilesYamlTestTemplate = (
|
|
|
12
12
|
await TestAdapter.testBuildPass(inputPath, outputPath, {
|
|
13
13
|
md2md: true,
|
|
14
14
|
md2html: false,
|
|
15
|
-
args: args.concat(['--keep-not-var']).join(' '),
|
|
15
|
+
args: args.concat(['--keep-not-var', '--id-generator', 'deterministic']).join(' '),
|
|
16
16
|
});
|
|
17
17
|
await TestAdapter.testBuildPass(outputPath, outputPath + '-html', {
|
|
18
18
|
md2md: false,
|
|
19
19
|
md2html: true,
|
|
20
|
-
args: args.join(' '),
|
|
20
|
+
args: args.concat(['--id-generator', 'deterministic']).join(' '),
|
|
21
21
|
});
|
|
22
22
|
await compareDirectories(outputPath);
|
|
23
23
|
});
|
package/e2e/regression.test.ts
CHANGED
|
@@ -10,17 +10,17 @@ function test(_description: string) {
|
|
|
10
10
|
await TestAdapter.testBuildPass(inputPath, outputPath, {
|
|
11
11
|
md2md: true,
|
|
12
12
|
md2html: false,
|
|
13
|
-
args: '-j2 --keep-not-var --add-system-meta',
|
|
13
|
+
args: '-j2 --keep-not-var --add-system-meta --id-generator deterministic',
|
|
14
14
|
});
|
|
15
15
|
await TestAdapter.testBuildPass(outputPath, outputPath + '-html', {
|
|
16
16
|
md2md: false,
|
|
17
17
|
md2html: true,
|
|
18
|
-
args: '-j2',
|
|
18
|
+
args: '-j2 --id-generator deterministic',
|
|
19
19
|
});
|
|
20
20
|
await TestAdapter.testBuildPass(outputPath, outputPath + '-static-html', {
|
|
21
21
|
md2md: false,
|
|
22
22
|
md2html: true,
|
|
23
|
-
args: '-j2 --static-content',
|
|
23
|
+
args: '-j2 --static-content --id-generator deterministic',
|
|
24
24
|
});
|
|
25
25
|
await compareDirectories(outputPath);
|
|
26
26
|
await compareDirectories(outputPath + '-html');
|
package/fixtures/utils/test.ts
CHANGED
|
@@ -31,35 +31,125 @@ export function platformless(text: string): string {
|
|
|
31
31
|
export function hashless(text: string): string {
|
|
32
32
|
return text
|
|
33
33
|
.replace(/-[a-z0-9]{12,16}\./g, '-hash.')
|
|
34
|
-
.replace(/rnd-[a-z0-9]{
|
|
34
|
+
.replace(/(rnd|svg)-[a-z0-9]{3,8}__/g, 'rnd-hash__')
|
|
35
35
|
.replace(/(\/|\\)[a-z0-9]{12,16}-(index|registry|resources)\./g, '/hash-$2.');
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Replaces hashed bundle filenames from the client manifest with stable labels
|
|
40
|
+
* and sorts bundle references to normalize platform-dependent ordering.
|
|
41
|
+
*
|
|
42
|
+
* Examples: "app-3ff8bc0b40bc2914.js" → "app-js",
|
|
43
|
+
* "vendor-00121562c7b7d3b5.rtl.css" → "vendor-rtl-css",
|
|
44
|
+
* "976-40cbc1d2518eb8ea.js" → "chunk-js" (numeric webpack chunk ID).
|
|
45
|
+
*/
|
|
38
46
|
export function bundleless(text: string): string {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
// On Windows the CLI may emit `_bundle\file` or JSON-escaped `_bundle\\/file`.
|
|
48
|
+
// Collapse any mix of backslashes and forward slashes after `_bundle` into `/`.
|
|
49
|
+
text = text.replace(/_bundle[/\\]+/g, '_bundle/');
|
|
50
|
+
|
|
51
|
+
for (const entry of Object.values(assets)) {
|
|
52
|
+
for (const files of Object.values(entry)) {
|
|
53
|
+
for (const filename of files) {
|
|
54
|
+
const match = filename.match(
|
|
55
|
+
/^([a-z0-9]+)-[a-f0-9]{12,16}((?:\.[a-z]+)*)\.([a-z]+)$/,
|
|
56
|
+
);
|
|
57
|
+
if (!match) continue;
|
|
58
|
+
|
|
59
|
+
const [, rawBase, suffixes, ext] = match;
|
|
60
|
+
const base = /^\d+$/.test(rawBase) ? 'chunk' : rawBase;
|
|
61
|
+
const suffixPart = suffixes.replace(/\./g, '-').slice(1); // ".rtl" → "rtl"
|
|
62
|
+
const label = suffixPart ? `${base}-${suffixPart}-${ext}` : `${base}-${ext}`;
|
|
63
|
+
const escapedSuffixes = suffixes.replace(/\./g, '\\.');
|
|
64
|
+
const pattern = new RegExp(
|
|
65
|
+
`${rawBase}-[a-f0-9]{12,16}${escapedSuffixes}\\.${ext}`,
|
|
66
|
+
'g',
|
|
67
|
+
);
|
|
68
|
+
text = text.replace(pattern, label);
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
}
|
|
63
72
|
|
|
73
|
+
return sortBundleTokens(text);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Sorts contiguous runs of `_bundle/` references so snapshots stay stable
|
|
78
|
+
* regardless of manifest ordering or platform.
|
|
79
|
+
*
|
|
80
|
+
* Pass 1 (line-level): sorts groups of consecutive lines containing `_bundle/`
|
|
81
|
+
* — handles HTML `<link>` / `<script>` tags on separate lines.
|
|
82
|
+
*
|
|
83
|
+
* Pass 2 (inline): sorts groups of `_bundle/` tokens on the same line separated
|
|
84
|
+
* only by punctuation — handles JSON arrays like `"cssLink":["_bundle/b","_bundle/a"]`.
|
|
85
|
+
*/
|
|
86
|
+
function sortBundleTokens(text: string): string {
|
|
87
|
+
const bundleRe = /_bundle\/[a-z0-9][-a-z0-9]*/;
|
|
88
|
+
|
|
89
|
+
// --- Pass 1: line-level sort ---
|
|
90
|
+
const lines = text.split('\n');
|
|
91
|
+
let i = 0;
|
|
92
|
+
while (i < lines.length) {
|
|
93
|
+
if (!bundleRe.test(lines[i])) {
|
|
94
|
+
i++;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const start = i;
|
|
98
|
+
while (i < lines.length && bundleRe.test(lines[i])) {
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
if (i - start < 2) continue;
|
|
102
|
+
|
|
103
|
+
const slice = lines.slice(start, i);
|
|
104
|
+
const sorted = [...slice].sort((a, b) => {
|
|
105
|
+
const ka = a.match(bundleRe)?.[0] ?? '';
|
|
106
|
+
const kb = b.match(bundleRe)?.[0] ?? '';
|
|
107
|
+
return ka < kb ? -1 : ka > kb ? 1 : 0;
|
|
108
|
+
});
|
|
109
|
+
for (let j = 0; j < sorted.length; j++) {
|
|
110
|
+
lines[start + j] = sorted[j];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
text = lines.join('\n');
|
|
114
|
+
|
|
115
|
+
// --- Pass 2: inline sort (JSON arrays, etc.) ---
|
|
116
|
+
const tokenRe = /_bundle\/[a-z0-9][-a-z0-9]*/g;
|
|
117
|
+
const hits: {start: number; end: number; value: string}[] = [];
|
|
118
|
+
let m;
|
|
119
|
+
while ((m = tokenRe.exec(text)) !== null) {
|
|
120
|
+
hits.push({start: m.index, end: m.index + m[0].length, value: m[0]});
|
|
121
|
+
}
|
|
122
|
+
if (hits.length < 2) return text;
|
|
123
|
+
|
|
124
|
+
// Gap between two tokens is "glue" when it contains no alphanumeric chars
|
|
125
|
+
// (ignoring `_bundle` substrings that may appear in the gap).
|
|
126
|
+
const isGlue = (gap: string) => !/[a-zA-Z0-9]/.test(gap.replace(/_bundle/g, ''));
|
|
127
|
+
|
|
128
|
+
// Group consecutive same-line tokens into runs.
|
|
129
|
+
const runs: number[][] = [[0]];
|
|
130
|
+
for (let idx = 1; idx < hits.length; idx++) {
|
|
131
|
+
const gap = text.slice(hits[idx - 1].end, hits[idx].start);
|
|
132
|
+
if (!gap.includes('\n') && isGlue(gap)) {
|
|
133
|
+
runs[runs.length - 1].push(idx);
|
|
134
|
+
} else {
|
|
135
|
+
runs.push([idx]);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Sort each run with 2+ tokens, rewriting from the end to preserve offsets.
|
|
140
|
+
for (let r = runs.length - 1; r >= 0; r--) {
|
|
141
|
+
const run = runs[r];
|
|
142
|
+
if (run.length < 2) continue;
|
|
143
|
+
|
|
144
|
+
const values = run.map((idx) => hits[idx].value);
|
|
145
|
+
const sorted = [...values].sort();
|
|
146
|
+
if (values.every((v, idx) => v === sorted[idx])) continue;
|
|
147
|
+
|
|
148
|
+
for (let k = run.length - 1; k >= 0; k--) {
|
|
149
|
+
const {start: s, end: e} = hits[run[k]];
|
|
150
|
+
text = text.slice(0, s) + sorted[k] + text.slice(e);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
64
154
|
return text;
|
|
65
155
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Main Document
|
|
2
|
+
|
|
3
|
+
Content before list.
|
|
4
|
+
|
|
5
|
+
1. First item
|
|
6
|
+
|
|
7
|
+
2. Second item with hash-section include (section has no HTML, but other section in same file does):
|
|
8
|
+
|
|
9
|
+
{% include notitle [no-style](_includes/mixed.md#no-style) %}
|
|
10
|
+
|
|
11
|
+
3. Third item
|
|
12
|
+
|
|
13
|
+
Content after list.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Buy now at [our store](https://example.com/store).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" aria-hidden="true" class="g-icon Item-module__gn-composite-bar-item__collapse-item-icon___CD-ao"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16"><path fill="#657B8F" d="M7.181 13.318h3.887v2.045H5.136V5.341h2.045zm7.774-6.955h-3.683v4.705H9.227v-6.75h3.682V2.682h-9.41V5.34H1.456V.637h13.5z"/></svg></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# PDF mock index
|
|
Binary file
|
|
@@ -21,6 +21,38 @@ Special local title
|
|
|
21
21
|
Circular title
|
|
22
22
|
[{#T}](./autotitle.md#header)
|
|
23
23
|
|
|
24
|
+
Autotitle from include
|
|
25
|
+
<!-- [{#T}](includes/fragments.md#f3) -->
|
|
26
|
+
|
|
27
|
+
Include with autotitle
|
|
28
|
+
<!-- {% include [test](includes/fragments.md#f4) %} -->
|
|
29
|
+
|
|
30
|
+
link with [some local term1](*term1-1)
|
|
31
|
+
|
|
32
|
+
{% list tabs %}
|
|
33
|
+
|
|
34
|
+
- Название таба 1
|
|
35
|
+
|
|
36
|
+
Текст таба 1.
|
|
37
|
+
|
|
38
|
+
* Можно использовать списки.
|
|
39
|
+
* И **другую** разметку.
|
|
40
|
+
|
|
41
|
+
- Название таба 2
|
|
42
|
+
|
|
43
|
+
Текст таба 2.
|
|
44
|
+
|
|
45
|
+
{% endlist %}
|
|
46
|
+
|
|
47
|
+
All fragments
|
|
48
|
+
|
|
49
|
+
{% include [test](includes/fragments.md) %}
|
|
50
|
+
|
|
51
|
+
|
|
24
52
|
## Header {#header}
|
|
25
53
|
|
|
26
|
-
|
|
54
|
+
Content2
|
|
55
|
+
|
|
56
|
+
{% include [test](includes/styles.md) %}
|
|
57
|
+
|
|
58
|
+
[*term1-1]: {% include [test](includes/fragments.md#f3) %}
|
|
@@ -15,3 +15,27 @@ Some paragraph with anchor {#p1}
|
|
|
15
15
|
Some paragraph without anchor
|
|
16
16
|
|
|
17
17
|
Some paragraph with anchor {#p2}
|
|
18
|
+
|
|
19
|
+
## F4 {#f4}
|
|
20
|
+
Content F4
|
|
21
|
+
[{#T}](../autotitle.md#header)
|
|
22
|
+
|
|
23
|
+
### F4.1 {#f4.1}
|
|
24
|
+
link with [some term1](*term1)
|
|
25
|
+
|
|
26
|
+
{% list tabs %}
|
|
27
|
+
|
|
28
|
+
- Название таба 1
|
|
29
|
+
|
|
30
|
+
Текст таба 1.
|
|
31
|
+
|
|
32
|
+
* Можно использовать списки.
|
|
33
|
+
* И **другую** разметку.
|
|
34
|
+
|
|
35
|
+
- Название таба 2
|
|
36
|
+
|
|
37
|
+
Текст таба 2.
|
|
38
|
+
|
|
39
|
+
{% endlist %}
|
|
40
|
+
|
|
41
|
+
[*term1]: Some description
|
|
@@ -15,10 +15,16 @@ Text
|
|
|
15
15
|
|
|
16
16
|
[[?](*term)](http://ya.ru)
|
|
17
17
|
|
|
18
|
+
[Term 1](*term1) [Term 2](*term2)
|
|
18
19
|
|
|
19
20
|
Link after include
|
|
20
21
|
[{#T}](./1.md#subtitle)
|
|
21
22
|
|
|
23
|
+
Autotitle include
|
|
24
|
+
<!-- [{#T}](includes/fragments.md#f3) -->
|
|
25
|
+
|
|
22
26
|
Link after include
|
|
23
27
|
|
|
24
28
|
[*term]: Test terms
|
|
29
|
+
[*term1]: {% include [test](includes/fragments.md#f3) %}
|
|
30
|
+
[*term2]: {% include [test](includes/fragments.md#f3) %}
|