@mui/internal-markdown 1.0.2 → 1.0.4

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/index.d.ts CHANGED
@@ -12,7 +12,24 @@ export function createRender(context: {
12
12
  ignoreLanguagePages: (path: string) => boolean;
13
13
  }): (markdown: string) => string;
14
14
 
15
- export function getHeaders(markdown: string): Record<string, string | string[]>;
15
+ export interface MarkdownHeaders {
16
+ packageName?: string;
17
+ productId: string;
18
+ githubLabel?: string;
19
+ waiAria?: string;
20
+ materialDesign?: string;
21
+ components: string[];
22
+ hooks?: string[];
23
+ slug?: string;
24
+ title?: string;
25
+ description?: string;
26
+ image?: string;
27
+ tags?: string[];
28
+ authors?: string[];
29
+ date?: string;
30
+ }
31
+
32
+ export function getHeaders(markdown: string): MarkdownHeaders;
16
33
 
17
34
  export function getTitle(markdown: string): string;
18
35
 
package/loader.js CHANGED
@@ -1,4 +1,4 @@
1
- const { promises: fs, readdirSync } = require('fs');
1
+ const { promises: fs, readdirSync, statSync } = require('fs');
2
2
  const path = require('path');
3
3
  const prepareMarkdown = require('./prepareMarkdown');
4
4
  const extractImports = require('./extractImports');
