@xyd-js/content 0.1.0-xyd.13 → 0.1.0-xyd.16

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.
Files changed (89) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/ISSUES.md +1 -0
  3. package/TODO.md +2 -0
  4. package/dist/index.d.ts +24 -5
  5. package/dist/index.js +1411 -21776
  6. package/dist/index.js.map +1 -1
  7. package/dist/md.d.ts +62 -7
  8. package/dist/md.js +18071 -15085
  9. package/dist/md.js.map +1 -1
  10. package/dist/{mdToc-CYxzibVZ.d.ts → mdToc-NBBxMJ4l.d.ts} +1 -0
  11. package/dist/vite.d.ts +81 -2
  12. package/dist/vite.js +9716 -10090
  13. package/dist/vite.js.map +1 -1
  14. package/example.txt +0 -0
  15. package/package.json +24 -6
  16. package/packages/md/index.ts +17 -8
  17. package/packages/md/plugins/component-directives/index.ts +3 -0
  18. package/packages/md/plugins/component-directives/mdComponentDirective.ts +524 -0
  19. package/packages/md/plugins/component-directives/types.ts +1 -0
  20. package/packages/md/plugins/component-directives/utils.ts +27 -0
  21. package/packages/md/plugins/composer/__fixtures__/1.single-example/input.md +7 -0
  22. package/packages/md/plugins/composer/__fixtures__/1.single-example/output.json +63 -0
  23. package/packages/md/plugins/composer/__fixtures__/2.single-example-with-name/input.md +7 -0
  24. package/packages/md/plugins/composer/__fixtures__/2.single-example-with-name/output.json +63 -0
  25. package/packages/md/plugins/composer/__fixtures__/3.multiple-examples/input.md +15 -0
  26. package/packages/md/plugins/composer/__fixtures__/3.multiple-examples/output.json +122 -0
  27. package/packages/md/plugins/composer/__fixtures__/4.example-groups/input.md +23 -0
  28. package/packages/md/plugins/composer/__fixtures__/4.example-groups/output.json +184 -0
  29. package/packages/md/plugins/composer/__tests__/mdComposer.test.ts +41 -0
  30. package/packages/md/plugins/composer/__tests__/testHelpers.ts +48 -0
  31. package/packages/md/plugins/composer/index.ts +1 -0
  32. package/packages/md/plugins/composer/mdComposer.ts +146 -0
  33. package/packages/md/plugins/developer-writing/index.ts +3 -0
  34. package/packages/md/plugins/developer-writing/mdCodeRehype.ts +78 -0
  35. package/packages/md/plugins/functions/__fixtures__/external.ts +4 -0
  36. package/packages/md/plugins/functions/__fixtures__/test.js +11 -0
  37. package/packages/md/plugins/functions/__fixtures__/test.py +9 -0
  38. package/packages/md/plugins/functions/__fixtures__/test.ts +18 -0
  39. package/packages/md/plugins/functions/__tests__/mdFunctionImportCode.test.ts +295 -0
  40. package/packages/md/plugins/functions/__tests__/parseFunctionCall.test.ts +47 -0
  41. package/packages/md/plugins/functions/__tests__/testHelpers.ts +71 -0
  42. package/packages/md/plugins/functions/index.ts +11 -0
  43. package/packages/md/plugins/functions/mdFunctionChangelog.ts +124 -0
  44. package/packages/md/plugins/functions/mdFunctionImportCode.ts +83 -0
  45. package/packages/md/plugins/functions/mdFunctionUniform.ts +79 -0
  46. package/packages/md/plugins/functions/types.ts +6 -0
  47. package/packages/md/plugins/functions/uniformProcessor.ts +349 -0
  48. package/packages/md/plugins/functions/utils.ts +423 -0
  49. package/packages/md/plugins/index.ts +56 -11
  50. package/packages/md/plugins/mdCode.ts +52 -4
  51. package/packages/md/plugins/mdHeadingId.ts +47 -0
  52. package/packages/md/plugins/mdPage.ts +3 -0
  53. package/packages/md/plugins/mdThemeSettings.ts +4 -0
  54. package/packages/md/plugins/mdToc.ts +108 -17
  55. package/packages/md/plugins/meta/index.ts +1 -0
  56. package/packages/md/plugins/meta/mdMeta.ts +189 -0
  57. package/packages/md/plugins/output-variables/__fixtures__/1.simple/input.md +22 -0
  58. package/packages/md/plugins/output-variables/__fixtures__/1.simple/output.json +191 -0
  59. package/packages/md/plugins/output-variables/__fixtures__/2.multiple-vars/input.md +21 -0
  60. package/packages/md/plugins/output-variables/__fixtures__/2.multiple-vars/output.json +127 -0
  61. package/packages/md/plugins/output-variables/__tests__/index.test.ts +28 -0
  62. package/packages/md/plugins/output-variables/__tests__/testHelpers.ts +36 -0
  63. package/packages/md/plugins/output-variables/index.ts +1 -0
  64. package/packages/md/plugins/output-variables/lib/const.ts +4 -0
  65. package/packages/md/plugins/output-variables/lib/factoryAttributes.ts +350 -0
  66. package/packages/md/plugins/output-variables/lib/factoryLabel.ts +135 -0
  67. package/packages/md/plugins/output-variables/lib/factoryName.ts +59 -0
  68. package/packages/md/plugins/output-variables/lib/index.ts +21 -0
  69. package/packages/md/plugins/output-variables/lib/outputVarsContainer.ts +328 -0
  70. package/packages/md/plugins/output-variables/lib/util.ts +494 -0
  71. package/packages/md/plugins/output-variables/remarkOutputVars.ts +22 -0
  72. package/packages/md/plugins/rehypeHeading.ts +50 -0
  73. package/packages/md/plugins/types.ts +15 -0
  74. package/packages/md/plugins/utils/componentLike.ts +72 -0
  75. package/packages/md/plugins/utils/index.ts +2 -0
  76. package/packages/md/plugins/utils/mdParameters.test.ts +114 -0
  77. package/packages/md/plugins/utils/mdParameters.ts +249 -0
  78. package/packages/md/plugins/utils/mdastTypes.ts +42 -0
  79. package/packages/md/search/index.ts +251 -0
  80. package/packages/md/search/types.ts +36 -0
  81. package/packages/vite/index.ts +8 -2
  82. package/src/fs.ts +51 -36
  83. package/src/index.ts +4 -4
  84. package/src/navigation.ts +50 -38
  85. package/src/types.ts +8 -0
  86. package/tsconfig.json +31 -8
  87. package/vitest.config.ts +17 -0
  88. package/packages/md/plugins/mdCodeGroup.ts +0 -40
  89. package/packages/md/plugins/mdComponentDirective.ts +0 -141
