@mui/internal-code-infra 0.0.4-canary.97 → 0.0.4-canary.99

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,40 +1,103 @@
1
1
  export type LinkStructure = Map<string, Set<string>>;
2
2
  export type SerializedLinkStructure = {
3
+ /**
4
+ * - Object mapping page URLs to arrays of target IDs
5
+ */
3
6
  targets: Record<string, string[]>;
4
7
  };
5
8
  export type CrawlWorkerInput = {
9
+ /**
10
+ * - The page URL to crawl
11
+ */
6
12
  pageUrl: string;
13
+ /**
14
+ * - Fully resolved crawl options
15
+ */
7
16
  options: ResolvedCrawlOptions;
8
17
  };
9
18
  export type CrawlWorkerPageData = {
19
+ /**
20
+ * - The normalized page URL
21
+ */
10
22
  url: string;
23
+ /**
24
+ * - HTTP status code
25
+ */
11
26
  status: number;
27
+ /**
28
+ * - Array of anchor targets (e.g., '#intro')
29
+ */
12
30
  targets: string[];
31
+ /**
32
+ * - Content-type of the page
33
+ */
13
34
  contentType: string;
14
35
  };
15
36
  export type CrawlWorkerOutput = {
37
+ /**
38
+ * - Serialized page data
39
+ */
16
40
  pageData: CrawlWorkerPageData;
41
+ /**
42
+ * - Links discovered on the page
43
+ */
17
44
  links: Link[];
45
+ /**
46
+ * - HTML validation results, or null if validation was skipped/passed
47
+ */
18
48
  htmlValidateResults: {
19
49
  pageUrl: string;
20
50
  results: import('html-validate').Result[];
21
51
  } | null;
22
52
  };
23
53
  export type PageData = {
54
+ /**
55
+ * - The normalized page URL (without trailing slash unless root)
56
+ */
24
57
  url: string;
58
+ /**
59
+ * - HTTP status code from the response (e.g., 200, 404, 500)
60
+ */
25
61
  status: number;
62
+ /**
63
+ * - Set of available anchor targets on the page, keyed by hash (e.g., '#intro')
64
+ */
26
65
  targets: Set<string>;
66
+ /**
67
+ * - Content-type of the page (e.g., 'text/html', 'text/markdown')
68
+ */
27
69
  contentType: string;
28
70
  };
29
71
  export type Link = {
72
+ /**
73
+ * - URL of the page where this link was found, or null for seed URLs
74
+ */
30
75
  src: string | null;
76
+ /**
77
+ * - Accessible name/text content of the link element, or null for seed URLs
78
+ */
31
79
  text: string | null;
80
+ /**
81
+ * - The href attribute value (may be relative or absolute, with or without hash)
82
+ */
32
83
  href: string;
84
+ /**
85
+ * - Content-type of the source page (e.g., 'text/html', 'text/markdown')
86
+ */
33
87
  contentType?: string | null;
34
88
  };
35
89
  export type IgnoreRule = {
90
+ /**
91
+ * - Pattern(s) to match source page path
92
+ */
36
93
  path?: (string | RegExp) | (string | RegExp)[];
94
+ /**
95
+ * - Pattern(s) to match broken link href
96
+ */
37
97
  href?: (string | RegExp) | (string | RegExp)[];
98
+ /**
99
+ * - Pattern(s) to match source page content-type
100
+ */
38
101
  contentType?: (string | RegExp) | (string | RegExp)[];
39
102
  };
40
103
  export type NormalizedIgnoreRule = {
@@ -43,22 +106,67 @@ export type NormalizedIgnoreRule = {
43
106
  contentType: (string | RegExp)[] | undefined;
44
107
  };
45
108
  export type CrawlOptions = {
109
+ /**
110
+ * - Shell command to start the dev server (e.g., 'npm run dev'). If null, assumes server is already running
111
+ */
46
112
  startCommand?: string | null;
113
+ /**
114
+ * - Base URL of the site to crawl (e.g., 'http://localhost:3000')
115
+ */
47
116
  host: string;
117
+ /**
118
+ * - File path to write discovered link targets to. If null, targets are not persisted
119
+ */
48
120
  outPath?: string | null;
121
+ /**
122
+ * - Array of regex patterns to exclude from crawling (e.g., [/^\/api\//] to skip /api/* routes)
123
+ */
49
124
  ignoredPaths?: RegExp[];
125
+ /**
126
+ * - CSS selectors for elements whose nested links should be ignored (e.g., ['.sidebar', 'footer'])
127
+ */
50
128
  ignoredContent?: string[];
129
+ /**
130
+ * - Set of element IDs to ignore as link targets (defaults to '__next', '__NEXT_DATA__')
131
+ */
51
132
  ignoredTargets?: Set<string>;
133
+ /**
134
+ * - Pre-populated map of known valid targets to skip crawling (useful for external pages)
135
+ */
52
136
  knownTargets?: Map<string, Set<string>>;
137
+ /**
138
+ * - URLs to fetch known targets from (fetched JSON will be merged with knownTargets)
139
+ */
53
140
  knownTargetsDownloadUrl?: string[];
141
+ /**
142
+ * - Number of concurrent page fetches (defaults to 4)
143
+ */
54
144
  concurrency?: number;
145
+ /**
146
+ * - Starting URLs for the crawl (defaults to ['/'])
147
+ */
55
148
  seedUrls?: string[];
149
+ /**
150
+ * - Rules to ignore broken links. Each rule can have path, href, contentType, and/or has properties. All specified properties must match (AND logic). Within a property, multiple values use OR logic.
151
+ */
56
152
  ignores?: IgnoreRule[];
153
+ /**
154
+ * - Enable HTML validation on crawled pages. `false` (default): disabled. `true`: validate with recommended rules. Object: use as html-validate config — `mui:recommended` is always applied as the baseline, so most callers only need to set `rules`. Array: per-path config overrides — `mui:recommended` is applied once as the baseline and every entry whose `path` matches the page URL is layered on top; later matching entries win on conflicting rule keys. If an entry omits `extends`, it behaves like a rule patch and typically only changes the rules it names. If an entry includes `extends` (for example, re-extending `mui:recommended`), it can re-introduce or reset baseline presets rather than acting as a pure patch. An entry without `path` matches every page. If no entry matches, the page is not validated.
155
+ */
57
156
  htmlValidate?: HtmlValidateOption;
157
+ /**
158
+ * - Log extra diagnostics during crawling (e.g. resolved html-validate config per page). Defaults to `false`.
159
+ */
58
160
  verbose?: boolean;
59
161
  };
60
162
  export type HtmlValidateOverride = {
163
+ /**
164
+ * - Pattern(s) to match the page URL. Strings use exact match. Omit to match every page.
165
+ */
61
166
  path?: (string | RegExp) | (string | RegExp)[];
167
+ /**
168
+ * - html-validate config (or `true` for `mui:recommended`).
169
+ */
62
170
  config: true | import('html-validate').ConfigData;
63
171
  };
64
172
  export type HtmlValidateOption = boolean | import('html-validate').ConfigData | HtmlValidateOverride[];
@@ -71,26 +179,65 @@ export type ResolvedCrawlOptions = Omit<Required<CrawlOptions>, 'ignores' | 'htm
71
179
  htmlValidate: ResolvedHtmlValidateEntry[];
72
180
  };
