@mui/internal-code-infra 0.0.4-canary.2 → 0.0.4-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 CHANGED
@@ -35,24 +35,35 @@ This is stored in the `docs` top-level directory.
35
35
 
36
36
  ### Adding and publishing new packages
37
37
 
38
- Whenever news 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.
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
- 1. Goto your repo's code base on your system, open terminal and run:
40
+ 1. Go to your repo's code base on your system, then log in to npm using
41
+
42
+ ```bash
43
+ npm login
44
+ ```
45
+
46
+ 2. Once logged-in, open terminal and run:
41
47
 
42
48
  ```bash
43
49
  pnpm code-infra publish-new-package
44
50
  ```
45
51
 
46
52
  This command detects the new public packages in the repo and asks for your confirmation before publishing them to the npm registry. Add the `--dryRun` flag to skip the actual publishing.
53
+ If publishing fails with npm asking for `otp`, run the command again with 6 digit auth code from your authenticator app where you've added npm; Google Authenticator, Authy or similar:
54
+
55
+ ```bash
56
+ pnpm code-infra publish-new-package --otp=123456
57
+ ```
47
58
 
48
- 2. Goto the settings link for each packages, ie, https://www.npmjs.com/package/<pkg-name>/access , and setup `Trusted Publisher`.
49
- 3. In `Select your publisher` step in the above link, click on the `Github Actions` button to configure Github actions based trusted publishing.
50
- 4. Fill in the details of the repo -
59
+ 3. Go to the settings link for each package, e.g., `https://www.npmjs.com/package/<pkg-name>/access`, and setup `Trusted Publisher`.
60
+ 4. In the `Select your publisher` step in the above link, click on the `GitHub Actions` button to configure GitHub Actions-based trusted publishing.
61
+ 5. Fill in the details of the repo -
51
62
  1. `Organization or user` as `mui`,
52
63
  2. `Repository` as per the new package
53
64
  3. `Workflow filename*` should be `publish.yml`
54
- 4. `Environment name` should be `npm-publish`
55
- 5. In the `Publishing access` section, toggle the recommended option of `Require two-factor authentication and disallow tokens`.
56
- 6. Finally, save the changes by clicking on `Update Package Settings` button.
65
+ 4. `Environment name` should be `npm-publish` or `npm-publish-internal` based on whether the package is user facing package or internal package respectively.
66
+ 6. In the `Publishing access` section, toggle the recommended option of `Require two-factor authentication and disallow tokens`.
67
+ 7. Finally, save the changes by clicking on `Update Package Settings` button.
57
68
 
58
69
  After following these steps, the `Publish` workflow can be invoked again.
@@ -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,10 +12,11 @@
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 {string} [param0.reactCompilerMode]
15
+ * @param {ReactCompilationMode} [param0.reactCompilerMode]
16
+ * @param {{ allowedCallees?: Record<string, string[]> }} [param0.displayName] - Options for the display name plugin.
12
17
  * @returns {import('@babel/core').TransformOptions} The base Babel configuration.
13
18
  */
