@mui/internal-code-infra 0.0.3-canary.6 → 0.0.3-canary.61
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/README.md +55 -0
- package/build/babel-config.d.mts +40 -0
- package/build/brokenLinksChecker/index.d.mts +138 -0
- package/build/cli/cmdArgosPush.d.mts +13 -0
- package/build/cli/cmdBuild.d.mts +56 -0
- package/build/cli/cmdCopyFiles.d.mts +20 -0
- package/build/cli/cmdExtractErrorCodes.d.mts +3 -0
- package/build/cli/cmdGithubAuth.d.mts +6 -0
- package/build/cli/cmdListWorkspaces.d.mts +18 -0
- package/build/cli/cmdPublish.d.mts +27 -0
- package/build/cli/cmdPublishCanary.d.mts +30 -0
- package/build/cli/cmdPublishNewPackage.d.mts +8 -0
- package/build/cli/cmdSetVersionOverrides.d.mts +9 -0
- package/build/cli/cmdValidateBuiltTypes.d.mts +2 -0
- package/build/cli/index.d.mts +1 -0
- package/build/eslint/baseConfig.d.mts +10 -0
- package/build/eslint/docsConfig.d.mts +4 -0
- package/build/eslint/extensions.d.mts +8 -0
- package/build/eslint/index.d.mts +4 -0
- package/build/eslint/jsonConfig.d.mts +4 -0
- package/build/eslint/material-ui/config.d.mts +8 -0
- package/build/eslint/material-ui/index.d.mts +2 -0
- package/build/eslint/material-ui/rules/disallow-active-element-as-key-event-target.d.mts +5 -0
- package/build/eslint/material-ui/rules/disallow-react-api-in-server-components.d.mts +2 -0
- package/build/eslint/material-ui/rules/docgen-ignore-before-comment.d.mts +2 -0
- package/build/eslint/material-ui/rules/mui-name-matches-component-name.d.mts +5 -0
- package/build/eslint/material-ui/rules/no-empty-box.d.mts +5 -0
- package/build/eslint/material-ui/rules/no-restricted-resolved-imports.d.mts +12 -0
- package/build/eslint/material-ui/rules/no-styled-box.d.mts +5 -0
- package/build/eslint/material-ui/rules/rules-of-use-theme-variants.d.mts +9 -0
- package/build/eslint/material-ui/rules/straight-quotes.d.mts +5 -0
- package/build/eslint/testConfig.d.mts +14 -0
- package/build/markdownlint/duplicate-h1.d.mts +27 -0
- package/build/markdownlint/git-diff.d.mts +8 -0
- package/build/markdownlint/index.d.mts +56 -0
- package/build/markdownlint/straight-quotes.d.mts +8 -0
- package/build/markdownlint/table-alignment.d.mts +8 -0
- package/build/markdownlint/terminal-language.d.mts +8 -0
- package/build/prettier.d.mts +20 -0
- package/build/stylelint/index.d.mts +32 -0
- package/build/utils/babel.d.mts +71 -0
- package/build/utils/build.d.mts +50 -0
- package/build/utils/changelog.d.mts +64 -0
- package/build/utils/credentials.d.mts +17 -0
- package/build/utils/extractErrorCodes.d.mts +19 -0
- package/build/utils/git.d.mts +26 -0
- package/build/utils/github.d.mts +41 -0
- package/build/utils/pnpm.d.mts +238 -0
- package/build/utils/typescript.d.mts +35 -0
- package/package.json +92 -42
- package/src/babel-config.mjs +52 -8
- package/src/brokenLinksChecker/__fixtures__/static-site/broken-links.html +20 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/broken-targets.html +22 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/example.md +9 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/external-links.html +21 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/ignored-page.html +17 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/index.html +26 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/known-targets.json +5 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/nested/page.html +19 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/orphaned-page.html +20 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/page-with-api-links.html +20 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/page-with-custom-targets.html +24 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/page-with-ignored-content.html +28 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/page-with-known-target-links.html +19 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/valid.html +20 -0
- package/src/brokenLinksChecker/__fixtures__/static-site/with-anchors.html +31 -0
- package/src/brokenLinksChecker/index.mjs +641 -0
- package/src/brokenLinksChecker/index.test.ts +178 -0
- package/src/cli/cmdArgosPush.mjs +13 -2
- package/src/cli/cmdBuild.mjs +228 -31
- package/src/cli/cmdGithubAuth.mjs +36 -0
- package/src/cli/cmdListWorkspaces.mjs +2 -2
- package/src/cli/cmdPublish.mjs +203 -49
- package/src/cli/cmdPublishCanary.mjs +404 -46
- package/src/cli/cmdPublishNewPackage.mjs +86 -0
- package/src/cli/cmdSetVersionOverrides.mjs +17 -1
- package/src/cli/cmdValidateBuiltTypes.mjs +49 -0
- package/src/cli/index.mjs +6 -2
- package/src/cli/packageJson.d.ts +729 -0
- package/src/eslint/baseConfig.mjs +96 -78
- package/src/eslint/docsConfig.mjs +26 -13
- package/src/eslint/extensions.mjs +8 -8
- package/src/eslint/jsonConfig.mjs +40 -0
- package/src/eslint/material-ui/config.mjs +8 -9
- package/src/eslint/material-ui/rules/mui-name-matches-component-name.mjs +4 -2
- package/src/eslint/material-ui/rules/rules-of-use-theme-variants.mjs +2 -1
- package/src/eslint/testConfig.mjs +72 -66
- package/src/stylelint/index.mjs +46 -0
- package/src/untyped-plugins.d.ts +13 -0
- package/src/{cli → utils}/babel.mjs +10 -3
- package/src/utils/build.mjs +27 -1
- package/src/utils/changelog.mjs +157 -0
- package/src/utils/credentials.mjs +71 -0
- package/src/utils/extractErrorCodes.mjs +2 -2
- package/src/utils/git.mjs +67 -0
- package/src/utils/github.mjs +263 -0
- package/src/{cli → utils}/pnpm.mjs +23 -13
- package/src/{cli → utils}/typescript.mjs +13 -7
- package/src/cli/cmdJsonLint.mjs +0 -69
|
@@ -3,31 +3,371 @@
|
|
|
3
3
|
/* eslint-disable no-console */
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @typedef {import('
|
|
7
|
-
* @typedef {import('
|
|
8
|
-
* @typedef {import('
|
|
6
|
+
* @typedef {import('../utils/pnpm.mjs').PublicPackage} PublicPackage
|
|
7
|
+
* @typedef {import('../utils/pnpm.mjs').VersionInfo} VersionInfo
|
|
8
|
+
* @typedef {import('../utils/pnpm.mjs').PublishOptions} PublishOptions
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { createActionAuth } from '@octokit/auth-action';
|
|
13
|
+
import { Octokit } from '@octokit/rest';
|
|
11
14
|
import { $ } from 'execa';
|
|
12
15
|
import * as semver from 'semver';
|
|
13
16
|
|
|
14
|
-
/**
|
|
15
|
-
* @typedef {Object} Args
|
|
16
|
-
* @property {boolean} [dryRun] - Whether to run in dry-run mode
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
17
|
import {
|
|
20
|
-
getWorkspacePackages,
|
|
21
18
|
getPackageVersionInfo,
|
|
19
|
+
getWorkspacePackages,
|
|
22
20
|
publishPackages,
|
|
23
21
|
readPackageJson,
|
|
24
|
-
writePackageJson,
|
|
25
|
-
getCurrentGitSha,
|
|
26
22
|
semverMax,
|
|
27
|
-
|
|
23
|
+
writePackageJson,
|
|
24
|
+
} from '../utils/pnpm.mjs';
|
|
25
|
+
import { getCurrentGitSha, getRepositoryInfo } from '../utils/git.mjs';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Object} Args
|
|
29
|
+
* @property {boolean} [dryRun] - Whether to run in dry-run mode
|
|
30
|
+
* @property {boolean} [githubRelease] - Whether to create GitHub releases for canary packages
|
|
31
|
+
*/
|
|
28
32
|
|
|
29
33
|
const CANARY_TAG = 'canary';
|
|
30
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Get Octokit instance with authentication
|
|
37
|
+
* @returns {Octokit} Authenticated Octokit instance
|
|
38
|
+
*/
|
|
39
|
+
function getOctokit() {
|
|
40
|
+
return new Octokit({ authStrategy: createActionAuth });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {Object} Commit
|
|
45
|
+
* @property {string} sha - Commit SHA
|
|
46
|
+
* @property {string} message - Commit message
|
|
47
|
+
* @property {string} author - Commit author
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {Object} param0
|
|
52
|
+
* @param {string} param0.packagePath
|
|
53
|
+
* @returns {Promise<Commit[]>} Commits between the tag and current HEAD for the package
|
|
54
|
+
*/
|
|
55
|
+
async function fetchCommitsForPackage({ packagePath }) {
|
|
56
|
+
/**
|
|
57
|
+
* @type {Commit[]}
|
|
58
|
+
*/
|
|
59
|
+
const results = [];
|
|
60
|
+
const fieldSeparator = '\u001f'; // ASCII unit separator is extremely unlikely to appear in git metadata
|
|
61
|
+
const formatArg = '--format=%H%x1f%s%x1f%an%x1f%ae'; // SHA, subject, author name, author email separated by unit separator
|
|
62
|
+
const res = $`git log --oneline --no-decorate ${
|
|
63
|
+
// to avoid escaping by execa
|
|
64
|
+
[formatArg]
|
|
65
|
+
} ${CANARY_TAG}..HEAD -- ${packagePath}`;
|
|
66
|
+
for await (const line of res) {
|
|
67
|
+
const commitLine = line.trimEnd();
|
|
68
|
+
if (!commitLine) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const parts = commitLine.split(fieldSeparator);
|
|
72
|
+
if (parts.length < 3) {
|
|
73
|
+
console.error(`Failed to parse commit log line: ${commitLine}`);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const [sha, message, commitAuthor, commitEmail] = parts;
|
|
77
|
+
let author = commitAuthor;
|
|
78
|
+
// try to get github username from email
|
|
79
|
+
if (commitEmail) {
|
|
80
|
+
const emailUsername = commitEmail.split('@')[0];
|
|
81
|
+
if (emailUsername) {
|
|
82
|
+
const [, githubUserName] = emailUsername.split('+');
|
|
83
|
+
if (githubUserName) {
|
|
84
|
+
author = `@${githubUserName}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
results.push({ sha, message, author });
|
|
89
|
+
}
|
|
90
|
+
return results;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const AUTHOR_EXCLUDE_LIST = ['renovate[bot]', 'dependabot[bot]'];
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {string} message
|
|
97
|
+
* @returns {string}
|
|
98
|
+
*/
|
|
99
|
+
function cleanupCommitMessage(message) {
|
|
100
|
+
// AI generated: clean up commit message by removing leading bracketed tokens except [breaking]
|
|
101
|
+
let msg = message || '';
|
|
102
|
+
|
|
103
|
+
// Extract and remove leading bracketed tokens like "[foo][bar] message"
|
|
104
|
+
const tokens = [];
|
|
105
|
+
const bracketRe = /^\s*\[([^\]]+)\]\s*/;
|
|
106
|
+
let match = msg.match(bracketRe);
|
|
107
|
+
while (match) {
|
|
108
|
+
tokens.push(match[1]);
|
|
109
|
+
msg = msg.slice(match[0].length);
|
|
110
|
+
match = msg.match(bracketRe);
|
|
111
|
+
}
|
|
112
|
+
msg = msg.trim();
|
|
113
|
+
|
|
114
|
+
// If any of the leading tokens is "breaking" keep that token (preserve original casing)
|
|
115
|
+
const breakingToken = tokens.find((t) => t.toLowerCase() === 'breaking');
|
|
116
|
+
const prefix = breakingToken ? `[${breakingToken}]${msg ? ' ' : ''}` : '';
|
|
117
|
+
|
|
118
|
+
return `${prefix}${msg}`.trim();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function getPackageToDependencyMap() {
|
|
122
|
+
/**
|
|
123
|
+
* @type {(PublicPackage & { dependencies: Record<string, unknown>; private: boolean; })[]}
|
|
124
|
+
*/
|
|
125
|
+
const packagesWithDeps = JSON.parse(
|
|
126
|
+
(await $`pnpm ls -r --json --exclude-peers --only-projects --prod`).stdout,
|
|
127
|
+
);
|
|
128
|
+
/** @type {Record<string, string[]>} */
|
|
129
|
+
const directPkgDependencies = packagesWithDeps
|
|
130
|
+
.filter((pkg) => !pkg.private)
|
|
131
|
+
.reduce((acc, pkg) => {
|
|
132
|
+
if (!pkg.name) {
|
|
133
|
+
return acc;
|
|
134
|
+
}
|
|
135
|
+
const deps = Object.keys(pkg.dependencies || {});
|
|
136
|
+
if (!deps.length) {
|
|
137
|
+
return acc;
|
|
138
|
+
}
|
|
139
|
+
acc[pkg.name] = deps;
|
|
140
|
+
return acc;
|
|
141
|
+
}, /** @type {Record<string, string[]>} */ ({}));
|
|
142
|
+
return directPkgDependencies;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @param {Record<string, string[]>} pkgGraph
|
|
147
|
+
*/
|
|
148
|
+
function resolveTransitiveDependencies(pkgGraph = {}) {
|
|
149
|
+
// Compute transitive (nested) dependencies limited to workspace packages and avoid cycles.
|
|
150
|
+
const workspacePkgNames = new Set(Object.keys(pkgGraph));
|
|
151
|
+
const nestedMap = /** @type {Record<string, string[]>} */ ({});
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
*
|
|
155
|
+
* @param {string} pkgName
|
|
156
|
+
* @returns {string[]}
|
|
157
|
+
*/
|
|
158
|
+
const getTransitiveDeps = (pkgName) => {
|
|
159
|
+
/**
|
|
160
|
+
* @type {Set<string>}
|
|
161
|
+
*/
|
|
162
|
+
const seen = new Set();
|
|
163
|
+
const stack = (pkgGraph[pkgName] || []).slice();
|
|
164
|
+
|
|
165
|
+
while (stack.length) {
|
|
166
|
+
const dep = stack.pop();
|
|
167
|
+
if (!dep || seen.has(dep)) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
// Only consider workspace packages for transitive expansion
|
|
171
|
+
if (!workspacePkgNames.has(dep)) {
|
|
172
|
+
// still record external deps as direct deps but don't traverse into them
|
|
173
|
+
seen.add(dep);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
seen.add(dep);
|
|
177
|
+
const children = pkgGraph[dep] || [];
|
|
178
|
+
for (const c of children) {
|
|
179
|
+
if (!seen.has(c)) {
|
|
180
|
+
stack.push(c);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return Array.from(seen);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
for (const name of Object.keys(pkgGraph)) {
|
|
189
|
+
nestedMap[name] = getTransitiveDeps(name);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return nestedMap;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Prepare changelog data for packages using GitHub API
|
|
197
|
+
* @param {PublicPackage[]} packagesToPublish - Packages that will be published
|
|
198
|
+
* @param {PublicPackage[]} allPackages - All packages in the repository
|
|
199
|
+
* @param {Map<string, string>} canaryVersions - Map of package names to their canary versions
|
|
200
|
+
* @returns {Promise<Map<string, string[]>>} Map of package names to their changelogs
|
|
201
|
+
*/
|
|
202
|
+
async function prepareChangelogsFromGitCli(packagesToPublish, allPackages, canaryVersions) {
|
|
203
|
+
/**
|
|
204
|
+
* @type {Map<string, string[]>}
|
|
205
|
+
*/
|
|
206
|
+
const changelogs = new Map();
|
|
207
|
+
|
|
208
|
+
await Promise.all(
|
|
209
|
+
packagesToPublish.map(async (pkg) => {
|
|
210
|
+
const commits = await fetchCommitsForPackage({ packagePath: pkg.path });
|
|
211
|
+
if (commits.length > 0) {
|
|
212
|
+
console.log(`Found ${commits.length} commits for package ${pkg.name}`);
|
|
213
|
+
}
|
|
214
|
+
const changeLogStrs = commits
|
|
215
|
+
// Exclude commits authored by bots
|
|
216
|
+
.filter(
|
|
217
|
+
// We want to allow commits from copilot or other AI tools, so only filter known bots
|
|
218
|
+
(commit) => !AUTHOR_EXCLUDE_LIST.includes(commit.author),
|
|
219
|
+
)
|
|
220
|
+
.map((commit) => `- ${cleanupCommitMessage(commit.message)} by ${commit.author}`);
|
|
221
|
+
|
|
222
|
+
if (changeLogStrs.length > 0) {
|
|
223
|
+
changelogs.set(pkg.name, changeLogStrs);
|
|
224
|
+
}
|
|
225
|
+
}),
|
|
226
|
+
);
|
|
227
|
+
// Second pass: check for dependency updates in other packages not part of git history
|
|
228
|
+
const pkgDependencies = await getPackageToDependencyMap();
|
|
229
|
+
const transitiveDependencies = resolveTransitiveDependencies(pkgDependencies);
|
|
230
|
+
|
|
231
|
+
for (let i = 0; i < allPackages.length; i += 1) {
|
|
232
|
+
const pkg = allPackages[i];
|
|
233
|
+
const depsToPublish = (transitiveDependencies[pkg.name] ?? []).filter((dep) =>
|
|
234
|
+
packagesToPublish.some((p) => p.name === dep),
|
|
235
|
+
);
|
|
236
|
+
if (depsToPublish.length === 0) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const changelog = changelogs.get(pkg.name) ?? [];
|
|
240
|
+
changelog.push('- Updated dependencies:');
|
|
241
|
+
depsToPublish.forEach((dep) => {
|
|
242
|
+
const depVersion = canaryVersions.get(dep);
|
|
243
|
+
if (depVersion) {
|
|
244
|
+
changelog.push(` - Bumped \`${dep}@${depVersion}\``);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
return changelogs;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Prepare changelog data for packages
|
|
253
|
+
* @param {PublicPackage[]} packagesToPublish - Packages that will be published
|
|
254
|
+
* @param {PublicPackage[]} allPackages - All packages in the repository
|
|
255
|
+
* @param {Map<string, string>} canaryVersions - Map of package names to their canary versions
|
|
256
|
+
* @returns {Promise<Map<string, string[]>>} Map of package names to their changelogs
|
|
257
|
+
*/
|
|
258
|
+
async function prepareChangelogsForPackages(packagesToPublish, allPackages, canaryVersions) {
|
|
259
|
+
console.log('\n📝 Preparing changelogs for packages...');
|
|
260
|
+
|
|
261
|
+
const repoInfo = await getRepositoryInfo();
|
|
262
|
+
console.log(`📂 Repository: ${repoInfo.owner}/${repoInfo.repo}`);
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* @type {Map<string, string[]>}
|
|
266
|
+
*/
|
|
267
|
+
const changelogs = await prepareChangelogsFromGitCli(
|
|
268
|
+
packagesToPublish,
|
|
269
|
+
allPackages,
|
|
270
|
+
canaryVersions,
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Log changelog content for each package
|
|
274
|
+
for (const pkg of packagesToPublish) {
|
|
275
|
+
const version = canaryVersions.get(pkg.name);
|
|
276
|
+
if (!version) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const changelog = changelogs.get(pkg.name) || [];
|
|
281
|
+
console.log(`\n📦 ${pkg.name}@${version}`);
|
|
282
|
+
if (changelog.length > 0) {
|
|
283
|
+
console.log(
|
|
284
|
+
` Changelog:\n${changelog.map((/** @type {string} */ line) => ` ${line}`).join('\n')}`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
console.log('\n✅ Changelogs prepared successfully');
|
|
290
|
+
return changelogs;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Create GitHub releases and tags for published packages
|
|
295
|
+
* @param {PublicPackage[]} publishedPackages - Packages that were published
|
|
296
|
+
* @param {Map<string, string>} canaryVersions - Map of package names to their canary versions
|
|
297
|
+
* @param {Map<string, string[]>} changelogs - Map of package names to their changelogs
|
|
298
|
+
* @param {{dryRun?: boolean}} options - Publishing options
|
|
299
|
+
* @returns {Promise<void>}
|
|
300
|
+
*/
|
|
301
|
+
async function createGitHubReleasesForPackages(
|
|
302
|
+
publishedPackages,
|
|
303
|
+
canaryVersions,
|
|
304
|
+
changelogs,
|
|
305
|
+
options,
|
|
306
|
+
) {
|
|
307
|
+
console.log('\n🚀 Creating GitHub releases and tags for published packages...');
|
|
308
|
+
|
|
309
|
+
const repoInfo = await getRepositoryInfo();
|
|
310
|
+
const gitSha = await getCurrentGitSha();
|
|
311
|
+
const octokit = getOctokit();
|
|
312
|
+
|
|
313
|
+
for (const pkg of publishedPackages) {
|
|
314
|
+
const version = canaryVersions.get(pkg.name);
|
|
315
|
+
if (!version) {
|
|
316
|
+
console.log(`⚠️ No version found for ${pkg.name}, skipping...`);
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const changelog = changelogs.get(pkg.name);
|
|
321
|
+
if (!changelog) {
|
|
322
|
+
console.log(`⚠️ No changelog found for ${pkg.name}, skipping release creation...`);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const tagName = `${pkg.name}@${version}`;
|
|
326
|
+
const releaseName = tagName;
|
|
327
|
+
|
|
328
|
+
console.log(`\n📦 Processing ${pkg.name}@${version}...`);
|
|
329
|
+
|
|
330
|
+
// Create git tag
|
|
331
|
+
if (options.dryRun) {
|
|
332
|
+
console.log(`🏷️ Would create and push git tag: ${tagName} (dry-run)`);
|
|
333
|
+
console.log(`📝 Would publish a Github release:`);
|
|
334
|
+
console.log(` - Name: ${releaseName}`);
|
|
335
|
+
console.log(` - Tag: ${tagName}`);
|
|
336
|
+
console.log(` - Body:\n${changelog.join('\n')}`);
|
|
337
|
+
} else {
|
|
338
|
+
// eslint-disable-next-line no-await-in-loop
|
|
339
|
+
await $({
|
|
340
|
+
env: {
|
|
341
|
+
...process.env,
|
|
342
|
+
GIT_COMMITTER_NAME: 'Code infra',
|
|
343
|
+
GIT_COMMITTER_EMAIL: 'code-infra@mui.com',
|
|
344
|
+
},
|
|
345
|
+
})`git tag -a ${tagName} -m ${`Canary release ${pkg.name}@${version}`}`;
|
|
346
|
+
|
|
347
|
+
// eslint-disable-next-line no-await-in-loop
|
|
348
|
+
await $`git push origin ${tagName}`;
|
|
349
|
+
console.log(`✅ Created and pushed git tag: ${tagName}`);
|
|
350
|
+
|
|
351
|
+
// Create GitHub release
|
|
352
|
+
// eslint-disable-next-line no-await-in-loop
|
|
353
|
+
const res = await octokit.repos.createRelease({
|
|
354
|
+
owner: repoInfo.owner,
|
|
355
|
+
repo: repoInfo.repo,
|
|
356
|
+
tag_name: tagName,
|
|
357
|
+
target_commitish: gitSha,
|
|
358
|
+
name: releaseName,
|
|
359
|
+
body: changelog.join('\n'),
|
|
360
|
+
draft: false,
|
|
361
|
+
prerelease: true, // Mark as prerelease since these are canary versions
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
console.log(`✅ Created GitHub release: ${releaseName} at ${res.data.html_url}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
console.log('\n✅ Finished creating GitHub releases');
|
|
369
|
+
}
|
|
370
|
+
|
|
31
371
|
/**
|
|
32
372
|
* Check if the canary git tag exists
|
|
33
373
|
* @returns {Promise<string|null>} Canary tag name if exists, null otherwise
|
|
@@ -66,11 +406,13 @@ async function createCanaryTag(dryRun = false) {
|
|
|
66
406
|
}
|
|
67
407
|
|
|
68
408
|
/**
|
|
69
|
-
* Publish canary versions with updated dependencies
|
|
409
|
+
* Publish canary versions with updated dependencies. A big assumption here is that
|
|
410
|
+
* all packages are already built before calling this function.
|
|
411
|
+
*
|
|
70
412
|
* @param {PublicPackage[]} packagesToPublish - Packages that need canary publishing
|
|
71
413
|
* @param {PublicPackage[]} allPackages - All workspace packages
|
|
72
414
|
* @param {Map<string, VersionInfo>} packageVersionInfo - Version info map
|
|
73
|
-
* @param {PublishOptions} [options={}] - Publishing options
|
|
415
|
+
* @param {PublishOptions & {githubRelease?: boolean}} [options={}] - Publishing options
|
|
74
416
|
* @returns {Promise<void>}
|
|
75
417
|
*/
|
|
76
418
|
async function publishCanaryVersions(
|
|
@@ -90,7 +432,6 @@ async function publishCanaryVersions(
|
|
|
90
432
|
|
|
91
433
|
const gitSha = await getCurrentGitSha();
|
|
92
434
|
const canaryVersions = new Map();
|
|
93
|
-
const originalPackageJsons = new Map();
|
|
94
435
|
|
|
95
436
|
// First pass: determine canary version numbers for all packages
|
|
96
437
|
const changedPackageNames = new Set(packagesToPublish.map((pkg) => pkg.name));
|
|
@@ -117,41 +458,44 @@ async function publishCanaryVersions(
|
|
|
117
458
|
}
|
|
118
459
|
|
|
119
460
|
// Second pass: read and update ALL package.json files in parallel
|
|
461
|
+
// Packages are already built at this point.
|
|
120
462
|
const packageUpdatePromises = allPackages.map(async (pkg) => {
|
|
121
|
-
|
|
463
|
+
let basePkgJson = await readPackageJson(pkg.path);
|
|
464
|
+
let pkgJsonDirectory = pkg.path;
|
|
465
|
+
if (basePkgJson.publishConfig?.directory) {
|
|
466
|
+
pkgJsonDirectory = path.join(pkg.path, basePkgJson.publishConfig.directory);
|
|
467
|
+
basePkgJson = await readPackageJson(pkgJsonDirectory);
|
|
468
|
+
}
|
|
122
469
|
|
|
123
470
|
const canaryVersion = canaryVersions.get(pkg.name);
|
|
124
471
|
if (canaryVersion) {
|
|
125
472
|
const updatedPackageJson = {
|
|
126
|
-
...
|
|
473
|
+
...basePkgJson,
|
|
127
474
|
version: canaryVersion,
|
|
128
475
|
gitSha,
|
|
129
476
|
};
|
|
130
|
-
|
|
131
|
-
await writePackageJson(pkg.path, updatedPackageJson);
|
|
477
|
+
await writePackageJson(pkgJsonDirectory, updatedPackageJson);
|
|
132
478
|
console.log(`📝 Updated ${pkg.name} package.json to ${canaryVersion}`);
|
|
133
479
|
}
|
|
134
|
-
|
|
135
|
-
return { pkg, originalPackageJson };
|
|
480
|
+
return { pkg, basePkgJson, pkgJsonDirectory };
|
|
136
481
|
});
|
|
137
482
|
|
|
138
483
|
const updateResults = await Promise.all(packageUpdatePromises);
|
|
139
484
|
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
485
|
+
// Prepare changelogs before building and publishing (so it can error out early if there are issues)
|
|
486
|
+
/**
|
|
487
|
+
* @type {Map<string, string[]>}
|
|
488
|
+
*/
|
|
489
|
+
let changelogs = new Map();
|
|
490
|
+
if (options.githubRelease) {
|
|
491
|
+
changelogs = await prepareChangelogsForPackages(packagesToPublish, allPackages, canaryVersions);
|
|
143
492
|
}
|
|
144
493
|
|
|
145
|
-
// Run release build after updating package.json files
|
|
146
|
-
console.log('\n🔨 Running release build...');
|
|
147
|
-
await $({ stdio: 'inherit' })`pnpm release:build`;
|
|
148
|
-
console.log('✅ Release build completed successfully');
|
|
149
|
-
|
|
150
494
|
// Third pass: publish only the changed packages using recursive publish
|
|
151
495
|
let publishSuccess = false;
|
|
152
496
|
try {
|
|
153
497
|
console.log(`📤 Publishing ${packagesToPublish.length} canary versions...`);
|
|
154
|
-
await publishPackages(packagesToPublish,
|
|
498
|
+
await publishPackages(packagesToPublish, { ...options, noGitChecks: true, tag: CANARY_TAG });
|
|
155
499
|
|
|
156
500
|
packagesToPublish.forEach((pkg) => {
|
|
157
501
|
const canaryVersion = canaryVersions.get(pkg.name);
|
|
@@ -161,9 +505,11 @@ async function publishCanaryVersions(
|
|
|
161
505
|
} finally {
|
|
162
506
|
// Always restore original package.json files in parallel
|
|
163
507
|
console.log('\n🔄 Restoring original package.json files...');
|
|
164
|
-
const restorePromises =
|
|
165
|
-
|
|
166
|
-
|
|
508
|
+
const restorePromises = updateResults.map(async ({ pkg, basePkgJson, pkgJsonDirectory }) => {
|
|
509
|
+
// no need to restore package.json files in build directories
|
|
510
|
+
if (pkgJsonDirectory === pkg.path) {
|
|
511
|
+
await writePackageJson(pkg.path, basePkgJson);
|
|
512
|
+
}
|
|
167
513
|
});
|
|
168
514
|
|
|
169
515
|
await Promise.all(restorePromises);
|
|
@@ -172,6 +518,12 @@ async function publishCanaryVersions(
|
|
|
172
518
|
if (publishSuccess) {
|
|
173
519
|
// Create/update the canary tag after successful publish
|
|
174
520
|
await createCanaryTag(options.dryRun);
|
|
521
|
+
|
|
522
|
+
// Create GitHub releases if requested
|
|
523
|
+
if (options.githubRelease) {
|
|
524
|
+
await createGitHubReleasesForPackages(packagesToPublish, canaryVersions, changelogs, options);
|
|
525
|
+
}
|
|
526
|
+
|
|
175
527
|
console.log('\n🎉 All canary versions published successfully!');
|
|
176
528
|
}
|
|
177
529
|
}
|
|
@@ -180,21 +532,31 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
180
532
|
command: 'publish-canary',
|
|
181
533
|
describe: 'Publish canary packages to npm',
|
|
182
534
|
builder: (yargs) => {
|
|
183
|
-
return yargs
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
535
|
+
return yargs
|
|
536
|
+
.option('dry-run', {
|
|
537
|
+
type: 'boolean',
|
|
538
|
+
default: false,
|
|
539
|
+
description: 'Run in dry-run mode without publishing',
|
|
540
|
+
})
|
|
541
|
+
.option('github-release', {
|
|
542
|
+
type: 'boolean',
|
|
543
|
+
default: false,
|
|
544
|
+
description: 'Create GitHub releases for published packages',
|
|
545
|
+
});
|
|
188
546
|
},
|
|
189
547
|
handler: async (argv) => {
|
|
190
|
-
const { dryRun = false } = argv;
|
|
548
|
+
const { dryRun = false, githubRelease = false } = argv;
|
|
191
549
|
|
|
192
|
-
const options = { dryRun };
|
|
550
|
+
const options = { dryRun, githubRelease };
|
|
193
551
|
|
|
194
552
|
if (dryRun) {
|
|
195
553
|
console.log('🧪 Running in DRY RUN mode - no actual publishing will occur\n');
|
|
196
554
|
}
|
|
197
555
|
|
|
556
|
+
if (githubRelease) {
|
|
557
|
+
console.log('📝 GitHub releases will be created for published packages\n');
|
|
558
|
+
}
|
|
559
|
+
|
|
198
560
|
// Always get all packages first
|
|
199
561
|
console.log('🔍 Discovering all workspace packages...');
|
|
200
562
|
const allPackages = await getWorkspacePackages({ publicOnly: true });
|
|
@@ -207,16 +569,12 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
207
569
|
// Check for canary tag to determine selective publishing
|
|
208
570
|
const canaryTag = await getLastCanaryTag();
|
|
209
571
|
|
|
210
|
-
console.log(
|
|
211
|
-
canaryTag
|
|
212
|
-
? '🔍 Checking for packages changed since canary tag...'
|
|
213
|
-
: '🔍 No canary tag found, will publish all packages',
|
|
214
|
-
);
|
|
572
|
+
console.log('🔍 Checking for packages changed since canary tag...');
|
|
215
573
|
const packages = canaryTag
|
|
216
574
|
? await getWorkspacePackages({ sinceRef: canaryTag, publicOnly: true })
|
|
217
575
|
: allPackages;
|
|
218
576
|
|
|
219
|
-
console.log(`📋 Found ${packages.length} packages
|
|
577
|
+
console.log(`📋 Found ${packages.length} packages(s) for canary publishing:`);
|
|
220
578
|
packages.forEach((pkg) => {
|
|
221
579
|
console.log(` • ${pkg.name}@${pkg.version}`);
|
|
222
580
|
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
import confirm from '@inquirer/confirm';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { $ } from 'execa';
|
|
6
|
+
import fs from 'node:fs/promises';
|
|
7
|
+
import os from 'node:os';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
|
|
10
|
+
import { getWorkspacePackages } from '../utils/pnpm.mjs';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {Object} Args
|
|
14
|
+
* @property {boolean} [dryRun] If true, will only log the commands without executing them
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
18
|
+
command: 'publish-new-package [pkg...]',
|
|
19
|
+
describe: 'Publish new empty package(s) to the npm registry.',
|
|
20
|
+
builder: (yargs) =>
|
|
21
|
+
yargs.option('dryRun', {
|
|
22
|
+
type: 'boolean',
|
|
23
|
+
default: false,
|
|
24
|
+
description: 'If true, will only log the commands without executing them.',
|
|
25
|
+
}),
|
|
26
|
+
async handler(args) {
|
|
27
|
+
console.log(`🔍 Detecting new packages to publish in workspace...`);
|
|
28
|
+
const newPackages = await getWorkspacePackages({ nonPublishedOnly: true });
|
|
29
|
+
|
|
30
|
+
if (!newPackages.length) {
|
|
31
|
+
console.log('No new packages to publish.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(`Found ${newPackages.map((pkg) => pkg.name).join(', ')} to publish.`);
|
|
36
|
+
|
|
37
|
+
const answer = await confirm({
|
|
38
|
+
message: `Do you want to publish ${newPackages.length} new package(s) to the npm registry?`,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!answer) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
await Promise.all(
|
|
46
|
+
newPackages.map(async (pkg) => {
|
|
47
|
+
const newPkgDir = await fs.mkdtemp(path.join(os.tmpdir(), 'publish-new-package-'));
|
|
48
|
+
try {
|
|
49
|
+
await fs.mkdir(newPkgDir, { recursive: true });
|
|
50
|
+
const packageJson = {
|
|
51
|
+
name: pkg.name,
|
|
52
|
+
version: '0.0.1',
|
|
53
|
+
};
|
|
54
|
+
await fs.writeFile(
|
|
55
|
+
path.join(newPkgDir, 'package.json'),
|
|
56
|
+
`${JSON.stringify(packageJson, null, 2)}\n`,
|
|
57
|
+
);
|
|
58
|
+
/**
|
|
59
|
+
* @type {string[]}
|
|
60
|
+
*/
|
|
61
|
+
const publishArgs = [];
|
|
62
|
+
|
|
63
|
+
if (args.dryRun) {
|
|
64
|
+
publishArgs.push('--dry-run');
|
|
65
|
+
}
|
|
66
|
+
await $({
|
|
67
|
+
cwd: newPkgDir,
|
|
68
|
+
})`npm publish --access public --tag=canary ${publishArgs}`;
|
|
69
|
+
console.log(
|
|
70
|
+
`✅ ${args.dryRun ? '[Dry run] ' : ''}Published ${chalk.bold(`${pkg.name}@${packageJson.version}`)} to npm registry.`,
|
|
71
|
+
);
|
|
72
|
+
} finally {
|
|
73
|
+
await fs.rm(newPkgDir, { recursive: true, force: true });
|
|
74
|
+
}
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const trustedPublisherLinks = newPackages
|
|
79
|
+
.map((pkg) => `https://www.npmjs.com/package/${pkg.name}/access`)
|
|
80
|
+
.join('\n');
|
|
81
|
+
console.log(`
|
|
82
|
+
📝 Please ensure that the ${chalk.underline(chalk.bold('Trusted Publishers'))} settings are configured for the new package(s):
|
|
83
|
+
${trustedPublisherLinks}
|
|
84
|
+
Read how to do that here - https://github.com/mui/mui-public/blob/master/packages/code-infra/README.md#adding-and-publishing-new-packages`);
|
|
85
|
+
},
|
|
86
|
+
});
|
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import * as semver from 'semver';
|
|
7
7
|
import { $ } from 'execa';
|
|
8
|
-
import { resolveVersion, findDependencyVersionFromSpec } from '
|
|
8
|
+
import { resolveVersion, findDependencyVersionFromSpec } from '../utils/pnpm.mjs';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @typedef {Object} Args
|
|
@@ -51,6 +51,22 @@ async function processPackageOverride(packageSpec) {
|
|
|
51
51
|
if (reactMajor === 17) {
|
|
52
52
|
overrides['@testing-library/react'] = await resolveVersion('@testing-library/react@^12.1.0');
|
|
53
53
|
}
|
|
54
|
+
} else if (packageName === '@mui/material') {
|
|
55
|
+
// Special case for MUI - also override related packages
|
|
56
|
+
overrides['@mui/material'] = await resolveVersion(`@mui/material@${version}`);
|
|
57
|
+
overrides['@mui/system'] = await resolveVersion(`@mui/system@${version}`);
|
|
58
|
+
overrides['@mui/icons-material'] = await resolveVersion(`@mui/icons-material@${version}`);
|
|
59
|
+
overrides['@mui/utils'] = await resolveVersion(`@mui/utils@${version}`);
|
|
60
|
+
overrides['@mui/material-nextjs'] = await resolveVersion(`@mui/material-nextjs@${version}`);
|
|
61
|
+
|
|
62
|
+
const latest = await resolveVersion(`@mui/material@latest`);
|
|
63
|
+
const latestMajor = semver.major(latest);
|
|
64
|
+
const muiMajor = semver.major(overrides['@mui/material']);
|
|
65
|
+
if (muiMajor < latestMajor) {
|
|
66
|
+
overrides['@mui/lab'] = await resolveVersion(`@mui/lab@latest-v${muiMajor}`);
|
|
67
|
+
} else {
|
|
68
|
+
overrides['@mui/lab'] = await resolveVersion(`@mui/lab@latest`);
|
|
69
|
+
}
|
|
54
70
|
} else {
|
|
55
71
|
// Generic case for other packages
|
|
56
72
|
overrides[packageName] = await resolveVersion(packageSpec);
|