73
181
  export type BrokenLinkIssue = {
182
+ /**
183
+ * - Type of issue: 'broken-link' for 404 pages, 'broken-target' for missing anchors
184
+ */
74
185
  type: 'broken-link' | 'broken-target';
186
+ /**
187
+ * - Human-readable description of the issue (e.g., 'Target not found', 'Page returned error 404')
188
+ */
75
189
  message: string;
190
+ /**
191
+ * - The link object that has the issue
192
+ */
76
193
  link: Link;
77
194
  };
78
195
  export type HtmlValidateIssue = {
196
+ /**
197
+ * - Issue type discriminator
198
+ */
79
199
  type: 'html-validate';
200
+ /**
201
+ * - Human-readable description of the issue
202
+ */
80
203
  message: string;
204
+ /**
205
+ * - The page URL where the issue was found
206
+ */
81
207
  pageUrl: string;
208
+ /**
209
+ * - The html-validate rule that triggered this issue (e.g., 'no-dup-id')
210
+ */
82
211
  ruleId: string;
212
+ /**
213
+ * - Severity level (1 = warning, 2 = error)
214
+ */
83
215
  severity: number;
216
+ /**
217
+ * - Source location of the issue
218
+ */
84
219
  location: {
85
220
  line: number;
86
221
  column: number;
87
222
  };
223
+ /**
224
+ * - DOM selector for the element, or null
225
+ */
88
226
  selector: string | null;
89
227
  };
90
228
  export type Issue = BrokenLinkIssue | HtmlValidateIssue;
91
229
  export type CrawlResult = {
230
+ /**
231
+ * - All links discovered during the crawl
232
+ */
92
233
  links: Set<Link>;
234
+ /**
235
+ * - All pages crawled, keyed by normalized URL
236
+ */
93
237
  pages: Map<string, PageData>;
238
+ /**
239
+ * - All issues found (broken links, broken targets, and HTML validation issues)
240
+ */
94
241
  issues: Issue[];
95
242
  };
96
243
  /**
@@ -4,6 +4,9 @@ export type FetchCommitsOptions = {
4
4
  repo: string;
5
5
  lastRelease: string;
6
6
  release: string;
7
+ /**
8
+ * - GitHub organization name, defaults to 'mui'
9
+ */
7
10
  org?: string;
8
11
  };
9
12
  /**
@@ -1,6 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  export type Args = {
3
+ /**
4
+ * - Run with verbose logging
5
+ */
3
6
  verbose?: boolean;
7
+ /**
8
+ * - Screenshots folder path
9
+ */
4
10
  folder: string;
5
11
  };
6
12
  /**
@@ -1,18 +1,63 @@
1
1
  export type Args = {
2
+ /**
3
+ * - The bundles to build.
4
+ */
2
5
  bundle: import('../utils/build.mjs').BundleType[];
6
+ /**
7
+ * - The large files to build.
8
+ */
3
9
  hasLargeFiles: boolean;
10
+ /**
11
+ * - Whether to skip generating a package.json file in the /esm folder.
12
+ */
4
13
  skipBundlePackageJson: boolean;
14
+ /**
15
+ * - Whether to enable verbose logging.
16
+ */
5
17
  verbose: boolean;
18
+ /**
19
+ * - Whether to build types for the package.
20
+ */
6
21
  buildTypes: boolean;
22
+ /**
23
+ * - Whether to build types for the package.
24
+ */
7
25
  skipTsc: boolean;
26
+ /**
27
+ * - Whether to skip checking for Babel runtime dependencies in the package.
28
+ */
8
29
  skipBabelRuntimeCheck: boolean;
30
+ /**
31
+ * - Whether to skip generating the package.json file in the bundle output.
32
+ */
9
33
  skipPackageJson: boolean;
34
+ /**
35
+ * - Whether to skip checking for main field in package.json.
36
+ */
10
37
  skipMainCheck: boolean;
38
+ /**
39
+ * - Globs to be ignored by Babel.
40
+ */
11
41
  ignore: string[];
42
+ /**
43
+ * - Files/Directories to be copied. Can be a glob pattern.
44
+ */
12
45
  copy?: string[];
46
+ /**
47
+ * - Whether to use the React compiler.
48
+ */
13
49
  enableReactCompiler?: boolean;
50
+ /**
51
+ * - Whether to build types using typescript native (tsgo).
52
+ */
14
53
  tsgo?: boolean;
54
+ /**
55
+ * - Deprecated no-op; flat builds are always used.
56
+ */
15
57
  flat?: boolean;
