Skip to content

Commit e811756

Browse files
authored
feat: Fragment identifier full line ignore (#2626)
1 parent f793e26 commit e811756

File tree

5 files changed

+79
-11
lines changed

5 files changed

+79
-11
lines changed

docs/embed-files.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ Sometimes you don't want to embed a whole file. Maybe because you need just a fe
6565
```
6666

6767
In your code file you need to surround the fragment between `/// [demo]` lines (before and after the fragment).
68-
Alternatively you can use `### [demo]`.
68+
Alternatively you can use `### [demo]`. By default, only identifiers are omitted. To omit the entire line containing the identifier in the fragment output, you can add the `:omitFragmentLine` option.
6969

7070
Example:
7171

src/core/render/compiler.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export class Compiler {
135135
}
136136

137137
embed.fragment = config.fragment;
138+
embed.omitFragmentLine = config.omitFragmentLine;
138139

139140
return embed;
140141
}

src/core/render/compiler/media.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ export const compileMedia = {
1818
},
1919
video(url, title) {
2020
return {
21-
html: `<video src="${url}" ${title || 'controls'}>Not Support</video>`,
21+
html: `<video src="${url}" ${title || 'controls'}>Not Supported</video>`,
2222
};
2323
},
2424
audio(url, title) {
2525
return {
26-
html: `<audio src="${url}" ${title || 'controls'}>Not Support</audio>`,
26+
html: `<audio src="${url}" ${title || 'controls'}>Not Supported</audio>`,
2727
};
2828
},
2929
code(url, title) {

src/core/render/embed.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,22 @@ const cached = {};
1212
*
1313
* @param {string} text - The input text that may contain embedded fragments.
1414
* @param {string} fragment - The fragment identifier to search for.
15-
* @returns {string} - The extracted and demented content, or an empty string if not found.
15+
* @param {boolean} fullLine - Boolean flag to enable full-line matching of fragment identifiers.
16+
* @returns {string} - The extracted and dedented content, or an empty string if not found.
1617
*/
17-
function extractFragmentContent(text, fragment) {
18+
function extractFragmentContent(text, fragment, fullLine) {
1819
if (!fragment) {
1920
return text;
2021
}
21-
22+
let fragmentRegex = `(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`;
23+
const contentRegex = `[\\s\\S]*?`;
24+
if (fullLine) {
25+
// Match full line containing fragment identifier (e.g. /// [demo])
26+
fragmentRegex = `.*${fragmentRegex}.*\n`;
27+
}
2228
const pattern = new RegExp(
23-
`(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]([\\s\\S]*?)(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`,
24-
);
29+
`(?:${fragmentRegex})(${contentRegex})(?:${fragmentRegex})`,
30+
); // content is the capture group
2531
const match = text.match(pattern);
2632
return stripIndent((match || [])[1] || '').trim();
2733
}
@@ -68,13 +74,21 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
6874
}
6975

7076
if (currentToken.embed.fragment) {
71-
text = extractFragmentContent(text, currentToken.embed.fragment);
77+
text = extractFragmentContent(
78+
text,
79+
currentToken.embed.fragment,
80+
currentToken.embed.omitFragmentLine,
81+
);
7282
}
7383

7484
embedToken = compile.lexer(text);
7585
} else if (currentToken.embed.type === 'code') {
7686
if (currentToken.embed.fragment) {
77-
text = extractFragmentContent(text, currentToken.embed.fragment);
87+
text = extractFragmentContent(
88+
text,
89+
currentToken.embed.fragment,
90+
currentToken.embed.omitFragmentLine,
91+
);
7892
}
7993

8094
embedToken = compile.lexer(

test/integration/example.test.js

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,48 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
173173
);
174174
});
175175

176+
test('embed file full line fragment identifier', async () => {
177+
await docsifyInit({
178+
markdown: {
179+
homepage: `
180+
# Embed Test
181+
182+
[filename](_media/example1.html ':include :type=code :fragment=demo :omitFragmentLine')
183+
`,
184+
},
185+
routes: {
186+
'_media/example1.html': `
187+
<script>
188+
let myURL = 'https://api.example.com/data';
189+
/// [demo] Full line fragment identifier (all of these words here should not be included in fragment)
190+
const result = fetch(myURL)
191+
.then(response => {
192+
return response.json();
193+
})
194+
.then(myJson => {
195+
console.log(JSON.stringify(myJson));
196+
});
197+
<!-- /// [demo] -->
198+
result.then(console.log).catch(console.error);
199+
</script>
200+
`,
201+
},
202+
});
203+
204+
// Wait for the embedded fragment to be fetched and rendered into #main
205+
expect(
206+
await waitForText('#main', 'console.log(JSON.stringify(myJson));'),
207+
).toBeTruthy();
208+
209+
const mainText = document.querySelector('#main').textContent;
210+
expect(mainText).not.toContain('https://api.example.com/data');
211+
expect(mainText).not.toContain('Full line fragment identifier');
212+
expect(mainText).not.toContain('-->');
213+
expect(mainText).not.toContain(
214+
'result.then(console.log).catch(console.error);',
215+
);
216+
});
217+
176218
test('embed multiple file code fragments', async () => {
177219
await docsifyInit({
178220
markdown: {
@@ -186,7 +228,9 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
186228
# Text between
187229
188230
[filename](_media/example3.js ':include :fragment=something_else_not_code')
189-
231+
232+
[filename](_media/example4.js ':include :fragment=demo')
233+
190234
# Text after
191235
`,
192236
},
@@ -209,6 +253,12 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
209253
example3 += 10;
210254
/// [something_else_not_code]
211255
console.log(example3);`,
256+
'_media/example4.js': `
257+
let example4 = 1;
258+
### No fragment here
259+
example4 += 10;
260+
/// No fragment here
261+
console.log(example4);`,
212262
},
213263
});
214264

@@ -225,6 +275,9 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
225275
expect(mainText).not.toContain('console.log(example1);');
226276
expect(mainText).not.toContain('console.log(example2);');
227277
expect(mainText).not.toContain('console.log(example3);');
278+
expect(mainText).not.toContain('console.log(example4);');
279+
expect(mainText).not.toContain('example4 += 10;');
280+
expect(mainText).not.toContain('No fragment here');
228281
});
229282

230283
test('embed file table cell', async () => {

0 commit comments

Comments
 (0)