@mui/internal-code-infra 0.0.4-canary.9 → 0.0.4-canary.90

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 (140) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +19 -8
  3. package/build/babel-config.d.mts +5 -1
  4. package/build/brokenLinksChecker/crawlWorker.d.mts +1 -0
  5. package/build/brokenLinksChecker/index.d.mts +45 -2
  6. package/build/changelog/types.d.ts +3 -1
  7. package/build/cli/cmdArgosPush.d.mts +2 -2
  8. package/build/cli/cmdBuild.d.mts +3 -2
  9. package/build/cli/cmdCopyFiles.d.mts +2 -2
  10. package/build/cli/cmdExtractErrorCodes.d.mts +2 -2
  11. package/build/cli/cmdGenerateChangelog.d.mts +2 -2
  12. package/build/cli/cmdGithubAuth.d.mts +2 -2
  13. package/build/cli/cmdListWorkspaces.d.mts +6 -4
  14. package/build/cli/cmdNetlifyIgnore.d.mts +3 -2
  15. package/build/cli/cmdPublish.d.mts +4 -2
  16. package/build/cli/cmdPublishCanary.d.mts +3 -3
  17. package/build/cli/cmdPublishNewPackage.d.mts +4 -2
  18. package/build/cli/cmdSetVersionOverrides.d.mts +2 -2
  19. package/build/cli/cmdVale.d.mts +46 -0
  20. package/build/cli/cmdValidateBuiltTypes.d.mts +2 -2
  21. package/build/eslint/baseConfig.d.mts +3 -1
  22. package/build/eslint/mui/rules/disallow-react-api-in-server-components.d.mts +2 -2
  23. package/build/eslint/mui/rules/docgen-ignore-before-comment.d.mts +2 -2
  24. package/build/eslint/mui/rules/no-floating-cleanup.d.mts +5 -0
  25. package/build/eslint/mui/rules/no-guarded-throw.d.mts +31 -0
  26. package/build/eslint/mui/rules/no-presentation-role.d.mts +5 -0
  27. package/build/eslint/mui/rules/no-restricted-resolved-imports.d.mts +2 -2
  28. package/build/eslint/mui/rules/nodeEnvUtils.d.mts +18 -0
  29. package/build/remark/config.d.mts +43 -0
  30. package/build/remark/createLintTester.d.mts +10 -0
  31. package/build/remark/firstBlockHeading.d.mts +4 -0
  32. package/build/remark/gitDiff.d.mts +2 -0
  33. package/build/remark/noSpaceInLinks.d.mts +2 -0
  34. package/build/remark/straightQuotes.d.mts +2 -0
  35. package/build/remark/tableAlignment.d.mts +2 -0
  36. package/build/remark/terminalLanguage.d.mts +2 -0
  37. package/build/utils/babel.d.mts +1 -1
  38. package/build/utils/build.d.mts +56 -37
  39. package/build/utils/git.d.mts +7 -0
  40. package/build/utils/github.d.mts +1 -1
  41. package/build/utils/pnpm.d.mts +81 -2
  42. package/build/utils/testUtils.d.mts +7 -0
  43. package/build/utils/typescript.d.mts +6 -16
  44. package/package.json +74 -50
  45. package/src/babel-config.mjs +3 -1
  46. package/src/brokenLinksChecker/crawlWorker.mjs +240 -0
  47. package/src/brokenLinksChecker/index.mjs +263 -188
  48. package/src/changelog/fetchChangelogs.mjs +8 -2
  49. package/src/changelog/renderChangelog.mjs +1 -1
  50. package/src/changelog/types.ts +3 -1
  51. package/src/cli/cmdBuild.mjs +51 -85
  52. package/src/cli/cmdListWorkspaces.mjs +12 -27
  53. package/src/cli/cmdNetlifyIgnore.mjs +34 -93
  54. package/src/cli/cmdPublish.mjs +95 -20
  55. package/src/cli/cmdPublishCanary.mjs +128 -132
  56. package/src/cli/cmdPublishNewPackage.mjs +27 -6
  57. package/src/cli/cmdSetVersionOverrides.mjs +8 -9
  58. package/src/cli/cmdVale.mjs +513 -0
  59. package/src/cli/index.mjs +2 -0
  60. package/src/cli/packageJson.d.ts +1 -1
  61. package/src/eslint/baseConfig.mjs +51 -19
  62. package/src/eslint/docsConfig.mjs +2 -1
  63. package/src/eslint/mui/config.mjs +24 -4
  64. package/src/eslint/mui/index.mjs +6 -0
  65. package/src/eslint/mui/rules/no-floating-cleanup.mjs +187 -0
  66. package/src/eslint/mui/rules/no-guarded-throw.mjs +115 -0
  67. package/src/eslint/mui/rules/no-presentation-role.mjs +60 -0
  68. package/src/eslint/mui/rules/nodeEnvUtils.mjs +52 -0
  69. package/src/eslint/mui/rules/require-dev-wrapper.mjs +25 -40
  70. package/src/estree-typescript.d.ts +1 -1
  71. package/src/remark/config.mjs +157 -0
  72. package/src/remark/createLintTester.mjs +19 -0
  73. package/src/remark/firstBlockHeading.mjs +87 -0
  74. package/src/remark/gitDiff.mjs +43 -0
  75. package/src/remark/noSpaceInLinks.mjs +42 -0
  76. package/src/remark/straightQuotes.mjs +31 -0
  77. package/src/remark/tableAlignment.mjs +23 -0
  78. package/src/remark/terminalLanguage.mjs +19 -0
  79. package/src/untyped-plugins.d.ts +11 -11
  80. package/src/utils/build.mjs +604 -270
  81. package/src/utils/git.mjs +27 -7
  82. package/src/utils/pnpm.mjs +277 -10
  83. package/src/utils/testUtils.mjs +18 -0
  84. package/src/utils/typescript.mjs +23 -42
  85. package/vale/.vale.ini +1 -0
  86. package/vale/styles/MUI/CorrectReferenceAllCases.yml +42 -0
  87. package/vale/styles/MUI/CorrectRererenceCased.yml +14 -0
  88. package/vale/styles/MUI/GoogleLatin.yml +11 -0
  89. package/vale/styles/MUI/MuiBrandName.yml +22 -0
  90. package/vale/styles/MUI/NoBritish.yml +112 -0
  91. package/vale/styles/MUI/NoCompanyName.yml +17 -0
  92. package/build/markdownlint/duplicate-h1.d.mts +0 -56
  93. package/build/markdownlint/git-diff.d.mts +0 -11
  94. package/build/markdownlint/index.d.mts +0 -52
  95. package/build/markdownlint/straight-quotes.d.mts +0 -11
  96. package/build/markdownlint/table-alignment.d.mts +0 -11
  97. package/build/markdownlint/terminal-language.d.mts +0 -11
  98. package/src/brokenLinksChecker/__fixtures__/static-site/broken-links.html +0 -20
  99. package/src/brokenLinksChecker/__fixtures__/static-site/broken-targets.html +0 -22
  100. package/src/brokenLinksChecker/__fixtures__/static-site/example.md +0 -20
  101. package/src/brokenLinksChecker/__fixtures__/static-site/external-links.html +0 -21
  102. package/src/brokenLinksChecker/__fixtures__/static-site/ignored-page.html +0 -17
  103. package/src/brokenLinksChecker/__fixtures__/static-site/index.html +0 -28
  104. package/src/brokenLinksChecker/__fixtures__/static-site/known-targets.json +0 -5
  105. package/src/brokenLinksChecker/__fixtures__/static-site/nested/page.html +0 -21
  106. package/src/brokenLinksChecker/__fixtures__/static-site/orphaned-page.html +0 -20
  107. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-api-links.html +0 -20
  108. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-custom-targets.html +0 -24
  109. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-ignored-content.html +0 -28
  110. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-known-target-links.html +0 -19
  111. package/src/brokenLinksChecker/__fixtures__/static-site/unclosed-tags.html +0 -1
  112. package/src/brokenLinksChecker/__fixtures__/static-site/valid.html +0 -20
  113. package/src/brokenLinksChecker/__fixtures__/static-site/with-anchors.html +0 -31
  114. package/src/brokenLinksChecker/index.test.ts +0 -261
  115. package/src/changelog/categorizeCommits.test.ts +0 -319
  116. package/src/changelog/filterCommits.test.ts +0 -363
  117. package/src/changelog/parseCommitLabels.test.ts +0 -509
  118. package/src/changelog/renderChangelog.test.ts +0 -378
  119. package/src/changelog/sortSections.test.ts +0 -199
  120. package/src/eslint/mui/rules/add-undef-to-optional.test.mjs +0 -361
  121. package/src/eslint/mui/rules/consistent-production-guard.test.mjs +0 -162
  122. package/src/eslint/mui/rules/disallow-active-elements-as-key-event-target.test.mjs +0 -66
  123. package/src/eslint/mui/rules/disallow-react-api-in-server-components.test.mjs +0 -305
  124. package/src/eslint/mui/rules/docgen-ignore-before-comment.test.mjs +0 -52
  125. package/src/eslint/mui/rules/flatten-parentheses.test.mjs +0 -245
  126. package/src/eslint/mui/rules/mui-name-matches-component-name.test.mjs +0 -247
  127. package/src/eslint/mui/rules/no-empty-box.test.mjs +0 -40
  128. package/src/eslint/mui/rules/no-styled-box.test.mjs +0 -73
  129. package/src/eslint/mui/rules/require-dev-wrapper.test.mjs +0 -265
  130. package/src/eslint/mui/rules/rules-of-use-theme-variants.test.mjs +0 -149
  131. package/src/eslint/mui/rules/straight-quotes.test.mjs +0 -67
  132. package/src/markdownlint/duplicate-h1.mjs +0 -69
  133. package/src/markdownlint/git-diff.mjs +0 -31
  134. package/src/markdownlint/index.mjs +0 -62
  135. package/src/markdownlint/straight-quotes.mjs +0 -26
  136. package/src/markdownlint/table-alignment.mjs +0 -42
  137. package/src/markdownlint/terminal-language.mjs +0 -19
  138. package/src/utils/build.test.mjs +0 -705
  139. package/src/utils/template.test.mjs +0 -133
  140. package/src/utils/typescript.test.mjs +0 -380