58
+ /**
59
+ * - Whether to enumerate glob patterns in exports/imports into concrete entries.
60
+ */
16
61
  expand: boolean;
17
62
  };
18
63
  declare const _default: import('yargs').CommandModule<{}, Args>;
@@ -1,7 +1,19 @@
1
1
  export type Args = {
2
+ /**
3
+ * Run in silent mode without logging
4
+ */
2
5
  silent?: boolean;
6
+ /**
7
+ * Exclude default files from the copy operation
8
+ */
3
9
  excludeDefaults?: boolean;
10
+ /**
11
+ * Glob patterns to copy
12
+ */
4
13
  glob?: string[];
14
+ /**
15
+ * Extra files to copy
16
+ */
5
17
  files?: string[];
6
18
  };
7
19
  declare const _default: import('yargs').CommandModule<{}, Args>;
@@ -1,8 +1,23 @@
1
1
  export type Args = {
2
+ /**
3
+ * Path to the changelog configuration file
4
+ */
2
5
  config: string;
6
+ /**
7
+ * The git reference (tag/commit) of the last release
8
+ */
3
9
  lastRelease: string;
10
+ /**
11
+ * The git reference (tag/commit) of the new release
12
+ */
4
13
  release: string;
14
+ /**
15
+ * The version number for the new release
16
+ */
5
17
  releaseVersion: string;
18
+ /**
19
+ * The current working directory to run the command in
20
+ */
6
21
  cwd: string;
7
22
  };
8
23
  /**
@@ -1,9 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  export type PublicPackage = import('../utils/pnpm.mjs').PublicPackage;
3
3
  export type Args = {
4
+ /**
5
+ * - Whether to filter to only public packages
6
+ */
4
7
  publicOnly?: boolean;
8
+ /**
9
+ * - Output format (name, path, or json)
10
+ */
5
11
  output?: 'json' | 'path' | 'name' | 'publish-dir';
12
+ /**
13
+ * - Git reference to filter changes since
14
+ */
6
15
  sinceRef?: string;
16
+ /**
17
+ * - Same as filtering packages with --filter in pnpm. Only include packages matching the filter. See https://pnpm.io/filtering.
18
+ */
7
19
  filter?: string[];
8
20
  };
9
21
  /**
@@ -1,7 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  export type Args = {
3
+ /**
4
+ * - List of workspace names to process
5
+ */
3
6
  workspaces: string[];
7
+ /**
8
+ * - Check mode - error if the generated content differs from current
9
+ */
4
10
  check?: boolean;
11
+ /**
12
+ * - Branch to compare PRs against (default: master)
13
+ */
5
14
  baseBranch?: string;
6
15
  };
7
16
  declare const _default: import('yargs').CommandModule<{}, Args>;
@@ -3,11 +3,29 @@ export type PublicPackage = import('../utils/pnpm.mjs').PublicPackage;
3
3
  export type PublishOptions = import('../utils/pnpm.mjs').PublishOptions;
4
4
  export type PublishSummaryEntry = import('../utils/pnpm.mjs').PublishSummaryEntry;
5
5
  export type Args = {
6
- dry-run: boolean;
7
- github-release: boolean;
6
+ /**
7
+ * Run in dry-run mode without publishing
8
+ */
9
+ "dry-run": boolean;
10
+ /**
11
+ * Create a GitHub draft release after publishing
12
+ */
13
+ "github-release": boolean;
14
+ /**
15
+ * NPM dist tag to publish to
16
+ */
8
17
  tag: string;
18
+ /**
19
+ * Runs in CI environment
20
+ */
9
21
  ci: boolean;
22
+ /**
23
+ * Git SHA to use for the GitHub release workflow (local only)
24
+ */
10
25
  sha?: string;
26
+ /**
27
+ * Same as filtering packages with --filter in pnpm. Only publish packages matching the filter. See https://pnpm.io/filtering.
28
+ */
11
29
  filter?: string[];
12
30
  };
13
31
  declare const _default: import('yargs').CommandModule<{}, Args>;
@@ -3,13 +3,31 @@ export type PublicPackage = import('../utils/pnpm.mjs').PublicPackage;
3
3
  export type VersionInfo = import('../utils/pnpm.mjs').VersionInfo;
4
4
  export type PublishOptions = import('../utils/pnpm.mjs').PublishOptions;
5
5
  export type Args = {
6
+ /**
7
+ * - Whether to run in dry-run mode
8
+ */
6
9
  dryRun?: boolean;
10
+ /**
11
+ * - Whether to create GitHub releases for canary packages
12
+ */
7
13
  githubRelease?: boolean;
14
+ /**
15
+ * - Same as filtering packages with --filter in pnpm. Only publish packages matching the filter. See https://pnpm.io/filtering.
16
+ */
8
17
  filter?: string[];
9
18
  };
10
19
  export type Commit = {
20
+ /**
21
+ * - Commit SHA
22
+ */
11
23
  sha: string;
24
+ /**
25
+ * - Commit message
26
+ */
12
27
  message: string;
28
+ /**
29
+ * - Commit author
30
+ */
13
31
  author: string;
14
32
  };
15
33
  declare const _default: import('yargs').CommandModule<{}, Args>;
@@ -1,5 +1,11 @@
1
1
  export type Args = {
2
+ /**
3
+ * If true, will only log the commands without executing them
4
+ */
2
5
  dryRun?: boolean;
6
+ /**
7
+ * 6 digit auth code to forward to npm for two-factor authentication
8
+ */
3
9
  otp?: string;
4
10
  };
5
11
  /**
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ export type Args = {
3
+ /**
4
+ * Source and target scope
5
+ */
6
+ alias: [string, string];
7
+ };
8
+ /**
9
+ * @typedef {Object} Args
10
+ * @property {[string, string]} alias Source and target scope
11
+ */
12
+ /**
13
+ * @param {string} alias
14
+ * @returns {[string, string]}
15
+ */
16
+ export declare function parseAlias(alias: string): [string, string];
17
+ declare const _default: import('yargs').CommandModule<{}, Args>;
18
+ export default _default;
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  export type Args = {
3
+ /**
4
+ * - Package version specifiers in format 'package@version'
5
+ */
3
6
  pkg?: string[];
4
7
  };
