@mui/internal-code-infra 0.0.4-canary.1 → 0.0.4-canary.10
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 +1 -1
- package/build/babel-config.d.mts +6 -2
- package/build/cli/cmdListWorkspaces.d.mts +2 -0
- package/build/cli/cmdPublish.d.mts +1 -0
- package/build/cli/cmdPublishCanary.d.mts +1 -0
- package/build/utils/pnpm.d.mts +34 -0
- package/package.json +30 -27
- package/src/babel-config.mjs +6 -2
- package/src/build-env.d.ts +13 -0
- package/src/cli/cmdListWorkspaces.mjs +9 -2
- package/src/cli/cmdNetlifyIgnore.mjs +4 -88
- package/src/cli/cmdPublish.mjs +31 -4
- package/src/cli/cmdPublishCanary.mjs +54 -90
- package/src/utils/pnpm.mjs +133 -1
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ This is stored in the `docs` top-level directory.
|
|
|
35
35
|
|
|
36
36
|
### Adding and publishing new packages
|
|
37
37
|
|
|
38
|
-
Whenever
|
|
38
|
+
Whenever new packages are added to the repo (that will get published to npm) or a private package is turned into a public one, follow the below steps before invoking the publish workflow of the previous section.
|
|
39
39
|
|
|
40
40
|
1. Goto your repo's code base on your system, open terminal and run:
|
|
41
41
|
|
package/build/babel-config.d.mts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export type ReactCompilationMode = 'annotation' | 'syntax' | 'infer' | 'all';
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {'annotation' | 'syntax' | 'infer' | 'all'} ReactCompilationMode
|
|
4
|
+
*/
|
|
1
5
|
/**
|
|
2
6
|
* @param {Object} param0
|
|
3
7
|
* @param {boolean} [param0.debug]
|
|
@@ -8,7 +12,7 @@
|
|
|
8
12
|
* @param {string | null} param0.outExtension - Specify the output file extension.
|
|
9
13
|
* @param {string} param0.runtimeVersion
|
|
10
14
|
* @param {string} [param0.reactCompilerReactVersion]
|
|
11
|
-
* @param {
|
|
15
|
+
* @param {ReactCompilationMode} [param0.reactCompilerMode]
|
|
12
16
|
* @returns {import('@babel/core').TransformOptions} The base Babel configuration.
|
|
13
17
|
*/
|
|
14
18
|
export declare function getBaseConfig({ debug, optimizeClsx, removePropTypes, noResolveImports, bundle, runtimeVersion, outExtension, reactCompilerReactVersion, reactCompilerMode }: {
|
|
@@ -20,7 +24,7 @@ export declare function getBaseConfig({ debug, optimizeClsx, removePropTypes, no
|
|
|
20
24
|
outExtension: string | null;
|
|
21
25
|
runtimeVersion: string;
|
|
22
26
|
reactCompilerReactVersion?: string;
|
|
23
|
-
reactCompilerMode?:
|
|
27
|
+
reactCompilerMode?: ReactCompilationMode;
|
|
24
28
|
}): import('@babel/core').TransformOptions;
|
|
25
29
|
export type Options = {
|
|
26
30
|
bundle?: 'esm' | 'cjs';
|
|
@@ -4,12 +4,14 @@ export type Args = {
|
|
|
4
4
|
publicOnly?: boolean;
|
|
5
5
|
output?: 'json' | 'path' | 'name' | 'publish-dir';
|
|
6
6
|
sinceRef?: string;
|
|
7
|
+
filter?: string[];
|
|
7
8
|
};
|
|
8
9
|
/**
|
|
9
10
|
* @typedef {Object} Args
|
|
10
11
|
* @property {boolean} [publicOnly] - Whether to filter to only public packages
|
|
11
12
|
* @property {'json'|'path'|'name'|'publish-dir'} [output] - Output format (name, path, or json)
|
|
12
13
|
* @property {string} [sinceRef] - Git reference to filter changes since
|
|
14
|
+
* @property {string[]} [filter] - Same as filtering packages with --filter in pnpm. Only include packages matching the filter. See https://pnpm.io/filtering.
|
|
13
15
|
*/
|
|
14
16
|
declare const _default: import("yargs").CommandModule<{}, Args>;
|
|
15
17
|
export default /** @type {import('yargs').CommandModule<{}, Args>} */ _default;
|
package/build/utils/pnpm.d.mts
CHANGED
|
@@ -31,6 +31,7 @@ export type GetWorkspacePackagesOptions = {
|
|
|
31
31
|
publicOnly?: boolean;
|
|
32
32
|
nonPublishedOnly?: boolean;
|
|
33
33
|
cwd?: string;
|
|
34
|
+
filter?: string[];
|
|
34
35
|
};
|
|
35
36
|
export declare function getWorkspacePackages(options?: {
|
|
36
37
|
publicOnly: true;
|
|
@@ -56,6 +57,39 @@ export declare function getPackageVersionInfo(packageName: string, baseVersion:
|
|
|
56
57
|
* @returns {Promise<void>}
|
|
57
58
|
*/
|
|
58
59
|
export declare function publishPackages(packages: PublicPackage[], options?: PublishOptions): Promise<void>;
|
|
60
|
+
export type GetTransitiveDependenciesOptions = {
|
|
61
|
+
workspacePathByName?: Map<string, string>;
|
|
62
|
+
includeDev?: boolean;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* @typedef {Object} GetTransitiveDependenciesOptions
|
|
66
|
+
* @property {Map<string, string>} [workspacePathByName] - Map of workspace package name to directory path
|
|
67
|
+
* @property {boolean} [includeDev=true] - Whether to include devDependencies in the traversal
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* Get all transitive workspace dependencies for a set of packages.
|
|
71
|
+
*
|
|
72
|
+
* Traverses `dependencies`, `peerDependencies`, and optionally `devDependencies`,
|
|
73
|
+
* following only packages that exist in `workspacePathByName`. Results are cached
|
|
74
|
+
* per package so each package is read from disk at most once regardless of how many
|
|
75
|
+
* roots depend on it.
|
|
76
|
+
*
|
|
77
|
+
* @param {string[]} packageNames - Package names to start the traversal from
|
|
78
|
+
* @param {GetTransitiveDependenciesOptions} [options]
|
|
79
|
+
* @returns {Promise<Set<string>>} All reachable workspace package names, including the input packages themselves
|
|
80
|
+
*/
|
|
81
|
+
export declare function getTransitiveDependencies(packageNames: string[], options?: GetTransitiveDependenciesOptions): Promise<Set<string>>;
|
|
82
|
+
/**
|
|
83
|
+
* Validate that a set of packages covers all of their transitive workspace dependencies,
|
|
84
|
+
* and that none of those dependencies are private (which would make them unpublishable).
|
|
85
|
+
*
|
|
86
|
+
* @param {PublicPackage[]} packages - The packages intended for publishing
|
|
87
|
+
* @returns {Promise<{issues: string[]}>}
|
|
88
|
+
* List of human-readable issue strings. Empty when the dependency set is valid.
|
|
89
|
+
*/
|
|
90
|
+
export declare function validatePublishDependencies(packages: PublicPackage[]): Promise<{
|
|
91
|
+
issues: string[];
|
|
92
|
+
}>;
|
|
59
93
|
/**
|
|
60
94
|
* Read package.json from a directory
|
|
61
95
|
* @param {string} packagePath - Path to package directory
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mui/internal-code-infra",
|
|
3
|
-
"version": "0.0.4-canary.
|
|
3
|
+
"version": "0.0.4-canary.10",
|
|
4
4
|
"description": "Infra scripts and configs to be used across MUI repos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,13 +43,16 @@
|
|
|
43
43
|
"./brokenLinksChecker": {
|
|
44
44
|
"types": "./build/brokenLinksChecker/index.d.mts",
|
|
45
45
|
"default": "./src/brokenLinksChecker/index.mjs"
|
|
46
|
+
},
|
|
47
|
+
"./build-env": {
|
|
48
|
+
"types": "./src/build-env.d.ts"
|
|
46
49
|
}
|
|
47
50
|
},
|
|
48
51
|
"bin": {
|
|
49
52
|
"code-infra": "./bin/code-infra.mjs"
|
|
50
53
|
},
|
|
51
54
|
"dependencies": {
|
|
52
|
-
"@argos-ci/core": "^
|
|
55
|
+
"@argos-ci/core": "^5.1.1",
|
|
53
56
|
"@babel/cli": "^7.28.6",
|
|
54
57
|
"@babel/core": "^7.29.0",
|
|
55
58
|
"@babel/plugin-syntax-jsx": "^7.28.6",
|
|
@@ -58,19 +61,19 @@
|
|
|
58
61
|
"@babel/preset-env": "^7.29.0",
|
|
59
62
|
"@babel/preset-react": "^7.28.5",
|
|
60
63
|
"@babel/preset-typescript": "^7.28.5",
|
|
61
|
-
"@eslint/compat": "^2.0.
|
|
64
|
+
"@eslint/compat": "^2.0.3",
|
|
62
65
|
"@eslint/js": "^10.0.1",
|
|
63
|
-
"@eslint/json": "^1.0
|
|
64
|
-
"@inquirer/confirm": "^6.0.
|
|
65
|
-
"@inquirer/select": "^5.0
|
|
66
|
+
"@eslint/json": "^1.1.0",
|
|
67
|
+
"@inquirer/confirm": "^6.0.8",
|
|
68
|
+
"@inquirer/select": "^5.1.0",
|
|
66
69
|
"@napi-rs/keyring": "^1.2.0",
|
|
67
70
|
"@octokit/auth-action": "^6.0.2",
|
|
68
71
|
"@octokit/oauth-methods": "^6.0.2",
|
|
69
72
|
"@octokit/rest": "^22.0.1",
|
|
70
73
|
"@pnpm/find-workspace-dir": "^1000.1.4",
|
|
71
|
-
"@typescript-eslint/types": "^8.
|
|
72
|
-
"@typescript-eslint/utils": "^8.
|
|
73
|
-
"@vitest/eslint-plugin": "^1.6.
|
|
74
|
+
"@typescript-eslint/types": "^8.57.1",
|
|
75
|
+
"@typescript-eslint/utils": "^8.57.1",
|
|
76
|
+
"@vitest/eslint-plugin": "^1.6.11",
|
|
74
77
|
"babel-plugin-optimize-clsx": "^2.6.2",
|
|
75
78
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
76
79
|
"babel-plugin-transform-import-meta": "^2.3.3",
|
|
@@ -78,13 +81,13 @@
|
|
|
78
81
|
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
|
|
79
82
|
"babel-plugin-transform-remove-imports": "^1.8.1",
|
|
80
83
|
"chalk": "^5.6.2",
|
|
81
|
-
"clipboardy": "^5.3.
|
|
84
|
+
"clipboardy": "^5.3.1",
|
|
82
85
|
"content-type": "^1.0.5",
|
|
83
86
|
"env-ci": "^11.2.0",
|
|
84
87
|
"eslint-config-prettier": "^10.1.8",
|
|
85
88
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
86
89
|
"eslint-module-utils": "^2.12.1",
|
|
87
|
-
"eslint-plugin-compat": "^
|
|
90
|
+
"eslint-plugin-compat": "^7.0.1",
|
|
88
91
|
"eslint-plugin-import": "^2.32.0",
|
|
89
92
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
90
93
|
"eslint-plugin-mocha": "^11.2.0",
|
|
@@ -92,14 +95,14 @@
|
|
|
92
95
|
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
|
|
93
96
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
94
97
|
"eslint-plugin-testing-library": "^7.16.0",
|
|
95
|
-
"es-toolkit": "^1.
|
|
98
|
+
"es-toolkit": "^1.45.1",
|
|
96
99
|
"execa": "^9.6.1",
|
|
97
100
|
"git-url-parse": "^16.1.0",
|
|
98
|
-
"globals": "^
|
|
101
|
+
"globals": "^17.4.0",
|
|
99
102
|
"globby": "^16.1.1",
|
|
100
|
-
"minimatch": "^10.2.
|
|
101
|
-
"node-html-parser": "^7.0
|
|
102
|
-
"open": "^
|
|
103
|
+
"minimatch": "^10.2.4",
|
|
104
|
+
"node-html-parser": "^7.1.0",
|
|
105
|
+
"open": "^11.0.0",
|
|
103
106
|
"postcss-styled-syntax": "^0.7.1",
|
|
104
107
|
"regexp.escape": "^2.0.1",
|
|
105
108
|
"rehype-slug": "^6.0.0",
|
|
@@ -110,12 +113,12 @@
|
|
|
110
113
|
"resolve-pkg-maps": "^1.0.0",
|
|
111
114
|
"semver": "^7.7.4",
|
|
112
115
|
"stylelint-config-standard": "^40.0.0",
|
|
113
|
-
"typescript-eslint": "^8.
|
|
116
|
+
"typescript-eslint": "^8.57.1",
|
|
114
117
|
"unified": "^11.0.5",
|
|
115
118
|
"yargs": "^18.0.0",
|
|
116
|
-
"@mui/internal-babel-plugin-display-name": "1.0.4-canary.
|
|
117
|
-
"@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.
|
|
118
|
-
"@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.
|
|
119
|
+
"@mui/internal-babel-plugin-display-name": "1.0.4-canary.14",
|
|
120
|
+
"@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.33",
|
|
121
|
+
"@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.24"
|
|
119
122
|
},
|
|
120
123
|
"peerDependencies": {
|
|
121
124
|
"@next/eslint-plugin-next": "*",
|
|
@@ -143,13 +146,13 @@
|
|
|
143
146
|
"@types/estree-jsx": "1.0.5",
|
|
144
147
|
"@types/regexp.escape": "2.0.0",
|
|
145
148
|
"@types/yargs": "17.0.35",
|
|
146
|
-
"@typescript-eslint/parser": "8.
|
|
147
|
-
"@typescript-eslint/rule-tester": "8.
|
|
148
|
-
"eslint": "10.0.
|
|
149
|
+
"@typescript-eslint/parser": "8.57.1",
|
|
150
|
+
"@typescript-eslint/rule-tester": "8.57.1",
|
|
151
|
+
"eslint": "10.0.3",
|
|
149
152
|
"get-port": "7.1.0",
|
|
150
153
|
"prettier": "3.8.1",
|
|
151
|
-
"serve": "14.2.
|
|
152
|
-
"typescript-eslint": "8.
|
|
154
|
+
"serve": "14.2.6",
|
|
155
|
+
"typescript-eslint": "8.57.1"
|
|
153
156
|
},
|
|
154
157
|
"files": [
|
|
155
158
|
"bin",
|
|
@@ -161,10 +164,10 @@
|
|
|
161
164
|
"publishConfig": {
|
|
162
165
|
"access": "public"
|
|
163
166
|
},
|
|
164
|
-
"gitSha": "
|
|
167
|
+
"gitSha": "b989d008bd34fbf9fb9d3395da2f6a32536f574d",
|
|
165
168
|
"scripts": {
|
|
166
169
|
"build": "tsgo -p tsconfig.build.json",
|
|
167
|
-
"typescript": "tsgo -
|
|
170
|
+
"typescript": "tsgo -noEmit",
|
|
168
171
|
"test": "pnpm -w test --project @mui/internal-code-infra",
|
|
169
172
|
"test:copy": "rm -rf build && node bin/code-infra.mjs copy-files --glob \"src/cli/*.mjs\" --glob \"src/eslint/**/*.mjs:esm\""
|
|
170
173
|
}
|
package/src/babel-config.mjs
CHANGED
|
@@ -10,6 +10,10 @@ import pluginTransformImportMeta from 'babel-plugin-transform-import-meta';
|
|
|
10
10
|
import pluginTransformInlineEnvVars from 'babel-plugin-transform-inline-environment-variables';
|
|
11
11
|
import pluginRemovePropTypes from 'babel-plugin-transform-react-remove-prop-types';
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {'annotation' | 'syntax' | 'infer' | 'all'} ReactCompilationMode
|
|
15
|
+
*/
|
|
16
|
+
|
|
13
17
|
/**
|
|
14
18
|
* @param {Object} param0
|
|
15
19
|
* @param {boolean} [param0.debug]
|
|
@@ -20,7 +24,7 @@ import pluginRemovePropTypes from 'babel-plugin-transform-react-remove-prop-type
|
|
|
20
24
|
* @param {string | null} param0.outExtension - Specify the output file extension.
|
|
21
25
|
* @param {string} param0.runtimeVersion
|
|
22
26
|
* @param {string} [param0.reactCompilerReactVersion]
|
|
23
|
-
* @param {
|
|
27
|
+
* @param {ReactCompilationMode} [param0.reactCompilerMode]
|
|
24
28
|
* @returns {import('@babel/core').TransformOptions} The base Babel configuration.
|
|
25
29
|
*/
|
|
26
30
|
export function getBaseConfig({
|
|
@@ -183,6 +187,6 @@ export default function getBabelConfig(api) {
|
|
|
183
187
|
removePropTypes: process.env.MUI_REMOVE_PROP_TYPES === 'true',
|
|
184
188
|
noResolveImports,
|
|
185
189
|
reactCompilerReactVersion: process.env.MUI_REACT_COMPILER_REACT_VERSION,
|
|
186
|
-
reactCompilerMode: process.env.MUI_REACT_COMPILER_MODE,
|
|
190
|
+
reactCompilerMode: /** @type {ReactCompilationMode} */ (process.env.MUI_REACT_COMPILER_MODE),
|
|
187
191
|
});
|
|
188
192
|
}
|
|
@@ -15,6 +15,7 @@ import { getWorkspacePackages } from '../utils/pnpm.mjs';
|
|
|
15
15
|
* @property {boolean} [publicOnly] - Whether to filter to only public packages
|
|
16
16
|
* @property {'json'|'path'|'name'|'publish-dir'} [output] - Output format (name, path, or json)
|
|
17
17
|
* @property {string} [sinceRef] - Git reference to filter changes since
|
|
18
|
+
* @property {string[]} [filter] - Same as filtering packages with --filter in pnpm. Only include packages matching the filter. See https://pnpm.io/filtering.
|
|
18
19
|
*/
|
|
19
20
|
|
|
20
21
|
export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
@@ -37,13 +38,19 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
37
38
|
.option('since-ref', {
|
|
38
39
|
type: 'string',
|
|
39
40
|
description: 'Filter packages changed since git reference',
|
|
41
|
+
})
|
|
42
|
+
.option('filter', {
|
|
43
|
+
type: 'string',
|
|
44
|
+
array: true,
|
|
45
|
+
description:
|
|
46
|
+
'Same as filtering packages with --filter in pnpm. Only include packages matching the filter. See https://pnpm.io/filtering.',
|
|
40
47
|
});
|
|
41
48
|
},
|
|
42
49
|
handler: async (argv) => {
|
|
43
|
-
const { publicOnly = false, output = 'name', sinceRef } = argv;
|
|
50
|
+
const { publicOnly = false, output = 'name', sinceRef, filter = [] } = argv;
|
|
44
51
|
|
|
45
52
|
// Get packages using our helper function
|
|
46
|
-
const packages = await getWorkspacePackages({ sinceRef, publicOnly });
|
|
53
|
+
const packages = await getWorkspacePackages({ sinceRef, publicOnly, filter });
|
|
47
54
|
|
|
48
55
|
if (output === 'json') {
|
|
49
56
|
// Serialize packages to JSON
|
|
@@ -12,93 +12,7 @@ import * as fs from 'node:fs/promises';
|
|
|
12
12
|
import * as path from 'node:path';
|
|
13
13
|
import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
|
|
14
14
|
import { toPosixPath } from '../utils/path.mjs';
|
|
15
|
-
import { getWorkspacePackages } from '../utils/pnpm.mjs';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Get all workspace dependencies (direct and transitive) from a package
|
|
19
|
-
* @param {string} packageName - Package name
|
|
20
|
-
* @param {Map<string, string>} workspaceMap - Map of workspace name to path
|
|
21
|
-
* @param {Map<string, Promise<Set<string>>>} cache - Cache of package resolution promises
|
|
22
|
-
* @returns {Promise<Set<string>>} Set of workspace package names (dependencies only, not including the package itself)
|
|
23
|
-
*/
|
|
24
|
-
async function getWorkspaceDependenciesRecursive(packageName, workspaceMap, cache) {
|
|
25
|
-
// Check cache first
|
|
26
|
-
const cached = cache.get(packageName);
|
|
27
|
-
if (cached) {
|
|
28
|
-
return cached;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Create the resolution promise
|
|
32
|
-
const promise = (async () => {
|
|
33
|
-
const packagePath = workspaceMap.get(packageName);
|
|
34
|
-
if (!packagePath) {
|
|
35
|
-
throw new Error(`Workspace "${packageName}" not found in the repository`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const packageJsonPath = path.join(packagePath, 'package.json');
|
|
39
|
-
const content = await fs.readFile(packageJsonPath, 'utf8');
|
|
40
|
-
const packageJson = JSON.parse(content);
|
|
41
|
-
|
|
42
|
-
// Collect all dependency names
|
|
43
|
-
/** @type {Set<string>} */
|
|
44
|
-
const allDeps = new Set();
|
|
45
|
-
if (packageJson.dependencies) {
|
|
46
|
-
Object.keys(packageJson.dependencies).forEach((dep) => allDeps.add(dep));
|
|
47
|
-
}
|
|
48
|
-
if (packageJson.devDependencies) {
|
|
49
|
-
Object.keys(packageJson.devDependencies).forEach((dep) => allDeps.add(dep));
|
|
50
|
-
}
|
|
51
|
-
if (packageJson.peerDependencies) {
|
|
52
|
-
Object.keys(packageJson.peerDependencies).forEach((dep) => allDeps.add(dep));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Filter to only workspace dependencies
|
|
56
|
-
const workspaceDeps = Array.from(allDeps).filter((dep) => workspaceMap.has(dep));
|
|
57
|
-
|
|
58
|
-
// Recursively process workspace dependencies in parallel
|
|
59
|
-
const recursiveResults = await Promise.all(
|
|
60
|
-
workspaceDeps.map(async (dep) => {
|
|
61
|
-
return getWorkspaceDependenciesRecursive(dep, workspaceMap, cache);
|
|
62
|
-
}),
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
// Merge all results using flatMap
|
|
66
|
-
return new Set(recursiveResults.flatMap((result) => Array.from(result)).concat(workspaceDeps));
|
|
67
|
-
})();
|
|
68
|
-
|
|
69
|
-
// Store in cache before returning
|
|
70
|
-
cache.set(packageName, promise);
|
|
71
|
-
|
|
72
|
-
return promise;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Get transitive workspace dependencies for a list of workspace names
|
|
77
|
-
* @param {string[]} workspaceNames - Array of workspace names
|
|
78
|
-
* @param {Map<string, string>} workspaceMap - Map of workspace name to path
|
|
79
|
-
* @returns {Promise<Set<string>>} Set of workspace package names (including requested packages and all their dependencies)
|
|
80
|
-
*/
|
|
81
|
-
async function getTransitiveDependencies(workspaceNames, workspaceMap) {
|
|
82
|
-
// Shared cache for all workspace dependency resolution
|
|
83
|
-
const cache = new Map();
|
|
84
|
-
|
|
85
|
-
// Validate all workspace names exist
|
|
86
|
-
for (const workspaceName of workspaceNames) {
|
|
87
|
-
if (!workspaceMap.has(workspaceName)) {
|
|
88
|
-
throw new Error(`Workspace "${workspaceName}" not found in the repository`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Process each requested workspace in parallel
|
|
93
|
-
const workspaceResults = await Promise.all(
|
|
94
|
-
workspaceNames.map((workspaceName) =>
|
|
95
|
-
getWorkspaceDependenciesRecursive(workspaceName, workspaceMap, cache),
|
|
96
|
-
),
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
// Merge all results using flatMap and add the original package names
|
|
100
|
-
return new Set(workspaceNames.concat(workspaceResults.flatMap((result) => Array.from(result))));
|
|
101
|
-
}
|
|
15
|
+
import { getTransitiveDependencies, getWorkspacePackages } from '../utils/pnpm.mjs';
|
|
102
16
|
|
|
103
17
|
/**
|
|
104
18
|
* Generate the ignore command string for netlify.toml
|
|
@@ -220,7 +134,9 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
220
134
|
console.log(`Processing ${workspaceName}...`);
|
|
221
135
|
|
|
222
136
|
// Get transitive dependencies for this specific workspace
|
|
223
|
-
const dependencyNames = await getTransitiveDependencies([workspaceName],
|
|
137
|
+
const dependencyNames = await getTransitiveDependencies([workspaceName], {
|
|
138
|
+
workspacePathByName: workspaceMap,
|
|
139
|
+
});
|
|
224
140
|
|
|
225
141
|
// Convert package names to relative paths (normalize to POSIX separators for git)
|
|
226
142
|
const relativePaths = Array.from(dependencyNames)
|
package/src/cli/cmdPublish.mjs
CHANGED
|
@@ -17,7 +17,11 @@ import * as fs from 'node:fs/promises';
|
|
|
17
17
|
import * as semver from 'semver';
|
|
18
18
|
|
|
19
19
|
import { persistentAuthStrategy } from '../utils/github.mjs';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
getWorkspacePackages,
|
|
22
|
+
publishPackages,
|
|
23
|
+
validatePublishDependencies,
|
|
24
|
+
} from '../utils/pnpm.mjs';
|
|
21
25
|
import { getCurrentGitSha, getRepositoryInfo } from '../utils/git.mjs';
|
|
22
26
|
|
|
23
27
|
const isCI = envCI().isCi;
|
|
@@ -33,6 +37,7 @@ function getOctokit() {
|
|
|
33
37
|
* @property {string} tag NPM dist tag to publish to
|
|
34
38
|
* @property {boolean} ci Runs in CI environment
|
|
35
39
|
* @property {string} [sha] Git SHA to use for the GitHub release workflow (local only)
|
|
40
|
+
* @property {string[]} [filter] Same as filtering packages with --filter in pnpm. Only publish packages matching the filter. See https://pnpm.io/filtering.
|
|
36
41
|
*/
|
|
37
42
|
|
|
38
43
|
/**
|
|
@@ -260,10 +265,16 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
260
265
|
.option('sha', {
|
|
261
266
|
type: 'string',
|
|
262
267
|
description: 'Git SHA to use for the GitHub release workflow (local only)',
|
|
268
|
+
})
|
|
269
|
+
.option('filter', {
|
|
270
|
+
type: 'string',
|
|
271
|
+
array: true,
|
|
272
|
+
description:
|
|
273
|
+
'Same as filtering packages with --filter in pnpm. Only publish packages matching the filter. See https://pnpm.io/filtering.',
|
|
263
274
|
});
|
|
264
275
|
},
|
|
265
276
|
handler: async (argv) => {
|
|
266
|
-
const { dryRun = false, githubRelease = false, tag = 'latest', sha } = argv;
|
|
277
|
+
const { dryRun = false, githubRelease = false, tag = 'latest', sha, filter = [] } = argv;
|
|
267
278
|
|
|
268
279
|
if (isCI && !argv.ci) {
|
|
269
280
|
console.error(
|
|
@@ -290,13 +301,29 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
290
301
|
// Get all packages
|
|
291
302
|
console.log('🔍 Discovering all workspace packages...');
|
|
292
303
|
|
|
293
|
-
const allPackages = await getWorkspacePackages({ publicOnly: true });
|
|
304
|
+
const allPackages = await getWorkspacePackages({ publicOnly: true, filter });
|
|
294
305
|
|
|
295
306
|
if (allPackages.length === 0) {
|
|
296
|
-
console.log(
|
|
307
|
+
console.log(
|
|
308
|
+
`⚠️ No publishable packages found in workspace${filter.length > 0 ? ` matching filter "${filter.join(', ')}"` : ''}`,
|
|
309
|
+
);
|
|
297
310
|
return;
|
|
298
311
|
}
|
|
299
312
|
|
|
313
|
+
if (filter.length > 0) {
|
|
314
|
+
console.log('🔍 Validating workspace dependencies for filtered packages...');
|
|
315
|
+
|
|
316
|
+
const { issues } = await validatePublishDependencies(allPackages);
|
|
317
|
+
|
|
318
|
+
if (issues.length > 0) {
|
|
319
|
+
throw new Error('Invalid dependencies structure of packages to be published.', {
|
|
320
|
+
cause: issues,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.log('✅ All workspace dependency requirements satisfied');
|
|
325
|
+
}
|
|
326
|
+
|
|
300
327
|
// Get version from root package.json
|
|
301
328
|
const version = await getReleaseVersion();
|
|
302
329
|
|
|
@@ -16,10 +16,12 @@ import * as semver from 'semver';
|
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
18
|
getPackageVersionInfo,
|
|
19
|
+
getTransitiveDependencies,
|
|
19
20
|
getWorkspacePackages,
|
|
20
21
|
publishPackages,
|
|
21
22
|
readPackageJson,
|
|
22
23
|
semverMax,
|
|
24
|
+
validatePublishDependencies,
|
|
23
25
|
writePackageJson,
|
|
24
26
|
} from '../utils/pnpm.mjs';
|
|
25
27
|
import { getCurrentGitSha, getRepositoryInfo } from '../utils/git.mjs';
|
|
@@ -28,6 +30,7 @@ import { getCurrentGitSha, getRepositoryInfo } from '../utils/git.mjs';
|
|
|
28
30
|
* @typedef {Object} Args
|
|
29
31
|
* @property {boolean} [dryRun] - Whether to run in dry-run mode
|
|
30
32
|
* @property {boolean} [githubRelease] - Whether to create GitHub releases for canary packages
|
|
33
|
+
* @property {string[]} [filter] - Same as filtering packages with --filter in pnpm. Only publish packages matching the filter. See https://pnpm.io/filtering.
|
|
31
34
|
*/
|
|
32
35
|
|
|
33
36
|
const CANARY_TAG = 'canary';
|
|
@@ -118,80 +121,6 @@ function cleanupCommitMessage(message) {
|
|
|
118
121
|
return `${prefix}${msg}`.trim();
|
|
119
122
|
}
|
|
120
123
|
|
|
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
124
|
/**
|
|
196
125
|
* Prepare changelog data for packages using GitHub API
|
|
197
126
|
* @param {PublicPackage[]} packagesToPublish - Packages that will be published
|
|
@@ -224,14 +153,20 @@ async function prepareChangelogsFromGitCli(packagesToPublish, allPackages, canar
|
|
|
224
153
|
}
|
|
225
154
|
}),
|
|
226
155
|
);
|
|
227
|
-
// Second pass: check for dependency updates in other packages not part of git history
|
|
228
|
-
const
|
|
229
|
-
const
|
|
156
|
+
// Second pass: check for dependency updates in other packages not part of git history.
|
|
157
|
+
const workspacePathByName = new Map(allPackages.map((pkg) => [pkg.name, pkg.path]));
|
|
158
|
+
const publishedNames = new Set(packagesToPublish.map((p) => p.name));
|
|
159
|
+
|
|
160
|
+
const transitiveDepSets = await Promise.all(
|
|
161
|
+
allPackages.map((pkg) =>
|
|
162
|
+
getTransitiveDependencies([pkg.name], { includeDev: false, workspacePathByName }),
|
|
163
|
+
),
|
|
164
|
+
);
|
|
230
165
|
|
|
231
166
|
for (let i = 0; i < allPackages.length; i += 1) {
|
|
232
167
|
const pkg = allPackages[i];
|
|
233
|
-
const depsToPublish =
|
|
234
|
-
|
|
168
|
+
const depsToPublish = [...transitiveDepSets[i]].filter(
|
|
169
|
+
(dep) => dep !== pkg.name && publishedNames.has(dep),
|
|
235
170
|
);
|
|
236
171
|
if (depsToPublish.length === 0) {
|
|
237
172
|
continue;
|
|
@@ -495,7 +430,11 @@ async function publishCanaryVersions(
|
|
|
495
430
|
let publishSuccess = false;
|
|
496
431
|
try {
|
|
497
432
|
console.log(`📤 Publishing ${packagesToPublish.length} canary versions...`);
|
|
498
|
-
await publishPackages(packagesToPublish, {
|
|
433
|
+
await publishPackages(packagesToPublish, {
|
|
434
|
+
dryRun: options.dryRun,
|
|
435
|
+
noGitChecks: true,
|
|
436
|
+
tag: CANARY_TAG,
|
|
437
|
+
});
|
|
499
438
|
|
|
500
439
|
packagesToPublish.forEach((pkg) => {
|
|
501
440
|
const canaryVersion = canaryVersions.get(pkg.name);
|
|
@@ -542,10 +481,16 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
542
481
|
type: 'boolean',
|
|
543
482
|
default: false,
|
|
544
483
|
description: 'Create GitHub releases for published packages',
|
|
484
|
+
})
|
|
485
|
+
.option('filter', {
|
|
486
|
+
type: 'string',
|
|
487
|
+
array: true,
|
|
488
|
+
description:
|
|
489
|
+
'Same as filtering packages with --filter in pnpm. Only publish packages matching the filter. See https://pnpm.io/filtering.',
|
|
545
490
|
});
|
|
546
491
|
},
|
|
547
492
|
handler: async (argv) => {
|
|
548
|
-
const { dryRun = false, githubRelease = false } = argv;
|
|
493
|
+
const { dryRun = false, githubRelease = false, filter = [] } = argv;
|
|
549
494
|
|
|
550
495
|
const options = { dryRun, githubRelease };
|
|
551
496
|
|
|
@@ -557,22 +502,41 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
557
502
|
console.log('📝 GitHub releases will be created for published packages\n');
|
|
558
503
|
}
|
|
559
504
|
|
|
560
|
-
//
|
|
505
|
+
// All public packages — needed by publishCanaryVersions to bump versions and update
|
|
506
|
+
// package.json across the entire workspace, even for packages not being published.
|
|
561
507
|
console.log('🔍 Discovering all workspace packages...');
|
|
562
|
-
const
|
|
508
|
+
const filteredPackages = await getWorkspacePackages({ publicOnly: true, filter });
|
|
563
509
|
|
|
564
|
-
if (
|
|
565
|
-
console.log(
|
|
510
|
+
if (filteredPackages.length === 0) {
|
|
511
|
+
console.log(
|
|
512
|
+
`⚠️ No publishable packages found in workspace${filter.length > 0 ? ` matching filter "${filter.join(', ')}"` : ''}`,
|
|
513
|
+
);
|
|
566
514
|
return;
|
|
567
515
|
}
|
|
568
516
|
|
|
569
|
-
|
|
517
|
+
if (filter.length > 0) {
|
|
518
|
+
console.log('🔍 Validating workspace dependencies for filtered packages...');
|
|
519
|
+
|
|
520
|
+
const { issues } = await validatePublishDependencies(filteredPackages);
|
|
521
|
+
|
|
522
|
+
if (issues.length > 0) {
|
|
523
|
+
throw new Error('Invalid dependencies structure of packages to be published.', {
|
|
524
|
+
cause: issues,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
console.log('✅ All workspace dependency requirements satisfied');
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Check for canary tag to determine selective publishing.
|
|
532
|
+
// --filter is applied on top of sinceRef: publish only packages that have
|
|
533
|
+
// changed since the last canary tag AND match the filter.
|
|
570
534
|
const canaryTag = await getLastCanaryTag();
|
|
571
535
|
|
|
572
536
|
console.log('🔍 Checking for packages changed since canary tag...');
|
|
573
537
|
const packages = canaryTag
|
|
574
|
-
? await getWorkspacePackages({ sinceRef: canaryTag, publicOnly: true })
|
|
575
|
-
:
|
|
538
|
+
? await getWorkspacePackages({ sinceRef: canaryTag, publicOnly: true, filter })
|
|
539
|
+
: filteredPackages;
|
|
576
540
|
|
|
577
541
|
console.log(`📋 Found ${packages.length} packages(s) for canary publishing:`);
|
|
578
542
|
packages.forEach((pkg) => {
|
|
@@ -581,7 +545,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
581
545
|
|
|
582
546
|
// Fetch version info for all packages in parallel
|
|
583
547
|
console.log('\n🔍 Fetching package version information...');
|
|
584
|
-
const versionInfoPromises =
|
|
548
|
+
const versionInfoPromises = filteredPackages.map(async (pkg) => {
|
|
585
549
|
const versionInfo = await getPackageVersionInfo(pkg.name, pkg.version);
|
|
586
550
|
return { packageName: pkg.name, versionInfo };
|
|
587
551
|
});
|
|
@@ -593,7 +557,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
593
557
|
packageVersionInfo.set(packageName, versionInfo);
|
|
594
558
|
}
|
|
595
559
|
|
|
596
|
-
await publishCanaryVersions(packages,
|
|
560
|
+
await publishCanaryVersions(packages, filteredPackages, packageVersionInfo, options);
|
|
597
561
|
|
|
598
562
|
console.log('\n🏁 Publishing complete!');
|
|
599
563
|
},
|
package/src/utils/pnpm.mjs
CHANGED
|
@@ -48,6 +48,7 @@ import * as semver from 'semver';
|
|
|
48
48
|
* @property {boolean} [publicOnly=false] - Whether to filter to only public packages
|
|
49
49
|
* @property {boolean} [nonPublishedOnly=false] - Whether to filter to only non-published packages. It by default means public packages yet to be published.
|
|
50
50
|
* @property {string} [cwd] - Current working directory to run pnpm command in
|
|
51
|
+
* @property {string[]} [filter] - Same as filtering packages with --filter in pnpm. Only include packages matching the filter. See https://pnpm.io/filtering.
|
|
51
52
|
*/
|
|
52
53
|
|
|
53
54
|
/**
|
|
@@ -73,10 +74,15 @@ import * as semver from 'semver';
|
|
|
73
74
|
* @returns {Promise<(PrivatePackage | PublicPackage)[]>} Array of packages
|
|
74
75
|
*/
|
|
75
76
|
export async function getWorkspacePackages(options = {}) {
|
|
76
|
-
const { sinceRef = null, publicOnly = false, nonPublishedOnly = false } = options;
|
|
77
|
+
const { sinceRef = null, publicOnly = false, nonPublishedOnly = false, filter = [] } = options;
|
|
77
78
|
|
|
78
79
|
// Build command with conditional filter
|
|
79
80
|
const filterArg = sinceRef ? ['--filter', `...[${sinceRef}]`] : [];
|
|
81
|
+
if (filter.length > 0) {
|
|
82
|
+
filter.forEach((f) => {
|
|
83
|
+
filterArg.push('--filter', f);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
80
86
|
const result = options.cwd
|
|
81
87
|
? await $({ cwd: options.cwd })`pnpm ls -r --json --depth -1 ${filterArg}`
|
|
82
88
|
: await $`pnpm ls -r --json --depth -1 ${filterArg}`;
|
|
@@ -175,6 +181,132 @@ export async function publishPackages(packages, options = {}) {
|
|
|
175
181
|
await $({ stdio: 'inherit' })`pnpm -r publish --access public --tag=${tag} ${args}`;
|
|
176
182
|
}
|
|
177
183
|
|
|
184
|
+
/**
|
|
185
|
+
* @typedef {Object} GetTransitiveDependenciesOptions
|
|
186
|
+
* @property {Map<string, string>} [workspacePathByName] - Map of workspace package name to directory path
|
|
187
|
+
* @property {boolean} [includeDev=true] - Whether to include devDependencies in the traversal
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get all transitive workspace dependencies for a set of packages.
|
|
192
|
+
*
|
|
193
|
+
* Traverses `dependencies`, `peerDependencies`, and optionally `devDependencies`,
|
|
194
|
+
* following only packages that exist in `workspacePathByName`. Results are cached
|
|
195
|
+
* per package so each package is read from disk at most once regardless of how many
|
|
196
|
+
* roots depend on it.
|
|
197
|
+
*
|
|
198
|
+
* @param {string[]} packageNames - Package names to start the traversal from
|
|
199
|
+
* @param {GetTransitiveDependenciesOptions} [options]
|
|
200
|
+
* @returns {Promise<Set<string>>} All reachable workspace package names, including the input packages themselves
|
|
201
|
+
*/
|
|
202
|
+
export async function getTransitiveDependencies(packageNames, options = {}) {
|
|
203
|
+
const { includeDev = true, workspacePathByName = new Map() } = options;
|
|
204
|
+
|
|
205
|
+
/** @type {Map<string, Promise<Set<string>>>} */
|
|
206
|
+
const cache = new Map();
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @param {string} packageName
|
|
210
|
+
* @returns {Promise<Set<string>>}
|
|
211
|
+
*/
|
|
212
|
+
function collectDeps(packageName) {
|
|
213
|
+
const cached = cache.get(packageName);
|
|
214
|
+
if (cached) {
|
|
215
|
+
return cached;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const promise = (async () => {
|
|
219
|
+
const packagePath = workspacePathByName.get(packageName);
|
|
220
|
+
if (!packagePath) {
|
|
221
|
+
throw new Error(`Workspace "${packageName}" not found`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const pkgJson = await readPackageJson(packagePath);
|
|
225
|
+
const allDeps = new Set([
|
|
226
|
+
...Object.keys(pkgJson.dependencies ?? {}),
|
|
227
|
+
...(includeDev ? Object.keys(pkgJson.devDependencies ?? {}) : []),
|
|
228
|
+
...Object.keys(pkgJson.peerDependencies ?? {}),
|
|
229
|
+
]);
|
|
230
|
+
const workspaceDeps = [...allDeps].filter((dep) => workspacePathByName.has(dep));
|
|
231
|
+
|
|
232
|
+
const recursiveResults = await Promise.all(workspaceDeps.map(collectDeps));
|
|
233
|
+
return new Set([...workspaceDeps, ...recursiveResults.flatMap((s) => [...s])]);
|
|
234
|
+
})();
|
|
235
|
+
|
|
236
|
+
cache.set(packageName, promise);
|
|
237
|
+
return promise;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
for (const name of packageNames) {
|
|
241
|
+
if (!workspacePathByName.has(name)) {
|
|
242
|
+
throw new Error(`Workspace "${name}" not found`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const results = await Promise.all(packageNames.map(collectDeps));
|
|
247
|
+
return new Set([...packageNames, ...results.flatMap((s) => [...s])]);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Validate that a set of packages covers all of their transitive workspace dependencies,
|
|
252
|
+
* and that none of those dependencies are private (which would make them unpublishable).
|
|
253
|
+
*
|
|
254
|
+
* @param {PublicPackage[]} packages - The packages intended for publishing
|
|
255
|
+
* @returns {Promise<{issues: string[]}>}
|
|
256
|
+
* List of human-readable issue strings. Empty when the dependency set is valid.
|
|
257
|
+
*/
|
|
258
|
+
export async function validatePublishDependencies(packages) {
|
|
259
|
+
const allWorkspacePackages = await getWorkspacePackages();
|
|
260
|
+
|
|
261
|
+
/** @type {Map<string, PublicPackage | PrivatePackage>} */
|
|
262
|
+
const workspacePackageByName = new Map(
|
|
263
|
+
allWorkspacePackages.flatMap((pkg) => (pkg.name ? [[pkg.name, pkg]] : [])),
|
|
264
|
+
);
|
|
265
|
+
const workspacePathByName = new Map(
|
|
266
|
+
allWorkspacePackages.flatMap((pkg) => (pkg.name ? [[pkg.name, pkg.path]] : [])),
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const publishedNames = new Set(packages.map((pkg) => pkg.name));
|
|
270
|
+
const transitiveDeps = await getTransitiveDependencies(
|
|
271
|
+
packages.map((pkg) => pkg.name),
|
|
272
|
+
{ includeDev: false, workspacePathByName },
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
/** @type {Set<string>} */
|
|
276
|
+
const privateButRequired = new Set();
|
|
277
|
+
/** @type {Set<string>} */
|
|
278
|
+
const missingFromPublish = new Set();
|
|
279
|
+
|
|
280
|
+
for (const depName of transitiveDeps) {
|
|
281
|
+
if (publishedNames.has(depName)) {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
const workspacePkg = workspacePackageByName.get(depName);
|
|
285
|
+
if (workspacePkg?.isPrivate) {
|
|
286
|
+
privateButRequired.add(depName);
|
|
287
|
+
} else {
|
|
288
|
+
missingFromPublish.add(depName);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** @type {string[]} */
|
|
293
|
+
const issues = [];
|
|
294
|
+
|
|
295
|
+
if (privateButRequired.size > 0) {
|
|
296
|
+
issues.push(
|
|
297
|
+
`The following private workspace packages are required as dependencies but cannot be published: ${[...privateButRequired].join(', ')}`,
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (missingFromPublish.size > 0) {
|
|
302
|
+
issues.push(
|
|
303
|
+
`The following workspace packages are required as dependencies but are not included in the publish set: ${[...missingFromPublish].join(', ')}. Add them to the --filter list.`,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return { issues };
|
|
308
|
+
}
|
|
309
|
+
|
|
178
310
|
/**
|
|
179
311
|
* Read package.json from a directory
|
|
180
312
|
* @param {string} packagePath - Path to package directory
|