Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[codemod] Add codemods for optimal tree-shakeable imports #16192

Merged
merged 1 commit into from
Jul 24, 2019

Conversation

jedwards1211
Copy link
Contributor

@jedwards1211 jedwards1211 changed the title [IconButton] add shortcut prop for applying negative margins on the left or right add top-level-imports codemod Jun 12, 2019
@jedwards1211 jedwards1211 reopened this Jun 12, 2019
@eps1lon
Copy link
Member

eps1lon commented Jun 12, 2019

We don't need this codemod for anything below 4.x. I think this is currently a bit overzealous. @material-ui/core/styles is totally fine.

A good heuristic would not change any code documented in our docs.

@jedwards1211 jedwards1211 force-pushed the codemod/top-level-imports branch from 7d24e4b to 2abc72e Compare June 12, 2019 21:02
@jedwards1211
Copy link
Contributor Author

jedwards1211 commented Jun 12, 2019

@eps1lon I'm planning to make a separate PR for a codemod that preserves 2nd-level subpaths. Having multiple options seems to be leading to unnecessary debate about where to import from. If you don't want this one then I'll have to publish it in its own repository.

@jedwards1211
Copy link
Contributor Author

Also, would this not be useful for 3.x? I assume that the tree-shaking problem applies to at least 3.x as well as 4.x.

@jedwards1211 jedwards1211 force-pushed the codemod/top-level-imports branch from 2abc72e to 80bb909 Compare June 12, 2019 21:07
@eps1lon
Copy link
Member

eps1lon commented Jun 12, 2019

I assume that the tree-shaking problem applies to at least 3.x as well as 4.x.

Yeah now that I think about this might be better since 3.x only has tree-shaking for top level imports. Only 4.x supports tree shaking for second level imports.

We should test this on the 3.x docs code and see if it still compiles though.

It needs an exception for test-utils. Those are not part of the top level export.

@eps1lon eps1lon added package: codemod Specific to @mui/codemod new feature New feature or request labels Jun 12, 2019
@mui-pr-bot
Copy link

mui-pr-bot commented Jun 12, 2019

No bundle size changes comparing 2853239...a0ef850

Generated by 🚫 dangerJS against a0ef850

@jedwards1211
Copy link
Contributor Author

jedwards1211 commented Jun 12, 2019

@eps1lon theoretically 3.x should be handled properly and test-utils should be excluded already because of how the codemod builds a whitelist of all exported identifiers in @material-ui/core/index.d.ts.

The separate 2nd-level-imports codemod I'm making (maybe I should call it 4.x/optimal-imports.js? Will have to have case-by-case logic to exclude internal and test-utils.

@jedwards1211 jedwards1211 force-pushed the codemod/top-level-imports branch from 555f43e to 9cd40f6 Compare June 12, 2019 22:17
@jedwards1211 jedwards1211 changed the base branch from master to v3.x June 12, 2019 22:17
@jedwards1211
Copy link
Contributor Author

jedwards1211 commented Jun 12, 2019

Okay I rebased this onto v3.x, transformed the docs and I was able to run the documentation site with no compile errors.

I had to do the following hack to get the path to index.d.ts in test mode; do you know of any better way to do this? All I can think of would be using babel-plugin-module-resolver in test mode...

  const whitelist = getTSExports(process.env.TEST
    ? require.resolve('../../../material-ui/src/index.d.ts')
    : require.resolve(`${importModule}/index.d.ts`, {
      paths: [dirname(fileInfo.source)],
    })
  );

@eps1lon
Copy link
Member

eps1lon commented Jun 12, 2019

I don't think this should parse the TS files. For one it requires @babel/preset-typescript. The other issue is that types are an afterthought i.e. might be outdated (we're getting better). Can't you just use index.js at which point you can safely leverage @material-ui/core/package.json and the main entry.

@oliviertassinari
Copy link
Member

Do we all agree that this pull request should target the master branch?

@jedwards1211
Copy link
Contributor Author

jedwards1211 commented Jun 12, 2019

@oliviertassinari as discussed above this codemod is useful for 3.x...but maybe you have better ideas about how to release it.

@jedwards1211
Copy link
Contributor Author

@eps1lon that's a good point, I'll do that

@oliviertassinari
Copy link
Member

oliviertassinari commented Jun 12, 2019

I don't understand the logic. From what I understand:

  • v4 supports tree-shaking, v3 doesn't. I wish we could apply this codemod to all the demos of the master branch.
  • We don't optimize for v3 users.

@eps1lon
Copy link
Member

eps1lon commented Jun 12, 2019

It should target master. The codemod however should only be used for 3.x and below. I meant we should only test it manually on 3.x not target at 3.x

@jedwards1211
Copy link
Contributor Author

@oliviertassinari I'm so confused, v3.x has "sideEffects": false, so shouldn't at least some tree shaking work?

@jedwards1211 jedwards1211 force-pushed the codemod/top-level-imports branch from 9cd40f6 to 5b2ac0c Compare June 12, 2019 23:01
@jedwards1211 jedwards1211 changed the base branch from v3.x to master June 12, 2019 23:03
@jedwards1211
Copy link
Contributor Author

okay rebased onto master

@eps1lon
Copy link
Member

eps1lon commented Jun 12, 2019

import Button from '@material-ui/core/Button' will point to a commonJS build in 3.x. Tree-shaking is not supported for those modules (webpack 4 default).

@jedwards1211
Copy link
Contributor Author