5
8
  declare const _default: import('yargs').CommandModule<{}, Args>;
@@ -1,5 +1,11 @@
1
1
  export type PatternConfig = {
2
+ /**
3
+ * - The pattern to match against resolved imports
4
+ */
2
5
  pattern: string;
6
+ /**
7
+ * - Custom message to show when the pattern matches
8
+ */
3
9
  message?: string;
4
10
  };
5
11
  /**
@@ -15,7 +15,13 @@ export declare function cjsCopy({ from, to }: {
15
15
  to: string;
16
16
  }): Promise<void>;
17
17
  export type ErrorCodeMetadata = {
18
+ /**
19
+ * - The path where the error code file should be written.
20
+ */
18
21
  outputPath: string;
22
+ /**
23
+ * - The runtime module to replace the errors with.
24
+ */
19
25
  runtimeModule?: string;
20
26
  };
21
27
  /**
@@ -2,6 +2,9 @@ export type BundleType = 'esm' | 'cjs';
2
2
  export type BundleMeta = {
3
3
  type: BundleType;
4
4
  dir: string;
5
+ /**
6
+ * - The package.json condition this bundle maps to.
7
+ */
5
8
  condition: 'import' | 'require';
6
9
  outExtension: string;
7
10
  typeOutExtension: string;
@@ -1,6 +1,15 @@
1
1
  export type Args = {
2
+ /**
3
+ * - The output path to write the extracted error codes.
4
+ */
2
5
  errorCodesPath: string;
6
+ /**
7
+ * - List of package names to skip. By default, all workspace packages are considered.
8
+ */
3
9
  skip?: string[];
10
+ /**
11
+ * - The detection strategy to use when extracting error codes.
12
+ */
4
13
  detection?: import('@mui/internal-babel-plugin-minify-errors').Options['detection'];
5
14
  };
6
15
  /**
@@ -1,6 +1,15 @@
1
1
  export type RepoInfo = {
2
+ /**
3
+ * - Repository owner
4
+ */
2
5
  owner: string;
6
+ /**
7
+ * - Repository name
8
+ */
3
9
  repo: string;
10
+ /**
11
+ * - Remote name
12
+ */
4
13
  remoteName: string;
5
14
  };
6
15
  /**
@@ -7,9 +7,21 @@
7
7
  *
8
8
  */
9
9
  export type GitHubAppAuthenticationWithRefreshToken = {
10
+ /**
11
+ * - The access token
12
+ */
10
13
  token: string;
14
+ /**
15
+ * - ISO string when the access token expires
16
+ */
11
17
  expiresAt?: string;
18
+ /**
19
+ * - The refresh token
20
+ */
12
21
  refreshToken?: string;
22
+ /**
23
+ * - ISO string when the refresh token expires
24
+ */
13
25
  refreshTokenExpiresAt?: string;
14
26
  };
15
27
  /**
@@ -1,35 +1,98 @@
1
1
  #!/usr/bin/env node
2
2
  export type PrivatePackage = {
3
+ /**
4
+ * - Package name
5
+ */
3
6
  name?: string;
7
+ /**
8
+ * - Package version
9
+ */
4
10
  version?: string;
11
+ /**
12
+ * - Package directory path
13
+ */
5
14
  path: string;
15
+ /**
16
+ * - Whether the package is private
17
+ */
6
18
  isPrivate: true;
7
19
  };
8
20
  export type PublicPackage = {
21
+ /**
22
+ * - Package name
23
+ */
9
24
  name: string;
25
+ /**
26
+ * - Package version
27
+ */
10
28
  version: string;
29
+ /**
30
+ * - Package directory path
31
+ */
11
32
  path: string;
33
+ /**
34
+ * - Whether the package is private
35
+ */
12
36
  isPrivate: false;
13
37
  };
14
38
  export type VersionInfo = {
39
+ /**
40
+ * - Whether current version exists on npm
41
+ */
15
42
  currentVersionExists: boolean;
43
+ /**
44
+ * - Latest canary version if available
45
+ */
16
46
  latestCanaryVersion: string | null;
17
47
  };
18
48
  export type PublishOptions = {
49
+ /**
50
+ * - Whether to run in dry-run mode
51
+ */
19
52
  dryRun?: boolean;
53
+ /**
54
+ * - Whether to skip git checks
55
+ */
20
56
  noGitChecks?: boolean;
57
+ /**
58
+ * - NPM dist tag to publish to
59
+ */
21
60
  tag?: string;
22
61
  };
23
62
  export type PnpmListResultItem = {
63
+ /**
64
+ * - Package name
65
+ */
24
66
  name?: string;
67
+ /**
68
+ * - Package version
69
+ */
25
70
  version?: string;
71
+ /**
72
+ * - Package directory path
73
+ */
26
74
  path: string;
75
+ /**
76
+ * - Whether the package is private
77
+ */
27
78
  private: boolean;
28
79
  };
29
80
  export type GetWorkspacePackagesOptions = {
81
+ /**
82
+ * - Git reference to filter changes since
83
+ */
30
84
  sinceRef?: string | null;
85
+ /**
86
+ * - Whether to filter to only public packages
87
+ */
31
88
  publicOnly?: boolean;
89
+ /**
90
+ * - Current working directory to run pnpm command in
91
+ */
32
92
  cwd?: string;
93
+ /**
94
+ * - Same as filtering packages with --filter in pnpm. Only include packages matching the filter. See https://pnpm.io/filtering.
95
+ */
33
96
  filter?: string[];
34
97
  };