@@ -19,7 +19,7 @@ function upperCaseFirst(string) {
19
19
  * @example moduleIDToJSIdentifier('../Box-new.js') === '$$$BoxNewJs'
20
20
  */
21
21
  function moduleIDToJSIdentifier(moduleID) {
22
- const delimiter = /(\.|-|\/|:)/;
22
+ const delimiter = /(@|\.|-|\/|:)/;
23
23
  return moduleID
24
24
  .split(delimiter)
25
25
  .filter((part) => !delimiter.test(part))
@@ -127,6 +127,7 @@ module.exports = async function demoLoader() {
127
127
  const demoModuleIDs = new Set();
128
128
  const componentModuleIDs = new Set();
129
129
  const nonEditableDemos = new Set();
130
+ const relativeModules = new Map();
130
131
  const demoNames = Array.from(
131
132
  new Set(
132
133
  docs.en.rendered
@@ -142,6 +143,63 @@ module.exports = async function demoLoader() {
142
143
  ),
143
144
  );
144
145
 
146
+ /**
147
+ * @param {*} demoName
148
+ * @param {*} moduleFilepath
149
+ * @param {*} variant
150
+ * @param {*} importModuleID
151
+ * @example detectRelativeImports('ComboBox.js', '', JS', './top100Films') => relativeModules.set('ComboBox.js', new Map([['./top100Films.js', ['JS']]]))
152
+ */
153
+ function detectRelativeImports(demoName, moduleFilepath, variant, importModuleID) {
154
+ if (importModuleID.startsWith('.')) {
155
+ let relativeModuleFilename = importModuleID;
156
+ const demoMap = relativeModules.get(demoName);
157
+ // If the moduleID does not end with an extension, or ends with an unsupported extension (e.g. ".styling") we need to resolve it
158
+ // Fastest way to get a file extension, see: https://stackoverflow.com/a/12900504/
159
+ const importType = importModuleID.slice(
160
+ (Math.max(0, importModuleID.lastIndexOf('.')) || Infinity) + 1,
161
+ );
162
+ const supportedTypes = ['js', 'jsx', 'ts', 'tsx', 'css', 'json'];
163
+ if (!importType || !supportedTypes.includes(importType)) {
164
+ // If the demo is a JS demo, we can assume that the relative import is either
165
+ // a `.js` or a `.jsx` file, with `.js` taking precedence over `.jsx`
166
+ // likewise for TS demos, with `.ts` taking precedence over `.tsx`
167
+ const extensions =
168
+ variant === 'JS' ? ['.js', '.jsx', '.ts', '.tsx'] : ['.ts', '.tsx', '.js', '.jsx'];
169
+ const extension = extensions.find((ext) => {
170
+ try {
171
+ return statSync(path.join(moduleFilepath, '..', `${importModuleID}${ext}`));
172
+ } catch (error) {
173
+ // If the file does not exist, we return false and continue to the next extension
174
+ return false;
175
+ }
176
+ });
177
+ if (!extension) {
178
+ throw new Error(
179
+ [
180
+ `You are trying to import a module "${importModuleID}" in the demo "${demoName}" that could not be resolved.`,
181
+ `Please make sure that one of the following file exists:`,
182
+ ...extensions.map((ext) => `- ${importModuleID}${ext}`),
183
+ ].join('\n'),
184
+ );
185
+ } else {
186
+ relativeModuleFilename = `${importModuleID}${extension}`;
187
+ }
188
+ }
189
+
190
+ if (!demoMap) {
191
+ relativeModules.set(demoName, new Map([[relativeModuleFilename, [variant]]]));
192
+ } else {
193
+ const variantArray = demoMap.get(relativeModuleFilename);
194
+ if (variantArray) {
195
+ variantArray.push(variant);
196
+ } else {
197
+ demoMap.set(relativeModuleFilename, [variant]);
198
+ }
199
+ }
200
+ }
201
+ }
202
+
145
203
  await Promise.all(
146
204
  demoNames.map(async (demoName) => {
147
205
  const multipleDemoVersionsUsed = !demoName.endsWith('.js');
@@ -168,11 +226,14 @@ module.exports = async function demoLoader() {
168
226
  raw: await fs.readFile(moduleFilepath, { encoding: 'utf8' }),
169
227
  };
170
228
  demoModuleIDs.add(moduleID);
229
+
171
230
  // Skip non-editable demos
172
231
  if (!nonEditableDemos.has(demoName)) {
173
- extractImports(demos[demoName].raw).forEach((importModuleID) =>
174
- importedModuleIDs.add(importModuleID),
175
- );
232
+ extractImports(demos[demoName].raw).forEach((importModuleID) => {
233
+ // detect relative import
234
+ detectRelativeImports(demoName, moduleFilepath, 'JS', importModuleID);
235
+ importedModuleIDs.add(importModuleID);
236
+ });
176
237
  }
177
238
 
178
239
  if (multipleDemoVersionsUsed) {
@@ -343,10 +404,69 @@ module.exports = async function demoLoader() {
343
404
  // But this leads to building both demo version i.e. more build time.
344
405
  demos[demoName].moduleTS = this.mode === 'production' ? moduleID : moduleTS;
345
406
  demos[demoName].rawTS = rawTS;
407
+
408
+ // Extract relative imports from the TypeScript version
409
+ // of demos which have relative imports in the JS version
410
+ if (relativeModules.has(demoName)) {
411
+ extractImports(demos[demoName].rawTS).forEach((importModuleID) => {
412
+ detectRelativeImports(demoName, moduleTSFilepath, 'TS', importModuleID);
413
+ importedModuleIDs.add(importModuleID);
414
+ });
415
+ }
416
+
346
417
  demoModuleIDs.add(demos[demoName].moduleTS);
347
418
  } catch (error) {
348
419
  // TS version of the demo doesn't exist. This is fine.
349
420
  }
421
+
422
+ /* Map over relative import module IDs and resolve them
423
+ * while grouping by demo variant
424
+ * From:
425
+ * relativeModules: { 'ComboBox.js' =>
426
+ * { './top100Films.js' => ['JS', 'TS'] }
427
+ * }
428
+ * To:
429
+ * demos["ComboBox.js"].relativeModules = {
430
+ * JS: [{ module: './top100Films.js', raw: '...' }],
431
+ * TS: [{ module: './top100Films.js', raw: '...' }]
432
+ * }
433
+ * }
434
+ */
435
+
436
+ if (relativeModules.has(demoName)) {
437
+ if (!demos[demoName].relativeModules) {
438
+ demos[demoName].relativeModules = {};
439
+ }
440
+
441
+ await Promise.all(
442
+ Array.from(relativeModules.get(demoName)).map(async ([relativeModuleID, variants]) => {
443
+ let raw = '';
444
+ try {
445
+ raw = await fs.readFile(path.join(path.dirname(moduleFilepath), relativeModuleID), {
446
+ encoding: 'utf8',
447
+ });
448
+ } catch {
449
+ throw new Error(
450
+ `Could not find a module for the relative import "${relativeModuleID}" in the demo "${demoName}"`,
451
+ );
452
+ }
453
+
454
+ const moduleData = { module: relativeModuleID, raw };
455
+ const modules = demos[demoName].relativeModules;
456
+
457
+ variants.forEach((variant) => {
458
+ if (modules[variant]) {
459
+ // Avoid duplicates
460
+ if (!modules[variant].some((elem) => elem.module === relativeModuleID)) {
461
+ modules[variant].push(moduleData);
462
+ }
463
+ } else {
464
+ modules[variant] = [moduleData];
465
+ }
466
+ });
467
+ }),
468
+ );
469
+ }
350
470
  }),
351
471
  );
352
472
 
@@ -365,7 +485,9 @@ module.exports = async function demoLoader() {
365
485
  );
366
486
 
367
487
  componentNames.forEach((componentName) => {
368
- const moduleID = path.join(this.rootContext, 'src', componentName).replace(/\\/g, '/');
488
+ const moduleID = componentName.startsWith('@mui/docs/')
489
+ ? componentName
490
+ : path.join(this.rootContext, 'src', componentName).replace(/\\/g, '/');
369
491
 
370
492
  components[moduleID] = componentName;
371
493
  componentModuleIDs.add(moduleID);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/internal-markdown",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "author": "MUI Team",
5
5
  "description": "MUI markdown parser. This is an internal package not meant for general use.",
6
6
  "main": "./index.js",
@@ -16,13 +16,13 @@
16
16
  "directory": "packages/markdown"
17
17
  },
18
18
  "dependencies": {
19
- "@babel/runtime": "^7.24.4",
19
+ "@babel/runtime": "^7.24.5",
20
20
  "lodash": "^4.17.21",
21
21
  "marked": "^5.1.2",
22
22
  "prismjs": "^1.29.0"
23
23
  },
24
24
  "devDependencies": {
25
- "@types/chai": "^4.3.14",
25
+ "@types/chai": "^4.3.16",
26
26
  "chai": "^4.4.1"
27
27
  },
28
28
  "publishConfig": {
package/parseMarkdown.js CHANGED
@@ -364,11 +364,7 @@ function createRender(context) {
364
364
  }
365
365
 
366
366
  return [
367
- `<h${level} id="${hash}">`,
368
- headingHtml,
369
- `<a aria-labelledby="${hash}" class="anchor-link" href="#${hash}" tabindex="-1">`,
370
- '<svg><use xlink:href="#anchor-link-icon" /></svg>',
371
- '</a>',
367
+ `<h${level} id="${hash}"><a href="#${hash}" class="title-link-to-anchor">${headingHtml}<span class="anchor-icon"><svg><use xlink:href="#anchor-link-icon" /></svg></span></a>`,
372
368
  `<button title="Post a comment" class="comment-link" data-feedback-hash="${hash}">`,
373
369
  '<svg><use xlink:href="#comment-link-icon" /></svg>',
374
370
  `</button>`,
@@ -427,10 +423,8 @@ function createRender(context) {
427
423
  escaped ? code : escape(code, true)
428
424
  }</code></pre>${[
429
425
  '<button data-ga-event-category="code" data-ga-event-action="copy-click" aria-label="Copy the code" class="MuiCode-copy">',
430
- '<svg focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="ContentCopyRoundedIcon">',
431
- '<use class="MuiCode-copy-icon" xlink:href="#copy-icon" />',
432
- '<use class="MuiCode-copied-icon" xlink:href="#copied-icon" />',
433
- '</svg>',
426
+ '<span class="MuiCode-copy-label">Copy</span>',
427
+ '<span class="MuiCode-copied-label">Copied</span>',
434
428
  '<span class="MuiCode-copyKeypress"><span>(or</span> $keyC<span>)</span></span></button></div>',
435
429
  ].join('')}\n`;
436
430
  };
@@ -295,9 +295,9 @@ authors:
295
295
  ).to.equal(
296
296
  [
297
297
  `<h1>Accordion</h1>`,
298
- `<h2 id="basic-features">Basic features 🧪<a aria-labelledby="basic-features" class="anchor-link" href="#basic-features" tabindex="-1"><svg><use xlink:href="#anchor-link-icon" /></svg></a><button title="Post a comment" class="comment-link" data-feedback-hash="basic-features"><svg><use xlink:href="#comment-link-icon" /></svg></button></h2>`,
299
- `<h2 id="using-slots-and-slotprops">Using <code>slots</code> and <code>slotProps</code><a aria-labelledby="using-slots-and-slotprops" class="anchor-link" href="#using-slots-and-slotprops" tabindex="-1"><svg><use xlink:href="#anchor-link-icon" /></svg></a><button title="Post a comment" class="comment-link" data-feedback-hash="using-slots-and-slotprops"><svg><use xlink:href="#comment-link-icon" /></svg></button></h2>`,
300
- `<h3 id="specific-example">Specific example<a aria-labelledby="specific-example" class="anchor-link" href="#specific-example" tabindex="-1"><svg><use xlink:href="#anchor-link-icon" /></svg></a><button title="Post a comment" class="comment-link" data-feedback-hash="specific-example"><svg><use xlink:href="#comment-link-icon" /></svg></button></h3>`,
298
+ `<h2 id="basic-features"><a href="#basic-features" class="title-link-to-anchor">Basic features 🧪<span class="anchor-icon"><svg><use xlink:href="#anchor-link-icon" /></svg></span></a><button title="Post a comment" class="comment-link" data-feedback-hash="basic-features"><svg><use xlink:href="#comment-link-icon" /></svg></button></h2>`,
299
+ `<h2 id="using-slots-and-slotprops"><a href="#using-slots-and-slotprops" class="title-link-to-anchor">Using <code>slots</code> and <code>slotProps</code><span class="anchor-icon"><svg><use xlink:href="#anchor-link-icon" /></svg></span></a><button title="Post a comment" class="comment-link" data-feedback-hash="using-slots-and-slotprops"><svg><use xlink:href="#comment-link-icon" /></svg></button></h2>`,
300
+ `<h3 id="specific-example"><a href="#specific-example" class="title-link-to-anchor">Specific example<span class="anchor-icon"><svg><use xlink:href="#anchor-link-icon" /></svg></span></a><button title="Post a comment" class="comment-link" data-feedback-hash="specific-example"><svg><use xlink:href="#comment-link-icon" /></svg></button></h3>`,
301
301
  ].join(''),
302
302
  );
303
303
 
@@ -110,7 +110,7 @@ This unstyled version of the component is the ideal choice for heavy customizati
110
110
  `);
111
111
  }
112
112
 
113
- if (headers.components.length > 0) {
113
+ if (headers.components.length > 0 && headers.productId !== 'base-ui') {
114
114
  contents.push(`
115
115
  ## API
116
116