@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 ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ First release as an npm package.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Call-Em-All
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # @mui-internal/markdown
2
+
3
+ MUI markdown parser and webpack loader.
4
+ This is an internal package not meant for general use.
5
+
6
+ ## Release
7
+
8
+ There is no build step.
9
+ Sources are meant to be used directly.
10
+
11
+ To publish the build artifacts to npm run `pnpm release:publish`.
@@ -0,0 +1,8 @@
1
+ const importModuleRegexp =
2
+ /^\s*import (?:["'\s]*(?:[\w*{}\n, ]+)from\s*)?["'\s]*([^"'{}$\s]+)["'\s].*/gm;
3
+
4
+ function extractImports(code) {
5
+ return (code.match(importModuleRegexp) || []).map((x) => x.replace(importModuleRegexp, '$1'));
6
+ }
7
+
8
+ module.exports = extractImports;
@@ -0,0 +1,39 @@
1
+ import { expect } from 'chai';
2
+ import extractImports from './extractImports';
3
+
4
+ describe('extractImports', () => {
5
+ it('finds all imports', () => {
6
+ const imports = extractImports(`
7
+ import {
8
+ Component
9
+ } from '@angular2/core';
10
+ import defaultMember from "module-1";
11
+ import * as name from "module-2 ";
12
+ import { member } from " module-3";
13
+ import { member as alias } from "module-4";
14
+ import { member1 ,
15
+ member2 } from "module-5";
16
+ import { member1 , member2 as alias2 , member3 as alias3 } from "module-6";
17
+ import defaultMember, { member, member } from "module-7";
18
+ import defaultMember, * as name from "module-8";
19
+ import "module-9";
20
+ import "module-10";
21
+ import * from './smdn';
22
+ import \${importName} from 'module11/\${importName}';
23
+ `);
24
+
25
+ expect(imports[0]).to.equal('@angular2/core');
26
+ expect(imports[1]).to.equal('module-1');
27
+ expect(imports[2]).to.equal('module-2');
28
+ expect(imports[3]).to.equal('module-3');
29
+ expect(imports[4]).to.equal('module-4');
30
+ expect(imports[5]).to.equal('module-5');
31
+ expect(imports[6]).to.equal('module-6');
32
+ expect(imports[7]).to.equal('module-7');
33
+ expect(imports[8]).to.equal('module-8');
34
+ expect(imports[9]).to.equal('module-9');
35
+ expect(imports[10]).to.equal('module-10');
36
+ expect(imports[11]).to.equal('./smdn');
37
+ expect(imports[12]).to.equal(undefined); // It's not a valid import
38
+ });
39
+ });
package/index.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ interface TableOfContentsEntry {
2
+ children: TableOfContentsEntry;
3
+ hash: string;
4
+ level: number;
5
+ text: string;
6
+ }
7
+
8
+ export function createRender(context: {
9
+ headingHashes: Record<string, string>;
10
+ toc: TableOfContentsEntry[];
11
+ userLanguage: string;
12
+ ignoreLanguagePages: (path: string) => boolean;
13
+ }): (markdown: string) => string;
14
+
15
+ export function getHeaders(markdown: string): Record<string, string | string[]>;
16
+
17
+ export function getTitle(markdown: string): string;
18
+
19
+ export function renderMarkdown(markdown: string): string;
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ const { createRender, getHeaders, getTitle, renderMarkdown } = require('./parseMarkdown');
2
+
3
+ module.exports = { createRender, getHeaders, getTitle, renderMarkdown };
package/loader.js ADDED
@@ -0,0 +1,415 @@
1
+ const { promises: fs, readdirSync } = require('fs');
2
+ const path = require('path');
3
+ const prepareMarkdown = require('./prepareMarkdown');
4
+ const extractImports = require('./extractImports');
5
+
6
+ const notEnglishMarkdownRegExp = /-([a-z]{2})\.md$/;
7
+
8
+ /**
9
+ * @param {string} string
10
+ */
11
+ function upperCaseFirst(string) {
12
+ return `${string[0].toUpperCase()}${string.slice(1)}`;
13
+ }
14
+
15
+ /**
16
+ * @param {string} moduleID
17
+ * @example moduleIDToJSIdentifier('./Box.js') === '$$IndexJs'
18
+ * @example moduleIDToJSIdentifier('./Box-new.js') === '$$BoxNewJs'
19
+ * @example moduleIDToJSIdentifier('../Box-new.js') === '$$$BoxNewJs'
20
+ */
21
+ function moduleIDToJSIdentifier(moduleID) {
22
+ const delimiter = /(\.|-|\/|:)/;
23
+ return moduleID
24
+ .split(delimiter)
25
+ .filter((part) => !delimiter.test(part))
26
+ .map((part) => (part.length === 0 ? '$' : part))
27
+ .map(upperCaseFirst)
28
+ .join('');
29
+ }
30
+
31
+ let componentPackageMapping = null;
32
+
33
+ function findComponents(packages) {
34
+ const mapping = {};
35
+
36
+ packages.forEach((pkg) => {
37
+ pkg.paths.forEach((pkgPath) => {
38
+ const match = pkgPath.match(/packages(?:\\|\/)([^/\\]+)(?:\\|\/)src/);
39
+ const packageName = match ? match[1] : null;
40
+ if (!packageName) {
41
+ throw new Error(`cannot find package name from path: ${pkgPath}`);
42
+ }
43
+ const filePaths = readdirSync(pkgPath);
44
+ filePaths.forEach((folder) => {
45
+ if (folder.match(/^[A-Z]/)) {
46
+ if (!mapping[pkg.productId]) {
47
+ mapping[pkg.productId] = {};
48
+ }
49
+ // filename starts with Uppercase = component
50
+ mapping[pkg.productId][folder] = packageName;
51
+ }
52
+ });
53
+ });
54
+ });
55
+
56
+ return mapping;
57
+ }
58
+
59
+ /**
60
+ * @type {import('webpack').loader.Loader}
61
+ */
62
+ module.exports = async function demoLoader() {
63
+ const englishFilepath = this.resourcePath;
64
+ const options = this.getOptions();
65
+
66
+ if (componentPackageMapping === null) {
67
+ componentPackageMapping = findComponents(options.packages ?? []);
68
+ }
69
+
70
+ const englishFilename = path.basename(englishFilepath, '.md');
71
+
72
+ const files = await fs.readdir(path.dirname(englishFilepath));
73
+ const translations = await Promise.all(
74
+ files
75
+ .map((filename) => {
76
+ if (filename === `${englishFilename}.md`) {
77
+ return {
78
+ filename,
79
+ userLanguage: 'en',
80
+ };
81
+ }
82
+
83
+ const matchNotEnglishMarkdown = filename.match(notEnglishMarkdownRegExp);
84
+
85
+ if (
86
+ filename.startsWith(englishFilename) &&
87
+ matchNotEnglishMarkdown !== null &&
88
+ options.languagesInProgress.indexOf(matchNotEnglishMarkdown[1]) !== -1
89
+ ) {
90
+ return {
91
+ filename,
92
+ userLanguage: matchNotEnglishMarkdown[1],
93
+ };
94
+ }
95
+
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
+ }),
109
+ );
110
+
111
+ // Use .. as the docs runs from the /docs folder
112
+ const fileRelativeContext = path
113
+ .relative(options.workspaceRoot, this.context)
114
+ // win32 to posix
115
+ .replace(/\\/g, '/');
116
+
117
+ const { docs } = prepareMarkdown({
118
+ fileRelativeContext,
119
+ translations,
120
+ componentPackageMapping,
121
+ options,
122
+ });
123
+
124
+ const demos = {};
125
+ const importedModuleIDs = new Set();
126
+ const components = {};
127
+ const demoModuleIDs = new Set();
128
+ const componentModuleIDs = new Set();
129
+ const demoNames = Array.from(
130
+ new Set(
131
+ docs.en.rendered
132
+ .filter((markdownOrComponentConfig) => {
133
+ return typeof markdownOrComponentConfig !== 'string' && markdownOrComponentConfig.demo;
134
+ })
135
+ .map((demoConfig) => {
136
+ return demoConfig.demo;
137
+ }),
138
+ ),
139
+ );
140
+
141
+ await Promise.all(
142
+ demoNames.map(async (demoName) => {
143
+ const multipleDemoVersionsUsed = !demoName.endsWith('.js');
144
+
145
+ // TODO: const moduleID = demoName;
146
+ // The import paths currently use a completely different format.
147
+ // They should just use relative imports.
148
+ let moduleID = `./${demoName.replace(
149
+ `pages/${fileRelativeContext.replace(/^docs\/src\/pages\//, '')}/`,
150
+ '',
151
+ )}`;
152
+
153
+ if (multipleDemoVersionsUsed) {
154
+ moduleID = `${moduleID}/system/index.js`;
155
+ }
156
+
157
+ const moduleFilepath = path.join(
158
+ path.dirname(this.resourcePath),
159
+ moduleID.replace(/\//g, path.sep),
160
+ );
161
+ this.addDependency(moduleFilepath);
162
+ demos[demoName] = {
163
+ module: moduleID,
164
+ raw: await fs.readFile(moduleFilepath, { encoding: 'utf8' }),
165
+ };
166
+ demoModuleIDs.add(moduleID);
167
+ extractImports(demos[demoName].raw).forEach((importModuleID) =>
168
+ importedModuleIDs.add(importModuleID),
169
+ );
170
+
171
+ if (multipleDemoVersionsUsed) {
172
+ // Add Tailwind demo data
173
+ const tailwindModuleID = moduleID.replace('/system/index.js', '/tailwind/index.js');
174
+ try {
175
+ // Add JS demo data
176
+ const tailwindModuleFilepath = path.join(
177
+ path.dirname(this.resourcePath),
178
+ tailwindModuleID.replace(/\//g, path.sep),
179
+ );
180
+
181
+ demos[demoName].moduleTailwind = tailwindModuleID;
182
+ demos[demoName].rawTailwind = await fs.readFile(tailwindModuleFilepath, {
183
+ encoding: 'utf8',
184
+ });
185
+
186
+ this.addDependency(tailwindModuleFilepath);
187
+
188
+ demoModuleIDs.add(tailwindModuleID);
189
+
190
+ extractImports(demos[demoName].rawTailwind).forEach((importModuleID) =>
191
+ importedModuleIDs.add(importModuleID),
192
+ );
193
+
194
+ demoModuleIDs.add(demos[demoName].moduleTailwind);
195
+ } catch (error) {
196
+ // tailwind js demo doesn't exists
197
+ }
198
+
199
+ try {
200
+ // Add TS demo data
201
+ const tailwindTSModuleID = tailwindModuleID.replace('.js', '.tsx');
202
+
203
+ const tailwindTSModuleFilepath = path.join(
204
+ path.dirname(this.resourcePath),
205
+ tailwindTSModuleID.replace(/\//g, path.sep),
206
+ );
207
+
208
+ demos[demoName].moduleTSTailwind = tailwindTSModuleID;
209
+ demos[demoName].rawTailwindTS = await fs.readFile(tailwindTSModuleFilepath, {
210
+ encoding: 'utf8',
211
+ });
212
+
213
+ this.addDependency(tailwindTSModuleFilepath);
214
+
215
+ demoModuleIDs.add(tailwindTSModuleID);
216
+
217
+ extractImports(demos[demoName].rawTailwindTS).forEach((importModuleID) =>
218
+ importedModuleIDs.add(importModuleID),
219
+ );
220
+
221
+ demoModuleIDs.add(demos[demoName].moduleTSTailwind);
222
+ } catch (error) {
223
+ // tailwind TS demo doesn't exists
224
+ }
225
+
226
+ // Add plain CSS demo data
227
+ const cssModuleID = moduleID.replace('/system/index.js', '/css/index.js');
228
+ try {
229
+ // Add JS demo data
230
+ const cssModuleFilepath = path.join(
231
+ path.dirname(this.resourcePath),
232
+ cssModuleID.replace(/\//g, path.sep),
233
+ );
234
+
235
+ demos[demoName].moduleCSS = cssModuleID;
236
+ demos[demoName].rawCSS = await fs.readFile(cssModuleFilepath, {
237
+ encoding: 'utf8',
238
+ });
239
+
240
+ this.addDependency(cssModuleFilepath);
241
+
242
+ demoModuleIDs.add(cssModuleID);
243
+
244
+ extractImports(demos[demoName].rawCSS).forEach((importModuleID) =>
245
+ importedModuleIDs.add(importModuleID),
246
+ );
247
+
248
+ demoModuleIDs.add(demos[demoName].moduleCSS);
249
+ } catch (error) {
250
+ // plain css js demo doesn't exists
251
+ }
252
+
253
+ try {
254
+ // Add TS demo data
255
+ const cssTSModuleID = cssModuleID.replace('.js', '.tsx');
256
+
257
+ const cssTSModuleFilepath = path.join(
258
+ path.dirname(this.resourcePath),
259
+ cssTSModuleID.replace(/\//g, path.sep),
260
+ );
261
+
262
+ demos[demoName].moduleTSCSS = cssTSModuleID;
263
+ demos[demoName].rawCSSTS = await fs.readFile(cssTSModuleFilepath, {
264
+ encoding: 'utf8',
265
+ });
266
+
267
+ this.addDependency(cssTSModuleFilepath);
268
+
269
+ demoModuleIDs.add(cssTSModuleID);
270
+
271
+ extractImports(demos[demoName].rawCSSTS).forEach((importModuleID) =>
272
+ importedModuleIDs.add(importModuleID),
273
+ );
274
+
275
+ demoModuleIDs.add(demos[demoName].moduleTSCSS);
276
+ } catch (error) {
277
+ // plain css demo doesn't exists
278
+ }
279
+
280
+ // Tailwind preview
281
+ try {
282
+ const tailwindPreviewFilepath = moduleFilepath.replace(
283
+ `${path.sep}system${path.sep}index.js`,
284
+ `${path.sep}tailwind${path.sep}index.tsx.preview`,
285
+ );
286
+
287
+ const tailwindJsxPreview = await fs.readFile(tailwindPreviewFilepath, {
288
+ encoding: 'utf8',
289
+ });
290
+ this.addDependency(tailwindPreviewFilepath);
291
+
292
+ demos[demoName].tailwindJsxPreview = tailwindJsxPreview;
293
+ } catch (error) {
294
+ // No preview exists. This is fine.
295
+ }
296
+
297
+ // CSS preview
298
+ try {
299
+ const cssPreviewFilepath = moduleFilepath.replace(
300
+ `${path.sep}system${path.sep}index.js`,
301
+ `${path.sep}css${path.sep}index.tsx.preview`,
302
+ );
303
+
304
+ const cssJsxPreview = await fs.readFile(cssPreviewFilepath, {
305
+ encoding: 'utf8',
306
+ });
307
+ this.addDependency(cssPreviewFilepath);
308
+
309
+ demos[demoName].cssJsxPreview = cssJsxPreview;
310
+ } catch (error) {
311
+ // No preview exists. This is fine.
312
+ }
313
+ }
314
+
315
+ try {
316
+ const previewFilepath = moduleFilepath.replace(/\.js$/, '.tsx.preview');
317
+
318
+ const jsxPreview = await fs.readFile(previewFilepath, { encoding: 'utf8' });
319
+ this.addDependency(previewFilepath);
320
+
321
+ demos[demoName].jsxPreview = jsxPreview;
322
+ } catch (error) {
323
+ // No preview exists. This is fine.
324
+ }
325
+
326
+ try {
327
+ const moduleTS = moduleID.replace(/\.js$/, '.tsx');
328
+ const moduleTSFilepath = path.join(
329
+ path.dirname(this.resourcePath),
330
+ moduleTS.replace(/\//g, path.sep),
331
+ );
332
+ this.addDependency(moduleTSFilepath);
333
+ const rawTS = await fs.readFile(moduleTSFilepath, { encoding: 'utf8' });
334
+
335
+ // In development devs can choose whether they want to work on the TS or JS version.
336
+ // But this leads to building both demo version i.e. more build time.
337
+ demos[demoName].moduleTS = this.mode === 'production' ? moduleID : moduleTS;
338
+ demos[demoName].rawTS = rawTS;
339
+ demoModuleIDs.add(demos[demoName].moduleTS);
340
+ } catch (error) {
341
+ // TS version of the demo doesn't exist. This is fine.
342
+ }
343
+ }),
344
+ );
345
+
346
+ const componentNames = Array.from(
347
+ new Set(
348
+ docs.en.rendered
349
+ .filter((markdownOrComponentConfig) => {
350
+ return (
351
+ typeof markdownOrComponentConfig !== 'string' && markdownOrComponentConfig.component
352
+ );
353
+ })
354
+ .map((componentConfig) => {
355
+ return componentConfig.component;
356
+ }),
357
+ ),
358
+ );
359
+
360
+ componentNames.forEach((componentName) => {
361
+ const moduleID = path.join(this.rootContext, 'src', componentName).replace(/\\/g, '/');
362
+
363
+ components[moduleID] = componentName;
364
+ componentModuleIDs.add(moduleID);
365
+ });
366
+
367
+ const transformed = `
368
+ ${Array.from(importedModuleIDs)
369
+ .map((moduleID) => {
370
+ return `import * as ${moduleIDToJSIdentifier(
371
+ moduleID.replace('@', '$'),
372
+ )} from '${moduleID}';`;
373
+ })
374
+ .join('\n')}
375
+
376
+ ${Array.from(demoModuleIDs)
377
+ .map((moduleID) => {
378
+ return `import ${moduleIDToJSIdentifier(moduleID)} from '${moduleID}';`;
379
+ })
380
+ .join('\n')}
381
+ ${Array.from(componentModuleIDs)
382
+ .map((moduleID) => {
383
+ return `import ${moduleIDToJSIdentifier(moduleID)} from '${moduleID}';`;
384
+ })
385
+ .join('\n')}
386
+ export const docs = ${JSON.stringify(docs, null, 2)};
387
+ export const demos = ${JSON.stringify(demos, null, 2)};
388
+
389
+ demos.scope = {
390
+ process: {},
391
+ import: {
392
+ ${Array.from(importedModuleIDs)
393
+ .map((moduleID) => ` "${moduleID}": ${moduleIDToJSIdentifier(moduleID.replace('@', '$'))},`)
394
+ .join('\n')}
395
+ },
396
+ };
397
+
398
+ export const demoComponents = {
399
+ ${Array.from(demoModuleIDs)
400
+ .map((moduleID) => {
401
+ return ` "${moduleID}": ${moduleIDToJSIdentifier(moduleID)},`;
402
+ })
403
+ .join('\n')}
404
+ };
405
+ export const srcComponents = {
406
+ ${Array.from(componentModuleIDs)
407
+ .map((moduleID) => {
408
+ return ` "${components[moduleID]}": ${moduleIDToJSIdentifier(moduleID)},`;
409
+ })
410
+ .join('\n')}
411
+ };
412
+ `;
413
+
414
+ return transformed;
415
+ };
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@mui/internal-markdown",
3
+ "version": "1.0.0",
4
+ "author": "MUI Team",
5
+ "description": "MUI markdown parser. This is an internal package not meant for general use.",
6
+ "main": "./index.js",
7
+ "types": "./index.d.ts",
8
+ "exports": {
9
+ ".": "./index.js",
10
+ "./loader": "./loader.js",
11
+ "./prism": "./prism.js"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/mui/material-ui.git",
16
+ "directory": "packages/docs-utils"
17
+ },
18
+ "dependencies": {
19
+ "@babel/runtime": "^7.23.9",
20
+ "lodash": "^4.17.21",
21
+ "marked": "^5.1.2",
22
+ "prismjs": "^1.29.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/chai": "^4.3.11",
26
+ "chai": "^4.4.1"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "scripts": {
32
+ "release:publish": "pnpm publish --tag latest",
33
+ "release:publish:dry-run": "pnpm publish --tag latest --registry=\"http://localhost:4873/\""
34
+ }
35
+ }