@@ -0,0 +1,423 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import {VFile} from 'vfile';
4
+
5
+ import {Settings} from '@xyd-js/core';
6
+
7
+ /**
8
+ * Common options for all function plugins
9
+ */
10
+ export interface FunctionOptions {
11
+ resolveFrom?: string;
12
+ }
13
+
14
+ /**
15
+ * Parse a function call with arguments
16
+ * @param node The AST node to parse
17
+ * @param functionName The name of the function to look for
18
+ */
19
+ export function parseFunctionCall<args = any>(node: any, functionName: string): [string, args] | null {
20
+ function resp(args: any) {
21
+ const response: any[] = []
22
+
23
+ response.push(args[0])
24
+ let jsonArgs = undefined;
25
+ let argsString = args[1]
26
+ if (argsString) {
27
+ try {
28
+ argsString = argsString
29
+ .replace(/'/g, '"')
30
+ .replace(/(\w+):/g, '"$1":');
31
+
32
+ jsonArgs = JSON.parse(argsString);
33
+ response.push(jsonArgs);
34
+ } catch (e) {
35
+ }
36
+ }
37
+
38
+ return response as [string, any];
39
+ }
40
+
41
+ // Check for the simple case with a single text node
42
+ if (node.children && node.children.length === 1 && node.children[0].type === 'text') {
43
+ const textNode = node.children[0];
44
+
45
+ // Check for parentheses syntax with multiple arguments
46
+ const parenthesesMatch = textNode.value.match(new RegExp(`^${functionName}\\((.*)\\)$`));
47
+ if (parenthesesMatch) {
48
+ const argsText = parenthesesMatch[1];
49
+ // Split by comma and trim each argument
50
+ const args = argsText.split(',').map((arg: string) => arg.trim().replace(/^["']|["']$/g, ''));
51
+ // Return the first argument as the path
52
+
53
+ return resp(args)
54
+ }
55
+
56
+ // Check for the original syntax
57
+ const originalMatch = textNode.value.match(new RegExp(`^${functionName}\\s+['"](.*)['"]$`));
58
+ if (originalMatch) {
59
+ return resp([originalMatch[1], originalMatch[2]]);
60
+ }
61
+ }
62
+
63
+ // Check for the complex case with multiple nodes
64
+ if (!node || !node.children || node.children.length < 3) {
65
+ return null;
66
+ }
67
+
68
+ // Check if the first node contains the function part
69
+ const firstNode = node.children[0];
70
+ const middleNode = node.children[1];
71
+ const lastNode = node.children[2];
72
+
73
+ if (firstNode.type === 'text' &&
74
+ firstNode.value.startsWith(`${functionName} "`) &&
75
+ middleNode.type === 'link' &&
76
+ lastNode.type === 'text' &&
77
+ lastNode.value === '"') {
78
+
79
+ // We found a split command, extract the URL from the link node
80
+ const url = middleNode.url;
81
+
82
+ return resp([url]) // Create a match array with the URL in position 1
83
+ }
84
+
85
+ // Check for parentheses syntax with multiple arguments
86
+ if (firstNode.type === 'text' &&
87
+ firstNode.value.startsWith(`${functionName}(`) &&
88
+ lastNode.type === 'text' &&
89
+ lastNode.value === ')') {
90
+
91
+ // Extract the arguments from the middle node
92
+ if (middleNode.type === 'text') {
93
+ // Simple case: all arguments in a single text node
94
+ const argsText = middleNode.value;
95
+ // Split by comma and trim each argument
96
+ const args = argsText.split(',').map((arg: string) => arg.trim().replace(/^["']|["']$/g, ''));
97
+ // Return the first argument as the path
98
+ return resp(args);
99
+ } else if (middleNode.type === 'link') {
100
+ // Case with a link node (for URLs)
101
+ const url = middleNode.url;
102
+ return resp([url]);
103
+ }
104
+ }
105
+
106
+ return null;
107
+ }
108
+
109
+ /**
110
+ * Parse an import path to extract file path, regions, and line ranges
111
+ */
112
+ export function parseImportPath(importPath: string): {
113
+ filePath: string;
114
+ regions: Region[];
115
+ lineRanges: LineRange[]
116
+ } {
117
+ // Initialize result
118
+ const result = {
119
+ filePath: importPath,
120
+ regions: [] as Region[],
121
+ lineRanges: [] as LineRange[]
122
+ };
123
+
124
+ // First, handle line ranges in the main path (not in regions)
125
+ // Only match line ranges that contain numbers
126
+ const mainLineRangeMatch = result.filePath.match(/\{([0-9,\s:-]+)\}/);
127
+ if (mainLineRangeMatch) {
128
+ const lineRangeStr = mainLineRangeMatch[1];
129
+ result.filePath = result.filePath.replace(/\{[0-9,\s:-]+\}/, '');
130
+
131
+ // Parse line ranges like "1,2-4, 8:, :10"
132
+ const rangeParts = lineRangeStr.split(',').map(part => part.trim());
133
+
134
+ for (const part of rangeParts) {
135
+ if (part.includes('-')) {
136
+ // Range like "2-4"
137
+ const [start, end] = part.split('-').map(num => parseInt(num, 10));
138
+ result.lineRanges.push({start, end});
139
+ } else if (part.endsWith(':')) {
140
+ // Range like "8:"
141
+ const start = parseInt(part.replace(':', ''), 10);
142
+ result.lineRanges.push({start});
143
+ } else if (part.startsWith(':')) {
144
+ // Range like ":10"
145
+ const end = parseInt(part.replace(':', ''), 10);
146
+ result.lineRanges.push({end});
147
+ } else {
148
+ // Single line like "1"
149
+ const line = parseInt(part, 10);
150
+ result.lineRanges.push({start: line, end: line});
151
+ }
152
+ }
153
+ }
154
+
155
+ // Then, handle regions
156
+ const hashIndex = result.filePath.indexOf('#');
157
+ if (hashIndex !== -1) {
158
+ // Split the path at the hash
159
+ const basePath = result.filePath.substring(0, hashIndex);
160
+ const regionPart = result.filePath.substring(hashIndex + 1);
161
+
162
+ // Update the file path
163
+ result.filePath = basePath;
164
+
165
+ // Check if the region part contains a numeric line range
166
+ const regionLineRangeMatch = regionPart.match(/\{([0-9,\s:-]+)\}/);
167
+
168
+ if (regionLineRangeMatch) {
169
+ // If there are numeric line ranges in the region part, extract them
170
+ const regionLineRangeStr = regionLineRangeMatch[1];
171
+ const regionName = regionPart.replace(/\{[0-9,\s:-]+\}/, '').trim();
172
+
173
+ // Parse the line ranges
174
+ const rangeParts = regionLineRangeStr.split(',').map(part => part.trim());
175
+ const regionLineRanges: LineRange[] = [];
176
+
177
+ for (const part of rangeParts) {
178
+ if (part.includes('-')) {
179
+ // Range like "2-4"
180
+ const [start, end] = part.split('-').map(num => parseInt(num, 10));
181
+ regionLineRanges.push({start, end});
182
+ } else if (part.endsWith(':')) {
183
+ // Range like "8:"
184
+ const start = parseInt(part.replace(':', ''), 10);
185
+ regionLineRanges.push({start});
186
+ } else if (part.startsWith(':')) {
187
+ // Range like ":10"
188
+ const end = parseInt(part.replace(':', ''), 10);
189
+ regionLineRanges.push({end});
190
+ } else {
191
+ // Single line like "1"
192
+ const line = parseInt(part, 10);
193
+ regionLineRanges.push({start: line, end: line});
194
+ }
195
+ }
196
+
197
+ // Add the region with its line ranges
198
+ result.regions.push({name: regionName, lineRanges: regionLineRanges});
199
+ } else {
200
+ // If there are no numeric line ranges in the region part, check if it contains commas
201
+ if (regionPart.includes(',')) {
202
+ // Split by comma for multiple regions
203
+ const regionNames = regionPart.split(',');
204
+ for (const name of regionNames) {
205
+ result.regions.push({name: name.trim()});
206
+ }
207
+ } else {
208
+ // Single region
209
+ result.regions.push({name: regionPart.trim()});
210
+ }
211
+ }
212
+ }
213
+
214
+ return result;
215
+ }
216
+
217
+ /**
218
+ * Process content based on regions and line ranges
219
+ */
220
+ export function processContent(content: string, regions: Region[], lineRanges: LineRange[]): string {
221
+ const lines = content.split('\n');
222
+
223
+ // If no regions or line ranges specified, return the original content
224
+ if (regions.length === 0 && lineRanges.length === 0) {
225
+ return content;
226
+ }
227
+
228
+ // Process regions if present
229
+ if (regions.length > 0) {
230
+ const regionLines: string[] = [];
231
+
232
+ for (const region of regions) {
233
+ const regionStart = lines.findIndex(line => line.includes(`#region ${region.name}`));
234
+ const regionEnd = lines.findIndex(line => line.includes(`#endregion ${region.name}`));
235
+
236
+ if (regionStart !== -1 && regionEnd !== -1) {
237
+ // Only include the content between region markers, not the markers themselves
238
+ for (let i = regionStart + 1; i < regionEnd; i++) {
239
+ regionLines.push(lines[i]);
240
+ }
241
+ }
242
+ }
243
+
244
+ // If we found regions, return only the region content
245
+ if (regionLines.length > 0) {
246
+ return regionLines.join('\n');
247
+ }
248
+ }
249
+
250
+ // Process line ranges if present
251
+ if (lineRanges.length > 0) {
252
+ // Create a set of line numbers to include
253
+ const lineSet = new Set<number>();
254
+
255
+ for (const range of lineRanges) {
256
+ const start = range.start || 1;
257
+ const end = range.end || lines.length;
258
+
259
+ // Adjust for 0-based indexing
260
+ const startIndex = Math.max(0, start - 1);
261
+ const endIndex = Math.min(lines.length, end);
262
+
263
+ for (let i = startIndex; i < endIndex; i++) {
264
+ lineSet.add(i);
265
+ }
266
+ }
267
+
268
+ // Filter lines based on the set
269
+ const selectedLines = lines.filter((_, index) => lineSet.has(index));
270
+ return selectedLines.join('\n');
271
+ }
272
+
273
+ return content;
274
+ }
275
+
276
+ // Map common extensions to languages
277
+ const languageMap: Record<string, string> = {
278
+ 'js': 'javascript',
279
+ 'jsx': 'jsx',
280
+ 'ts': 'typescript',
281
+ 'tsx': 'tsx',
282
+ 'py': 'python',
283
+ 'rb': 'ruby',
284
+ 'java': 'java',
285
+ 'c': 'c',
286
+ 'cpp': 'cpp',
287
+ 'cs': 'csharp',
288
+ 'go': 'go',
289
+ 'rs': 'rust',
290
+ 'php': 'php',
291
+ 'swift': 'swift',
292
+ 'kt': 'kotlin',
293
+ 'scala': 'scala',
294
+ 'html': 'html',
295
+ 'css': 'css',
296
+ 'scss': 'scss',
297
+ 'less': 'less',
298
+ 'json': 'json',
299
+ 'xml': 'xml',
300
+ 'yaml': 'yaml',
301
+ 'yml': 'yaml',
302
+ 'md': 'markdown',
303
+ 'mdx': 'mdx',
304
+ 'sh': 'bash',
305
+ 'bash': 'bash',
306
+ 'sql': 'sql',
307
+ 'graphql': 'graphql',
308
+ 'vue': 'vue',
309
+ 'svelte': 'svelte',
310
+ };
311
+
312
+ /**
313
+ * Detect language from file extension
314
+ */
315
+ export function detectLanguage(filePath: string): string {
316
+ const extension = path.extname(filePath).toLowerCase().replace('.', '');
317
+
318
+ return languageMap[extension] || path.extname(filePath).split('.').pop() || '';
319
+ }
320
+
321
+ /**
322
+ * Read a local file
323
+ */
324
+ export function readLocalFile(filePath: string, baseDir: string): string {
325
+ // Handle "~/" prefix by replacing it with the current working directory
326
+ if (filePath.startsWith('~/')) {
327
+ filePath = filePath.replace('~/', process.cwd() + '/');
328
+ }
329
+
330
+ const fullPath = path.resolve(baseDir, filePath);
331
+ return fs.readFileSync(fullPath, 'utf8');
332
+ }
333
+
334
+ /**
335
+ * Fetch file content from a URL
336
+ */
337
+ export async function fetchFileContent(url: string): Promise<string> {
338
+ const response = await fetch(url);
339
+ if (!response.ok) {
340
+ throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
341
+ }
342
+ return response.text();
343
+ }
344
+
345
+ /**
346
+ * Interface for line range
347
+ */
348
+ export interface LineRange {
349
+ start?: number;
350
+ end?: number;
351
+ }
352
+
353
+ /**
354
+ * Interface for region
355
+ */
356
+ export interface Region {
357
+ name: string;
358
+ lineRanges?: LineRange[];
359
+ }
360
+
361
+
362
+ export async function downloadContent(
363
+ filePath: string,
364
+ file: VFile,
365
+ resolveFrom?: string,
366
+ ) {
367
+ const isExternal = filePath.startsWith('http://') || filePath.startsWith('https://');
368
+
369
+ let content: string;
370
+
371
+ if (isExternal) {
372
+ // Fetch external content
373
+ content = await fetchFileContent(filePath);
374
+ } else {
375
+ const baseDir = resolveFrom || (file.dirname || process.cwd());
376
+ content = readLocalFile(filePath, baseDir);
377
+ }
378
+
379
+ return content;
380
+ }
381
+
382
+ export function functionMatch(value: string, functionName: string): boolean {
383
+ return value.startsWith(functionName); // TODO: better function matching like args etc
384
+ }
385
+
386
+ /**
387
+ * Resolves a path alias if the given path is an alias defined in the settings.
388
+ * @param inputPath The path to resolve
389
+ * @param settings The settings object containing path aliases
390
+ * @param baseDir Optional base directory to resolve the final path from
391
+ * @returns The resolved path or the original path if no alias is found
392
+ */
393
+ export function resolvePathAlias(inputPath: string, settings?: Settings, baseDir?: string): string {
394
+ if (!settings?.engine?.paths) {
395
+ return inputPath;
396
+ }
397
+
398
+ // Find the longest matching alias
399
+ let resolvedPath = inputPath;
400
+ let longestMatch = '';
401
+
402
+ for (const [alias, aliasPaths] of Object.entries(settings.engine.paths)) {
403
+ // Convert alias pattern to regex, replacing * with .*
404
+ const aliasPattern = alias.replace(/\*/g, '.*');
405
+ const aliasRegex = new RegExp(`^${aliasPattern}`);
406
+
407
+ if (aliasRegex.test(inputPath) && alias.length > longestMatch.length) {
408
+ longestMatch = alias;
409
+ // Replace the alias with the first path from the array
410
+ const aliasPath = aliasPaths[0];
411
+ // Replace * in the alias path with the matched part from the input path
412
+ const matchedPart = inputPath.slice(alias.indexOf('*'));
413
+ resolvedPath = aliasPath.replace(/\*/g, matchedPart);
414
+ }
415
+ }
416
+
417
+ // If we found a match and have a base directory, resolve the path relative to it
418
+ if (longestMatch && baseDir) {
419
+ resolvedPath = path.resolve(baseDir, resolvedPath);
420
+ }
421
+
422
+ return resolvedPath;
423
+ }
@@ -3,24 +3,69 @@ import remarkMdxFrontmatter from "remark-mdx-frontmatter";
3
3
  import remarkGfm from "remark-gfm";
4
4
  import remarkDirective from 'remark-directive'
5
5
 
6
- import {remarkMdxToc, RemarkMdxTocOptions} from "./mdToc";
7
- import {remarkInjectCodeMeta} from "./mdCode";
8
- import {extractThemeSettings} from "./mdThemeSettings";
9
- import {extractPage} from "./mdPage";
10
- import {mdCodeGroup} from "./mdCodeGroup";
11
- import {remarkDirectiveWithMarkdown} from "./mdComponentDirective";
6
+ import { Settings } from "@xyd-js/core";
12
7
 
13
- export function defaultPlugins(toc: RemarkMdxTocOptions) {
8
+ import { remarkMdxToc, RemarkMdxTocOptions } from "./mdToc";
9
+ import { remarkInjectCodeMeta } from "./mdCode";
10
+ import { extractThemeSettings } from "./mdThemeSettings";
11
+ import { extractPage } from "./mdPage";
12
+ import { mdHeadingId } from "./mdHeadingId";
13
+ import { rehypeHeading } from "./rehypeHeading";
14
+ import { mdComponentDirective } from "./component-directives";
15
+ import { mdFunctionChangelog, mdFunctionImportCode, mdFunctionUniform } from "./functions"
16
+ import { mdCodeRehype } from "./developer-writing";
17
+ import { mdMeta } from "./meta";
18
+ import { mdComposer } from "./composer/mdComposer";
19
+ import { outputVars } from "./output-variables";
20
+
21
+ export function defaultRemarkPlugins(
22
+ toc: RemarkMdxTocOptions,
23
+ settings?: Settings
24
+ ) {
25
+ return [
26
+ ...thirdPartyRemarkPlugins(),
27
+ ...remarkPlugins(toc, settings),
28
+ ]
29
+ }
30
+
31
+ function thirdPartyRemarkPlugins() {
14
32
  return [
15
33
  remarkFrontmatter,
16
34
  remarkMdxFrontmatter,
17
35
  remarkGfm,
18
36
  remarkDirective,
19
- remarkMdxToc(toc),
37
+ ]
38
+ }
39
+
40
+ function remarkPlugins(
41
+ toc: RemarkMdxTocOptions,
42
+ settings?: Settings
43
+ ) {
44
+ return [
45
+ mdHeadingId,
20
46
  remarkInjectCodeMeta,
47
+ remarkMdxToc(toc),
21
48
  extractThemeSettings,
22
49
  extractPage,
23
- mdCodeGroup,
24
- remarkDirectiveWithMarkdown
50
+ outputVars,
51
+ mdComponentDirective(settings),
52
+ mdComposer(settings),
53
+ ...remarkFunctionPlugins(settings),
54
+ mdMeta(settings),
55
+ ]
56
+ }
57
+
58
+ function remarkFunctionPlugins(settings?: Settings) {
59
+ return [
60
+ mdFunctionImportCode(settings),
61
+ mdFunctionUniform(settings),
62
+ mdFunctionChangelog(),
63
+ ]
64
+ }
65
+
66
+ export function defaultRehypePlugins(settings?: Settings) {
67
+ return [
68
+ rehypeHeading,
69
+ mdCodeRehype(settings)
25
70
  ]
26
- }
71
+ }
@@ -1,4 +1,6 @@
1
- import {visit} from "unist-util-visit";
1
+ import { visit } from "unist-util-visit";
2
+ import { parseImportPath } from "./functions/utils";
3
+ import { mdParameters } from "./utils/mdParameters";
2
4
 
3
5
  /**
4
6
  * This plugin injects the code meta into the code node's data
@@ -6,14 +8,60 @@ import {visit} from "unist-util-visit";
6
8
  */
7
9
  export function remarkInjectCodeMeta() {
8
10
  return (tree: any) => {
11
+ console.time('plugin:remarkInjectCodeMeta');
9
12
  visit(tree, 'code', (node) => {
10
- if (node.meta) {
11
- node.data = node.data || {};
13
+ const { filePath, regions, lineRanges } = parseImportPath(node.lang || "") || {}
14
+ node.lang = filePath
15
+ node.data = node.data || {}
16
+ node.data.regions = regions
17
+ node.data.lineRanges = lineRanges
18
+
19
+ const { attributes, sanitizedText } = mdParameters(node.meta || "", {
20
+ htmlMd: true
21
+ });
22
+
23
+ const props: { [prop: string]: any } = {
24
+ regions: JSON.stringify(node.data.regions),
25
+ lineRanges: JSON.stringify(node.data.lineRanges)
26
+ }
27
+
28
+ let meta = ""
29
+ if (attributes && attributes.lines === "true") {
30
+ props.lineNumbers = true
31
+ }
32
+ if (attributes && attributes.scroll === "false") {
33
+ props.size = "full"
34
+ }
35
+ if (attributes && attributes.scroll === "true") {
36
+ props.size = ""
37
+ }
38
+ if (attributes && attributes.descHead && attributes.descHead !== "false") {
39
+ props.descriptionHead = attributes.descHead
40
+ }
41
+ if (attributes && attributes.desc && attributes.desc !== "false") {
42
+ props.descriptionContent = attributes.desc
43
+ }
44
+ if (attributes && attributes.descIcon && attributes.descIcon !== "false") {
45
+ props.descriptionIcon = attributes.descIcon
46
+ }
47
+ if (attributes && attributes.meta && attributes.meta !== "false") {
48
+ meta = attributes.meta
49
+ node.meta = meta
50
+ }
51
+
52
+ node.data.hProperties = {
53
+ ...(node.data.hProperties || {}),
54
+ ...props
55
+ }
56
+
57
+ meta = meta || sanitizedText
58
+ if (meta) {
12
59
  node.data.hProperties = {
13
60
  ...(node.data.hProperties || {}),
14
- meta: node.meta,
61
+ meta: meta
15
62
  };
16
63
  }
17
64
  });
65
+ console.timeEnd('plugin:remarkInjectCodeMeta');
18
66
  };
19
67
  }
@@ -0,0 +1,47 @@
1
+ import { visit } from 'unist-util-visit';
2
+ import GithubSlugger from 'github-slugger';
3
+ import type { Plugin } from 'unified';
4
+ import type { Root, Heading } from 'mdast';
5
+
6
+ import {mdParameters} from "./utils/mdParameters"
7
+
8
+ interface HeadingData {
9
+ hProperties?: {
10
+ id?: string;
11
+ [key: string]: unknown;
12
+ };
13
+ }
14
+
15
+ export const mdHeadingId: Plugin<[], Root> = () => {
16
+ let slugger = new GithubSlugger();
17
+
18
+ return (tree) => {
19
+ visit(tree, 'heading', (node: Heading & { data?: HeadingData }) => {
20
+ if (!node.data) {
21
+ node.data = {
22
+ hProperties: {}
23
+ };
24
+ }
25
+
26
+ if (node.depth === 1) {
27
+ slugger.reset();
28
+ }
29
+
30
+ // Create a slug from the heading text
31
+ const text = node.children
32
+ .map((child) => ('value' in child ? child.value : ''))
33
+ .join('');
34
+
35
+
36
+ const {props, sanitizedText} = mdParameters(text)
37
+
38
+ const id = props.id || slugger.slug(sanitizedText);
39
+
40
+ // Add the id to the heading's data
41
+ node.data.hProperties = {
42
+ ...node.data.hProperties,
43
+ id,
44
+ };
45
+ });
46
+ };
47
+ };
@@ -6,6 +6,7 @@ declare global {
6
6
  var page: boolean | null | undefined
7
7
  }
8
8
 
9
+ // TODO: to delete
9
10
  /**
10
11
  * This plugin extracts the `page` variable from the markdown file.
11
12
  * This variable(`page`) is used to determine if theme should be dropped out.
@@ -14,6 +15,7 @@ declare global {
14
15
  */
15
16
  export const extractPage: Plugin = () => {
16
17
  return (tree: UnistNode) => {
18
+ console.time('plugin:extractPage');
17
19
  visit(tree, 'exportNamedDeclaration', (node: any) => {
18
20
  const declaration = node.declaration;
19
21
 
@@ -28,5 +30,6 @@ export const extractPage: Plugin = () => {
28
30
  }
29
31
  });
30
32
  });
33
+ console.timeEnd('plugin:extractPage');
31
34
  };
32
35
  };
@@ -10,6 +10,7 @@ declare global {
10
10
  var themeSettings: ThemeSettings | undefined;
11
11
  }
12
12
 
13
+ // TODO: to delete
13
14
  /**
14
15
  * This plugin adds the `themeSettings` variable to the global scope.
15
16
  * This variable is used to determine the theme settings for the current page.
@@ -17,6 +18,8 @@ declare global {
17
18
  export const extractThemeSettings: Plugin = () => {
18
19
  return (tree: UnistNode) => {
19
20
  visit(tree, 'exportNamedDeclaration', (node: any) => {
21
+ console.time('plugin:extractThemeSettings');
22
+
20
23
  const declaration = node.declaration;
21
24
  if (declaration && declaration.declarations) {
22
25
  declaration.declarations.forEach((decl: any) => {
@@ -25,6 +28,7 @@ export const extractThemeSettings: Plugin = () => {
25
28
  }
26
29
  });
27
30
  }
31
+ console.timeEnd('plugin:extractThemeSettings');
28
32
  });
29
33
  };
30
34
  };