@eps1lon gotcha. I just added a v4.0.0/optimal-imports.js codeshift that preserves 1 level deep like: @material-ui/core/styles

@jedwards1211
Copy link
Contributor Author

let me know if there are any other codemods I could help write...I love writing codemods

@eps1lon eps1lon self-requested a review June 13, 2019 00:10
@jedwards1211
Copy link
Contributor Author

not sure what's up with the argos difference

@eps1lon eps1lon changed the title add top-level-imports codemod [codemod] Add codemods for tree-shakeable imports Jun 13, 2019
@eps1lon eps1lon changed the title [codemod] Add codemods for tree-shakeable imports [codemod] Add codemods for optimal tree-shakeable imports Jun 13, 2019
Copy link
Member

@eps1lon eps1lon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test look good to me. Just some minor questions/suggestions about the implementation.

import addImports from 'jscodeshift-add-imports';

// istanbul ignore next
if (process.env.NODE_ENV === 'test') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't publish env branches unless they compare with production.

You should be able to get the esModules index barrel by reading @material-ui/core/package.json and using the module entry. Then you can use main as a fallback to account for test runs in the monorepo.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand about branches, i'm checking if it's test mode, not if it's a branch

Copy link
Contributor Author

@jedwards1211 jedwards1211 Jun 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh you mean a branch statement, not a code branch.

How do you want me to get the import path in testing? babel-plugin-module-resolver doesn't work for for dynamic require.resolve statements. Mocking require.resolve outside of this file won't work AFAIK. Maybe using a define plugin to inject some code in test mode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw reading the package json and using it in require.resolve on subpaths still won't work in test mode. Unlike the v3 codemod, this one parses the 2nd-level files to build whitelists, rather than the top-level file

@@ -0,0 +1,12 @@
const memoize = (func, resolver = a => a) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yarn add -D -W memoize-one

It's already in node_modules from 4 other dependencies

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately memoize-one is not great for this, as it only remembers the most recent arguments. With this impl I assume cached info from parsing will stay in memory from one file being transformed to the next. (If passing a dir to jscodeshift instead of the strange usage of find/xargs, at least)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code size isn't a major concern for the codemods package is it?

@@ -0,0 +1,22 @@
import memoize from './memoize';
import { readFileSync } from 'fs';
import { parseSync } from '@babel/core';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use the jscodeshift API instead i.e. api.jscodeshift(muiCoreIndexSource).findExportSpecifier() (no idea if that find method exists). I guess it's not guarenteed that the parser config of the user can also read node_modules source but that's also not guarenteed in this form since we don't publish our babel config anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that, got parse errors on the export from statements...perhaps I could build this list at compile time?

Copy link
Contributor Author

@jedwards1211 jedwards1211 Jun 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

packages/material-ui/package.json doesn't have module or main entries, otherwise i would have done what you suggest. Should i add them? Or do they get added at build time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the babel config is not published, is it up to someone importing from es/ to set the right babel options, or do you transpile at least things like export from statements in the build?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eps1lon I still want to finish up this PR, how do you want me to resolve this?

@oliviertassinari
Copy link
Member

Rebased.

@oliviertassinari oliviertassinari force-pushed the codemod/top-level-imports branch 2 times, most recently from d2dafaa to 51a064a Compare July 21, 2019 22:26
@mui mui deleted a comment from jedwards1211 Jul 21, 2019
@mui mui deleted a comment from jedwards1211 Jul 21, 2019
@oliviertassinari oliviertassinari force-pushed the codemod/top-level-imports branch 5 times, most recently from 55946a3 to 2237fe0 Compare July 21, 2019 22:57
Copy link
Member

@oliviertassinari oliviertassinari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have rebased on the master branch, fix the markdown and move the two codemod under the v4 folder. From my end, it's good enough to ship and to put between our user's hand.

@oliviertassinari oliviertassinari force-pushed the codemod/top-level-imports branch from 604fffe to a0ef850 Compare July 24, 2019 10:09
@oliviertassinari oliviertassinari merged commit fd24690 into mui:master Jul 24, 2019
@Blinnikov
Copy link

Guys, I'm a little bit confused with those two options.
As I understand both optimal-imports and top-level-imports allow me to have tree-shakeable imports.
But what is the preferable way to import material-ui modules?
From reading the Docs I can only conclude that by default I should use top-level-imports, but if it's a problem in development I can switch on using of optimal-imports. But what are the downsides of optimal-imports in this case? Could you please clarify?
Thanks.

@oliviertassinari
Copy link
Member

oliviertassinari commented Jul 29, 2019

@Blinnikov To sum-up:

optimal-imports cons:

  • Duplication of the module name. I don't know you but with this approach, to use a new module, I duplicate an existing import line and replace the two occurrences of the copoed module name. Sometimes, I only replace one by mistake. Boy, good luck figuring out the source of your problem when that happens.
  • Verbose.

top-level-import cons:

  • No TypeScript support for the xProps types yet.
  • Can be slow when a lot of modules are exported, e.g. with the icons package (5k modules).

@eps1lon
Copy link
Member

eps1lon commented Jul 29, 2019

Can be slow when a lot of modules are exported, e.g. with the icons package (5k modules).

To clarify: This is slow for cold starts of development builds or when building for production. It has no impact on the consumer of your production bundles. This situation will improve the more advanced build caches get (webpack is currently working on it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new feature New feature or request package: codemod Specific to @mui/codemod
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants