@mui/internal-code-infra 0.1.1-canary.2 → 0.1.1-canary.21
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 +7 -4
- package/build/changelog/fetchChangelogs.d.mts +9 -10
- package/build/changelog/index.d.mts +1 -0
- package/build/cli/cmdBaseline.d.mts +18 -0
- package/build/cli/cmdNetlifyIgnore.d.mts +1 -1
- package/build/eslint/mui/rules/no-constructor-assigned-field.d.mts +50 -0
- package/build/eslint/packageJsonConfig.d.mts +7 -0
- package/build/remark/config.d.mts +5 -14
- package/build/utils/build.d.mts +14 -0
- package/build/utils/credentials.d.mts +2 -2
- package/build/utils/git.d.mts +30 -9
- package/build/utils/github.d.mts +8 -0
- package/build/utils/pnpm.d.mts +49 -0
- package/package.json +38 -35
- package/src/changelog/fetchChangelogs.mjs +66 -26
- package/src/changelog/index.mjs +3 -0
- package/src/cli/cmdBaseline.mjs +31 -0
- package/src/cli/cmdBuild.mjs +2 -0
- package/src/cli/cmdNetlifyIgnore.mjs +14 -7
- package/src/cli/cmdPublish.mjs +15 -0
- package/src/cli/cmdPublishNewPackage.mjs +6 -2
- package/src/cli/index.mjs +2 -0
- package/src/eslint/baseConfig.mjs +2 -0
- package/src/eslint/docsConfig.mjs +28 -10
- package/src/eslint/jsonConfig.mjs +2 -1
- package/src/eslint/mui/config.mjs +16 -1
- package/src/eslint/mui/index.mjs +2 -0
- package/src/eslint/mui/rules/no-constructor-assigned-field.mjs +183 -0
- package/src/eslint/packageJsonConfig.mjs +50 -0
- package/src/eslint/testConfig.mjs +1 -2
- package/src/remark/config.mjs +60 -13
- package/src/utils/build.mjs +34 -0
- package/src/utils/credentials.mjs +4 -3
- package/src/utils/extractErrorCodes.mjs +1 -3
- package/src/utils/git.mjs +107 -46
- package/src/utils/github.mjs +27 -0
- package/src/utils/pnpm.mjs +109 -0
- package/vale/styles/MUI/GoogleLatin.yml +5 -2
- package/vale/styles/MUI/CorrectRererenceCased.yml +0 -14
package/src/utils/build.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from 'node:fs/promises';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
+
import getTargets from '@babel/helper-compilation-targets';
|
|
3
4
|
import { globby } from 'globby';
|
|
4
5
|
import * as semver from 'semver';
|
|
5
6
|
|
|
@@ -867,6 +868,39 @@ export function validatePkgJson(packageJson, options = {}) {
|
|
|
867
868
|
}
|
|
868
869
|
}
|
|
869
870
|
|
|
871
|
+
/**
|
|
872
|
+
* Validates that "engines.node" in package.json doesn't allow Node.js versions older than the
|
|
873
|
+
* "node" browserslist environment, which the CommonJS bundle is compiled for. Otherwise the
|
|
874
|
+
* build can emit syntax that the oldest supported Node.js version can't run.
|
|
875
|
+
* Targets are resolved the same way `@babel/preset-env` resolves them during the build.
|
|
876
|
+
* @param {{ name?: string; engines?: { node?: string } }} packageJson
|
|
877
|
+
* @param {string} cwd - The package directory, used to find the browserslist config.
|
|
878
|
+
*/
|
|
879
|
+
export function validateEnginesNode(packageJson, cwd) {
|
|
880
|
+
const enginesNode = packageJson.engines?.node;
|
|
881
|
+
if (!enginesNode) {
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
const minEnginesVersion = semver.minVersion(enginesNode);
|
|
886
|
+
if (!minEnginesVersion) {
|
|
887
|
+
throw new Error(
|
|
888
|
+
`Unable to determine the minimum Node.js version from "engines.node" in "${packageJson.name}" package.json: "${enginesNode}".`,
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
const targetNode = getTargets(undefined, { configPath: cwd, browserslistEnv: 'node' }).node;
|
|
893
|
+
if (!targetNode) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (semver.gt(targetNode, minEnginesVersion)) {
|
|
898
|
+
throw new Error(
|
|
899
|
+
`"engines.node" in "${packageJson.name}" package.json allows Node.js ${minEnginesVersion.version}, but the "node" browserslist environment compiles for Node.js ${targetNode} and up. Raise "engines.node" to at least ${targetNode}, or lower the "node" browserslist target.`,
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
870
904
|
/**
|
|
871
905
|
* Marks the start and end of a function execution for performance measurement.
|
|
872
906
|
* Uses the Performance API to create marks and measure the duration.
|
|
@@ -23,11 +23,12 @@ const accessedCredentials = new Map();
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* @param {string} key
|
|
26
|
-
* @returns {Promise<string |
|
|
26
|
+
* @returns {Promise<string | null>}
|
|
27
27
|
*/
|
|
28
28
|
export async function getPassword(key) {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const cached = accessedCredentials.get(key);
|
|
30
|
+
if (cached) {
|
|
31
|
+
return cached;
|
|
31
32
|
}
|
|
32
33
|
let credential = credentials.get(key);
|
|
33
34
|
if (!credential) {
|
|
@@ -99,9 +99,7 @@ export default async function extractErrorCodes(args) {
|
|
|
99
99
|
const packages = basePackages.filter(
|
|
100
100
|
(pkg) =>
|
|
101
101
|
// Ignore obvious packages that do not have user-facing errors
|
|
102
|
-
!pkg.name.startsWith('@mui/internal-') &&
|
|
103
|
-
!pkg.name.startsWith('@mui-internal/') &&
|
|
104
|
-
!skipPackages.includes(pkg.name),
|
|
102
|
+
!pkg.name.startsWith('@mui/internal-') && !skipPackages.includes(pkg.name),
|
|
105
103
|
);
|
|
106
104
|
const files = await Promise.all(packages.map((pkg) => getFilesForPackage(pkg)));
|
|
107
105
|
packages.forEach((pkg, index) => {
|
package/src/utils/git.mjs
CHANGED
|
@@ -9,68 +9,63 @@ import gitUrlParse from 'git-url-parse';
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* The repository a remote URL points at, if it is a GitHub repository under `mui`.
|
|
13
|
+
* @param {string} url
|
|
14
|
+
* @returns {{ owner: string, repo: string } | null}
|
|
15
|
+
*/
|
|
16
|
+
function muiRepositoryOf(url) {
|
|
17
|
+
try {
|
|
18
|
+
const parsed = gitUrlParse(url);
|
|
19
|
+
return parsed.source === 'github.com' && parsed.owner === 'mui'
|
|
20
|
+
? { owner: parsed.owner, repo: parsed.name }
|
|
21
|
+
: null;
|
|
22
|
+
} catch {
|
|
23
|
+
// Not a URL git-url-parse understands, such as a local path: not a mui repository either.
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The canonical repository's remote, as opposed to a fork's: `upstream`, else `origin`, each only if
|
|
30
|
+
* it points at a mui repository. Reads the configured URLs, which `insteadOf` does not rewrite.
|
|
13
31
|
* @param {string} [cwd=process.cwd()]
|
|
14
|
-
* @returns {Promise<RepoInfo>}
|
|
32
|
+
* @returns {Promise<RepoInfo>}
|
|
15
33
|
*/
|
|
16
34
|
export async function getRepositoryInfo(cwd = process.cwd()) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @type {Map<string, { owner: string, repo: string }>}
|
|
29
|
-
*/
|
|
30
|
-
const validRemotes = new Map();
|
|
31
|
-
|
|
32
|
-
for (const line of lines) {
|
|
33
|
-
// Match pattern: "remoteName url (fetch|push)"
|
|
34
|
-
const [remoteName, url, type] = line.trim().split(/\s+/, 3);
|
|
35
|
-
repoRemotes.add(remoteName);
|
|
36
|
-
if (type === '(fetch)') {
|
|
37
|
-
try {
|
|
38
|
-
const parsed = gitUrlParse(url);
|
|
39
|
-
if (parsed.source !== 'github.com' || parsed.owner !== 'mui') {
|
|
40
|
-
cause[remoteName] = `Remote is not a GitHub repository under 'mui' organization: ${url}`;
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (!validRemotes.has(remoteName)) {
|
|
44
|
-
validRemotes.set(remoteName, { owner: parsed.owner, repo: parsed.name });
|
|
45
|
-
}
|
|
46
|
-
} catch (error) {
|
|
47
|
-
cause[remoteName] = `Failed to parse URL for remote ${remoteName}: ${url}`;
|
|
48
|
-
}
|
|
49
|
-
} else if (type !== '(push)') {
|
|
50
|
-
throw new Error(`Unexpected line format for "git remote -v": "${line}"`);
|
|
51
|
-
}
|
|
35
|
+
// Exits 1 when neither remote is configured; the error below reports that.
|
|
36
|
+
const { stdout } = await $({
|
|
37
|
+
cwd,
|
|
38
|
+
reject: false,
|
|
39
|
+
})`git config --get-regexp ${'^remote\\.(upstream|origin)\\.url$'}`;
|
|
40
|
+
/** @type {Map<string, string>} */
|
|
41
|
+
const urls = new Map();
|
|
42
|
+
for (const line of stdout.split('\n').filter(Boolean)) {
|
|
43
|
+
const [key, url] = line.split(' ', 2);
|
|
44
|
+
urls.set(key.slice('remote.'.length, -'.url'.length), url);
|
|
52
45
|
}
|
|
53
46
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
58
|
-
return { ...
|
|
47
|
+
for (const remoteName of ['upstream', 'origin']) {
|
|
48
|
+
const url = urls.get(remoteName);
|
|
49
|
+
const repository = url ? muiRepositoryOf(url) : null;
|
|
50
|
+
if (repository) {
|
|
51
|
+
return { ...repository, remoteName };
|
|
59
52
|
}
|
|
60
53
|
}
|
|
61
54
|
|
|
55
|
+
const found = [...urls].map(([name, url]) => `${name}: ${url}`).join(', ') || 'none';
|
|
62
56
|
throw new Error(
|
|
63
|
-
`
|
|
64
|
-
|
|
57
|
+
`No canonical remote: neither \`upstream\` nor \`origin\` points at a mui repository (${found}). ` +
|
|
58
|
+
'Add an `upstream` remote for it, or name the remote explicitly where the command allows it.',
|
|
65
59
|
);
|
|
66
60
|
}
|
|
67
61
|
|
|
68
62
|
/**
|
|
69
63
|
* Get current git SHA
|
|
64
|
+
* @param {string} [cwd=process.cwd()]
|
|
70
65
|
* @returns {Promise<string>} Current git commit SHA
|
|
71
66
|
*/
|
|
72
|
-
export async function getCurrentGitSha() {
|
|
73
|
-
const result = await
|
|
67
|
+
export async function getCurrentGitSha(cwd = process.cwd()) {
|
|
68
|
+
const result = await $({ cwd })`git rev-parse HEAD`;
|
|
74
69
|
return result.stdout.trim();
|
|
75
70
|
}
|
|
76
71
|
|
|
@@ -84,3 +79,69 @@ export async function remoteGitTagExists(tagName, cwd = process.cwd()) {
|
|
|
84
79
|
const { stdout } = await $({ cwd })`git ls-remote --tags origin refs/tags/${tagName}`;
|
|
85
80
|
return stdout.trim().length > 0;
|
|
86
81
|
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The default branch of `remote`, as the remote itself reports it.
|
|
85
|
+
* @param {string} remote
|
|
86
|
+
* @param {string} cwd
|
|
87
|
+
* @returns {Promise<string>}
|
|
88
|
+
*/
|
|
89
|
+
async function getDefaultBranchOf(remote, cwd) {
|
|
90
|
+
const { stdout } = await $({ cwd })`git ls-remote --symref ${remote} HEAD`;
|
|
91
|
+
const match = /^ref: refs\/heads\/(\S+)\tHEAD$/m.exec(stdout);
|
|
92
|
+
if (!match) {
|
|
93
|
+
throw new Error(`Remote "${remote}" does not report a default branch.`);
|
|
94
|
+
}
|
|
95
|
+
return match[1];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The commit HEAD should be compared against: where it forked from the base branch, or its parent
|
|
100
|
+
* when HEAD is already on the base branch.
|
|
101
|
+
* @param {Object} [options]
|
|
102
|
+
* @param {string} [options.cwd]
|
|
103
|
+
* @param {string} [options.remote] - Defaults to the canonical remote.
|
|
104
|
+
* @param {string} [options.baseBranch] - Defaults to the remote's default branch.
|
|
105
|
+
* @returns {Promise<string>}
|
|
106
|
+
*/
|
|
107
|
+
export async function resolveBaseline(options = {}) {
|
|
108
|
+
const cwd = options.cwd ?? process.cwd();
|
|
109
|
+
const remote = options.remote ?? (await getRepositoryInfo(cwd)).remoteName;
|
|
110
|
+
const baseBranch = options.baseBranch ?? (await getDefaultBranchOf(remote, cwd));
|
|
111
|
+
|
|
112
|
+
await $({ cwd })`git fetch --no-tags ${remote} ${baseBranch}`;
|
|
113
|
+
const [forkPoint, head] = await Promise.all([
|
|
114
|
+
$({ cwd })`git merge-base HEAD FETCH_HEAD`.then((result) => result.stdout.trim()),
|
|
115
|
+
getCurrentGitSha(cwd),
|
|
116
|
+
]);
|
|
117
|
+
if (forkPoint !== head) {
|
|
118
|
+
return forkPoint;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const parent = await $({ cwd, reject: false })`git rev-parse --verify HEAD~1`;
|
|
122
|
+
if (parent.exitCode !== 0) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
'HEAD has no parent commit to compare against. A shallow clone is the usual cause.',
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return parent.stdout.trim();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The most recent version tag reachable from HEAD — where a changelog's range starts.
|
|
132
|
+
* @param {Object} opts
|
|
133
|
+
* @param {string} opts.cwd
|
|
134
|
+
* @param {boolean} [opts.fetchAll=true] Whether to fetch all tags from all remotes before finding the latest tag.
|
|
135
|
+
* @returns {Promise<string>}
|
|
136
|
+
*/
|
|
137
|
+
export async function findLatestTaggedVersion(opts) {
|
|
138
|
+
const $$ = $({ cwd: opts.cwd });
|
|
139
|
+
const fetchAll = opts.fetchAll ?? true;
|
|
140
|
+
if (fetchAll) {
|
|
141
|
+
// --force to update any existing tags that may have changed to avoid the clobering error.
|
|
142
|
+
const { remoteName } = await getRepositoryInfo(opts.cwd);
|
|
143
|
+
await $$`git fetch --tags --force ${remoteName}`;
|
|
144
|
+
}
|
|
145
|
+
const { stdout } = await $$`git describe --tags --abbrev=0 --match ${'v*'}`; // only include "version-tags"
|
|
146
|
+
return stdout.trim();
|
|
147
|
+
}
|
package/src/utils/github.mjs
CHANGED
|
@@ -261,3 +261,30 @@ export async function endToEndGhAuthGetToken({ log = false } = {}) {
|
|
|
261
261
|
export async function clearGitHubAuth() {
|
|
262
262
|
await credentials.deleteKey(GITHUB_APP_CREDENTIAL_KEY);
|
|
263
263
|
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Returns when a rate-limited request may be retried, or `null` for other errors.
|
|
267
|
+
* https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api#exceeding-the-rate-limit
|
|
268
|
+
*
|
|
269
|
+
* @param {unknown} error
|
|
270
|
+
* @returns {Date | null}
|
|
271
|
+
*/
|
|
272
|
+
export function getRateLimitRetryTime(error) {
|
|
273
|
+
const { status, response, message } = /** @type {any} */ (error) ?? {};
|
|
274
|
+
if (status !== 403 && status !== 429) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
const headers = response?.headers ?? {};
|
|
278
|
+
const now = Date.now();
|
|
279
|
+
let retryTime;
|
|
280
|
+
if (headers['retry-after']) {
|
|
281
|
+
retryTime = now + Number(headers['retry-after']) * 1000;
|
|
282
|
+
} else if (headers['x-ratelimit-remaining'] === '0' && headers['x-ratelimit-reset']) {
|
|
283
|
+
retryTime = Number(headers['x-ratelimit-reset']) * 1000;
|
|
284
|
+
} else if (/rate limit/i.test(message ?? '')) {
|
|
285
|
+
retryTime = now;
|
|
286
|
+
} else {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
return new Date(Math.max(retryTime, now + 60_000));
|
|
290
|
+
}
|
package/src/utils/pnpm.mjs
CHANGED
|
@@ -12,6 +12,9 @@ import { parseDocument, isMap } from 'yaml';
|
|
|
12
12
|
/** Canonical npm registry URL, in the form `getPublishRegistry` returns. */
|
|
13
13
|
const NPMJS_REGISTRY = 'https://registry.npmjs.org/';
|
|
14
14
|
|
|
15
|
+
/** Version that `publish-new-package` publishes to reserve the name of a new package. */
|
|
16
|
+
export const PLACEHOLDER_VERSION = '0.0.1';
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* @typedef {Object} PrivatePackage
|
|
17
20
|
* @property {string} [name] - Package name
|
|
@@ -227,6 +230,112 @@ export async function getPackagesNeedingManualPublish(packages) {
|
|
|
227
230
|
return packages.filter((_pkg, index) => results[index]);
|
|
228
231
|
}
|
|
229
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Why publishing `version` with `tag` would move the dist-tag to the wrong release line.
|
|
235
|
+
*
|
|
236
|
+
* @param {string} tag - Dist-tag to publish with
|
|
237
|
+
* @param {string} version - Version to publish
|
|
238
|
+
* @param {string} current - Version that the dist-tag points to on the registry
|
|
239
|
+
* @returns {string | null} The reason, or null when the publish is safe
|
|
240
|
+
*/
|
|
241
|
+
function getDistTagConflict(tag, version, current) {
|
|
242
|
+
if (semver.lt(version, current)) {
|
|
243
|
+
return `"${tag}" can only move to a newer version, but ${version} is lower than the current ${current}`;
|
|
244
|
+
}
|
|
245
|
+
// A tag that already points to a prerelease can move to the next one, for packages still in beta.
|
|
246
|
+
if (
|
|
247
|
+
(tag === 'latest' || tag.startsWith('latest-')) &&
|
|
248
|
+
semver.prerelease(version) &&
|
|
249
|
+
!semver.prerelease(current)
|
|
250
|
+
) {
|
|
251
|
+
return `"${tag}" cannot point to a prerelease (${version})`;
|
|
252
|
+
}
|
|
253
|
+
if (tag.startsWith('latest-') && semver.major(version) !== semver.major(current)) {
|
|
254
|
+
return `"${tag}" cannot cross a major (${current} to ${version})`;
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @typedef {Object} RegistryPackage
|
|
261
|
+
* @property {Record<string, string>} distTags - Dist-tags of the package
|
|
262
|
+
* @property {string[]} versions - Published versions of the package
|
|
263
|
+
*/
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Read the dist-tags and the published versions of each package with `pnpm view`, so
|
|
267
|
+
* the lookup uses the same registry configuration and credentials as `pnpm publish`.
|
|
268
|
+
*
|
|
269
|
+
* @param {PublicPackage[]} packages - Packages to read
|
|
270
|
+
* @returns {Promise<Map<string, RegistryPackage | null>>} By package name, null for a package that is not on the registry
|
|
271
|
+
*/
|
|
272
|
+
export async function getRegistryPackages(packages) {
|
|
273
|
+
const entries = await Promise.all(
|
|
274
|
+
packages.map(async (pkg) => {
|
|
275
|
+
const registry = await getPublishRegistry(pkg.path);
|
|
276
|
+
const { exitCode, stdout, stderr } = await $({
|
|
277
|
+
cwd: pkg.path,
|
|
278
|
+
reject: false,
|
|
279
|
+
// pnpm resolves its own `packageManager` version from `--registry`, which may not serve it.
|
|
280
|
+
env: { PNPM_CONFIG_PM_ON_FAIL: 'ignore' },
|
|
281
|
+
})`pnpm view ${pkg.name} dist-tags versions --json --registry ${registry}`;
|
|
282
|
+
let info;
|
|
283
|
+
try {
|
|
284
|
+
info = JSON.parse(stdout || '{}');
|
|
285
|
+
} catch {
|
|
286
|
+
info = {};
|
|
287
|
+
}
|
|
288
|
+
if (exitCode !== 0) {
|
|
289
|
+
if (info.error?.code === 'ERR_PNPM_FETCH_404') {
|
|
290
|
+
return /** @type {const} */ ([pkg.name, null]);
|
|
291
|
+
}
|
|
292
|
+
throw new Error(
|
|
293
|
+
`Failed to read the dist-tags of ${pkg.name} from ${registry}: ${info.error?.message ?? stderr}`,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
/** @type {RegistryPackage} */
|
|
297
|
+
const registryPackage = {
|
|
298
|
+
distTags: info['dist-tags'] ?? {},
|
|
299
|
+
versions: [info.versions ?? []].flat(),
|
|
300
|
+
};
|
|
301
|
+
return /** @type {const} */ ([pkg.name, registryPackage]);
|
|
302
|
+
}),
|
|
303
|
+
);
|
|
304
|
+
return new Map(entries);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* @typedef {Object} DistTagConflict
|
|
309
|
+
* @property {PublicPackage} pkg - Package to publish
|
|
310
|
+
* @property {string} reason - Why the dist-tag would move to the wrong release line
|
|
311
|
+
*/
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Find the packages whose publish with `tag` would move the dist-tag to the wrong release line.
|
|
315
|
+
*
|
|
316
|
+
* @param {PublicPackage[]} packages - Packages to publish
|
|
317
|
+
* @param {string} tag - Dist-tag to publish with
|
|
318
|
+
* @param {Map<string, RegistryPackage | null>} registryPackages - Registry data from {@link getRegistryPackages}
|
|
319
|
+
* @returns {DistTagConflict[]}
|
|
320
|
+
*/
|
|
321
|
+
export function getDistTagConflicts(packages, tag, registryPackages) {
|
|
322
|
+
return packages.flatMap((pkg) => {
|
|
323
|
+
const registryPackage = registryPackages.get(pkg.name);
|
|
324
|
+
// A package that is not on the registry yet has no dist-tags to protect. pnpm skips versions
|
|
325
|
+
// that are already published, such as unchanged packages on a legacy branch.
|
|
326
|
+
if (!registryPackage || registryPackage.versions.includes(pkg.version)) {
|
|
327
|
+
return [];
|
|
328
|
+
}
|
|
329
|
+
const current = semver.valid(registryPackage.distTags[tag]);
|
|
330
|
+
// npm points `latest` at the first version, which is the placeholder for a new package.
|
|
331
|
+
if (!current || current === PLACEHOLDER_VERSION) {
|
|
332
|
+
return [];
|
|
333
|
+
}
|
|
334
|
+
const reason = getDistTagConflict(tag, pkg.version, current);
|
|
335
|
+
return reason ? [{ pkg, reason }] : [];
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
230
339
|
/**
|
|
231
340
|
* Get package version info from registry.
|
|
232
341
|
*
|
|
@@ -6,6 +6,9 @@ level: error
|
|
|
6
6
|
nonword: true
|
|
7
7
|
action:
|
|
8
8
|
name: replace
|
|
9
|
+
# Wider than upstream Google.Latin: also catches e.g/eg./i.e/ie. and any trailing
|
|
10
|
+
# punctuation, so no separate rule asks for "e.g." (which this one forbids).
|
|
11
|
+
# The lookbehind skips matches after a dot or inside a word (example.eg).
|
|
9
12
|
swap:
|
|
10
|
-
'\
|
|
11
|
-
'\
|
|
13
|
+
'(?<![\w.])e\.?g\.?(?=[^\w.]|$)': for example
|
|
14
|
+
'(?<![\w.])i\.?e\.?(?=[^\w.]|$)': that is
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# Write things correctly, please no wrong references.
|
|
2
|
-
extends: substitution
|
|
3
|
-
message: Use '%s' instead of '%s'
|
|
4
|
-
level: error
|
|
5
|
-
ignorecase: false
|
|
6
|
-
# swap maps tokens in form of bad: good
|
|
7
|
-
# for more information: https://vale.sh/docs/topics/styles/#substitution
|
|
8
|
-
swap:
|
|
9
|
-
eg: e.g.
|
|
10
|
-
eg\.: e.g.
|
|
11
|
-
e\.g: e.g.
|
|
12
|
-
ie: i.e.
|
|
13
|
-
ie\.: i.e.
|
|
14
|
-
i\.e: i.e.
|