@mui/internal-code-infra 0.0.2-canary.9 → 0.0.2
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/bin/code-infra.mjs +1 -0
- package/package.json +47 -28
- package/src/babel-config.mjs +138 -0
- package/src/cli/babel.mjs +156 -0
- package/src/cli/cmdArgosPush.mjs +116 -0
- package/src/cli/cmdBuild.mjs +433 -0
- package/src/cli/cmdCopyFiles.mjs +214 -0
- package/src/cli/cmdJsonLint.mjs +27 -31
- package/src/cli/cmdListWorkspaces.mjs +38 -43
- package/src/cli/cmdPublish.mjs +40 -44
- package/src/cli/cmdPublishCanary.mjs +10 -21
- package/src/cli/cmdSetVersionOverrides.mjs +110 -0
- package/src/cli/index.mjs +16 -2
- package/src/cli/pnpm.mjs +62 -23
- package/src/cli/typescript.mjs +175 -0
- package/src/eslint/baseConfig.mjs +35 -20
- package/src/eslint/material-ui/config.mjs +317 -2
- package/src/eslint/testConfig.mjs +7 -0
- package/src/untyped-plugins.d.ts +53 -0
- package/src/utils/build.mjs +20 -0
- package/src/eslint/airbnb/base.mjs +0 -113
- package/src/eslint/airbnb/typescript.mjs +0 -126
package/src/cli/pnpm.mjs
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { $ } from 'execa';
|
|
4
|
-
import * as fs from 'fs/promises';
|
|
5
|
-
import * as path from 'path';
|
|
4
|
+
import * as fs from 'node:fs/promises';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
6
|
import * as semver from 'semver';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* @typedef {Object}
|
|
9
|
+
* @typedef {Object} PrivatePackage
|
|
10
|
+
* @property {string} [name] - Package name
|
|
11
|
+
* @property {string} [version] - Package version
|
|
12
|
+
* @property {string} path - Package directory path
|
|
13
|
+
* @property {true} isPrivate - Whether the package is private
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} PublicPackage
|
|
10
18
|
* @property {string} name - Package name
|
|
11
19
|
* @property {string} version - Package version
|
|
12
20
|
* @property {string} path - Package directory path
|
|
21
|
+
* @property {false} isPrivate - Whether the package is private
|
|
13
22
|
*/
|
|
14
23
|
|
|
15
24
|
/**
|
|
@@ -21,7 +30,6 @@ import * as semver from 'semver';
|
|
|
21
30
|
/**
|
|
22
31
|
* @typedef {Object} PublishOptions
|
|
23
32
|
* @property {boolean} [dryRun] - Whether to run in dry-run mode
|
|
24
|
-
* @property {boolean} [provenance] - Whether to include provenance information
|
|
25
33
|
* @property {boolean} [noGitChecks] - Whether to skip git checks
|
|
26
34
|
*/
|
|
27
35
|
|
|
@@ -41,8 +49,21 @@ import * as semver from 'semver';
|
|
|
41
49
|
|
|
42
50
|
/**
|
|
43
51
|
* Get workspace packages with optional filtering
|
|
52
|
+
*
|
|
53
|
+
* @overload
|
|
54
|
+
* @param {{ publicOnly: true } & GetWorkspacePackagesOptions} [options={}] - Options for filtering packages
|
|
55
|
+
* @returns {Promise<PublicPackage[]>} Array of packages
|
|
56
|
+
*
|
|
57
|
+
* @overload
|
|
58
|
+
* @param {{ publicOnly?: false | undefined } & GetWorkspacePackagesOptions} [options={}] - Options for filtering packages
|
|
59
|
+
* @returns {Promise<PrivatePackage[]>} Array of packages
|
|
60
|
+
*
|
|
61
|
+
* @overload
|
|
62
|
+
* @param {GetWorkspacePackagesOptions} [options={}] - Options for filtering packages
|
|
63
|
+
* @returns {Promise<(PrivatePackage | PublicPackage)[]>} Array of packages
|
|
64
|
+
*
|
|
44
65
|
* @param {GetWorkspacePackagesOptions} [options={}] - Options for filtering packages
|
|
45
|
-
* @returns {Promise<
|
|
66
|
+
* @returns {Promise<(PrivatePackage | PublicPackage)[]>} Array of packages
|
|
46
67
|
*/
|
|
47
68
|
export async function getWorkspacePackages(options = {}) {
|
|
48
69
|
const { sinceRef = null, publicOnly = false } = options;
|
|
@@ -54,18 +75,20 @@ export async function getWorkspacePackages(options = {}) {
|
|
|
54
75
|
const packageData = JSON.parse(result.stdout);
|
|
55
76
|
|
|
56
77
|
// Filter packages based on options
|
|
57
|
-
const filteredPackages = packageData
|
|
58
|
-
.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
const filteredPackages = packageData.flatMap((pkg) => {
|
|
79
|
+
const isPrivate = pkg.private || !pkg.name || !pkg.version;
|
|
80
|
+
if (publicOnly && isPrivate) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
return [
|
|
84
|
+
/** @type {PublicPackage | PrivatePackage} */ ({
|
|
64
85
|
name: pkg.name,
|
|
65
86
|
version: pkg.version,
|
|
66
87
|
path: pkg.path,
|
|
67
|
-
|
|
68
|
-
|
|
88
|
+
isPrivate,
|
|
89
|
+
}),
|
|
90
|
+
];
|
|
91
|
+
});
|
|
69
92
|
|
|
70
93
|
return filteredPackages;
|
|
71
94
|
}
|
|
@@ -105,7 +128,7 @@ export async function getPackageVersionInfo(packageName, baseVersion) {
|
|
|
105
128
|
|
|
106
129
|
/**
|
|
107
130
|
* Publish packages with the given options
|
|
108
|
-
* @param {
|
|
131
|
+
* @param {PublicPackage[]} packages - Packages to publish
|
|
109
132
|
* @param {string} tag - npm tag to publish with
|
|
110
133
|
* @param {PublishOptions} [options={}] - Publishing options
|
|
111
134
|
* @returns {Promise<void>}
|
|
@@ -127,14 +150,7 @@ export async function publishPackages(packages, tag, options = {}) {
|
|
|
127
150
|
args.push('--no-git-checks');
|
|
128
151
|
}
|
|
129
152
|
|
|
130
|
-
|
|
131
|
-
/** @type {Record<string, string>} */
|
|
132
|
-
const env = {};
|
|
133
|
-
if (options.provenance) {
|
|
134
|
-
env.NPM_CONFIG_PROVENANCE = 'true';
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
await $({ stdio: 'inherit', env })`pnpm -r publish --access public --tag=${tag} ${args}`;
|
|
153
|
+
await $({ stdio: 'inherit' })`pnpm -r publish --access public --tag=${tag} ${args}`;
|
|
138
154
|
}
|
|
139
155
|
|
|
140
156
|
/**
|
|
@@ -167,6 +183,29 @@ export async function getCurrentGitSha() {
|
|
|
167
183
|
return result.stdout.trim();
|
|
168
184
|
}
|
|
169
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Resolve a package@version specifier to an exact version
|
|
188
|
+
* @param {string} packageSpec - Package specifier in format "package@version"
|
|
189
|
+
* @returns {Promise<string>} Exact version string
|
|
190
|
+
*/
|
|
191
|
+
export async function resolveVersion(packageSpec) {
|
|
192
|
+
const result = await $`pnpm info ${packageSpec} version --json`;
|
|
193
|
+
const versions = JSON.parse(result.stdout);
|
|
194
|
+
return typeof versions === 'string' ? versions : versions[versions.length - 1];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Find the version of a dependency for a specific package@version
|
|
199
|
+
* @param {string} packageSpec - Package specifier in format "package@version"
|
|
200
|
+
* @param {string} dependency - Dependency name to look up
|
|
201
|
+
* @returns {Promise<string>} Exact version string of the dependency
|
|
202
|
+
*/
|
|
203
|
+
export async function findDependencyVersionFromSpec(packageSpec, dependency) {
|
|
204
|
+
const result = await $`pnpm info ${packageSpec} dependencies.${dependency}`;
|
|
205
|
+
const spec = result.stdout.trim();
|
|
206
|
+
return resolveVersion(`${dependency}@${spec}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
170
209
|
/**
|
|
171
210
|
* Get the maximum semver version between two versions
|
|
172
211
|
* @param {string} a
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import * as babel from '@babel/core';
|
|
3
|
+
import pluginTypescriptSyntax from '@babel/plugin-syntax-typescript';
|
|
4
|
+
import pluginResolveImports from '@mui/internal-babel-plugin-resolve-imports';
|
|
5
|
+
import pluginRemoveImports from 'babel-plugin-transform-remove-imports';
|
|
6
|
+
import { $ } from 'execa';
|
|
7
|
+
import { globby } from 'globby';
|
|
8
|
+
import * as fs from 'node:fs/promises';
|
|
9
|
+
import * as os from 'node:os';
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
|
|
12
|
+
const $$ = $({ stdio: 'inherit' });
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Emits TypeScript declaration files.
|
|
16
|
+
* @param {string} tsconfig - The path to the tsconfig.json file.
|
|
17
|
+
* @param {string} outDir - The output directory for the declaration files.
|
|
18
|
+
*/
|
|
19
|
+
export async function emitDeclarations(tsconfig, outDir) {
|
|
20
|
+
const tsconfigDir = path.dirname(tsconfig);
|
|
21
|
+
const rootDir = path.resolve(tsconfigDir, './src');
|
|
22
|
+
await $$`tsc
|
|
23
|
+
-p ${tsconfig}
|
|
24
|
+
--rootDir ${rootDir}
|
|
25
|
+
--outDir ${outDir}
|
|
26
|
+
--declaration
|
|
27
|
+
--emitDeclarationOnly
|
|
28
|
+
--noEmit false
|
|
29
|
+
--composite false
|
|
30
|
+
--incremental false`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} sourceDirectory
|
|
35
|
+
* @param {string} destinationDirectory
|
|
36
|
+
*/
|
|
37
|
+
export async function copyDeclarations(sourceDirectory, destinationDirectory) {
|
|
38
|
+
const fullSourceDirectory = path.resolve(sourceDirectory);
|
|
39
|
+
const fullDestinationDirectory = path.resolve(destinationDirectory);
|
|
40
|
+
|
|
41
|
+
console.log(`Copying declarations from ${fullSourceDirectory} to ${fullDestinationDirectory}`);
|
|
42
|
+
|
|
43
|
+
await fs.cp(fullSourceDirectory, fullDestinationDirectory, {
|
|
44
|
+
recursive: true,
|
|
45
|
+
filter: async (src) => {
|
|
46
|
+
if (src.startsWith('.')) {
|
|
47
|
+
// ignore dotfiles
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const stats = await fs.stat(src);
|
|
51
|
+
if (stats.isDirectory()) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return src.endsWith('.d.ts') || src.endsWith('.d.mts');
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Post-processes TypeScript declaration files.
|
|
61
|
+
* @param {Object} param0
|
|
62
|
+
* @param {string} param0.directory - The directory containing the declaration files.
|
|
63
|
+
*/
|
|
64
|
+
async function postProcessDeclarations({ directory }) {
|
|
65
|
+
const dtsFiles = await globby('**/*.d.ts', {
|
|
66
|
+
absolute: true,
|
|
67
|
+
cwd: directory,
|
|
68
|
+
});
|
|
69
|
+
if (dtsFiles.length === 0) {
|
|
70
|
+
console.log(`No d.ts files found in ${directory}. Skipping post-processing.`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @type {import('@babel/core').PluginItem[]}
|
|
76
|
+
*/
|
|
77
|
+
const babelPlugins = [
|
|
78
|
+
[pluginTypescriptSyntax, { dts: true }],
|
|
79
|
+
[pluginResolveImports],
|
|
80
|
+
[pluginRemoveImports, { test: /\.css$/ }],
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
await Promise.all(
|
|
84
|
+
dtsFiles.map(async (dtsFile) => {
|
|
85
|
+
const result = await babel.transformFileAsync(dtsFile, {
|
|
86
|
+
configFile: false,
|
|
87
|
+
plugins: babelPlugins,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (typeof result?.code === 'string') {
|
|
91
|
+
await fs.writeFile(dtsFile, result.code);
|
|
92
|
+
} else {
|
|
93
|
+
console.error('failed to transform', dtsFile);
|
|
94
|
+
}
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Renames TypeScript declaration files.
|
|
101
|
+
* @param {Object} param0
|
|
102
|
+
* @param {string} param0.directory - The directory containing the declaration files.
|
|
103
|
+
*/
|
|
104
|
+
async function renameDeclarations({ directory }) {
|
|
105
|
+
const dtsFiles = await globby('**/*.d.ts', { absolute: true, cwd: directory });
|
|
106
|
+
if (dtsFiles.length === 0) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
console.log(`Renaming d.ts files to d.mts in ${directory}`);
|
|
110
|
+
await Promise.all(
|
|
111
|
+
dtsFiles.map(async (dtsFile) => {
|
|
112
|
+
const newFileName = dtsFile.replace(/\.d\.ts$/, '.d.mts');
|
|
113
|
+
await fs.rename(dtsFile, newFileName);
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Creates TypeScript declaration files for the specified bundles.
|
|
120
|
+
* Types are first created in a temporary directory and then copied to the appropriate bundle directories parallelly.
|
|
121
|
+
* After copying, babel transformations are applied to the copied files because they need to be alongside the actual js files for proper resolution.
|
|
122
|
+
*
|
|
123
|
+
* @param {Object} param0
|
|
124
|
+
* @param {boolean} [param0.isMjsBuild] - Whether the build is for ESM (ECMAScript Modules).
|
|
125
|
+
* @param {{type: import('../utils/build.mjs').BundleType, dir: string}[]} param0.bundles - The bundles to create declarations for.
|
|
126
|
+
* @param {string} param0.srcDir - The source directory.
|
|
127
|
+
* @param {string} param0.buildDir - The build directory.
|
|
128
|
+
* @param {string} param0.cwd - The current working directory.
|
|
129
|
+
* @param {boolean} param0.skipTsc - Whether to skip running TypeScript compiler (tsc) for building types.
|
|
130
|
+
*/
|
|
131
|
+
export async function createTypes({ bundles, srcDir, buildDir, cwd, skipTsc, isMjsBuild }) {
|
|
132
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'code-infra-build-tsc-'));
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
await copyDeclarations(srcDir, tmpDir);
|
|
136
|
+
const tsconfigPath = path.join(cwd, 'tsconfig.build.json');
|
|
137
|
+
const tsconfigExists = await fs.stat(tsconfigPath).then(
|
|
138
|
+
(file) => file.isFile(),
|
|
139
|
+
() => false,
|
|
140
|
+
);
|
|
141
|
+
if (!skipTsc) {
|
|
142
|
+
if (!tsconfigExists) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
'Unable to find a tsconfig to build this project. ' +
|
|
145
|
+
`The package root needs to contain a 'tsconfig.build.json'. ` +
|
|
146
|
+
`The package root is '${cwd}'`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
console.log(`Building types for ${tsconfigPath} in ${tmpDir}`);
|
|
150
|
+
await emitDeclarations(tsconfigPath, tmpDir);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
for (const bundle of bundles) {
|
|
154
|
+
const { type: bundleType, dir: bundleOutDir } = bundle;
|
|
155
|
+
const fullOutDir = path.join(buildDir, bundleOutDir);
|
|
156
|
+
// eslint-disable-next-line no-await-in-loop
|
|
157
|
+
await fs.cp(tmpDir, fullOutDir, {
|
|
158
|
+
recursive: true,
|
|
159
|
+
force: false,
|
|
160
|
+
});
|
|
161
|
+
// eslint-disable-next-line no-await-in-loop
|
|
162
|
+
await postProcessDeclarations({
|
|
163
|
+
directory: fullOutDir,
|
|
164
|
+
});
|
|
165
|
+
if (bundleType === 'esm' && isMjsBuild) {
|
|
166
|
+
// eslint-disable-next-line no-await-in-loop
|
|
167
|
+
await renameDeclarations({
|
|
168
|
+
directory: fullOutDir,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} finally {
|
|
173
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { includeIgnoreFile } from '@eslint/compat';
|
|
2
|
+
import eslintJs from '@eslint/js';
|
|
2
3
|
import prettier from 'eslint-config-prettier/flat';
|
|
3
|
-
import
|
|
4
|
+
import importPlugin from 'eslint-plugin-import';
|
|
5
|
+
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
|
|
6
|
+
import reactPlugin from 'eslint-plugin-react';
|
|
7
|
+
import { configs as reactCompilerPluginConfigs } from 'eslint-plugin-react-compiler';
|
|
4
8
|
import { configs as reactHookConfigs } from 'eslint-plugin-react-hooks';
|
|
5
9
|
import globals from 'globals';
|
|
6
|
-
import * as tseslint from 'typescript-eslint';
|
|
7
10
|
import * as fs from 'node:fs';
|
|
8
11
|
import * as path from 'node:path';
|
|
12
|
+
import * as tseslint from 'typescript-eslint';
|
|
9
13
|
|
|
10
|
-
import { airbnbBaseConfig, airbnbReactConfig } from './airbnb/base.mjs';
|
|
11
|
-
import airbnbTypescript from './airbnb/typescript.mjs';
|
|
12
14
|
import { createCoreConfig } from './material-ui/config.mjs';
|
|
13
15
|
import muiPlugin from './material-ui/index.mjs';
|
|
14
16
|
/**
|
|
@@ -36,16 +38,19 @@ export function createBaseConfig(
|
|
|
36
38
|
return /** @type {import('eslint').Linter.Config[]} */ (
|
|
37
39
|
tseslint.config(
|
|
38
40
|
...ignoreRules,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
eslintJs.configs.recommended,
|
|
42
|
+
importPlugin.flatConfigs.recommended,
|
|
43
|
+
importPlugin.flatConfigs.react,
|
|
44
|
+
jsxA11yPlugin.flatConfigs.recommended,
|
|
45
|
+
reactPlugin.configs.flat.recommended,
|
|
42
46
|
reactHookConfigs.recommended,
|
|
43
|
-
|
|
47
|
+
tseslint.configs.recommended,
|
|
48
|
+
importPlugin.flatConfigs.typescript,
|
|
49
|
+
enableReactCompiler ? reactCompilerPluginConfigs.recommended : {},
|
|
44
50
|
prettier,
|
|
45
51
|
{
|
|
46
52
|
name: 'typescript-eslint-parser',
|
|
47
53
|
languageOptions: {
|
|
48
|
-
parser: tseslint.parser,
|
|
49
54
|
ecmaVersion: 7,
|
|
50
55
|
globals: {
|
|
51
56
|
...globals.es2020,
|
|
@@ -54,19 +59,8 @@ export function createBaseConfig(
|
|
|
54
59
|
},
|
|
55
60
|
},
|
|
56
61
|
plugins: {
|
|
57
|
-
'@typescript-eslint': tseslint.plugin,
|
|
58
62
|
'material-ui': muiPlugin,
|
|
59
63
|
},
|
|
60
|
-
settings: {
|
|
61
|
-
'import/parsers': {
|
|
62
|
-
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
|
63
|
-
},
|
|
64
|
-
'import/resolver': {
|
|
65
|
-
typescript: {
|
|
66
|
-
project: ['tsconfig.node.json', 'apps/*/tsconfig.json', 'packages/*/tsconfig.json'],
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
64
|
extends: createCoreConfig({ reactCompilerEnabled: enableReactCompiler }),
|
|
71
65
|
},
|
|
72
66
|
{
|
|
@@ -82,6 +76,27 @@ export function createBaseConfig(
|
|
|
82
76
|
],
|
|
83
77
|
},
|
|
84
78
|
},
|
|
79
|
+
// Lint rule to disallow usage of typescript namespaces.We've seen at least two problems with them:
|
|
80
|
+
// * Creates non-portable types in base ui. [1]
|
|
81
|
+
// * This pattern [2] leads to broken bundling in codesandbox [3].
|
|
82
|
+
// Gauging the ecosystem it also looks like support for namespaces in tooling is poor and tends to
|
|
83
|
+
// be treated as a deprecated feature.
|
|
84
|
+
// [1] https://github.com/mui/base-ui/pull/2324
|
|
85
|
+
// [2] https://github.com/mui/mui-x/blob/1cf853ed45cf301211ece1c0ca21981ea208edfb/packages/x-virtualizer/src/models/core.ts#L4-L10
|
|
86
|
+
// [3] https://codesandbox.io/embed/kgylpd?module=/src/Demo.tsx&fontsize=12
|
|
87
|
+
{
|
|
88
|
+
rules: {
|
|
89
|
+
'@typescript-eslint/no-namespace': 'error',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
// Part of the migration away from airbnb config. Turned of initially.
|
|
93
|
+
{
|
|
94
|
+
rules: {
|
|
95
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
96
|
+
'@typescript-eslint/no-unsafe-function-type': 'off',
|
|
97
|
+
'@typescript-eslint/no-empty-object-type': 'off',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
85
100
|
)
|
|
86
101
|
);
|
|
87
102
|
}
|