@mui/internal-markdown 2.0.11 → 2.0.13

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.
@@ -1,6 +1,12 @@
1
+ // @ts-check
2
+
1
3
  const importModuleRegexp =
2
4
  /^\s*import (?:["'\s]*(?:[\w*{}\n, ]+)from\s*)?["'\s]*([^"'{}$\s]+)["'\s].*/gm;
3
5
 
6
+ /**
7
+ * @param {string} code
8
+ * @returns {string[]}
9
+ */
4
10
  export default function extractImports(code) {
5
11
  return (code.match(importModuleRegexp) || []).map((x) => x.replace(importModuleRegexp, '$1'));
6
12
  }
package/loader.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  import { promises as fs, readdirSync, statSync } from 'fs';
2
4
  import path from 'path';
3
5
  import prepareMarkdown from './prepareMarkdown.mjs';
@@ -28,9 +30,24 @@ function moduleIDToJSIdentifier(moduleID) {
28
30
  .join('');
29
31
  }
30
32
 
33
+ /**
34
+ * @typedef {Record<string, Record<string, string>> } ComponentPackageMapping
35
+ */
36
+
37
+ /** @type {ComponentPackageMapping | null} */
31
38
  let componentPackageMapping = null;
32
39
 
40
+ /**
41
+ * @typedef {Object} Package
42
+ * @property {string[]} paths
43
+ * @property {string} productId
44
+ */
45
+
46
+ /**
47
+ * @param {Package[]} packages
48
+ */
33
49
  function findComponents(packages) {
50
+ /** @type {ComponentPackageMapping} */
34
51
  const mapping = {};
35
52
 
36
53
  packages.forEach((pkg) => {
@@ -57,7 +74,48 @@ function findComponents(packages) {
57
74
  }
58
75
 
59
76
  /**
60
- * @type {import('webpack').loader.Loader}
77
+ * @typedef {Object} LoaderOptions
78
+ * @property {Package[]} packages
79
+ * @property {string[]} languagesInProgress
80
+ * @property {string} workspaceRoot
81
+ */
82
+
83
+ /**
84
+ * @typedef {Object} ModuleData
85
+ * @property {string} module
86
+ * @property {string} raw
87
+ */
88
+
89
+ /**
90
+ * @typedef {Object} Translation
91
+ * @property {string} filename
92
+ * @property {string} userLanguage
93
+ * @property {string} [markdown]
94
+ */
95
+
96
+ /**
97
+ * @typedef {Object} Demo
98
+ * @property {string} module
99
+ * @property {string} [moduleTS]
100
+ * @property {string} [moduleTailwind]
101
+ * @property {string} [moduleTSTailwind]
102
+ * @property {string} [moduleCSS]
103
+ * @property {string} [moduleTSCSS]
104
+ * @property {string} raw
105
+ * @property {string} [rawTS]
106
+ * @property {string} [rawTailwind]
107
+ * @property {string} [rawTailwindTS]
108
+ * @property {string} [rawCSS]
109
+ * @property {string} [rawCSSTS]
110
+ * @property {string} [jsxPreview]
111
+ * @property {string} [tailwindJsxPreview]
112
+ * @property {string} [cssJsxPreview]
113
+ * @property {Object.<string, ModuleData[]>} [relativeModules]
114
+ */
115
+
116
+ /**
117
+ * @type {import('webpack').LoaderDefinitionFunction<LoaderOptions>}
118
+ * @this {import('webpack').LoaderContext<LoaderOptions>}
61
119
  */
62
120
  export default async function demoLoader() {
63
121
  const englishFilepath = this.resourcePath;
@@ -71,41 +129,42 @@ export default async function demoLoader() {
71
129
 
72
130
  const files = await fs.readdir(path.dirname(englishFilepath));
73
131
  const translations = await Promise.all(
74
- files
75
- .map((filename) => {
76
- if (filename === `${englishFilename}.md`) {
77
- return {
78
- filename,
79
- userLanguage: 'en',
80
- };
81
- }
132
+ /** @type {Translation[]} */ (
133
+ files
134
+ .map((filename) => {
135
+ if (filename === `${englishFilename}.md`) {
136
+ return {
137
+ filename,
138
+ userLanguage: 'en',
139
+ };
140
+ }
82
141
 
83
- const matchNotEnglishMarkdown = filename.match(notEnglishMarkdownRegExp);
84
-
85
- if (
86
- filename.startsWith(englishFilename) &&
87
- matchNotEnglishMarkdown !== null &&
88
- options.languagesInProgress.includes(matchNotEnglishMarkdown[1])
89
- ) {
90
- return {
91
- filename,
92
- userLanguage: matchNotEnglishMarkdown[1],
93
- };
94
- }
142
+ const matchNotEnglishMarkdown = filename.match(notEnglishMarkdownRegExp);
143
+
144
+ if (
145
+ filename.startsWith(englishFilename) &&
146
+ matchNotEnglishMarkdown !== null &&
147
+ options.languagesInProgress.includes(matchNotEnglishMarkdown[1])
148
+ ) {
149
+ return {
150
+ filename,
151
+ userLanguage: matchNotEnglishMarkdown[1],
152
+ };
153
+ }
95
154
 
96
- return null;
97
- })
98
- .filter((translation) => translation)
99
- .map(async (translation) => {
100
- const filepath = path.join(path.dirname(englishFilepath), translation.filename);
101
- this.addDependency(filepath);
102
- const markdown = await fs.readFile(filepath, { encoding: 'utf8' });
103
-
104
- return {
105
- ...translation,
106
- markdown,
107
- };
108
- }),
155
+ return null;
156
+ })
157
+ .filter((translation) => translation)
158
+ ).map(async (translation) => {
159
+ const filepath = path.join(path.dirname(englishFilepath), translation.filename);
160
+ this.addDependency(filepath);
161
+ const markdown = await fs.readFile(filepath, { encoding: 'utf8' });
162
+
163
+ return {
164
+ ...translation,
165
+ markdown,
166
+ };
167
+ }),
109
168
  );
110
169
 
111
170
  // Use .. as the docs runs from the /docs folder
@@ -121,25 +180,33 @@ export default async function demoLoader() {
121
180
  options,
122
181
  });
123
182
 
183
+ /** @type {Record<string, Demo>} */
124
184
  const demos = {};
185
+ /** @type {Set<string>} */
125
186
  const importedModuleIDs = new Set();
187
+ /** @type {Record<string, string>} */
126
188
  const components = {};
189
+ /** @type {Set<string>} */
127
190
  const demoModuleIDs = new Set();
191
+ /** @type {Set<string>} */
128
192
  const componentModuleIDs = new Set();
193
+ /** @type {Set<string>} */
129
194
  const nonEditableDemos = new Set();
195
+ /** @type {Map<string, Map<string, string[]>>} */
130
196
  const relativeModules = new Map();
197
+ /** @type {string[]} */
131
198
  const demoNames = Array.from(
132
199
  new Set(
133
- docs.en.rendered
134
- .filter((markdownOrComponentConfig) => {
200
+ /** @type {import('./prepareMarkdown.mjs').DemoEntry[]} */ (
201
+ docs.en.rendered.filter((markdownOrComponentConfig) => {
135
202
  return typeof markdownOrComponentConfig !== 'string' && markdownOrComponentConfig.demo;
136
203
  })
137
- .map((demoConfig) => {
138
- if (demoConfig.hideToolbar) {
139
- nonEditableDemos.add(demoConfig.demo);
140
- }
141
- return demoConfig.demo;
142
- }),
204
+ ).map((demoConfig) => {
205
+ if (demoConfig.hideToolbar) {
206
+ nonEditableDemos.add(demoConfig.demo);
207
+ }
208
+ return demoConfig.demo;
209
+ }),
143
210
  ),
144
211
  );
145
212
 
@@ -204,21 +271,21 @@ export default async function demoLoader() {
204
271
 
205
272
  /**
206
273
  * Inserts the moduleData into the relativeModules object
207
- * @param string demoName
208
- * @param {*} moduleData
209
- * @param string variant
210
- * @example updateRelativeModules(demoName, {module: 'constants.js', raw: ... }, 'JS') => demos[demoName].relativeModules[variant].push(moduleData)
274
+ * @param {string} demoName
275
+ * @param {ModuleData} moduleData
276
+ * @param {string} variant
211
277
  */
212
278
  function updateRelativeModules(demoName, moduleData, variant) {
213
- if (demos[demoName].relativeModules[variant]) {
279
+ const variantModule = /** @type {Object.<string, ModuleData[]>} */ (
280
+ demos[demoName].relativeModules
281
+ );
282
+ if (variantModule[variant]) {
214
283
  // Avoid duplicates
215
- if (
216
- !demos[demoName].relativeModules[variant].some((elem) => elem.module === moduleData.module)
217
- ) {
218
- demos[demoName].relativeModules[variant].push(moduleData);
284
+ if (!variantModule[variant].some((elem) => elem.module === moduleData.module)) {
285
+ variantModule[variant].push(moduleData);
219
286
  }
220
287
  } else {
221
- demos[demoName].relativeModules[variant] = [moduleData];
288
+ variantModule[variant] = [moduleData];
222
289
  }
223
290
  }
224
291
 
@@ -460,10 +527,15 @@ export default async function demoLoader() {
460
527
  demos[demoName].relativeModules = {};
461
528
  }
462
529
 
530
+ /** @type {Record<string, Set<string>>} */
463
531
  const addedModulesRelativeToModulePathPerVariant = {};
464
532
 
533
+ const demoRelativeModules = /** @type {Map<string, string[]>} */ (
534
+ relativeModules.get(demoName)
535
+ );
536
+
465
537
  await Promise.all(
466
- Array.from(relativeModules.get(demoName)).map(async ([relativeModuleID, variants]) => {
538
+ Array.from(demoRelativeModules).map(async ([relativeModuleID, variants]) => {
467
539
  for (const variant of variants) {
468
540
  addedModulesRelativeToModulePathPerVariant[variant] ??= new Set();
469
541
  const addedModulesRelativeToModulePath =
@@ -523,7 +595,9 @@ export default async function demoLoader() {
523
595
  // We are only iterating through an array that looks
524
596
  // like this: ['JS', 'TS'], so it is safe to await
525
597
  // eslint-disable-next-line no-await-in-loop
526
- const rawEntry = await fs.readFile(entryModuleFilePath, { encoding: 'utf8' });
598
+ const rawEntry = await fs.readFile(entryModuleFilePath, {
599
+ encoding: 'utf8',
600
+ });
527
601
 
528
602
  extractImports(rawEntry).forEach((importModuleID) => {
529
603
  // detect relative import
@@ -570,17 +644,18 @@ export default async function demoLoader() {
570
644
  }),
571
645
  );
572
646
 
647
+ /** @type {string[]} */
573
648
  const componentNames = Array.from(
574
649
  new Set(
575
- docs.en.rendered
576
- .filter((markdownOrComponentConfig) => {
650
+ /** @type {import('./prepareMarkdown.mjs').ComponentEntry[]} */ (
651
+ docs.en.rendered.filter((markdownOrComponentConfig) => {
577
652
  return (
578
653
  typeof markdownOrComponentConfig !== 'string' && markdownOrComponentConfig.component
579
654
  );
580
655
  })
581
- .map((componentConfig) => {
582
- return componentConfig.component;
583
- }),
656
+ ).map((componentConfig) => {
657
+ return componentConfig.component;
658
+ }),
584
659
  ),
585
660
  );
586
661
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/internal-markdown",
3
- "version": "2.0.11",
3
+ "version": "2.0.13",
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.mjs",
@@ -22,12 +22,12 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@babel/runtime": "^7.28.4",
25
- "lodash": "^4.17.21",
26
- "marked": "^16.3.0",
25
+ "es-toolkit": "^1.41.0",
26
+ "marked": "^17.0.1",
27
27
  "prismjs": "^1.30.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/chai": "^4.3.20",
30
+ "@types/chai": "^5.2.3",
31
31
  "chai": "^6.0.1"
32
32
  },
33
33
  "publishConfig": {
@@ -283,6 +283,7 @@ title: "Our docs just got a major upgrade—here's what that means for you"
283
283
  describe('createRender', () => {
284
284
  it('should collect headers correctly', () => {
285
285
  const context = { toc: [], headingHashes: {} };
286
+ // eslint-disable-next-line testing-library/render-result-naming-convention
286
287
  const render = createRender(context);
287
288
 
288
289
  expect(
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import kebabCase from 'lodash/kebabCase.js';
3
+ import { kebabCase } from 'es-toolkit/string';
4
4
  import {
5
5
  createRender,
6
6
  getContents,
@@ -11,6 +11,9 @@ import {
11
11
  getTitle,
12
12
  } from './parseMarkdown.mjs';
13
13
 
14
+ /**
15
+ * @type {string | string[]}
16
+ */
14
17
  const BaseUIReexportedComponents = [];
15
18
 
16
19
  /**
@@ -47,17 +50,30 @@ function resolveComponentApiUrl(productId, componentPkg, component) {
47
50
  return `/${productId}/api/${kebabCase(component)}/`;
48
51
  }
49
52
 
53
+ /**
54
+ * @typedef {{ component: string, demo?: undefined }} ComponentEntry
55
+ * @typedef {{ component?: undefined, demo: string, hideToolbar?: boolean }} DemoEntry
56
+ */
57
+
58
+ /**
59
+ * @typedef {{ rendered: Array<string | ComponentEntry | DemoEntry> }} TranslatedDoc
60
+ */
61
+
50
62
  /**
51
63
  * @param {object} config
52
64
  * @param {Array<{ markdown: string, filename: string, userLanguage: string }>} config.translations - Mapping of locale to its markdown
53
65
  * @param {string} config.fileRelativeContext - posix filename relative to repository root directory
54
66
  * @param {object} config.options - provided to the webpack loader
67
+ * @param {string} config.options.workspaceRoot - The absolute path of the repository root directory
68
+ * @param {object} [config.componentPackageMapping] - Mapping of productId to mapping of component name to package name
69
+ * @example { 'material': { 'Button': 'mui-material' } }
70
+ * @returns {{ docs: Record<string, TranslatedDoc> }} - Mapping of locale to its prepared markdown
55
71
  */
56
72
  function prepareMarkdown(config) {
57
73
  const { fileRelativeContext, translations, componentPackageMapping = {}, options } = config;
58
74
 
59
75
  /**
60
- * @type {Record<string, { rendered: Array<string | { component: string } | { demo:string }> }>}
76
+ * @type {Record<string, TranslatedDoc>}
61
77
  */
62
78
  const docs = {};
63
79
  const headingHashes = {};
@@ -17,6 +17,7 @@ describe('textToHash', () => {
17
17
  ];
18
18
  table.forEach((entry, index) => {
19
19
  const [markdown, expected] = entry;
20
+ // eslint-disable-next-line testing-library/render-result-naming-convention
20
21
  const text = renderInlineMarkdown(markdown, { mangle: false, headerIds: false });
21
22
  const actual = textToHash(text);
22
23
 
@@ -0,0 +1,4 @@
1
+ // eslint-disable-next-line import/no-relative-packages
2
+ import sharedConfig from '../../vitest.shared.mts';
3
+
4
+ export default sharedConfig(import.meta.url);