Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/babel-plugin-resolve-imports/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function pathToNodeImportSpecifier(importPath) {
module.exports = function plugin({ types: t }, { outExtension }) {
/** @type {Map<string, string>} */
const cache = new Map();
const extensions = ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx'];
const extensions = ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.cjs', '.cts'];
const extensionsSet = new Set(extensions);

/**
Expand All @@ -53,7 +53,7 @@ module.exports = function plugin({ types: t }, { outExtension }) {

const importExt = nodePath.extname(importedPath);
// ignore if the import already has a desired extension or if it is a css import.
if (extensionsSet.has(importExt) || importExt === '.css') {
if ((extensionsSet.has(importExt) || importExt === '.css') && outExtension !== '.cjs') {
return;
}

Expand Down
2 changes: 0 additions & 2 deletions packages/code-infra/bin/code-infra.mjs

This file was deleted.

5 changes: 3 additions & 2 deletions packages/code-infra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
"./markdownlint": "./src/markdownlint/index.mjs"
},
"bin": {
"code-infra": "./bin/code-infra.mjs"
"code-infra": "./src/bin/code-infra.mjs"
},
"scripts": {
"build-test": "node src/bin/code-infra.mjs build --flag=bundle-extension --skipBabelRuntimeCheck --ignore=\"**/setupVitest.ts\"",
"typescript": "tsc -p tsconfig.json",
"test": "pnpm -w test --project @mui/internal-code-infra",
"test:copy": "rm -rf build && node bin/code-infra.mjs copy-files --glob \"src/cli/*.mjs\" --glob \"src/eslint/**/*.mjs:esm\""
"test:copy": "rm -rf build && node src/bin/code-infra.mjs copy-files --glob \"src/cli/*.mjs\" --glob \"src/eslint/**/*.mjs:esm\""
},
"dependencies": {
"@argos-ci/core": "^4.1.2",
Expand Down
2 changes: 2 additions & 0 deletions packages/code-infra/src/bin/code-infra.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
import '../cli/index.mjs';
15 changes: 12 additions & 3 deletions packages/code-infra/src/cli/babel.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as path from 'node:path';
import { $ } from 'execa';
import { BASE_IGNORES } from '../utils/build.mjs';

const TO_TRANSFORM_EXTENSIONS = ['.js', '.ts', '.tsx'];
const TO_TRANSFORM_EXTENSIONS = ['.js', '.ts', '.tsx', '.mjs', '.cjs', '.mts', '.cts'];

/**
* @param {string} pkgVersion
Expand Down Expand Up @@ -129,7 +129,14 @@ export async function babelBuild({
...process.env,
...env,
},
})`babel --config-file ${configFile} --extensions ${TO_TRANSFORM_EXTENSIONS.join(',')} ${sourceDir} --out-dir ${outDir} --ignore ${BASE_IGNORES.concat(ignores).join(',')} --out-file-extension ${outExtension !== '.js' ? outExtension : '.js'} --compact ${hasLargeFiles ? 'false' : 'auto'}`;
})`babel
--config-file ${configFile}
--extensions ${TO_TRANSFORM_EXTENSIONS.join(',')}
${sourceDir}
--out-dir ${outDir}
--ignore ${BASE_IGNORES.concat(ignores).join(',')}
--out-file-extension ${outExtension !== '.js' ? outExtension : '.js'}
--compact ${hasLargeFiles ? 'false' : 'auto'}`;

if (res.stderr) {
throw new Error(`Command: '${res.escapedCommand}' failed with \n${res.stderr}`);
Expand All @@ -141,5 +148,7 @@ export async function babelBuild({
// cjs for reexporting from commons only modules.
// If we need to rely more on this we can think about setting up a separate commonjs => commonjs build for .cjs files to .cjs
// `--extensions-.cjs --out-file-extension .cjs`
await cjsCopy({ from: sourceDir, to: outDir });
if (outExtension === '.js') {
await cjsCopy({ from: sourceDir, to: outDir });
}
}
52 changes: 37 additions & 15 deletions packages/code-infra/src/cli/cmdBuild.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { $ } from 'execa';
import set from 'lodash-es/set.js';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { getOutExtension, isMjsBuild, validatePkgJson } from '../utils/build.mjs';
import { getOutExtension, validatePkgJson } from '../utils/build.mjs';

/**
* @typedef {Object} Args
Expand All @@ -18,6 +18,7 @@ import { getOutExtension, isMjsBuild, validatePkgJson } from '../utils/build.mjs
* @property {boolean} skipPackageJson - Whether to skip generating the package.json file in the bundle output.
* @property {boolean} skipMainCheck - Whether to skip checking for main field in package.json.
* @property {string[]} ignore - Globs to be ignored by Babel.
* @property {string[]} [flag] - Flags to alter the build behavior.
*/

const validBundles = [
Expand All @@ -34,9 +35,17 @@ const validBundles = [
* @param {string} options.license - The license of the package.
* @param {import('../utils/build.mjs').BundleType} options.bundle
* @param {string} options.outputDir
* @param {boolean} [options.useBundleExtension]
*/
async function addLicense({ name, version, license, bundle, outputDir }) {
const outExtension = getOutExtension(bundle);
async function addLicense({
name,
version,
license,
bundle,
outputDir,
useBundleExtension = false,
}) {
const outExtension = getOutExtension(bundle, false, useBundleExtension);
const file = path.join(outputDir, `index${outExtension}`);
if (
!(await fs.stat(file).then(
Expand Down Expand Up @@ -128,8 +137,16 @@ async function createExportsFor({
* @param {string} param0.outputDir
* @param {string} param0.cwd
* @param {boolean} param0.addTypes - Whether to add type declarations for the package.
* @param {boolean} [param0.useBundleExtension] - Whether the build is using bundled extensions.
*/
async function writePackageJson({ packageJson, bundles, outputDir, cwd, addTypes = false }) {
async function writePackageJson({
packageJson,
bundles,
outputDir,
cwd,
addTypes = false,
useBundleExtension = false,
}) {
delete packageJson.scripts;
delete packageJson.publishConfig?.directory;
delete packageJson.devDependencies;
Expand All @@ -151,8 +168,8 @@ async function writePackageJson({ packageJson, bundles, outputDir, cwd, addTypes

await Promise.all(
bundles.map(async ({ type, dir }) => {
const outExtension = getOutExtension(type);
const typeOutExtension = getOutExtension(type, true);
const outExtension = getOutExtension(type, false, useBundleExtension);
const typeOutExtension = getOutExtension(type, true, useBundleExtension);
const indexFileExists = await fs.stat(path.join(outputDir, dir, `index${outExtension}`)).then(
(stats) => stats.isFile(),
() => false,
Expand Down Expand Up @@ -293,11 +310,12 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
default: false,
description: 'Skip generating the package.json file in the bundle output.',
})
.option('skipMainCheck', {
// Currently added only to support @mui/icons-material. To be removed separately.
type: 'boolean',
default: false,
description: 'Skip checking for main field in package.json.',
.option('flag', {
type: 'string',
array: true,
description: 'Flags to alter the build behavior.',
choices: ['bundle-extension'],
default: [],
});
},
async handler(args) {
Expand All @@ -312,7 +330,9 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
skipTsc,
skipBabelRuntimeCheck = false,
skipPackageJson = false,
flag: flags = [],
} = args;
const useBundleExtension = flags.includes('bundle-extension');

const cwd = process.cwd();
const pkgJsonPath = path.join(cwd, 'package.json');
Expand Down Expand Up @@ -356,9 +376,9 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
// js build start
await Promise.all(
bundles.map(async (bundle) => {
const outExtension = getOutExtension(bundle);
const outExtension = getOutExtension(bundle, false, useBundleExtension);
const relativeOutDir = relativeOutDirs[bundle];
const outputDir = path.join(buildDir, relativeOutDir);
const outputDir = useBundleExtension ? buildDir : path.join(buildDir, relativeOutDir);
await fs.mkdir(outputDir, { recursive: true });

const promises = [];
Expand All @@ -382,7 +402,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
}),
);

if (buildDir !== outputDir && !skipBundlePackageJson && !isMjsBuild) {
if (buildDir !== outputDir && !skipBundlePackageJson && !useBundleExtension) {
// @TODO - Not needed if the output extension is .mjs. Remove this before PR merge.
promises.push(
fs.writeFile(
Expand All @@ -402,6 +422,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
name: packageJson.name,
version: packageJson.version,
outputDir,
useBundleExtension,
});
}),
);
Expand All @@ -422,7 +443,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
srcDir: sourceDir,
cwd,
skipTsc,
isMjsBuild,
useBundleExtension,
buildDir,
});
}
Expand All @@ -441,6 +462,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
})),
outputDir: buildDir,
addTypes: buildTypes,
useBundleExtension,
});
},
});
63 changes: 32 additions & 31 deletions packages/code-infra/src/cli/typescript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { globby } from 'globby';
import * as fs from 'node:fs/promises';
import * as os from 'node:os';
import * as path from 'node:path';
import { getOutExtension } from '../utils/build.mjs';

const $$ = $({ stdio: 'inherit' });

Expand All @@ -24,6 +25,7 @@ export async function emitDeclarations(tsconfig, outDir) {
--rootDir ${rootDir}
--outDir ${outDir}
--declaration
--declarationMap false
--emitDeclarationOnly
--noEmit false
--composite false
Expand Down Expand Up @@ -60,8 +62,9 @@ export async function copyDeclarations(sourceDirectory, destinationDirectory) {
* Post-processes TypeScript declaration files.
* @param {Object} param0
* @param {string} param0.directory - The directory containing the declaration files.
* @param {string} [param0.outExtension] - The output file extension for the processed files.
*/
async function postProcessDeclarations({ directory }) {
async function postProcessDeclarations({ directory, outExtension }) {
const dtsFiles = await globby('**/*.d.ts', {
absolute: true,
cwd: directory,
Expand All @@ -76,7 +79,7 @@ async function postProcessDeclarations({ directory }) {
*/
const babelPlugins = [
[pluginTypescriptSyntax, { dts: true }],
[pluginResolveImports],
[pluginResolveImports, outExtension ? { outExtension } : {}],
[pluginRemoveImports, { test: /\.css$/ }],
];

Expand All @@ -86,49 +89,51 @@ async function postProcessDeclarations({ directory }) {
configFile: false,
plugins: babelPlugins,
});
let dtsOutExtension = '.ts';

if (outExtension === '.mjs') {
dtsOutExtension = '.mts';
} else if (outExtension === '.cjs') {
dtsOutExtension = '.cts';
}

console.log({
dtsOutExtension,
dtsFile,
out: dtsFile.replace(/\.ts$/, dtsOutExtension),
outExtension,
});
if (typeof result?.code === 'string') {
await fs.writeFile(dtsFile, result.code);
await fs.rm(dtsFile, { force: true });
await fs.writeFile(dtsFile.replace(/\.ts$/, dtsOutExtension), result.code);
} else {
console.error('failed to transform', dtsFile);
}
}),
);
}

/**
* Renames TypeScript declaration files.
* @param {Object} param0
* @param {string} param0.directory - The directory containing the declaration files.
*/
async function renameDeclarations({ directory }) {
const dtsFiles = await globby('**/*.d.ts', { absolute: true, cwd: directory });
if (dtsFiles.length === 0) {
return;
}
console.log(`Renaming d.ts files to d.mts in ${directory}`);
await Promise.all(
dtsFiles.map(async (dtsFile) => {
const newFileName = dtsFile.replace(/\.d\.ts$/, '.d.mts');
await fs.rename(dtsFile, newFileName);
}),
);
}

/**
* Creates TypeScript declaration files for the specified bundles.
* Types are first created in a temporary directory and then copied to the appropriate bundle directories parallelly.
* After copying, babel transformations are applied to the copied files because they need to be alongside the actual js files for proper resolution.
*
* @param {Object} param0
* @param {boolean} [param0.isMjsBuild] - Whether the build is for ESM (ECMAScript Modules).
* @param {boolean} [param0.useBundleExtension=false] - Whether the build is using bundled extensions.
* @param {{type: import('../utils/build.mjs').BundleType, dir: string}[]} param0.bundles - The bundles to create declarations for.
* @param {string} param0.srcDir - The source directory.
* @param {string} param0.buildDir - The build directory.
* @param {string} param0.cwd - The current working directory.
* @param {boolean} param0.skipTsc - Whether to skip running TypeScript compiler (tsc) for building types.
*/
export async function createTypes({ bundles, srcDir, buildDir, cwd, skipTsc, isMjsBuild }) {
export async function createTypes({
bundles,
srcDir,
buildDir,
cwd,
skipTsc,
useBundleExtension = false,
}) {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'code-infra-build-tsc-'));

try {
Expand All @@ -152,22 +157,18 @@ export async function createTypes({ bundles, srcDir, buildDir, cwd, skipTsc, isM

for (const bundle of bundles) {
const { type: bundleType, dir: bundleOutDir } = bundle;
const fullOutDir = path.join(buildDir, bundleOutDir);
const fullOutDir = useBundleExtension ? buildDir : path.join(buildDir, bundleOutDir);
// eslint-disable-next-line no-await-in-loop
await fs.cp(tmpDir, fullOutDir, {
recursive: true,
force: false,
});
const outExtension = getOutExtension(bundleType, false, useBundleExtension);
// eslint-disable-next-line no-await-in-loop
await postProcessDeclarations({
directory: fullOutDir,
outExtension,
});
if (bundleType === 'esm' && isMjsBuild) {
// eslint-disable-next-line no-await-in-loop
await renameDeclarations({
directory: fullOutDir,
});
}
}
} finally {
await fs.rm(tmpDir, { recursive: true, force: true });
Expand Down
11 changes: 5 additions & 6 deletions packages/code-infra/src/utils/build.mjs
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
/**
* @typedef {'esm' | 'cjs'} BundleType
*/
export const isMjsBuild = !!process.env.MUI_EXPERIMENTAL_MJS;

/**
* @param {BundleType} bundle
*/
export function getOutExtension(bundle, isType = false) {
export function getOutExtension(bundle, isType = false, useBundleExtension = false) {
if (isType) {
if (!isMjsBuild) {
if (!useBundleExtension) {
return '.d.ts';
}
return bundle === 'esm' ? '.d.mts' : '.d.ts';
return bundle === 'esm' ? '.d.mts' : '.d.cts';
}
if (!isMjsBuild) {
if (!useBundleExtension) {
return '.js';
}
return bundle === 'esm' ? '.mjs' : '.js';
return bundle === 'esm' ? '.mjs' : '.cjs';
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/docs-infra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
},
"homepage": "https://github.com/mui/mui-public/tree/master/packages/docs-infra",
"scripts": {
"build": "code-infra build --bundle esm && pnpm build:copy-files",
"build": "code-infra build --bundle esm --flag=bundle-extension && pnpm build:copy-files",
"build:copy-files": "code-infra copy-files",
"test": "exit 0",
"typescript": "tsc -p tsconfig.json"
Expand Down
Loading