@mui/internal-markdown 1.0.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/CHANGELOG.md +5 -0
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/extractImports.js +8 -0
- package/extractImports.test.js +39 -0
- package/index.d.ts +19 -0
- package/index.js +3 -0
- package/loader.js +415 -0
- package/package.json +35 -0
- package/parseMarkdown.js +481 -0
- package/parseMarkdown.test.js +326 -0
- package/prepareMarkdown.js +259 -0
- package/prepareMarkdown.test.js +417 -0
- package/prism.d.ts +1 -0
- package/prism.js +61 -0
- package/textToHash.js +36 -0
- package/textToHash.test.js +32 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import prepareMarkdown from './prepareMarkdown';
|
|
3
|
+
|
|
4
|
+
describe('prepareMarkdown', () => {
|
|
5
|
+
const defaultParams = {
|
|
6
|
+
fileRelativeContext: 'test/bar',
|
|
7
|
+
options: {
|
|
8
|
+
env: {},
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
it('returns the table of contents with html and emojis preserved and <a> tags stripped', () => {
|
|
13
|
+
const markdown = `
|
|
14
|
+
# Support
|
|
15
|
+
|
|
16
|
+
<p class="description">Foo</p>
|
|
17
|
+
|
|
18
|
+
## Community help (free)
|
|
19
|
+
### GitHub <img src="/static/images/logos/github.svg" width="24" height="24" alt="GitHub logo" loading="lazy" />
|
|
20
|
+
### Unofficial 👍
|
|
21
|
+
### Warning ⚠️
|
|
22
|
+
### Header with Pro plan <a title="Pro plan" href="/x/introduction/licensing/#plan-pro"><span class="plan-pro"></span></a>
|
|
23
|
+
### Header with \`code\`
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
docs: {
|
|
28
|
+
en: { toc },
|
|
29
|
+
},
|
|
30
|
+
} = prepareMarkdown({
|
|
31
|
+
...defaultParams,
|
|
32
|
+
translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }],
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(toc).to.have.deep.ordered.members([
|
|
36
|
+
{
|
|
37
|
+
children: [
|
|
38
|
+
{
|
|
39
|
+
hash: 'github',
|
|
40
|
+
level: 3,
|
|
41
|
+
text: 'GitHub <img src="/static/images/logos/github.svg" width="24" height="24" alt="GitHub logo" loading="lazy" />',
|
|
42
|
+
},
|
|
43
|
+
{ hash: 'unofficial', level: 3, text: 'Unofficial 👍' },
|
|
44
|
+
{ hash: 'warning', level: 3, text: 'Warning ⚠️' },
|
|
45
|
+
{
|
|
46
|
+
hash: 'header-with-pro-plan',
|
|
47
|
+
level: 3,
|
|
48
|
+
text: 'Header with Pro plan <span class="plan-pro"></span>',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
hash: 'header-with-code',
|
|
52
|
+
level: 3,
|
|
53
|
+
text: 'Header with code',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
hash: 'community-help-free',
|
|
57
|
+
level: 2,
|
|
58
|
+
text: 'Community help (free)',
|
|
59
|
+
},
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('enables word-break for function signatures', () => {
|
|
64
|
+
const markdown = `
|
|
65
|
+
# Theming
|
|
66
|
+
|
|
67
|
+
<p class="description">Foo</p>
|
|
68
|
+
|
|
69
|
+
## API
|
|
70
|
+
### responsiveFontSizes(theme, options) => theme
|
|
71
|
+
### createTheme(options, ...args) => theme
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
const {
|
|
75
|
+
docs: {
|
|
76
|
+
en: { toc },
|
|
77
|
+
},
|
|
78
|
+
} = prepareMarkdown({
|
|
79
|
+
...defaultParams,
|
|
80
|
+
translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }],
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(toc).to.have.deep.ordered.members([
|
|
84
|
+
{
|
|
85
|
+
children: [
|
|
86
|
+
{
|
|
87
|
+
hash: 'responsivefontsizes-theme-options-theme',
|
|
88
|
+
level: 3,
|
|
89
|
+
text: 'responsiveFontSizes(​theme, options) => theme',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
hash: 'createtheme-options-args-theme',
|
|
93
|
+
level: 3,
|
|
94
|
+
text: 'createTheme(​options, ...args) => theme',
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
hash: 'api',
|
|
98
|
+
level: 2,
|
|
99
|
+
text: 'API',
|
|
100
|
+
},
|
|
101
|
+
]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('use english hash for different locales', () => {
|
|
105
|
+
const markdownEn = `
|
|
106
|
+
# Localization
|
|
107
|
+
|
|
108
|
+
<p class="description">Foo</p>
|
|
109
|
+
|
|
110
|
+
## Locales
|
|
111
|
+
### Example
|
|
112
|
+
### Use same hash
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
const markdownPt = `
|
|
116
|
+
# Localização
|
|
117
|
+
|
|
118
|
+
<p class="description">Foo</p>
|
|
119
|
+
|
|
120
|
+
## Idiomas
|
|
121
|
+
### Exemplo
|
|
122
|
+
### Usar o mesmo hash
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
const markdownZh = `
|
|
126
|
+
# 所在位置
|
|
127
|
+
|
|
128
|
+
<p class="description">Foo</p>
|
|
129
|
+
|
|
130
|
+
## 语言环境
|
|
131
|
+
### 例
|
|
132
|
+
### 使用相同的哈希
|
|
133
|
+
`;
|
|
134
|
+
const {
|
|
135
|
+
docs: {
|
|
136
|
+
en: { toc: tocEn },
|
|
137
|
+
pt: { toc: tocPt },
|
|
138
|
+
zh: { toc: tocZh },
|
|
139
|
+
},
|
|
140
|
+
} = prepareMarkdown({
|
|
141
|
+
pageFilename: '/same-hash-test',
|
|
142
|
+
translations: [
|
|
143
|
+
{ filename: 'localization.md', markdown: markdownEn, userLanguage: 'en' },
|
|
144
|
+
{ filename: 'localization-pt.md', markdown: markdownPt, userLanguage: 'pt' },
|
|
145
|
+
{ filename: 'localization-zh.md', markdown: markdownZh, userLanguage: 'zh' },
|
|
146
|
+
],
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(tocZh).to.have.deep.ordered.members([
|
|
150
|
+
{
|
|
151
|
+
children: [
|
|
152
|
+
{
|
|
153
|
+
hash: 'example',
|
|
154
|
+
level: 3,
|
|
155
|
+
text: '例',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
hash: 'use-same-hash',
|
|
159
|
+
level: 3,
|
|
160
|
+
text: '使用相同的哈希',
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
hash: 'locales',
|
|
164
|
+
level: 2,
|
|
165
|
+
text: '语言环境',
|
|
166
|
+
},
|
|
167
|
+
]);
|
|
168
|
+
|
|
169
|
+
expect(tocPt).to.have.deep.ordered.members([
|
|
170
|
+
{
|
|
171
|
+
children: [
|
|
172
|
+
{
|
|
173
|
+
hash: 'example',
|
|
174
|
+
level: 3,
|
|
175
|
+
text: 'Exemplo',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
hash: 'use-same-hash',
|
|
179
|
+
level: 3,
|
|
180
|
+
text: 'Usar o mesmo hash',
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
hash: 'locales',
|
|
184
|
+
level: 2,
|
|
185
|
+
text: 'Idiomas',
|
|
186
|
+
},
|
|
187
|
+
]);
|
|
188
|
+
|
|
189
|
+
expect(tocEn).to.have.deep.ordered.members([
|
|
190
|
+
{
|
|
191
|
+
children: [
|
|
192
|
+
{
|
|
193
|
+
hash: 'example',
|
|
194
|
+
level: 3,
|
|
195
|
+
text: 'Example',
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
hash: 'use-same-hash',
|
|
199
|
+
level: 3,
|
|
200
|
+
text: 'Use same hash',
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
hash: 'locales',
|
|
204
|
+
level: 2,
|
|
205
|
+
text: 'Locales',
|
|
206
|
+
},
|
|
207
|
+
]);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('use translated hash for translations are not synced', () => {
|
|
211
|
+
const markdownEn = `
|
|
212
|
+
# Localization
|
|
213
|
+
|
|
214
|
+
<p class="description">Foo</p>
|
|
215
|
+
|
|
216
|
+
## Locales
|
|
217
|
+
### Example
|
|
218
|
+
### Use same hash
|
|
219
|
+
`;
|
|
220
|
+
|
|
221
|
+
const markdownPt = `
|
|
222
|
+
# Localização
|
|
223
|
+
|
|
224
|
+
<p class="description">Foo</p>
|
|
225
|
+
|
|
226
|
+
## Idiomas
|
|
227
|
+
### Exemplo
|
|
228
|
+
### Usar o mesmo hash
|
|
229
|
+
### Usar traduzido
|
|
230
|
+
`;
|
|
231
|
+
|
|
232
|
+
const {
|
|
233
|
+
docs: {
|
|
234
|
+
en: { toc: tocEn },
|
|
235
|
+
pt: { toc: tocPt },
|
|
236
|
+
},
|
|
237
|
+
} = prepareMarkdown({
|
|
238
|
+
pageFilename: '/same-hash-test',
|
|
239
|
+
translations: [
|
|
240
|
+
{ filename: 'localization.md', markdown: markdownEn, userLanguage: 'en' },
|
|
241
|
+
{ filename: 'localization-pt.md', markdown: markdownPt, userLanguage: 'pt' },
|
|
242
|
+
],
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
expect(tocPt).to.have.deep.ordered.members([
|
|
246
|
+
{
|
|
247
|
+
children: [
|
|
248
|
+
{
|
|
249
|
+
hash: 'example',
|
|
250
|
+
level: 3,
|
|
251
|
+
text: 'Exemplo',
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
hash: 'use-same-hash',
|
|
255
|
+
level: 3,
|
|
256
|
+
text: 'Usar o mesmo hash',
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
hash: 'usar-traduzido',
|
|
260
|
+
level: 3,
|
|
261
|
+
text: 'Usar traduzido',
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
hash: 'locales',
|
|
265
|
+
level: 2,
|
|
266
|
+
text: 'Idiomas',
|
|
267
|
+
},
|
|
268
|
+
]);
|
|
269
|
+
|
|
270
|
+
expect(tocEn).to.have.deep.ordered.members([
|
|
271
|
+
{
|
|
272
|
+
children: [
|
|
273
|
+
{
|
|
274
|
+
hash: 'example',
|
|
275
|
+
level: 3,
|
|
276
|
+
text: 'Example',
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
hash: 'use-same-hash',
|
|
280
|
+
level: 3,
|
|
281
|
+
text: 'Use same hash',
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
hash: 'locales',
|
|
285
|
+
level: 2,
|
|
286
|
+
text: 'Locales',
|
|
287
|
+
},
|
|
288
|
+
]);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should report missing trailing splashes', () => {
|
|
292
|
+
const markdown = `
|
|
293
|
+
# Localization
|
|
294
|
+
|
|
295
|
+
<p class="description">Foo</p>
|
|
296
|
+
|
|
297
|
+
[bar](/bar/)
|
|
298
|
+
[foo](/foo)
|
|
299
|
+
`;
|
|
300
|
+
|
|
301
|
+
expect(() => {
|
|
302
|
+
prepareMarkdown({
|
|
303
|
+
...defaultParams,
|
|
304
|
+
translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }],
|
|
305
|
+
});
|
|
306
|
+
}).to.throw(`docs-infra: Missing trailing slash. The following link:
|
|
307
|
+
[foo](/foo) in /test/bar/index.md is missing a trailing slash, please add it.
|
|
308
|
+
|
|
309
|
+
See https://ahrefs.com/blog/trailing-slash/ for more details.
|
|
310
|
+
`);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it('should report missing leading splashes', () => {
|
|
314
|
+
const markdown = `
|
|
315
|
+
# Localization
|
|
316
|
+
|
|
317
|
+
<p class="description">Foo</p>
|
|
318
|
+
|
|
319
|
+
[bar](/bar/)
|
|
320
|
+
[foo](foo/)
|
|
321
|
+
`;
|
|
322
|
+
|
|
323
|
+
expect(() => {
|
|
324
|
+
prepareMarkdown({
|
|
325
|
+
...defaultParams,
|
|
326
|
+
translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }],
|
|
327
|
+
});
|
|
328
|
+
}).to.throw(`docs-infra: Missing leading slash. The following link:
|
|
329
|
+
[foo](foo/) in /test/bar/index.md is missing a leading slash, please add it.
|
|
330
|
+
`);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('should report title too long', () => {
|
|
334
|
+
const markdown = `
|
|
335
|
+
# Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
|
|
336
|
+
|
|
337
|
+
<p class="description">Foo</p>
|
|
338
|
+
|
|
339
|
+
`;
|
|
340
|
+
|
|
341
|
+
expect(() => {
|
|
342
|
+
prepareMarkdown({
|
|
343
|
+
...defaultParams,
|
|
344
|
+
translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }],
|
|
345
|
+
});
|
|
346
|
+
}).to
|
|
347
|
+
.throw(`docs-infra: The title "Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" is too long (117 characters).
|
|
348
|
+
It needs to have fewer than 70 characters—ideally less than 60. For more details, see:
|
|
349
|
+
https://developers.google.com/search/docs/advanced/appearance/title-link
|
|
350
|
+
`);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('should report description too long', () => {
|
|
354
|
+
const markdown = `
|
|
355
|
+
# Foo
|
|
356
|
+
|
|
357
|
+
<p class="description">Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo</p>
|
|
358
|
+
|
|
359
|
+
`;
|
|
360
|
+
|
|
361
|
+
expect(() => {
|
|
362
|
+
prepareMarkdown({
|
|
363
|
+
...defaultParams,
|
|
364
|
+
translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }],
|
|
365
|
+
});
|
|
366
|
+
}).to
|
|
367
|
+
.throw(`docs-infra: The description "Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" is too long (188 characters).
|
|
368
|
+
It needs to have fewer than 170 characters—ideally less than 160. For more details, see:
|
|
369
|
+
https://ahrefs.com/blog/meta-description/#4-be-concise
|
|
370
|
+
`);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('should not accept sh', () => {
|
|
374
|
+
const markdown = `
|
|
375
|
+
# Foo
|
|
376
|
+
|
|
377
|
+
<p class="description">Fo</p>
|
|
378
|
+
|
|
379
|
+
\`\`\`sh
|
|
380
|
+
npm install @mui/material
|
|
381
|
+
\`\`\`
|
|
382
|
+
|
|
383
|
+
`;
|
|
384
|
+
|
|
385
|
+
expect(() => {
|
|
386
|
+
prepareMarkdown({
|
|
387
|
+
...defaultParams,
|
|
388
|
+
translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }],
|
|
389
|
+
});
|
|
390
|
+
}).to.throw(`docs-infra: Unsupported language: "sh" in:
|
|
391
|
+
|
|
392
|
+
\`\`\`sh
|
|
393
|
+
npm install @mui/material
|
|
394
|
+
\`\`\`
|
|
395
|
+
|
|
396
|
+
Use "bash" instead.
|
|
397
|
+
`);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('should report duplicated trailing splashes', () => {
|
|
401
|
+
const markdown = `
|
|
402
|
+
# Localization
|
|
403
|
+
|
|
404
|
+
<p class="description">Foo</p>
|
|
405
|
+
|
|
406
|
+
[foo](/foo/)
|
|
407
|
+
[bar](/bar//#foo)
|
|
408
|
+
`;
|
|
409
|
+
|
|
410
|
+
expect(() => {
|
|
411
|
+
prepareMarkdown({
|
|
412
|
+
...defaultParams,
|
|
413
|
+
translations: [{ filename: 'index.md', markdown, userLanguage: 'en' }],
|
|
414
|
+
});
|
|
415
|
+
}).to.throw(`docs-infra: Duplicated trailing slashes.`);
|
|
416
|
+
});
|
|
417
|
+
});
|
package/prism.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function highlight(code: string, language: string): string;
|
package/prism.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const prism = require('prismjs');
|
|
2
|
+
require('prismjs/components/prism-css');
|
|
3
|
+
require('prismjs/components/prism-bash');
|
|
4
|
+
require('prismjs/components/prism-diff');
|
|
5
|
+
require('prismjs/components/prism-javascript');
|
|
6
|
+
require('prismjs/components/prism-json');
|
|
7
|
+
require('prismjs/components/prism-jsx');
|
|
8
|
+
require('prismjs/components/prism-markup');
|
|
9
|
+
require('prismjs/components/prism-yaml');
|
|
10
|
+
require('prismjs/components/prism-tsx');
|
|
11
|
+
|
|
12
|
+
function highlight(code, language) {
|
|
13
|
+
let prismLanguage;
|
|
14
|
+
switch (language) {
|
|
15
|
+
case 'ts':
|
|
16
|
+
prismLanguage = prism.languages.tsx;
|
|
17
|
+
break;
|
|
18
|
+
|
|
19
|
+
case 'js':
|
|
20
|
+
prismLanguage = prism.languages.jsx;
|
|
21
|
+
break;
|
|
22
|
+
|
|
23
|
+
case 'sh':
|
|
24
|
+
throw new Error(
|
|
25
|
+
[
|
|
26
|
+
`docs-infra: Unsupported language: "sh" in:`,
|
|
27
|
+
'',
|
|
28
|
+
'```sh',
|
|
29
|
+
code,
|
|
30
|
+
'```',
|
|
31
|
+
'',
|
|
32
|
+
'Use "bash" instead.',
|
|
33
|
+
'',
|
|
34
|
+
].join('\n'),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
case 'diff':
|
|
38
|
+
prismLanguage = { ...prism.languages.diff };
|
|
39
|
+
// original `/^[-<].*$/m` matches lines starting with `<` which matches
|
|
40
|
+
// <SomeComponent />
|
|
41
|
+
// we will only use `-` as the deleted marker
|
|
42
|
+
prismLanguage.deleted = /^[-].*$/m;
|
|
43
|
+
break;
|
|
44
|
+
|
|
45
|
+
default:
|
|
46
|
+
prismLanguage = prism.languages[language];
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!prismLanguage) {
|
|
51
|
+
if (language) {
|
|
52
|
+
throw new Error(`unsupported language: "${language}", "${code}"`);
|
|
53
|
+
} else {
|
|
54
|
+
prismLanguage = prism.languages.jsx;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return prism.highlight(code, prismLanguage);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = highlight;
|
package/textToHash.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function makeUnique(hash, unique, i = 1) {
|
|
2
|
+
const uniqueHash = i === 1 ? hash : `${hash}-${i}`;
|
|
3
|
+
|
|
4
|
+
if (!unique[uniqueHash]) {
|
|
5
|
+
unique[uniqueHash] = true;
|
|
6
|
+
return uniqueHash;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return makeUnique(hash, unique, i + 1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} text - HTML from e.g. parseMarkdown#render
|
|
14
|
+
* @param {Record<string, boolean>} [unique] - Ensures that each output is unique in `unique`
|
|
15
|
+
* @returns {string} that is safe to use in fragment links
|
|
16
|
+
*/
|
|
17
|
+
function textToHash(text, unique = {}) {
|
|
18
|
+
return makeUnique(
|
|
19
|
+
encodeURI(
|
|
20
|
+
text
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.replace(/<\/?[^>]+(>|$)/g, '') // remove HTML
|
|
23
|
+
.replace(/=>|<| \/>|<code>|<\/code>|'/g, '')
|
|
24
|
+
.replace(/[!@#$%^&*()=_+[\]{}`~;:'"|,.<>/?\s]+/g, '-')
|
|
25
|
+
.replace(
|
|
26
|
+
/([\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])\uFE0F?/g,
|
|
27
|
+
'',
|
|
28
|
+
) // remove emojis
|
|
29
|
+
.replace(/-+/g, '-')
|
|
30
|
+
.replace(/^-|-$/g, ''),
|
|
31
|
+
),
|
|
32
|
+
unique,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = textToHash;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { parseInline as renderInlineMarkdown } from 'marked';
|
|
3
|
+
import textToHash from './textToHash';
|
|
4
|
+
|
|
5
|
+
describe('textToHash', () => {
|
|
6
|
+
it('should hash as expected', () => {
|
|
7
|
+
const table = [
|
|
8
|
+
['createTheme(options) => theme', 'createtheme-options-theme'],
|
|
9
|
+
['Typography - Font family', 'typography-font-family'],
|
|
10
|
+
["barre d'application", 'barre-dapplication'],
|
|
11
|
+
[
|
|
12
|
+
'createGenerateClassName([options]) => class name generator',
|
|
13
|
+
'creategenerateclassname-options-class-name-generator',
|
|
14
|
+
],
|
|
15
|
+
['@mui/material/styles vs @mui/styles', 'mui-material-styles-vs-mui-styles'],
|
|
16
|
+
['Blog 📝', 'blog'],
|
|
17
|
+
];
|
|
18
|
+
table.forEach((entry, index) => {
|
|
19
|
+
const [markdown, expected] = entry;
|
|
20
|
+
const text = renderInlineMarkdown(markdown, { mangle: false, headerIds: false });
|
|
21
|
+
const actual = textToHash(text);
|
|
22
|
+
|
|
23
|
+
expect(actual).to.equal(expected, `snapshot #${index} matches`);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should generate a unique hash', () => {
|
|
28
|
+
const unique = {};
|
|
29
|
+
expect(textToHash('Styling solution', unique)).to.equal('styling-solution');
|
|
30
|
+
expect(textToHash('Styling solution', unique)).to.equal('styling-solution-2');
|
|
31
|
+
});
|
|
32
|
+
});
|