@@ -0,0 +1,240 @@
1
+ import { workerData, parentPort } from 'node:worker_threads';
2
+ import { parse } from 'node-html-parser';
3
+ import { parse as parseContentType } from 'content-type';
4
+ import { HtmlValidate, StaticConfigLoader, staticResolver } from 'html-validate';
5
+ import { unified } from 'unified';
6
+ import remarkParse from 'remark-parse';
7
+ import remarkGfm from 'remark-gfm';
8
+ import remarkRehype from 'remark-rehype';
9
+ import rehypeSlug from 'rehype-slug';
10
+ import rehypeStringify from 'rehype-stringify';
11
+
12
+ /** @type {import('./index.mjs').CrawlWorkerInput} */
13
+ const { pageUrl, options } = workerData;
14
+
15
+ /**
16
+ * Tests if a value matches any of the patterns in the array.
17
+ * Returns true if patterns is undefined/empty (wildcard behavior).
18
+ * Strings use exact match, RegExp uses .test().
19
+ * @param {string} value
20
+ * @param {(string | RegExp)[] | undefined} patterns
21
+ * @returns {boolean}
22
+ */
23
+ function matchesAnyPattern(value, patterns) {
24
+ if (!patterns || patterns.length === 0) {
25
+ return true;
26
+ }
27
+ return patterns.some((pattern) =>
28
+ typeof pattern === 'string' ? value === pattern : pattern.test(value),
29
+ );
30
+ }
31
+
32
+ /**
33
+ * Posts the crawl result back to the parent thread.
34
+ * @param {import('./index.mjs').CrawlWorkerOutput} output
35
+ */
36
+ function postResult(output) {
37
+ if (!parentPort) {
38
+ throw new Error('crawlWorker must be run as a worker thread');
39
+ }
40
+ parentPort.postMessage(output);
41
+ }
42
+
43
+ /**
44
+ * Computes the accessible name of an element according to ARIA rules.
45
+ * @param {import('node-html-parser').HTMLElement | null} elm
46
+ * @param {import('node-html-parser').HTMLElement} ownerDocument
47
+ * @returns {string}
48
+ */
49
+ function getAccessibleName(elm, ownerDocument) {
50
+ if (!elm) {
51
+ return '';
52
+ }
53
+
54
+ const ariaLabel = elm.getAttribute('aria-label')?.trim();
55
+ if (ariaLabel) {
56
+ return ariaLabel;
57
+ }
58
+
59
+ const labelledby = elm.getAttribute('aria-labelledby');
60
+ if (labelledby) {
61
+ const labels = [];
62
+ for (const id of labelledby.split(/\s+/)) {
63
+ const label = getAccessibleName(ownerDocument.getElementById(id), ownerDocument);
64
+ if (label) {
65
+ labels.push(label);
66
+ }
67
+ }
68
+ const label = labels.join(' ').trim();
69
+ if (label) {
70
+ return label;
71
+ }
72
+ }
73
+
74
+ if (elm.id) {
75
+ const label = ownerDocument.querySelector(`label[for="${elm.id}"]`);
76
+ if (label) {
77
+ return getAccessibleName(label, ownerDocument);
78
+ }
79
+ }
80
+
81
+ if (elm.tagName === 'IMG') {
82
+ const alt = elm.getAttribute('alt')?.trim();
83
+ if (alt) {
84
+ return alt;
85
+ }
86
+ }
87
+
88
+ return elm.innerText.trim();
89
+ }
90
+
91
+ /**
92
+ * Converts markdown content to HTML using unified pipeline.
93
+ * @param {string} markdown
94
+ * @returns {Promise<string>}
95
+ */
96
+ async function markdownToHtml(markdown) {
97
+ const result = await unified()
98
+ .use(remarkParse)
99
+ .use(remarkGfm)
100
+ .use(remarkRehype)
101
+ .use(rehypeSlug)
102
+ .use(rehypeStringify)
103
+ .process(markdown);
104
+ return String(result);
105
+ }
106
+
107
+ const res = await fetch(new URL(pageUrl, options.host));
108
+
109
+ const contentTypeHeader = res.headers.get('content-type');
110
+ let type = 'text/html';
111
+
112
+ if (contentTypeHeader) {
113
+ try {
114
+ const parsed = parseContentType(contentTypeHeader);
115
+ type = parsed.type;
116
+ } catch {
117
+ // invalid content-type, default to text/html
118
+ }
119
+ }
120
+
121
+ /** @type {import('./index.mjs').CrawlWorkerPageData} */
122
+ const pageData = {
123
+ url: pageUrl,
124
+ status: res.status,
125
+ targets: [],
126
+ contentType: type,
127
+ };
128
+
129
+ if (pageData.status < 200 || pageData.status >= 400) {
130
+ postResult({ pageData, links: [], htmlValidateResults: null });
131
+ } else if (type.startsWith('image/') || (type !== 'text/html' && type !== 'text/markdown')) {
132
+ postResult({ pageData, links: [], htmlValidateResults: null });
133
+ } else {
134
+ const rawContent = await res.text();
135
+
136
+ const content = type === 'text/markdown' ? await markdownToHtml(rawContent) : rawContent;
137
+
138
+ const dom = parse(content, { parseNoneClosedTags: true });
139
+
140
+ // Extract targets
141
+ for (const target of dom.querySelectorAll('*[id]')) {
142
+ if (!options.ignoredTargets.has(target.id)) {
143
+ pageData.targets.push(`#${target.id}`);
144
+ }
145
+ }
146
+
147
+ // Extract links
148
+ let ignoredSelector = ':not(*)';
149
+ if (options.ignoredContent.length > 0) {
150
+ ignoredSelector = Array.from(options.ignoredContent)
151
+ .flatMap((selector) => [selector, `${selector} *`])
152
+ .join(',');
153
+ }
154
+ const linksSelector = `a[href]:not(${ignoredSelector})`;
155
+
156
+ const links = dom.querySelectorAll(linksSelector).map((a) => ({
157
+ src: pageUrl,
158
+ text: getAccessibleName(a, dom),
159
+ href: a.getAttribute('href') ?? '',
160
+ contentType: type,
161
+ }));
162
+
163
+ // HTML validation. Every entry whose path matches contributes to the
164
+ // page's config: each is registered as a synthetic preset and the page's
165
+ // root config `extends` `mui:recommended` first, then the matched presets
166
+ // in order. When override configs do not specify their own `extends`,
167
+ // they behave as pure rule patches layered on top of `mui:recommended`,
168
+ // so a later entry can only change the rules it names directly. Override
169
+ // configs are passed through as-is, though, so an entry that declares its
170
+ // own `extends` can still pull in additional presets (including
171
+ // `mui:recommended`) and affect earlier downgrades. html-validate merges
172
+ // `extends` left to right.
173
+ /** @type {{ pageUrl: string, results: import('html-validate').Result[] } | null} */
174
+ let htmlValidateResults = null;
175
+ if (type === 'text/html' && options.htmlValidate.length > 0) {
176
+ const matchedEntries = options.htmlValidate.filter((entry) =>
177
+ matchesAnyPattern(pageUrl, entry.path),
178
+ );
179
+
180
+ if (matchedEntries.length > 0) {
181
+ const overridePresets = Object.fromEntries(
182
+ matchedEntries.map((entry, index) => [`mui:override-${index}`, entry.config]),
183
+ );
184
+
185
+ const muiHtmlValidateResolver = staticResolver({
186
+ configs: {
187
+ 'mui:recommended': {
188
+ extends: ['html-validate:standard', 'html-validate:document', 'html-validate:browser'],
189
+ rules: {
190
+ // TODO: Enable when subresource integrity is adopted across projects
191
+ 'require-sri': 'off',
192
+ // Components that portal their content (menus, selects, listboxes,
193
+ // etc.) wire up aria-controls/aria-labelledby targets only after
194
+ // client hydration, so those targets are absent from the
195
+ // statically rendered HTML. Disable rather than report false
196
+ // positives.
197
+ 'no-missing-references': 'off',
198
+ },
199
+ elements: [
200
+ 'html5',
201
+ {
202
+ form: {
203
+ attributes: {
204
+ // React renders `action="javascript:throw new Error(...)"` on
205
+ // a <form> with a function action during SSR (a sentinel that
206
+ // throws if the form is submitted before hydration wires up
207
+ // the real handler). Permit that value while keeping the
208
+ // default check (`^\s*\S+\s*$`) for every other action value.
209
+ action: {
210
+ enum: ['/^\\s*\\S+\\s*$/', '/^\\s*javascript:throw new Error\\(/'],
211
+ },
212
+ },
213
+ },
214
+ },
215
+ ],
216
+ },
217
+ ...overridePresets,
218
+ },
219
+ });
220
+
221
+ const htmlValidator = new HtmlValidate(
222
+ new StaticConfigLoader([muiHtmlValidateResolver], {
223
+ extends: ['mui:recommended', ...Object.keys(overridePresets)],
224
+ }),
225
+ );
226
+
227
+ if (options.verbose) {
228
+ const resolved = await htmlValidator.getConfigFor(pageUrl);
229
+ console.warn(
230
+ `[html-validate config] ${pageUrl}\n${JSON.stringify(resolved.getConfigData(), null, 2)}`,
231
+ );
232
+ }
233
+
234
+ const report = await htmlValidator.validateString(rawContent, pageUrl);
235
+ htmlValidateResults = { pageUrl, results: report.results };
236
+ }
237
+ }
238
+
239
+ postResult({ pageData, links, htmlValidateResults });
240
+ }