From 0c7b82e72de610fee6fde4e2a50a9051ecf600cb Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sat, 5 May 2018 17:07:52 +0200 Subject: [PATCH] [codemod] Prepare the import path breaking change --- .../minimizing-bundle-size.md | 3 +- packages/material-ui-codemod/README.md | 15 ++++ .../src/v0.15.0/import-path.js | 4 +- .../src/v1.0.0/color-imports.js | 4 +- .../src/v1.0.0/import-path.js | 77 +++++++++++++++++++ .../src/v1.0.0/import-path.test.js | 51 ++++++++++++ .../src/v1.0.0/import-path.test/actual.js | 30 ++++++++ .../src/v1.0.0/import-path.test/expected.js | 56 ++++++++++++++ .../src/v1.0.0/svg-icon-imports.js | 4 +- 9 files changed, 236 insertions(+), 8 deletions(-) create mode 100644 packages/material-ui-codemod/src/v1.0.0/import-path.js create mode 100644 packages/material-ui-codemod/src/v1.0.0/import-path.test.js create mode 100644 packages/material-ui-codemod/src/v1.0.0/import-path.test/actual.js create mode 100644 packages/material-ui-codemod/src/v1.0.0/import-path.test/expected.js diff --git a/docs/src/pages/guides/minimizing-bundle-size/minimizing-bundle-size.md b/docs/src/pages/guides/minimizing-bundle-size/minimizing-bundle-size.md index 3ddb9630244805..1b01dbfab290f2 100644 --- a/docs/src/pages/guides/minimizing-bundle-size/minimizing-bundle-size.md +++ b/docs/src/pages/guides/minimizing-bundle-size/minimizing-bundle-size.md @@ -11,7 +11,7 @@ The size of the bundle is checked at each commit: ## How to reduce the bundle size? For convenience, Material-UI exposes its full API on the top-level `material-ui` import. -Using this is fine if you have tree shaking working, +Using this is fine if you have tree shaking working, however, in the case where tree shaking is not supported or configured in your build chain, **this causes the entire library and its dependencies to be included** in your client bundle. You have couple of options to overcome this situation: @@ -47,7 +47,6 @@ Pick one of the following plugins: - [babel-plugin-import](https://github.com/ant-design/babel-plugin-import) is quite customizable and with enough tweaks works with Material-UI. - [babel-transform-imports](https://bitbucket.org/amctheatres/babel-transform-imports) has a different api than a `babel-plugin-import` but does same thing. -- [babel-plugin-direct-import](https://github.com/umidbekkarimov/babel-plugin-direct-import) automatically scans exported modules so in most cases it works with zero configuration. - [babel-plugin-lodash](https://github.com/lodash/babel-plugin-lodash) aims to work out of the box with all the `package.json`. **Important note**: Both of these options *should be temporary* until you add tree shaking capabilities to your project. diff --git a/packages/material-ui-codemod/README.md b/packages/material-ui-codemod/README.md index 5bdedc6e1b4331..e1a8114102ee1c 100644 --- a/packages/material-ui-codemod/README.md +++ b/packages/material-ui-codemod/README.md @@ -21,6 +21,21 @@ APIs. ### v1.0.0 +#### `import-path` + +Updates the `import-paths` for the new location of the components. +Material-UI v1.0.0 flatten the import paths. +The diff should look like this: + +```diff +-import { MenuItem } from 'material-ui/Menu'; ++import MenuItem from 'material-ui/MenuItem'; +``` + +```sh +find src -name '*.js' -print | xargs jscodeshift -t node_modules/@material-ui/codemod/lib/v0.15.0/import-path.js +``` + #### `color-imports` Updates the `color-imports` for the new location of Material-UI color palettes. diff --git a/packages/material-ui-codemod/src/v0.15.0/import-path.js b/packages/material-ui-codemod/src/v0.15.0/import-path.js index 3cbe37aed6777d..54e7d336be5a16 100644 --- a/packages/material-ui-codemod/src/v0.15.0/import-path.js +++ b/packages/material-ui-codemod/src/v0.15.0/import-path.js @@ -69,10 +69,10 @@ function getPathsBase(path) { return new Error('Wrong path'); } -export default function transformer(file, api) { +export default function transformer(fileInfo, api) { const j = api.jscodeshift; - return j(file.source) + return j(fileInfo.source) .find(j.ImportDeclaration) .filter(path => { // Only consider Material-UI imports diff --git a/packages/material-ui-codemod/src/v1.0.0/color-imports.js b/packages/material-ui-codemod/src/v1.0.0/color-imports.js index 9a877f855bf644..94de236fba6375 100644 --- a/packages/material-ui-codemod/src/v1.0.0/color-imports.js +++ b/packages/material-ui-codemod/src/v1.0.0/color-imports.js @@ -147,9 +147,9 @@ function transformNamespaceImports(j, root, importPath, targetPath) { }); } -module.exports = function transformer(file, api, options = {}) { +module.exports = function transformer(fileInfo, api, options = {}) { const j = api.jscodeshift; - const root = j(file.source); + const root = j(fileInfo.source); const importPath = options.importPath || 'material-ui/styles/colors'; const targetPath = options.targetPath || 'material-ui/colors'; diff --git a/packages/material-ui-codemod/src/v1.0.0/import-path.js b/packages/material-ui-codemod/src/v1.0.0/import-path.js new file mode 100644 index 00000000000000..71ee6ed9ed8b44 --- /dev/null +++ b/packages/material-ui-codemod/src/v1.0.0/import-path.js @@ -0,0 +1,77 @@ +const entryModuleToFlatten = [ + 'Menu', + 'Tabs', + 'BottomNavigation', + 'Card', + 'Collapse', + 'List', + 'Dialog', + 'Slide', + 'Radio', + 'ExpansionPanel', + 'GridList', + 'Progress', + 'Form', + 'Fade', + 'Stepper', + 'Table', + 'Input', + 'Grow', +]; + +export default function transformer(fileInfo, api, options) { + const j = api.jscodeshift; + let hasModifications = false; + const printOptions = options.printOptions || { + quote: 'single', + trailingComma: true, + }; + + const root = j(fileInfo.source); + const importRegExp = /^material-ui\/(.+)/; + + root.find(j.ImportDeclaration).forEach(path => { + const importPath = path.value.source.value; + let entryModule = importPath.match(importRegExp); + + // Remove non-Material-UI imports + if (!entryModule) { + return; + } + entryModule = entryModule[1].split('/'); + entryModule = entryModule[entryModule.length - 1]; + + // No need to flatten + if (!entryModuleToFlatten.includes(entryModule)) { + return; + } + + hasModifications = true; + // console.log('entryModule', entryModule); + + path.node.specifiers.forEach(specifier => { + const localName = specifier.local.name; + const importedName = specifier.imported ? specifier.imported.name : null; + + if (!importedName) { + const importStatement = j.importDeclaration( + [j.importDefaultSpecifier(j.identifier(localName))], + j.literal(`material-ui/${entryModule}`), + ); + + j(path).insertBefore(importStatement); + } else { + const importStatement = j.importDeclaration( + [j.importDefaultSpecifier(j.identifier(localName))], + j.literal(`material-ui/${importedName}`), + ); + + j(path).insertBefore(importStatement); + } + }); + + path.prune(); + }); + + return hasModifications ? root.toSource(printOptions) : null; +} diff --git a/packages/material-ui-codemod/src/v1.0.0/import-path.test.js b/packages/material-ui-codemod/src/v1.0.0/import-path.test.js new file mode 100644 index 00000000000000..af1a41ba102e38 --- /dev/null +++ b/packages/material-ui-codemod/src/v1.0.0/import-path.test.js @@ -0,0 +1,51 @@ +import fs from 'fs'; +import path from 'path'; +import { assert } from 'chai'; +import jscodeshift from 'jscodeshift'; +import transform from './import-path'; + +function trim(str) { + return str ? str.replace(/^\s+|\s+$/, '') : ''; +} + +function read(fileName) { + return fs.readFileSync(path.join(__dirname, fileName), 'utf8').toString(); +} + +describe('@material-ui/codemod', () => { + describe('v1.0.0', () => { + describe('import-path', () => { + it('convert path as needed', () => { + const actual = transform( + { source: read('./import-path.test/actual.js') }, + { jscodeshift: jscodeshift }, + {}, + ); + + const expected = read('./import-path.test/expected.js'); + + assert.strictEqual( + trim(actual), + trim(expected), + 'The transformed version should be correct', + ); + }); + + it('should be idempotent', () => { + const actual = transform( + { source: read('./import-path.test/expected.js') }, + { jscodeshift: jscodeshift }, + {}, + ); + + const expected = read('./import-path.test/expected.js'); + + assert.strictEqual( + trim(actual), + trim(expected), + 'The transformed version should be correct', + ); + }) + }); + }); +}); diff --git a/packages/material-ui-codemod/src/v1.0.0/import-path.test/actual.js b/packages/material-ui-codemod/src/v1.0.0/import-path.test/actual.js new file mode 100644 index 00000000000000..424cce9b686cac --- /dev/null +++ b/packages/material-ui-codemod/src/v1.0.0/import-path.test/actual.js @@ -0,0 +1,30 @@ +import React from 'react' +import { withStyles } from 'material-ui/styles' +import { MenuItem } from 'material-ui/Menu'; +import MuiTabs, { Tab } from 'material-ui/Tabs'; +import BottomNavigation, { BottomNavigationAction } from 'material-ui/BottomNavigation'; +import Card, { CardActions, CardContent } from 'material-ui/Card'; +import { CardHeader, CardMedia } from 'material-ui/Card'; +import MuiCollapse from 'material-ui/transitions/Collapse'; +import List, { ListItemIcon, ListItem, ListItemAvatar, ListItemText, ListItemSecondaryAction } from 'material-ui/List'; +import Dialog, { DialogTitle } from 'material-ui/Dialog'; +import { withMobileDialog, DialogActions, DialogContent, DialogContentText } from 'material-ui/Dialog'; +import Slide from 'material-ui/transitions/Slide'; +import Radio, { RadioGroup } from 'material-ui/Radio'; +import { FormControlLabel } from 'material-ui/Form'; +import ExpansionPanel, { ExpansionPanelSummary, ExpansionPanelDetails, ExpansionPanelActions } from 'material-ui/ExpansionPanel'; +import GridList, { GridListTile } from 'material-ui/GridList'; +import { CircularProgress } from 'material-ui/Progress'; +import { LinearProgress as MuiLinearProgress } from 'material-ui/Progress'; +import { FormLabel, FormControl, FormGroup, FormControlLabel, FormHelperText } from 'material-ui/Form'; +import Fade from 'material-ui/transitions/Fade'; +import Stepper, { Step, StepButton, StepContent } from 'material-ui/Stepper'; +import Table, { + TableBody, + TableCell, + TableFooter, + TablePagination, + TableRow, +} from 'material-ui/Table'; +import Input, { InputLabel } from 'material-ui/Input'; +import Grow from 'material-ui/transitions/Grow'; diff --git a/packages/material-ui-codemod/src/v1.0.0/import-path.test/expected.js b/packages/material-ui-codemod/src/v1.0.0/import-path.test/expected.js new file mode 100644 index 00000000000000..21d7f6bf508eb6 --- /dev/null +++ b/packages/material-ui-codemod/src/v1.0.0/import-path.test/expected.js @@ -0,0 +1,56 @@ +import React from 'react' +import { withStyles } from 'material-ui/styles' +import MenuItem from 'material-ui/MenuItem'; +import Tab from 'material-ui/Tab'; +import MuiTabs from 'material-ui/Tabs'; +import BottomNavigationAction from 'material-ui/BottomNavigationAction'; +import BottomNavigation from 'material-ui/BottomNavigation'; +import CardContent from 'material-ui/CardContent'; +import CardActions from 'material-ui/CardActions'; +import Card from 'material-ui/Card'; +import CardMedia from 'material-ui/CardMedia'; +import CardHeader from 'material-ui/CardHeader'; +import MuiCollapse from 'material-ui/Collapse'; +import ListItemSecondaryAction from 'material-ui/ListItemSecondaryAction'; +import ListItemText from 'material-ui/ListItemText'; +import ListItemAvatar from 'material-ui/ListItemAvatar'; +import ListItem from 'material-ui/ListItem'; +import ListItemIcon from 'material-ui/ListItemIcon'; +import List from 'material-ui/List'; +import DialogTitle from 'material-ui/DialogTitle'; +import Dialog from 'material-ui/Dialog'; +import DialogContentText from 'material-ui/DialogContentText'; +import DialogContent from 'material-ui/DialogContent'; +import DialogActions from 'material-ui/DialogActions'; +import withMobileDialog from 'material-ui/withMobileDialog'; +import Slide from 'material-ui/Slide'; +import RadioGroup from 'material-ui/RadioGroup'; +import Radio from 'material-ui/Radio'; +import FormControlLabel from 'material-ui/FormControlLabel'; +import ExpansionPanelActions from 'material-ui/ExpansionPanelActions'; +import ExpansionPanelDetails from 'material-ui/ExpansionPanelDetails'; +import ExpansionPanelSummary from 'material-ui/ExpansionPanelSummary'; +import ExpansionPanel from 'material-ui/ExpansionPanel'; +import GridListTile from 'material-ui/GridListTile'; +import GridList from 'material-ui/GridList'; +import CircularProgress from 'material-ui/CircularProgress'; +import MuiLinearProgress from 'material-ui/LinearProgress'; +import FormHelperText from 'material-ui/FormHelperText'; +import FormControlLabel from 'material-ui/FormControlLabel'; +import FormGroup from 'material-ui/FormGroup'; +import FormControl from 'material-ui/FormControl'; +import FormLabel from 'material-ui/FormLabel'; +import Fade from 'material-ui/Fade'; +import StepContent from 'material-ui/StepContent'; +import StepButton from 'material-ui/StepButton'; +import Step from 'material-ui/Step'; +import Stepper from 'material-ui/Stepper'; +import TableRow from 'material-ui/TableRow'; +import TablePagination from 'material-ui/TablePagination'; +import TableFooter from 'material-ui/TableFooter'; +import TableCell from 'material-ui/TableCell'; +import TableBody from 'material-ui/TableBody'; +import Table from 'material-ui/Table'; +import InputLabel from 'material-ui/InputLabel'; +import Input from 'material-ui/Input'; +import Grow from 'material-ui/Grow'; diff --git a/packages/material-ui-codemod/src/v1.0.0/svg-icon-imports.js b/packages/material-ui-codemod/src/v1.0.0/svg-icon-imports.js index 89e4b3a2348b36..a0721863bd6d2f 100644 --- a/packages/material-ui-codemod/src/v1.0.0/svg-icon-imports.js +++ b/packages/material-ui-codemod/src/v1.0.0/svg-icon-imports.js @@ -39,9 +39,9 @@ function transformSVGIconImports(j, root) { }); } -module.exports = function transformer(file, api) { +module.exports = function transformer(fileInfo, api) { const j = api.jscodeshift; - const root = j(file.source); + const root = j(fileInfo.source); // transforms transformSVGIconImports(j, root);