35
98
  export declare function getWorkspacePackages(options?: {
@@ -90,19 +153,22 @@ export type PublishSummaryEntry = {
90
153
  */
91
154
  export declare function publishPackages(packages: PublicPackage[], options?: PublishOptions): Promise<PublishSummaryEntry[]>;
92
155
  export type GetTransitiveDependenciesOptions = {
156
+ /**
157
+ * - Map of workspace package name to directory path
158
+ */
93
159
  workspacePathByName?: Map<string, string>;
160
+ /**
161
+ * - Whether to include devDependencies in the traversal
162
+ */
94
163
  includeDev?: boolean;
95
164
  };
96
- /**
97
- * @typedef {Object} GetTransitiveDependenciesOptions
98
- * @property {Map<string, string>} [workspacePathByName] - Map of workspace package name to directory path
99
- * @property {boolean} [includeDev=true] - Whether to include devDependencies in the traversal
100
- */
101
165
  /**
102
166
  * Get all transitive workspace dependencies for a set of packages.
103
167
  *
104
168
  * Only follows deps whose version spec starts with `workspace:` (e.g. `workspace:*`
105
- * or `workspace:^`), meaning they are sourced directly from the monorepo. Pinned
169
+ * or `workspace:^`), meaning they are sourced directly from the monorepo. An
170
+ * alias spec (`workspace:@scope/name@range`) is followed to the package it
171
+ * targets, which need not match the dependency key. Pinned
106
172
  * external versions (e.g. `^1.0.0`) are ignored even when the package name exists
107
173
  * in the workspace. Traverses `dependencies` and optionally `devDependencies`.
108
174
  * Results are cached per package so each package is read from disk at most once
@@ -171,6 +237,21 @@ export declare function writePackageJson(packagePath: string, packageJson: Objec
171
237
  * @returns {Promise<void>}
172
238
  */
173
239
  export declare function writeOverridesToWorkspace(workspaceDir: string, overrides: Record<string, string>): Promise<void>;
240
+ /**
241
+ * Move the publishable workspace packages in one scope to another.
242
+ *
243
+ * Only packages that are part of the workspace are touched, so dependencies
244
+ * that merely share the scope but come from the registry (say `@base-ui/react`
245
+ * alongside a workspace `@base-ui/mosaic`) are left alone. Dependents keep the
246
+ * original dependency name and gain a `workspace:` alias, so imports in the
247
+ * repo resolve exactly as before.
248
+ *
249
+ * @param {(PublicPackage | PrivatePackage)[]} packages - All workspace packages
250
+ * @param {string} fromScope - Scope to move away from
251
+ * @param {string} toScope - Scope to move to
252
+ * @returns {Promise<Map<string, string>>} Old package name to new package name
253
+ */
254
+ export declare function renameWorkspaceScope(packages: (PublicPackage | PrivatePackage)[], fromScope: string, toScope: string): Promise<Map<string, string>>;
174
255
  /**
175
256
  * Resolve a package@version specifier to an exact version
176
257
  * @param {string} packageSpec - Package specifier in format "package@version"
@@ -5,3 +5,24 @@
5
5
  * @returns {Promise<string>} The path of the created temporary directory.
6
6
  */
7
7
  export declare function makeTempDir(): Promise<string>;
8
+ /**
9
+ * Write a package.json into a subdirectory of a temp workspace.
10
+ *
11
+ * @param {string} root - Workspace root
12
+ * @param {string} dir - Subdirectory to create
13
+ * @param {object} pkgJson - Manifest contents
14
+ * @returns {Promise<string>} The package directory
15
+ */
16
+ export declare function writePackage(root: string, dir: string, pkgJson: object): Promise<string>;
17
+ /**
18
+ * @param {string} name
19
+ * @param {string} pkgPath
20
+ * @returns {import('./pnpm.mjs').PublicPackage}
21
+ */
22
+ export declare function publicPkg(name: string, pkgPath: string): import('./pnpm.mjs').PublicPackage;
23
+ /**
24
+ * @param {string} name
25
+ * @param {string} pkgPath
26
+ * @returns {import('./pnpm.mjs').PrivatePackage}
27
+ */
28
+ export declare function privatePkg(name: string, pkgPath: string): import('./pnpm.mjs').PrivatePackage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/internal-code-infra",
3
- "version": "0.0.4-canary.97",
3
+ "version": "0.0.4-canary.99",
4
4
  "author": "MUI Team",
5
5
  "description": "Infra scripts and configs to be used across MUI repos.",
6
6
  "license": "MIT",
@@ -82,12 +82,12 @@
82
82
  "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
83
83
  "babel-plugin-transform-remove-imports": "^1.8.1",
84
84
  "chalk": "^5.6.2",
85
- "clipboardy": "^5.3.1",
85
+ "clipboardy": "^5.3.2",
86
86
  "content-type": "^2.0.0",
87
87
  "env-ci": "^11.2.0",
88
88
  "es-toolkit": "^1.49.0",
89
89
  "eslint-config-prettier": "^10.1.8",
90
- "eslint-import-resolver-typescript": "^4.4.4",
90
+ "eslint-import-resolver-typescript": "^4.4.5",
91
91
  "eslint-module-utils": "^2.14.0",
92
92
  "eslint-plugin-compat": "^7.0.2",
93
93
  "eslint-plugin-import": "^2.32.0",
@@ -101,12 +101,12 @@
101
101
  "execa": "^9.6.1",
102
102
  "git-url-parse": "^16.1.0",
103
103
  "globals": "^17.7.0",
104
- "globby": "^16.2.0",
105
- "html-validate": "^11.5.5",
104
+ "globby": "^16.2.2",
105
+ "html-validate": "^11.5.6",
106
106
  "minimatch": "^10.2.5",
107
107
  "node-html-parser": "^9.0.0",
108
108
  "open": "^11.0.0",
109
- "postcss-styled-syntax": "^0.7.1",
109
+ "postcss-styled-syntax": "^0.7.2",
110
110
  "regexp.escape": "^2.0.1",
111
111
  "rehype-slug": "^6.0.0",
112
112
  "rehype-stringify": "^10.0.1",
@@ -127,7 +127,7 @@
127
127
  "remark-parse": "^11.0.0",
128
128
  "remark-rehype": "^11.1.2",
129
129
  "resolve-pkg-maps": "^1.0.0",
130
- "semver": "^7.8.0",
130
+ "semver": "^7.8.5",
131
131
  "stylelint-config-standard": "^40.0.0",
132
132
  "typescript-eslint": "^8.64.0",
133
133
  "unified": "^11.0.5",
@@ -136,8 +136,8 @@
136
136
  "yaml": "^2.9.0",
137
137
  "yargs": "^18.0.0",
138
138
  "@mui/internal-babel-plugin-display-name": "1.0.4-canary.23",
139
- "@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.30",
140
- "@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.40"
139
+ "@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.41",
140
+ "@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.30"
141
141
  },
142
142
  "peerDependencies": {
143
143
  "@next/eslint-plugin-next": "*",
@@ -188,7 +188,7 @@
188
188
  "publishConfig": {
189
189
  "access": "public"
190
190
  },
191
- "gitSha": "f5098e75e68dc8dcfc3c4535b1a0a3fb97c6c68d",
191
+ "gitSha": "327b1eabb817e566026fbeb46767736701766d0d",
192
192
  "scripts": {
193
193
  "build": "tsgo -p tsconfig.build.json",
194
194
  "typescript": "tsgo -noEmit",
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+
3
+ /* eslint-disable no-console */
4
+
5
+ import chalk from 'chalk';
6
+
7
+ import { getWorkspacePackages, renameWorkspaceScope } from '../utils/pnpm.mjs';
8
+
9
+ /**
10
+ * A scope is a single `@`-prefixed segment. Letting a `/` through would build
11
+ * names like `@acme/private/pkg`, which only npm rejects, long after every
12
+ * matching manifest has been rewritten.
13
+ */
14
+ const SCOPE_PATTERN = /^@[a-z0-9-~][a-z0-9-._~]*$/;
15
+
16
+ /**
17
+ * @typedef {Object} Args
18
+ * @property {[string, string]} alias Source and target scope
19
+ */
20
+
21
+ /**
22
+ * @param {string} alias
23
+ * @returns {[string, string]}
24
+ */
25
+ export function parseAlias(alias) {
26
+ const parts = alias.split(':');
27
+ const [from, to] = parts;
28
+ if (parts.length !== 2 || !SCOPE_PATTERN.test(from) || !SCOPE_PATTERN.test(to)) {
29
+ throw new Error(
30
+ `Invalid scope mapping "${alias}". Expected exactly two npm scopes separated by a colon, e.g. "@acme:@acme-private".`,
31
+ );
32
+ }
33
+ if (from === to) {
34
+ throw new Error(`Invalid scope mapping "${alias}". The source and target scope are the same.`);
35
+ }
36
+ return [from, to];
37
+ }
38
+
39
+ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
40
+ command: 'rename-scope <alias>',
41
+ describe: 'Move publishable workspace packages to a different npm scope',
42
+ builder: (yargs) =>
43
+ yargs
44
+ .positional('alias', {
45
+ type: 'string',
46
+ describe: 'Scope mapping written as "@from:@to"',
47
+ coerce: parseAlias,
48
+ })
49
+ .example(
50
+ '$0 rename-scope @acme:@acme-private',
51
+ 'Publish the workspace @acme packages under @acme-private',
52
+ ),
53
+ handler: async (argv) => {
54
+ const [from, to] = argv.alias;
55
+ const packages = await getWorkspacePackages();
56
+ const renamed = await renameWorkspaceScope(packages, from, to);
57
+
58
+ // Matching nothing means the mapping is wrong or stale. Carrying on would
59
+ // publish under the original scope, so fail instead of reporting success.
60
+ if (renamed.size === 0) {
61
+ throw new Error(
62
+ `No publishable workspace packages found in ${from}. Check the scope mapping "${from}:${to}", or the workspace may already have been renamed.`,
63
+ );
64
+ }
65
+
66
+ for (const [oldName, newName] of renamed) {
67
+ console.log(`📦 ${oldName} → ${chalk.bold(newName)}`);
68
+ }
69
+
70
+ console.log(
71
+ chalk.yellow(
72
+ '\n⚠️ Workspace manifests were rewritten in place and are not restored. Discard them before committing.',
73
+ ),
74
+ );
75
+ },
76
+ });
package/src/cli/index.mjs CHANGED
@@ -14,6 +14,7 @@ import cmdNetlifyIgnore from './cmdNetlifyIgnore.mjs';
14
14
  import cmdPublish from './cmdPublish.mjs';
15
15
  import cmdPublishCanary from './cmdPublishCanary.mjs';
16
16
  import cmdPublishNewPackage from './cmdPublishNewPackage.mjs';
17
+ import cmdRenameScope from './cmdRenameScope.mjs';
17
18
  import cmdSetVersionOverrides from './cmdSetVersionOverrides.mjs';
18
19
  import cmdVale from './cmdVale.mjs';
19
20
  import cmdValidateBuiltTypes from './cmdValidateBuiltTypes.mjs';
@@ -47,6 +48,7 @@ await yargs(hideBin(process.argv))
47
48
  .command(cmdPublish)
48
49
  .command(cmdPublishCanary)
49
50
  .command(cmdPublishNewPackage)
51
+ .command(cmdRenameScope)
50
52
  .command(cmdSetVersionOverrides)
51
53
  .command(cmdVale)
52
54
  .command(cmdValidateBuiltTypes)
@@ -1,4 +1,4 @@
1
- import postcssStylesSyntax from 'postcss-styled-syntax';
1
+ import * as postcssStyledSyntax from 'postcss-styled-syntax';
2
2
 
3
3
  /** @type {import('stylelint').Config} */
4
4
  export default {
@@ -39,7 +39,7 @@ export default {
39
39
  overrides: [
40
40
  {
41
41
  files: ['**/*.?(c|m)[jt]s?(x)'],
42
- customSyntax: /** @type {any} */ (postcssStylesSyntax),
42
+ customSyntax: postcssStyledSyntax,
43
43
  },
44
44
  ],
45
45
  };
@@ -169,8 +169,8 @@ declare module 'stylelint-config-standard' {
169
169
  export default configExtends;
170
170
  }
171
171
  declare module 'postcss-styled-syntax' {
172
- import type { Syntax } from 'postcss';
172
+ import type { Parser, Stringifier } from 'postcss';
173
173
 
174
- declare const syntax: Syntax;
175
- export default syntax;
174
+ export const parse: Parser;
175
+ export const stringify: Stringifier;
176
176
  }
@@ -303,11 +303,29 @@ export async function publishPackages(packages, options = {}) {
303
303
  * @property {boolean} [includeDev=true] - Whether to include devDependencies in the traversal
304
304
  */
305
305
 
306
+ /**
307
+ * The package a `workspace:` alias spec resolves to, if it is one.
308
+ *
309
+ * @param {string} spec - Dependency spec
310
+ * @returns {string | null} The aliased package name, or null for a plain spec
311
+ */
312
+ function aliasTarget(spec) {
313
+ if (!spec.startsWith('workspace:')) {
314
+ return null;
315
+ }
316
+ const range = spec.slice('workspace:'.length);
317
+ // Search from 1 so a scoped name's own leading `@` isn't read as the separator.
318
+ const separator = range.indexOf('@', 1);
319
+ return separator === -1 ? null : range.slice(0, separator);
320
+ }
321
+
306
322
  /**
307
323
  * Get all transitive workspace dependencies for a set of packages.
308
324
  *
309
325
  * Only follows deps whose version spec starts with `workspace:` (e.g. `workspace:*`
310
- * or `workspace:^`), meaning they are sourced directly from the monorepo. Pinned
326
+ * or `workspace:^`), meaning they are sourced directly from the monorepo. An
327
+ * alias spec (`workspace:@scope/name@range`) is followed to the package it
328
+ * targets, which need not match the dependency key. Pinned
311
329
  * external versions (e.g. `^1.0.0`) are ignored even when the package name exists
312
330
  * in the workspace. Traverses `dependencies` and optionally `devDependencies`.
313
331
  * Results are cached per package so each package is read from disk at most once
@@ -344,14 +362,15 @@ export async function getTransitiveDependencies(packageNames, options = {}) {
344
362
  ...Object.entries(pkgJson.dependencies ?? {}),
345
363
  ...(includeDev ? Object.entries(pkgJson.devDependencies ?? {}) : []),
346
364
  ];
347
- const workspaceDeps = allDepEntries
348
- .filter(
349
- ([dep, spec]) =>
350
- workspacePathByName.has(dep) &&
351
- typeof spec === 'string' &&
352
- spec.startsWith('workspace:'),
353
- )
354
- .map(([dep]) => dep);
365
+ const workspaceDeps = allDepEntries.flatMap(([dep, spec]) => {
366
+ if (typeof spec !== 'string' || !spec.startsWith('workspace:')) {
367
+ return [];
368
+ }
369
+ // An aliased spec (`workspace:@scope/name@range`) names the workspace
370
+ // package it resolves to, which need not match the dependency key.
371
+ const name = aliasTarget(spec) ?? dep;
372
+ return workspacePathByName.has(name) ? [name] : [];
373
+ });
355
374
 
356
375
  const recursiveResults = await Promise.all(workspaceDeps.map(collectDeps));
357
376
  return new Set([...workspaceDeps, ...recursiveResults.flatMap((s) => [...s])]);
@@ -540,6 +559,151 @@ export async function writeOverridesToWorkspace(workspaceDir, overrides) {
540
559
  await fs.writeFile(workspaceYamlPath, doc.toString());
541
560
  }
542
561
 
562
+ /**
563
+ * Map a package name onto a different npm scope.
564
+ * @param {string} name - Package name, e.g. `@base-ui/mosaic`
565
+ * @param {string} fromScope - Scope to replace, e.g. `@base-ui`
566
+ * @param {string} toScope - Replacement scope, e.g. `@base-ui-private`
567
+ * @returns {string | null} The renamed package, or null when the scope doesn't match
568
+ */
569
+ function renameScope(name, fromScope, toScope) {
570
+ const prefix = `${fromScope}/`;
571
+ return name.startsWith(prefix) ? `${toScope}/${name.slice(prefix.length)}` : null;
572
+ }
573
+
574
+ /**
575
+ * Move the publishable workspace packages in one scope to another.
576
+ *
577
+ * Only packages that are part of the workspace are touched, so dependencies
578
+ * that merely share the scope but come from the registry (say `@base-ui/react`
579
+ * alongside a workspace `@base-ui/mosaic`) are left alone. Dependents keep the
580
+ * original dependency name and gain a `workspace:` alias, so imports in the
581
+ * repo resolve exactly as before.
582
+ *
583
+ * @param {(PublicPackage | PrivatePackage)[]} packages - All workspace packages
584
+ * @param {string} fromScope - Scope to move away from
585
+ * @param {string} toScope - Scope to move to
586
+ * @returns {Promise<Map<string, string>>} Old package name to new package name
587
+ */
588
+ export async function renameWorkspaceScope(packages, fromScope, toScope) {
589
+ // Renaming a scope onto itself only rewrites every manifest to itself. The
590
+ // CLI rejects this earlier, but the guard belongs with the operation so a
591
+ // direct caller cannot trip the silent churn either.
592
+ if (fromScope === toScope) {
593
+ throw new Error(`Cannot rename ${fromScope} to itself.`);
594
+ }
595
+
596
+ /** @type {Map<string, string>} */
597
+ const renamed = new Map();
598
+
599
+ for (const pkg of packages) {
600
+ if (pkg.isPrivate) {
601
+ continue;
602
+ }
603
+ const newName = renameScope(pkg.name, fromScope, toScope);
604
+ if (newName) {
605
+ renamed.set(pkg.name, newName);
606
+ }
607
+ }
608
+
609
+ // A rename that lands on a name another package keeps would leave the
610
+ // workspace with two packages sharing a name. Catch it up front, before any
611
+ // manifest is written.
612
+ /** @type {Map<string, string>} */
613
+ const finalNames = new Map();
614
+ for (const pkg of packages) {
615
+ if (!pkg.name) {
616
+ continue;
617
+ }
618
+ const finalName = renamed.get(pkg.name) ?? pkg.name;
619
+ const owner = finalNames.get(finalName);
620
+ if (owner) {
621
+ throw new Error(
622
+ `Cannot rename ${fromScope} to ${toScope}: ${pkg.name} and ${owner} would both be named ${finalName}.`,
623
+ );
624
+ }
625
+ finalNames.set(finalName, pkg.name);
626
+ }
627
+
628
+ // Rewrite in memory first. A dependency that cannot be pointed at its renamed
629
+ // package has to fail before anything is written, or the workspace is left
630
+ // half renamed with nothing to restore it.
631
+ const rewritten = await Promise.all(
632
+ packages.map(async (pkg) => {
633
+ const packageJson = await readPackageJson(pkg.path);
634
+ const label = pkg.name ?? pkg.path;
635
+ /** @type {string[]} */
636
+ const problems = [];
637
+ let changed = false;
638
+
639
+ const newName = pkg.name ? renamed.get(pkg.name) : undefined;
640
+ if (newName) {
641
+ packageJson.name = newName;
642
+ changed = true;
643
+ }
644
+
645
+ // peerDependencies are deliberately absent: a peer is supplied by the
646
+ // consumer, who installs the package under its original name. An alias
647
+ // range would be unsatisfiable for them.
648
+ for (const deps of [
649
+ packageJson.dependencies,
650
+ packageJson.devDependencies,
651
+ packageJson.optionalDependencies,
652
+ ]) {
653
+ if (!deps) {
654
+ continue;
655
+ }
656
+ for (const [depName, spec] of Object.entries(deps)) {
657
+ if (!spec) {
658
+ continue;
659
+ }
660
+
661
+ const existingTarget = aliasTarget(spec);
662
+ if (existingTarget) {
663
+ if (renamed.has(existingTarget)) {
664
+ problems.push(
665
+ `"${depName}" in ${label} already aliases ${existingTarget}, which is being renamed. Point it at the package directly so it can be rewritten.`,
666
+ );
667
+ }
668
+ // Otherwise it already aliases what it should, as a re-run does.
669
+ continue;
670
+ }
671
+
672
+ const target = renamed.get(depName);
673
+ if (!target) {
674
+ continue;
675
+ }
676
+ if (!spec.startsWith('workspace:')) {
677
+ // Only `workspace:` specs can be aliased. Anything else would keep
678
+ // resolving the original name from the registry after the rename.
679
+ problems.push(
680
+ `"${depName}" in ${label} is required as "${spec}" rather than a workspace: dependency, so it cannot be pointed at ${target}.`,
681
+ );
682
+ continue;
683
+ }
684
+ deps[depName] = `workspace:${target}@${spec.slice('workspace:'.length)}`;
685
+ changed = true;
686
+ }
687
+ }
688
+
689
+ return { path: pkg.path, packageJson, changed, problems };
690
+ }),
691
+ );
692
+
693
+ const problems = rewritten.flatMap((entry) => entry.problems);
694
+ if (problems.length > 0) {
695
+ throw new Error(`Cannot rename ${fromScope} to ${toScope}:\n ${problems.join('\n ')}`);
696
+ }
697
+
698
+ await Promise.all(
699
+ rewritten
700
+ .filter((entry) => entry.changed)
701
+ .map((entry) => writePackageJson(entry.path, entry.packageJson)),
702
+ );
703
+
704
+ return renamed;
705
+ }
706
+
543
707
  /**
544
708
  * Resolve a package@version specifier to an exact version
545
709
  * @param {string} packageSpec - Package specifier in format "package@version"
@@ -3,6 +3,8 @@ import * as os from 'node:os';
3
3
  import * as path from 'node:path';
4
4
  import { onTestFinished } from 'vitest';
5
5
 
6
+ import { writePackageJson } from './pnpm.mjs';
7
+
6
8
  /**
7
9
  * Creates a temporary directory and registers an `onTestFinished` hook to
8
10
  * remove it automatically when the current test ends — even if the test throws.
@@ -16,3 +18,36 @@ export async function makeTempDir() {
16
18
  });
17
19
  return tmpDir;
18
20
  }
21
+
22
+ /**
23
+ * Write a package.json into a subdirectory of a temp workspace.
24
+ *
25
+ * @param {string} root - Workspace root
26
+ * @param {string} dir - Subdirectory to create
27
+ * @param {object} pkgJson - Manifest contents
28
+ * @returns {Promise<string>} The package directory
29
+ */
30
+ export async function writePackage(root, dir, pkgJson) {
31
+ const pkgDir = path.join(root, dir);
32
+ await fs.mkdir(pkgDir, { recursive: true });
33
+ await writePackageJson(pkgDir, pkgJson);
34
+ return pkgDir;
35
+ }
36
+
37
+ /**
38
+ * @param {string} name
39
+ * @param {string} pkgPath
40
+ * @returns {import('./pnpm.mjs').PublicPackage}
41
+ */
42
+ export function publicPkg(name, pkgPath) {
43
+ return { name, version: '1.0.0', path: pkgPath, isPrivate: false };
44
+ }
45
+
46
+ /**
47
+ * @param {string} name
48
+ * @param {string} pkgPath
49
+ * @returns {import('./pnpm.mjs').PrivatePackage}
50
+ */
51
+ export function privatePkg(name, pkgPath) {
52
+ return { name, version: '1.0.0', path: pkgPath, isPrivate: true };
53
+ }