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

Dynamic import chunkname rule #1070

Merged
merged 5 commits into from
Apr 10, 2018

Conversation

byteme980
Copy link
Contributor

This PR adds a new rule dynamic-import-chunkname, which reports on any dynamic-import usages without a properly formatted webpackChunkName leading comment as mentioned in this tweet.

This is a useful rule to have, since explicitly defining the webpackChunkName will provide a consistent file name for the code split boundary.

cc @lencioni @ljharb

@coveralls
Copy link

coveralls commented Apr 9, 2018

Coverage Status

Coverage increased (+0.01%) to 96.479% when pulling 115b6fb on byteme980:dynamic-import-chunkname-rule into f0b4f3e on benmosher:master.

Copy link
Contributor

@lencioni lencioni left a comment

Choose a reason for hiding this comment

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

This looks great! Thanks for opening this PR 💃


This rule reports any dynamic imports without a webpackChunkName specified in a leading block comment in the proper format.

This is a useful rule because if the webpackChunkName is not defined in a dynamic import, Webpack will autogenerate the chunk name.
Copy link
Contributor

Choose a reason for hiding this comment

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

This would be better if it explained why it is better to have control over the chunk name vs letting webpack autogenerate one. Basically mention that autogenerated chunk names are not stable across builds, which prevents long-term browser caching.

nit: "webpack" should be lowercase everywhere

This is a useful rule because if the webpackChunkName is not defined in a dynamic import, Webpack will autogenerate the chunk name.

## Rule Details
This rule runs against `import` by default, but can be configured to also run against an alternative dynamic-import function, e.g. 'dynamicImport.'
Copy link
Contributor

Choose a reason for hiding this comment

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

import here should probably be import() to differentiate it as a dynamic import.

Copy link
Member

Choose a reason for hiding this comment

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

+1, this needs to be import() since it’s distinct from import


## Rule Details
This rule runs against `import` by default, but can be configured to also run against an alternative dynamic-import function, e.g. 'dynamicImport.'
You can also configure the regex format you'd like to accept for the webpackChunkName - for example, we don't want the number 6 to show up in our chunk names.
Copy link
Contributor

Choose a reason for hiding this comment

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

"for example, we don't" should maybe be "for example, if we don't". I'd probably also end this sentence with a : instead of a .

```javascript
{
"dynamic-import-chunkname": [2, {
importFunction: "dynamicImport",
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if this should be importFunctions and take an array instead of a string. What do you think?

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

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

LGTM pending comments

This is a useful rule because if the webpackChunkName is not defined in a dynamic import, Webpack will autogenerate the chunk name.

## Rule Details
This rule runs against `import` by default, but can be configured to also run against an alternative dynamic-import function, e.g. 'dynamicImport.'
Copy link
Member

Choose a reason for hiding this comment

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

+1, this needs to be import() since it’s distinct from import


create: function (context) {
const config = context.options[0]
let importFunction
Copy link
Member

Choose a reason for hiding this comment

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

instead of a let here, what about const { importFunction } = config || {}?

({ importFunction } = config)
}

let webpackChunknameFormat = '[0-9a-zA-Z-_/.]+'
Copy link
Member

Choose a reason for hiding this comment

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

same comment here re const

const commentRegex = new RegExp(commentFormat)

return {
CallExpression(node) {
Copy link
Member

Choose a reason for hiding this comment

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

Could this be something like:

  [`CallExpression[callee.name=“${importFunction}”],CallExpression[callee.type=“Import”]`](node) {

?

This might improve linting performance and would allow you to re,over the “if” on line 37

@ljharb ljharb added the accepted label Apr 9, 2018
Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

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

Could we add a test that has > 1 importFunctions, just to ensure we're checking them all?

importFunction: {
type: 'string',
importFunctions: {
type: 'array',
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 add uniqueItems: true to this array?

return {
[`CallExpression[callee.type="Import"],CallExpression[callee.name]`](node) {
const { callee: { name }} = node
if (name && !importFunctions.includes(name)) {
Copy link
Member

Choose a reason for hiding this comment

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

i'm not sure if we can use this in node 4, which eslint supports. you may want to do new Set(importFunctions) and then use .has?

const commentRegex = new RegExp(commentFormat)

return {
[`CallExpression[callee.type="Import"],CallExpression[callee.name]`](node) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ljharb, let me know if you know of a better way to write these selectors now that we're accepting an array of import functions instead of just a string

Copy link
Member

Choose a reason for hiding this comment

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

hmm - i don't think it has to be a blocker or anything, but you could, perhaps, dynamically construct the method name like so:

const selector = ['CallExpression[callee.type="Import"]'].concat(importFunctions.map(func => `CallExpression[callee.name="${func}"]`).join(',');

return {
  [selector](node) {

but at this point I'm not sure if the readability hit is worth the performance optimization. What do you think?

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 think this may be what's failing the Travis build for eslint version 2

Copy link
Member

Choose a reason for hiding this comment

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

oh, snap - good call. if eslint 2 doesn't have full AST selectors, then we should go back to your original code that just checks CallExpression and does the filtering inside the method body :-(

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

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

This is great! Once tests are passing (and pending #1070 (comment), if that's what we decide to do) then this is ready to go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

4 participants