diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 77143143..86d52120 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -10,7 +10,7 @@ on: jobs: test: runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 45 steps: - name: Checkout uses: actions/checkout@v4 @@ -18,8 +18,7 @@ jobs: - name: Set up Node uses: actions/setup-node@v4 with: - node-version-file: .nvmrc - check-latest: true + node-version: '20.x' - name: Install deps run: | diff --git a/.github/workflows/storybook-deploy.yml b/.github/workflows/storybook-deploy.yml index ab24ab04..2c0b7a05 100644 --- a/.github/workflows/storybook-deploy.yml +++ b/.github/workflows/storybook-deploy.yml @@ -17,12 +17,15 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.x' + - name: Install dependencies run: npm install + - uses: bitovi/github-actions-storybook-to-github-pages@v1.0.3 with: path: storybook-static diff --git a/babel.config.js b/babel.config.js index 1a72c483..8dca67f4 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,5 +1,3 @@ - - module.exports = function(api) { api.cache(true) api.assertVersion("^7.19.3") diff --git a/package.json b/package.json index 9f77d40c..e49a07c7 100644 --- a/package.json +++ b/package.json @@ -14,13 +14,6 @@ ":pretest": "npm-run-all test lint", "test": "jest -c ./config/jest.config.js", "test:watch": "jest -c ./config/jest.config.js --watchAll", - "build": "npm-run-all prebuild build:legacy build:modern build:node build:stable build:copy-files", - "build:legacy": "node ./scripts/build legacy", - "build:modern": "node ./scripts/build modern", - "build:node": "node ./scripts/build node", - "build:stable": "node ./scripts/build stable", - "build:stable:watch": "node ./scripts/build stable --watch", - "build:copy-files": "node ./scripts/copy-files.js", "lint": "npm-run-all :lint:eslint :lint:prettier", ":lint:eslint": "eslint --ext .js,.jsx,.ts,.tsx -c .eslintrc.js --max-warnings 0 ./src", ":lint:eslint:gh": "eslint --ext .js,.jsx,.ts,.tsx -c .eslintrc.js --max-warnings 0 --format @microsoft/eslint-formatter-sarif --output-file eslint-results.sarif ./src", diff --git a/scripts/build.js b/scripts/build.js deleted file mode 100644 index 2419f6ac..00000000 --- a/scripts/build.js +++ /dev/null @@ -1,131 +0,0 @@ -const childProcess = require('child_process') -const glob = require('fast-glob') -const path = require('path') -const { promisify } = require('util') -const yargs = require('yargs') - -const exec = promisify(childProcess.exec) - -const validBundles = [ - // legacy build using ES6 modules - 'legacy', - // modern build with a rolling target using ES6 modules - 'modern', - // build for node using commonJS modules - 'node', - // build with a hardcoded target using ES6 modules - 'stable', -] - -async function run(argv) { - process.env.BABEL_ENV = 'production' - process.env.NODE_ENV = 'production' - - const { bundle, largeFiles, watch, outDir: relativeOutDir, verbose } = argv - - if (validBundles.indexOf(bundle) === -1) { - throw new TypeError(`Unrecognized bundle '${bundle}'. Did you mean one of "${validBundles.join('", "')}"?`) - } - - const env = { - NODE_ENV: 'production', - BABEL_ENV: 'production', - } - // const babelConfigPath = path.resolve(__dirname, "../babel.config.js"); - const babelConfigPath = path.resolve(__dirname, '../.babelrc') - const srcDir = path.resolve('./src') - const extensions = ['.js', '.ts', '.tsx'] - const ignore = ['**/*.test.js', '**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx', '**/*.d.ts'] - - const topLevelNonIndexFiles = glob.sync(`*{${extensions.join(',')}}`, { cwd: srcDir, ignore }).filter((file) => { - return path.basename(file, path.extname(file)) !== 'index' - }) - const topLevelPathImportsCanBePackages = topLevelNonIndexFiles.length === 0 - - const outDir = path.resolve( - relativeOutDir, - // We generally support top level path imports e.g. - // 1. `import ArrowDownIcon from '@mui/icons-material/ArrowDown'`. - // 2. `import Typography from '@mui/material/Typography'`. - // The first case resolves to a file while the second case resolves to a package first i.e. a package.json - // This means that only in the second case the bundler can decide whether it uses ES modules or CommonJS modules. - // Different extensions are not viable yet since they require additional bundler config for users and additional transpilation steps in our repo. - // Switch to `exports` field in v6. - { - node: topLevelPathImportsCanBePackages ? './node' : './', - modern: './modern', - stable: topLevelPathImportsCanBePackages ? './' : './esm', - legacy: './legacy', - }[bundle] - ) - - const babelArgs = [ - '--config-file', - babelConfigPath, - '--extensions', - `"${extensions.join(',')}"`, - srcDir, - '--out-dir', - outDir, - '--ignore', - // Need to put these patterns in quotes otherwise they might be evaluated by the used terminal. - `"${ignore.join('","')}"`, - ] - - if (largeFiles) { - babelArgs.push('--compact false') - } - - if (watch) { - babelArgs.push('--watch') - } - - const command = ['babel', ...babelArgs].join(' ') - - if (verbose) { - // eslint-disable-next-line no-console - console.log(`running '${command}' with ${JSON.stringify(env)}`) - } - - const { stderr, stdout } = await exec(command, { - env: { ...process.env, ...env }, - }) - if (stderr) { - throw new Error(`'${command}' failed with \n${stderr}`) - } - - if (verbose) { - // eslint-disable-next-line no-console - console.log(stdout) - } -} - -yargs - .command({ - command: '$0 ', - description: 'build package', - builder: (command) => { - return command - .positional('bundle', { - description: `Valid bundles: "${validBundles.join('" | "')}"`, - type: 'string', - }) - .option('watch', { - type: 'boolean', - default: false, - describe: 'Set to `true` if you know you want to watch changes', - }) - .option('largeFiles', { - type: 'boolean', - default: false, - describe: 'Set to `true` if you know you are transpiling large files.', - }) - .option('out-dir', { default: './build', type: 'string' }) - .option('verbose', { type: 'boolean' }) - }, - handler: run, - }) - .help() - .strict(true) - .version(false) - .parse() diff --git a/scripts/copy-files.js b/scripts/copy-files.js deleted file mode 100644 index 962fc551..00000000 --- a/scripts/copy-files.js +++ /dev/null @@ -1,175 +0,0 @@ -/* eslint-disable no-console */ -const path = require('path'); -const fse = require('fs-extra'); -const glob = require('fast-glob'); - -const packagePath = process.cwd(); -const buildPath = path.join(packagePath, './build'); -const srcPath = path.join(packagePath, './src'); - -async function includeFileInBuild(file) { - const sourcePath = path.resolve(packagePath, file); - const targetPath = path.resolve(buildPath, path.basename(file)); - await fse.copy(sourcePath, targetPath); - console.log(`Copied ${sourcePath} to ${targetPath}`); -} - -/** - * Puts a package.json into every immediate child directory of rootDir. - * That package.json contains information about esm for bundlers so that imports - * like import Typography from '@mui/material/Typography' are tree-shakeable. - * - * It also tests that an this import can be used in TypeScript by checking - * if an index.d.ts is present at that path. - * @param {object} param0 - * @param {string} param0.from - * @param {string} param0.to - */ -async function createModulePackages({ from, to }) { - const directoryPackages = glob.sync('*/index.{js,ts,tsx}', { cwd: from }).map(path.dirname); - - await Promise.all( - directoryPackages.map(async (directoryPackage) => { - const packageJsonPath = path.join(to, directoryPackage, 'package.json'); - const topLevelPathImportsAreCommonJSModules = await fse.pathExists( - path.resolve(path.dirname(packageJsonPath), '../esm'), - ); - - const packageJson = { - sideEffects: false, - module: topLevelPathImportsAreCommonJSModules - ? path.posix.join('../esm', directoryPackage, 'index.js') - : './index.js', - main: topLevelPathImportsAreCommonJSModules - ? './index.js' - : path.posix.join('../node', directoryPackage, 'index.js'), - types: './index.d.ts', - }; - - 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)), - ]); - - const manifestErrorMessages = []; - // if (!typingsEntryExist) { - // manifestErrorMessages.push(`'types' entry '${packageJson.types}' does not exist`); - // } - if (!moduleEntryExists) { - manifestErrorMessages.push(`'module' entry '${packageJson.module}' does not exist`); - } - if (!mainEntryExists) { - manifestErrorMessages.push(`'main' entry '${packageJson.main}' does not exist`); - } - if (manifestErrorMessages.length > 0) { - // TODO: AggregateError - throw new Error(`${packageJsonPath}:\n${manifestErrorMessages.join('\n')}`); - } - - return packageJsonPath; - }), - ); -} - -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); -} - -async function createPackageFile() { - const packageData = await fse.readFile(path.resolve(packagePath, './package.json'), 'utf8'); - const { nyc, scripts, devDependencies, workspaces, ...packageDataOther } = - JSON.parse(packageData); - - const newPackageData = { - ...packageDataOther, - private: false, - ...(packageDataOther.main - ? { - main: fse.existsSync(path.resolve(buildPath, './node/index.js')) - ? './node/index.js' - : './index.js', - module: fse.existsSync(path.resolve(buildPath, './esm/index.js')) - ? './esm/index.js' - : './index.js', - } - : {}), - types: './index.d.ts', - }; - - const targetPath = path.resolve(buildPath, './package.json'); - - await fse.writeFile(targetPath, JSON.stringify(newPackageData, null, 2), 'utf8'); - console.log(`Created package.json in ${targetPath}`); - - return newPackageData; -} - -async function prepend(file, string) { - const data = await fse.readFile(file, 'utf8'); - await fse.writeFile(file, string + data, 'utf8'); -} - -async function addLicense(packageData) { - const license = `/** @license MUI v${packageData.version} - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ -`; - await Promise.all( - [ - './index.js', - './legacy/index.js', - './modern/index.js', - './node/index.js', - './umd/material-ui.development.js', - './umd/material-ui.production.min.js', - ].map(async (file) => { - try { - await prepend(path.resolve(buildPath, file), license); - } catch (err) { - if (err.code === 'ENOENT') { - console.log(`Skipped license for ${file}`); - } else { - throw err; - } - } - }), - ); -} - -async function run() { - try { - const packageData = await createPackageFile(); - - // await Promise.all( - // [ - // // use enhanced readme from workspace root for `@mui/material` - // // packageData.name === '@mui/material' ? '../../README.md' : './README.md', - // // '../../CHANGELOG.md', - // // '../../LICENSE', - // ].map((file) => includeFileInBuild(file)), - // ); - - // await addLicense(packageData); - - // TypeScript - // await typescriptCopy({ from: srcPath, to: buildPath }); - - await createModulePackages({ from: srcPath, to: buildPath }); - } catch (err) { - console.error(err); - process.exit(1); - } -} - -run(); \ No newline at end of file