14
- export declare function getBaseConfig({ debug, optimizeClsx, removePropTypes, noResolveImports, bundle, runtimeVersion, outExtension, reactCompilerReactVersion, reactCompilerMode }: {
19
+ export declare function getBaseConfig({ debug, optimizeClsx, removePropTypes, noResolveImports, bundle, runtimeVersion, outExtension, reactCompilerReactVersion, reactCompilerMode, displayName }: {
15
20
  debug?: boolean;
16
21
  optimizeClsx?: boolean;
17
22
  removePropTypes?: boolean;
@@ -20,7 +25,10 @@ export declare function getBaseConfig({ debug, optimizeClsx, removePropTypes, no
20
25
  outExtension: string | null;
21
26
  runtimeVersion: string;
22
27
  reactCompilerReactVersion?: string;
23
- reactCompilerMode?: string;
28
+ reactCompilerMode?: ReactCompilationMode;
29
+ displayName?: {
30
+ allowedCallees?: Record<string, string[]>;
31
+ };
24
32
  }): import('@babel/core').TransformOptions;
25
33
  export type Options = {
26
34
  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;
@@ -7,6 +7,7 @@ export type Args = {
7
7
  tag: string;
8
8
  ci: boolean;
9
9
  sha?: string;
10
+ filter?: string[];
10
11
  };
11
12
  declare const _default: import("yargs").CommandModule<{}, Args>;
12
13
  export default /** @type {import('yargs').CommandModule<{}, Args>} */ _default;
@@ -5,6 +5,7 @@ export type PublishOptions = import('../utils/pnpm.mjs').PublishOptions;
5
5
  export type Args = {
6
6
  dryRun?: boolean;
7
7
  githubRelease?: boolean;
8
+ filter?: string[];
8
9
  };
9
10
  export type Commit = {
10
11
  sha: string;
@@ -1,9 +1,11 @@
1
1
  export type Args = {
2
2
  dryRun?: boolean;
3
+ otp?: string;
3
4
  };
4
5
  /**
5
6
  * @typedef {Object} Args
6
7
  * @property {boolean} [dryRun] If true, will only log the commands without executing them
8
+ * @property {string} [otp] 6 digit auth code to forward to npm for two-factor authentication
7
9
  */
8
10
  declare const _default: import("yargs").CommandModule<{}, Args>;
9
11
  export default /** @type {import('yargs').CommandModule<{}, Args>} */ _default;
@@ -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,62 @@ 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
+ * Only follows deps whose version spec starts with `workspace:` (e.g. `workspace:*`
73
+ * or `workspace:^`), meaning they are sourced directly from the monorepo. Pinned
74
+ * external versions (e.g. `^1.0.0`) are ignored even when the package name exists
75
+ * in the workspace. Traverses `dependencies` and optionally `devDependencies`.
76
+ * Results are cached per package so each package is read from disk at most once
77
+ * regardless of how many roots depend on it.
78
+ *
79
+ * @param {string[]} packageNames - Package names to start the traversal from
80
+ * @param {GetTransitiveDependenciesOptions} [options]
81
+ * @returns {Promise<Set<string>>} All reachable workspace package names, including the input packages themselves
82
+ */
83
+ export declare function getTransitiveDependencies(packageNames: string[], options?: GetTransitiveDependenciesOptions): Promise<Set<string>>;
84
+ /**
85
+ * Pure validation logic: given a publish set and workspace maps, checks that all
86
+ * transitive hard workspace dependencies are covered and none are private.
87
+ *
88
+ * A hard dependency is one listed in `dependencies` (not `peerDependencies` or
89
+ * `devDependencies`) using a `workspace:` version specifier (e.g. `workspace:*` or
90
+ * `workspace:^`). Peer dependencies are never bundled and dev dependencies are not installed
91
+ * on consumer devices - both are excluded regardless of version specifier. Pinned-version
92
+ * references in `dependencies` are also excluded - they resolve from the registry and do
93
+ * not need to be co-published.
94
+ *
95
+ * @param {PublicPackage[]} packages - The packages intended for publishing
96
+ * @param {Map<string, PublicPackage | PrivatePackage>} workspacePackageByName - All workspace packages by name
97
+ * @param {Map<string, string>} workspacePathByName - Map of workspace package name to directory path
98
+ * @returns {Promise<{issues: string[]}>}
99
+ * List of human-readable issue strings. Empty when the dependency set is valid.
100
+ * @internal
101
+ */
102
+ export declare function checkPublishDependencies(packages: PublicPackage[], workspacePackageByName: Map<string, PublicPackage | PrivatePackage>, workspacePathByName: Map<string, string>): Promise<{
103
+ issues: string[];
104
+ }>;
105
+ /**
106
+ * Validate that a set of packages covers all of their transitive hard workspace dependencies,
107
+ * and that none of those dependencies are private (which would make them unpublishable).
108
+ *
109
+ * @param {PublicPackage[]} packages - The packages intended for publishing
110
+ * @returns {Promise<{issues: string[]}>}
111
+ * List of human-readable issue strings. Empty when the dependency set is valid.
112
+ */
113
+ export declare function validatePublishDependencies(packages: PublicPackage[]): Promise<{
114
+ issues: string[];
115
+ }>;
59
116
  /**
60
117
  * Read package.json from a directory
61
118
  * @param {string} packagePath - Path to package directory
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Creates a temporary directory and registers an `onTestFinished` hook to
3
+ * remove it automatically when the current test ends — even if the test throws.
4
+ *
5
+ * @returns {Promise<string>} The path of the created temporary directory.
6
+ */
7
+ export declare function makeTempDir(): Promise<string>;
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@mui/internal-code-infra",
3
- "version": "0.0.4-canary.2",
3
+ "version": "0.0.4-canary.21",
4
+ "author": "MUI Team",
4
5
  "description": "Infra scripts and configs to be used across MUI repos.",
5
- "type": "module",
6
6
  "license": "MIT",
7
+ "type": "module",
7
8
  "repository": {
8
9
  "type": "git",
9
10
  "url": "git+https://github.com/mui/mui-public.git",
@@ -43,34 +44,38 @@
43
44
  "./brokenLinksChecker": {
44
45
  "types": "./build/brokenLinksChecker/index.d.mts",
45
46
  "default": "./src/brokenLinksChecker/index.mjs"
47
+ },
48
+ "./build-env": {
49
+ "types": "./src/build-env.d.ts"
46
50
  }
47
51
  },
48
52
  "bin": {
49
53
  "code-infra": "./bin/code-infra.mjs"
50
54
  },
51
55
  "dependencies": {
52
- "@argos-ci/core": "^4.5.0",
56
+ "@argos-ci/core": "^5.1.2",
53
57
  "@babel/cli": "^7.28.6",
54
58
  "@babel/core": "^7.29.0",
55
59
  "@babel/plugin-syntax-jsx": "^7.28.6",
56
60
  "@babel/plugin-syntax-typescript": "^7.28.6",
57
61
  "@babel/plugin-transform-runtime": "^7.29.0",
58
- "@babel/preset-env": "^7.29.0",
62
+ "@babel/preset-env": "^7.29.2",
59
63
  "@babel/preset-react": "^7.28.5",
60
64
  "@babel/preset-typescript": "^7.28.5",
61
- "@eslint/compat": "^2.0.2",
65
+ "@eslint/compat": "^2.0.3",
66
+ "@eslint/config-helpers": "^0.5.4",
62
67
  "@eslint/js": "^10.0.1",
63
- "@eslint/json": "^1.0.1",
64
- "@inquirer/confirm": "^6.0.4",
65
- "@inquirer/select": "^5.0.4",
68
+ "@eslint/json": "^1.1.0",
69
+ "@inquirer/confirm": "^6.0.10",
70
+ "@inquirer/select": "^5.1.2",
66
71
  "@napi-rs/keyring": "^1.2.0",
67
72
  "@octokit/auth-action": "^6.0.2",
68
73
  "@octokit/oauth-methods": "^6.0.2",
69
74
  "@octokit/rest": "^22.0.1",
70
- "@pnpm/find-workspace-dir": "^1000.1.4",
71
- "@typescript-eslint/types": "^8.56.1",
72
- "@typescript-eslint/utils": "^8.56.1",
73
- "@vitest/eslint-plugin": "^1.6.9",
75
+ "@pnpm/find-workspace-dir": "^1000.1.5",
76
+ "@typescript-eslint/types": "^8.57.1",
77
+ "@typescript-eslint/utils": "^8.57.1",
78
+ "@vitest/eslint-plugin": "^1.6.11",
74
79
  "babel-plugin-optimize-clsx": "^2.6.2",
75
80
  "babel-plugin-react-compiler": "^1.0.0",
76
81
  "babel-plugin-transform-import-meta": "^2.3.3",
@@ -78,13 +83,14 @@
78
83
  "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
79
84
  "babel-plugin-transform-remove-imports": "^1.8.1",
80
85
  "chalk": "^5.6.2",
81
- "clipboardy": "^5.3.0",
86
+ "clipboardy": "^5.3.1",
82
87
  "content-type": "^1.0.5",
83
88
  "env-ci": "^11.2.0",
89
+ "es-toolkit": "^1.45.1",
84
90
  "eslint-config-prettier": "^10.1.8",
85
91
  "eslint-import-resolver-typescript": "^4.4.4",
86
92
  "eslint-module-utils": "^2.12.1",
87
- "eslint-plugin-compat": "^6.2.0",
93
+ "eslint-plugin-compat": "^7.0.1",
88
94
  "eslint-plugin-import": "^2.32.0",
89
95
  "eslint-plugin-jsx-a11y": "^6.10.2",
90
96
  "eslint-plugin-mocha": "^11.2.0",
@@ -92,14 +98,13 @@
92
98
  "eslint-plugin-react-compiler": "^19.1.0-rc.2",
93
99
  "eslint-plugin-react-hooks": "^7.0.1",
94
100
  "eslint-plugin-testing-library": "^7.16.0",
95
- "es-toolkit": "^1.44.0",
96
101
  "execa": "^9.6.1",
97
102
  "git-url-parse": "^16.1.0",
98
- "globals": "^16.5.0",
103
+ "globals": "^17.4.0",
99
104
  "globby": "^16.1.1",
100
- "minimatch": "^10.2.2",
101
- "node-html-parser": "^7.0.2",
102
- "open": "^10.2.0",
105
+ "minimatch": "^10.2.4",
106
+ "node-html-parser": "^7.1.0",
107
+ "open": "^11.0.0",
103
108
  "postcss-styled-syntax": "^0.7.1",
104
109
  "regexp.escape": "^2.0.1",
105
110
  "rehype-slug": "^6.0.0",
@@ -110,12 +115,12 @@
110
115
  "resolve-pkg-maps": "^1.0.0",
111
116
  "semver": "^7.7.4",
112
117
  "stylelint-config-standard": "^40.0.0",
113
- "typescript-eslint": "^8.56.1",
118
+ "typescript-eslint": "^8.57.1",
114
119
  "unified": "^11.0.5",
115
120
  "yargs": "^18.0.0",
116
- "@mui/internal-babel-plugin-display-name": "1.0.4-canary.13",
117
- "@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.32",
118
- "@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.23"
121
+ "@mui/internal-babel-plugin-display-name": "1.0.4-canary.17",
122
+ "@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.25",
123
+ "@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.34"
119
124
  },
120
125
  "peerDependencies": {
121
126
  "@next/eslint-plugin-next": "*",
@@ -143,13 +148,13 @@
143
148
  "@types/estree-jsx": "1.0.5",
144
149
  "@types/regexp.escape": "2.0.0",
145
150
  "@types/yargs": "17.0.35",
146
- "@typescript-eslint/parser": "8.56.1",
147
- "@typescript-eslint/rule-tester": "8.56.1",
148
- "eslint": "10.0.2",
149
- "get-port": "7.1.0",
151
+ "@typescript-eslint/parser": "8.57.1",
152
+ "@typescript-eslint/rule-tester": "8.57.1",
153
+ "eslint": "10.0.3",
154
+ "get-port": "7.2.0",
150
155
  "prettier": "3.8.1",
151
- "serve": "14.2.5",
152
- "typescript-eslint": "8.56.1"
156
+ "serve": "14.2.6",
157
+ "typescript-eslint": "8.57.1"
153
158
  },
154
159
  "files": [
155
160
  "bin",
@@ -161,10 +166,10 @@
161
166
  "publishConfig": {
162
167
  "access": "public"
163
168
  },
164
- "gitSha": "716b18b5736b5dbc740d49e481c99cb85b74e42e",
169
+ "gitSha": "df5ed4a21b443278e1ebb858d592c01461ce1608",
165
170
  "scripts": {
166
171
  "build": "tsgo -p tsconfig.build.json",
167
- "typescript": "tsgo -p tsconfig.json",
172
+ "typescript": "tsgo -noEmit",
168
173
  "test": "pnpm -w test --project @mui/internal-code-infra",
169
174
  "test:copy": "rm -rf build && node bin/code-infra.mjs copy-files --glob \"src/cli/*.mjs\" --glob \"src/eslint/**/*.mjs:esm\""
170
175
  }
@@ -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,8 @@ 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 {string} [param0.reactCompilerMode]
27
+ * @param {ReactCompilationMode} [param0.reactCompilerMode]
28
+ * @param {{ allowedCallees?: Record<string, string[]> }} [param0.displayName] - Options for the display name plugin.
24
29
  * @returns {import('@babel/core').TransformOptions} The base Babel configuration.
25
30
  */
26
31
  export function getBaseConfig({
@@ -33,6 +38,7 @@ export function getBaseConfig({
33
38
  outExtension,
34
39
  reactCompilerReactVersion,
35
40
  reactCompilerMode,
41
+ displayName,
36
42
  }) {
37
43
  /**
38
44
  * @type {import('@babel/preset-env').Options}
@@ -57,7 +63,7 @@ export function getBaseConfig({
57
63
  },
58
64
  '@babel/plugin-transform-runtime',
59
65
  ],
60
- [pluginDisplayName, {}, '@mui/internal-babel-plugin-display-name'],
66
+ [pluginDisplayName, { ...displayName }, '@mui/internal-babel-plugin-display-name'],
61
67
  [
62
68
  pluginTransformInlineEnvVars,
63
69
  {
@@ -183,6 +189,6 @@ export default function getBabelConfig(api) {
183
189
  removePropTypes: process.env.MUI_REMOVE_PROP_TYPES === 'true',
184
190
  noResolveImports,
185
191
  reactCompilerReactVersion: process.env.MUI_REACT_COMPILER_REACT_VERSION,
186
- reactCompilerMode: process.env.MUI_REACT_COMPILER_MODE,
192
+ reactCompilerMode: /** @type {ReactCompilationMode} */ (process.env.MUI_REACT_COMPILER_MODE),
187
193
  });
188
194
  }
@@ -0,0 +1,13 @@
1
+ export {};
2
+
3
+ declare global {
4
+ interface Env {
5
+ NODE_ENV?: 'production' | undefined;
6
+ }
7
+
8
+ interface Process {
9
+ env: Env;
10
+ }
11
+
12
+ const process: Process;
13
+ }
@@ -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], workspaceMap);
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)
@@ -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 { getWorkspacePackages, publishPackages } from '../utils/pnpm.mjs';
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,34 @@ 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('⚠️ No public packages found in workspace');
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(
320
+ `Invalid dependencies structure of packages to be published -
321
+ ${issues.join('\n ')}
322
+ `,
323
+ {
324
+ cause: issues,
325
+ },
326
+ );
327
+ }
328
+
329
+ console.log('✅ All workspace dependency requirements satisfied');
330
+ }
331
+
300
332
  // Get version from root package.json
301
333
  const version = await getReleaseVersion();
302
334