From 8bda78b88a5e35105928394210df8f8d3370d6f2 Mon Sep 17 00:00:00 2001 From: Bernardo Belchior Date: Fri, 15 Aug 2025 10:07:58 +0100 Subject: [PATCH 1/2] [infra] Remove dependency on `fs-extra` --- .../src/pages/fixtures/index.test.js | 4 +- package.json | 2 - packages-internal/test-utils/package.json | 1 - .../src/KarmaReporterReactProfiler.js | 15 +++--- packages/mui-material/package.json | 1 - pnpm-lock.yaml | 30 ------------ scripts/buildColorTypes.js | 6 +-- scripts/copyFilesUtils.mjs | 46 +++++++++---------- scripts/generateProptypes.ts | 6 +-- scripts/pigmentcss-render-mui-demos.mjs | 14 +++--- scripts/releaseTag.mjs | 8 +++- scripts/testBuiltTypes.mjs | 4 +- test/package.json | 1 - test/regressions/index.test.js | 7 +-- 14 files changed, 56 insertions(+), 89 deletions(-) diff --git a/apps/pigment-css-vite-app/src/pages/fixtures/index.test.js b/apps/pigment-css-vite-app/src/pages/fixtures/index.test.js index 52c2da94dabb1b..bcb29cde8f1739 100644 --- a/apps/pigment-css-vite-app/src/pages/fixtures/index.test.js +++ b/apps/pigment-css-vite-app/src/pages/fixtures/index.test.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import * as fse from 'fs-extra'; +import * as fs from 'node:fs/promises'; import { chromium } from '@playwright/test'; async function main() { @@ -74,7 +74,7 @@ async function main() { async function takeScreenshot({ testcase, route }) { const screenshotPath = path.resolve(screenshotDir, `.${route}.png`); - await fse.ensureDir(path.dirname(screenshotPath)); + await fs.mkdir(path.dirname(screenshotPath), { recursive: true }); const explicitScreenshotTarget = await page.$('[data-testid="screenshot-target"]'); const screenshotTarget = explicitScreenshotTarget || testcase; diff --git a/package.json b/package.json index 9567a72d67329c..511b6a10012a40 100644 --- a/package.json +++ b/package.json @@ -125,7 +125,6 @@ "@playwright/test": "1.55.0", "@types/babel__core": "^7.20.5", "@types/babel__register": "^7.17.3", - "@types/fs-extra": "^11.0.4", "@types/lodash": "^4.17.20", "@types/mocha": "^10.0.10", "@types/node": "^20.19.11", @@ -149,7 +148,6 @@ "eslint-plugin-consistent-default-export-name": "^0.0.15", "eslint-plugin-react": "^7.37.5", "fast-glob": "^3.3.3", - "fs-extra": "^11.3.1", "git-url-parse": "^16.1.0", "globby": "^14.1.0", "jsonc-parser": "^3.3.1", diff --git a/packages-internal/test-utils/package.json b/packages-internal/test-utils/package.json index 49816ac41b839e..1dd9251ee8dbc3 100644 --- a/packages-internal/test-utils/package.json +++ b/packages-internal/test-utils/package.json @@ -46,7 +46,6 @@ "chai-dom": "^1.12.1", "dom-accessibility-api": "^0.7.0", "format-util": "^1.0.5", - "fs-extra": "^11.3.1", "jsdom": "^26.1.0", "lodash": "^4.17.21", "mocha": "^11.7.2", diff --git a/packages-internal/test-utils/src/KarmaReporterReactProfiler.js b/packages-internal/test-utils/src/KarmaReporterReactProfiler.js index 8db18cf969940e..b07fdffc1ea54c 100644 --- a/packages-internal/test-utils/src/KarmaReporterReactProfiler.js +++ b/packages-internal/test-utils/src/KarmaReporterReactProfiler.js @@ -1,6 +1,6 @@ // File is not transpiled. const path = require('path'); -const fse = require('fs-extra'); +const fs = require('node:fs'); /** * @typedef {object} Browser @@ -25,7 +25,7 @@ function KarmaReporterReactProfiler(karmaConfig) { `Expected karma config to contain reactProfilerReporter.outputDir of type 'string' but got type '${typeof outputDir}'.`, ); } - fse.ensureDirSync(outputDir); + fs.mkdirSync(outputDir, { recursive: true }); /** * @param {Browser} browser @@ -44,7 +44,7 @@ function KarmaReporterReactProfiler(karmaConfig) { this.onBrowserStart = (browser) => { allRenders.set(browser.id, {}); // Create it on start to signal to users where the files will appear - fse.ensureDirSync(path.join(outputDir, browser.name)); + fs.mkdirSync(path.join(outputDir, browser.name), { recursive: true }); browser.emitter.addListener('browser_info', handleBrowserInfo); }; @@ -67,10 +67,11 @@ function KarmaReporterReactProfiler(karmaConfig) { return; } - fse.ensureDirSync(path.join(outputDir, browser.name)); - fse.writeJSONSync(path.join(outputDir, browser.name, `${Date.now()}.json`), browserRenders, { - spaces: 2, - }); + fs.mkdirSync(path.join(outputDir, browser.name), { recursive: true }); + fs.writeFileSync( + path.join(outputDir, browser.name, `${Date.now()}.json`), + JSON.stringify(browserRenders, null, 2), + ); }; } diff --git a/packages/mui-material/package.json b/packages/mui-material/package.json index b4f47201d44dbe..04e1bef4922f41 100644 --- a/packages/mui-material/package.json +++ b/packages/mui-material/package.json @@ -59,7 +59,6 @@ "chai": "^4.5.0", "css-mediaquery": "^0.1.2", "fast-glob": "^3.3.3", - "fs-extra": "^11.3.1", "lodash": "^4.17.21", "react": "^19.1.1", "react-dom": "^19.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 392705edc6e14a..0a55c9be81f0cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -106,9 +106,6 @@ importers: '@types/babel__register': specifier: ^7.17.3 version: 7.17.3 - '@types/fs-extra': - specifier: ^11.0.4 - version: 11.0.4 '@types/lodash': specifier: ^4.17.20 version: 4.17.20 @@ -178,9 +175,6 @@ importers: fast-glob: specifier: ^3.3.3 version: 3.3.3 - fs-extra: - specifier: ^11.3.1 - version: 11.3.1 git-url-parse: specifier: ^16.1.0 version: 16.1.0 @@ -966,9 +960,6 @@ importers: format-util: specifier: ^1.0.5 version: 1.0.5 - fs-extra: - specifier: ^11.3.1 - version: 11.3.1 jsdom: specifier: ^26.1.0 version: 26.1.0 @@ -1597,9 +1588,6 @@ importers: fast-glob: specifier: ^3.3.3 version: 3.3.3 - fs-extra: - specifier: ^11.3.1 - version: 11.3.1 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -2038,9 +2026,6 @@ importers: fast-glob: specifier: ^3.3.3 version: 3.3.3 - fs-extra: - specifier: ^11.3.1 - version: 11.3.1 html-webpack-plugin: specifier: ^5.6.3 version: 5.6.3(webpack@5.101.3(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.101.3))) @@ -5517,9 +5502,6 @@ packages: '@types/format-util@1.0.4': resolution: {integrity: sha512-xrCYOdHh5zA3LUrn6CvspYwlzSWxPso11Lx32WnAG6KvLCRecKZ/Rh21PLXUkzUFsQmrGcx/traJAFjR6dVS5Q==} - '@types/fs-extra@11.0.4': - resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} - '@types/gtag.js@0.0.20': resolution: {integrity: sha512-wwAbk3SA2QeU67unN7zPxjEHmPmlXwZXZvQEpbEUQuMCRGgKyE1m6XDuTUA9b6pCGb/GqJmdfMOY5LuDjJSbbg==} @@ -5549,9 +5531,6 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/jsonfile@6.1.1': - resolution: {integrity: sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==} - '@types/jsonwebtoken@9.0.7': resolution: {integrity: sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==} @@ -17720,11 +17699,6 @@ snapshots: '@types/format-util@1.0.4': {} - '@types/fs-extra@11.0.4': - dependencies: - '@types/jsonfile': 6.1.1 - '@types/node': 20.19.11 - '@types/gtag.js@0.0.20': {} '@types/hoist-non-react-statics@3.3.7(@types/react@19.1.11)': @@ -17749,10 +17723,6 @@ snapshots: '@types/json5@0.0.29': {} - '@types/jsonfile@6.1.1': - dependencies: - '@types/node': 20.19.11 - '@types/jsonwebtoken@9.0.7': dependencies: '@types/node': 20.19.11 diff --git a/scripts/buildColorTypes.js b/scripts/buildColorTypes.js index d0aa167eebbf24..6766985b1f230f 100644 --- a/scripts/buildColorTypes.js +++ b/scripts/buildColorTypes.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import * as fse from 'fs-extra'; +import * as fs from 'node:fs/promises'; import * as colors from '@mui/material/colors'; // use netlify deploy preview if you want to test changes @@ -38,7 +38,7 @@ ${Object.entries(variants) export default ${name}; `; - return fse.writeFile(typesFilename, typescript, { encoding: 'utf8' }); + return fs.writeFile(typesFilename, typescript, { encoding: 'utf8' }); } function buildColorPreviews(name, variants) { @@ -52,7 +52,7 @@ function buildColorPreviews(name, variants) { `; const filename = path.resolve(nextPublicPath, getColorHref(name, variant)); - await fse.writeFile(filename, svg, { encoding: 'utf8' }); + await fs.writeFile(filename, svg, { encoding: 'utf8' }); }), ); } diff --git a/scripts/copyFilesUtils.mjs b/scripts/copyFilesUtils.mjs index 0c1fa23a8f493f..db213dae80b516 100644 --- a/scripts/copyFilesUtils.mjs +++ b/scripts/copyFilesUtils.mjs @@ -1,6 +1,6 @@ /* eslint-disable no-console */ import path from 'path'; -import fse from 'fs-extra'; +import fs from 'node:fs/promises'; import glob from 'fast-glob'; const packagePath = process.cwd(); @@ -17,10 +17,17 @@ const buildPath = path.join(packagePath, './build'); export async function includeFileInBuild(file, target = path.basename(file)) { const sourcePath = path.resolve(packagePath, file); const targetPath = path.resolve(buildPath, target); - await fse.copy(sourcePath, targetPath); + await fs.copyFile(sourcePath, targetPath); console.log(`Copied ${sourcePath} to ${targetPath}`); } +function pathExists(pathToTest) { + return fs + .stat(pathToTest) + .then(() => true) + .catch(() => false); +} + /** * Puts a package.json into every immediate child directory of rootDir. * That package.json contains information about esm for bundlers so that imports @@ -39,7 +46,7 @@ export async function createModulePackages({ from, to }) { await Promise.all( directoryPackages.map(async (directoryPackage) => { const packageJsonPath = path.join(to, directoryPackage, 'package.json'); - const topLevelPathImportsAreCommonJSModules = await fse.pathExists( + const topLevelPathImportsAreCommonJSModules = await pathExists( path.resolve(path.dirname(packageJsonPath), '../esm'), ); @@ -55,10 +62,10 @@ export async function createModulePackages({ from, to }) { }; const [typingsEntryExist, moduleEntryExists, mainEntryExists] = await Promise.all([ - fse.pathExists(path.resolve(path.dirname(packageJsonPath), packageJson.types)), - fse.pathExists(path.resolve(path.dirname(packageJsonPath), packageJson.module)), - fse.pathExists(path.resolve(path.dirname(packageJsonPath), packageJson.main)), - fse.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)), + pathExists(path.resolve(path.dirname(packageJsonPath), packageJson.types)), + pathExists(path.resolve(path.dirname(packageJsonPath), packageJson.module)), + pathExists(path.resolve(path.dirname(packageJsonPath), packageJson.main)), + fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)), ]); const manifestErrorMessages = []; @@ -81,25 +88,14 @@ export async function createModulePackages({ from, to }) { ); } -export async function typescriptCopy({ from, to }) { - if (!(await fse.pathExists(to))) { - console.warn(`path ${to} does not exists`); - return []; - } - - const files = await glob('**/*.d.ts', { cwd: from }); - const cmds = files.map((file) => fse.copy(path.resolve(from, file), path.resolve(to, file))); - return Promise.all(cmds); -} - export async function cjsCopy({ from, to }) { - if (!(await fse.pathExists(to))) { + if (!(await pathExists(to))) { console.warn(`path ${to} does not exists`); return []; } const files = await glob('**/*.cjs', { cwd: from }); - const cmds = files.map((file) => fse.copy(path.resolve(from, file), path.resolve(to, file))); + const cmds = files.map((file) => fs.copyFile(path.resolve(from, file), path.resolve(to, file))); return Promise.all(cmds); } @@ -139,7 +135,7 @@ function createExportFor(exportName, conditions) { } export async function createPackageFile() { - const packageData = await fse.readFile(path.resolve(packagePath, './package.json'), 'utf8'); + const packageData = await fs.readFile(path.resolve(packagePath, './package.json'), 'utf8'); const { nyc, scripts, devDependencies, workspaces, ...packageDataOther } = JSON.parse(packageData); @@ -177,7 +173,7 @@ export async function createPackageFile() { }; const typeDefinitionsFilePath = path.resolve(buildPath, './index.d.ts'); - if (await fse.pathExists(typeDefinitionsFilePath)) { + if (await pathExists(typeDefinitionsFilePath)) { newPackageData.types = './index.d.ts'; } @@ -187,13 +183,13 @@ export async function createPackageFile() { const targetPath = path.resolve(buildPath, './package.json'); - await fse.writeFile(targetPath, JSON.stringify(newPackageData, null, 2), 'utf8'); + await fs.writeFile(targetPath, JSON.stringify(newPackageData, null, 2), 'utf8'); console.log(`Created package.json in ${targetPath}`); return newPackageData; } export async function prepend(file, string) { - const data = await fse.readFile(file, 'utf8'); - await fse.writeFile(file, string + data, 'utf8'); + const data = await fs.readFile(file, 'utf8'); + await fs.writeFile(file, string + data, 'utf8'); } diff --git a/scripts/generateProptypes.ts b/scripts/generateProptypes.ts index 11fb7da8c3f36c..9d374ade713328 100644 --- a/scripts/generateProptypes.ts +++ b/scripts/generateProptypes.ts @@ -1,6 +1,6 @@ /* eslint-disable no-console */ import * as path from 'path'; -import * as fse from 'fs-extra'; +import * as fs from 'node:fs/promises'; import * as prettier from 'prettier'; import glob from 'fast-glob'; import * as _ from 'lodash'; @@ -199,7 +199,7 @@ async function generateProptypes( }); }); - const sourceContent = await fse.readFile(sourceFile, 'utf8'); + const sourceContent = await fs.readFile(sourceFile, 'utf8'); const isTsFile = /(\.(ts|tsx))/.test(sourceFile); // If the component inherits the props from some unstyled components // we don't want to add those propTypes again in the Material UI/Joy UI propTypes @@ -296,7 +296,7 @@ async function generateProptypes( const formatted = fixBabelGeneratorIssues(prettified); const correctedLineEndings = fixLineEndings(sourceContent, formatted); - await fse.writeFile(sourceFile, correctedLineEndings); + await fs.writeFile(sourceFile, correctedLineEndings); } interface HandlerArgv { diff --git a/scripts/pigmentcss-render-mui-demos.mjs b/scripts/pigmentcss-render-mui-demos.mjs index 4cd7d12d4ba8e8..304b2fff0962cd 100644 --- a/scripts/pigmentcss-render-mui-demos.mjs +++ b/scripts/pigmentcss-render-mui-demos.mjs @@ -1,5 +1,5 @@ import path from 'path'; -import fse from 'fs-extra'; +import fs from 'node:fs/promises'; import * as prettier from 'prettier'; function pascalCase(string) { @@ -29,14 +29,14 @@ async function run() { }); // Find the demos of the component - const docSource = await fse.readFile( + const docSource = await fs.readFile( path.join(process.cwd(), `docs/pages/material-ui/${args[0]}.js`), 'utf8', ); const matches = docSource.match(/\/([a-z-]+)\.md\?/); const dataFolderName = matches[1]; - const filenames = await fse.readdir( + const filenames = await fs.readdir( path.join(process.cwd(), `docs/data/material/components/${dataFolderName}`), ); const tsFiles = filenames.filter((filename) => filename.endsWith('.tsx')); @@ -83,8 +83,8 @@ ${renders.join('\n')} ...prettierConfig, filepath: nextFilepath, }); - await fse.mkdirp(`apps/pigment-css-next-app/src/app/material-ui/${args[0]}`); - await fse.writeFile(nextFilepath, prettiedNextFileContent); + await fs.mkdir(`apps/pigment-css-next-app/src/app/material-ui/${args[0]}`, { recursive: true }); + await fs.writeFile(nextFilepath, prettiedNextFileContent); /** * Zero-Runtime Vite App @@ -115,8 +115,8 @@ ${renders.join('\n')} ...prettierConfig, filepath: viteFilepath, }); - await fse.mkdirp(`apps/pigment-css-vite-app/src/pages/material-ui`); - await fse.writeFile(viteFilepath, prettiedViteFileContent); + await fs.mkdir(`apps/pigment-css-vite-app/src/pages/material-ui`, { recursive: true }); + await fs.writeFile(viteFilepath, prettiedViteFileContent); } run(); diff --git a/scripts/releaseTag.mjs b/scripts/releaseTag.mjs index eeaedaaf650de4..79b2ca3a2af4a4 100644 --- a/scripts/releaseTag.mjs +++ b/scripts/releaseTag.mjs @@ -1,5 +1,5 @@ import childProcess from 'child_process'; -import fse from 'fs-extra'; +import fs from 'node:fs/promises'; import path from 'path'; import { promisify } from 'util'; import yargs from 'yargs'; @@ -49,7 +49,11 @@ async function main(argv) { const exec = dryRun ? execDry : execActual; const rootWorkspace = getWorkspaceRoot(); - const rootWorkspaceManifest = await fse.readJSON(path.join(rootWorkspace, 'package.json')); + const rootWorkspacePackageJson = await fs.readFile( + path.join(rootWorkspace, 'package.json'), + 'utf-8', + ); + const rootWorkspaceManifest = JSON.parse(rootWorkspacePackageJson, null); const tag = `v${rootWorkspaceManifest.version}`; const message = `Version ${rootWorkspaceManifest.version}`; diff --git a/scripts/testBuiltTypes.mjs b/scripts/testBuiltTypes.mjs index 65a31de0952b2d..a73e53dc27a0fd 100644 --- a/scripts/testBuiltTypes.mjs +++ b/scripts/testBuiltTypes.mjs @@ -1,5 +1,5 @@ import glob from 'fast-glob'; -import fse from 'fs-extra'; +import fs from 'node:fs/promises'; import path from 'path'; import { getWorkspaceRoot } from './utils.mjs'; @@ -15,7 +15,7 @@ async function main() { await Promise.all( declarationFiles.map(async (declarationFilePath) => { - const declarationFile = await fse.readFile(declarationFilePath, { encoding: 'utf8' }); + const declarationFile = await fs.readFile(declarationFilePath, { encoding: 'utf8' }); // find occurrences of e.g. `import("../../mui-*/src/...")` const typeImportsRelativeToWorkspace = declarationFile.match(/import\(("|')(\.\.\/)+mui/g); diff --git a/test/package.json b/test/package.json index 5d1da001d2ec78..0caa8971c2c962 100644 --- a/test/package.json +++ b/test/package.json @@ -26,7 +26,6 @@ "chai": "^4.5.0", "docs": "workspace:^", "fast-glob": "^3.3.3", - "fs-extra": "^11.3.1", "html-webpack-plugin": "^5.6.3", "lodash": "^4.17.21", "prop-types": "^15.8.1", diff --git a/test/regressions/index.test.js b/test/regressions/index.test.js index c0a6696909b12e..553ace920890c7 100644 --- a/test/regressions/index.test.js +++ b/test/regressions/index.test.js @@ -1,6 +1,6 @@ import * as url from 'url'; import * as path from 'path'; -import * as fse from 'fs-extra'; +import * as fs from 'node:fs/promises'; import { chromium } from '@playwright/test'; const currentDirectory = url.fileURLToPath(new URL('.', import.meta.url)); @@ -79,7 +79,7 @@ async function main() { async function takeScreenshot({ testcase, route }) { const screenshotPath = path.resolve(screenshotDir, `.${route}.png`); - await fse.ensureDir(path.dirname(screenshotPath)); + await fs.mkdir(path.dirname(screenshotPath), { recursive: true }); const explicitScreenshotTarget = await page.$('[data-testid="screenshot-target"]'); const screenshotTarget = explicitScreenshotTarget || testcase; @@ -92,7 +92,8 @@ async function main() { } // prepare screenshots - await fse.emptyDir(screenshotDir); + await fs.rmdir(screenshotDir, { recursive: true }); + await fs.mkdir(screenshotDir, { recursive: true }); describe('visual regressions', () => { beforeEach(async () => { From 92d48b487c22e98ebc66c9894093f4e43bc81d2b Mon Sep 17 00:00:00 2001 From: Bernardo Belchior Date: Fri, 15 Aug 2025 14:03:33 +0100 Subject: [PATCH 2/2] Use rm instead of rmdir --- test/regressions/index.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/regressions/index.test.js b/test/regressions/index.test.js index 553ace920890c7..0878a1bb434998 100644 --- a/test/regressions/index.test.js +++ b/test/regressions/index.test.js @@ -92,7 +92,7 @@ async function main() { } // prepare screenshots - await fs.rmdir(screenshotDir, { recursive: true }); + await fs.rm(screenshotDir, { recursive: true, force: true }); await fs.mkdir(screenshotDir, { recursive: true }); describe('visual regressions', () => {