-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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
Add a 'lazy' options to modules-commonjs #6952
Conversation
Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/6217/ |
typeof lazy !== "function" && | ||
(!Array.isArray(lazy) || !lazy.every(item => typeof item === "string")) | ||
) { | ||
throw new Error(`.lazy must be a boolean, array of strings, or a function`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the . at the beginning intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because it's a key in the configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we don't really have a good naming scheme for these errors. I just included it since it is an object property. I can change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least this is consistent with the other error messages used in Babel but it's not super clear to me. What about: "the key 'lazy' in the Babel configuration must be a boolean". Can be done in a second PR. That reminds my that I still have a PR to add code frames to configuration issues. I can continue my work, what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about: "the key 'lazy' in the Babel configuration must be a boolean".
I don't think it's much clearer than what I have here, but I do think we could do better in general with work focused on making the error messages give better feedback.
That reminds my that I still have a PR to add code frames to configuration issues. I can continue my work, what do you think?
Not sure I follow. What code frames could we show for this case? Do you mean showing information about the config file itself?
@@ -132,14 +143,26 @@ export default function(api, options) { | |||
|
|||
let header; | |||
if (isSideEffectImport(metadata)) { | |||
if (metadata.lazy) throw new Error("Assertion failure"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this should never really happen, but maybe we could make the error a little bit more descriptive?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For assertions I don't usually bother. I'd just assert(!metadata.lazy)
but usually that isn't enough to make Flowtype happy so I've kind of got into the habit of doing it like this. I really just threw this in for us.
Local paths are much more likely to have circular dependencies, which may break if loaded lazily, | ||
so they are not lazy by default, whereas dependencies between independent modules are rarely cyclical. | ||
|
||
* `Array<string>` - Lazy-initialize all imports with source matching one of the given strings. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need Array<string>
, do you think it is a common case? I guess boolean | (string) => boolean
would capture this case too. I was also thinking about more cases like "enable really everything" or "enable everything with a blacklist", but that can all be handled with the callback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The disadvantage of the callback is that it forces the user to use .babelrc.js
instead of .babelrc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the .babelrc
/package.json#babel
case was the reason I added the array. Seems like a whitelist was easy enough to support that I'd just throw it in.
} else if (typeof lazy === "function") { | ||
metadata.lazy = lazy(source); | ||
} else { | ||
throw new Error(`'lazy' must be a boolean, string array, or function`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it shold throw the same message as https://github.com/babel/babel/pull/6952/files#diff-71f62b3225be3bae0ddc0c8e49189aa7R29
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actualy, can this error be thrown? It should already be thrown there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One throws up front when you declare the plugin, which seemed like it would be best for users, this one throws inside the transform itself in case someone else decides to use helper-module-transforms
and calls it wrong. I can make the errors the same though, that's true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, that's really nice!
typeof lazy !== "function" && | ||
(!Array.isArray(lazy) || !lazy.every(item => typeof item === "string")) | ||
) { | ||
throw new Error(`.lazy must be a boolean, array of strings, or a function`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because it's a key in the configuration.
Super cool! Question, why did you decide to go with always calling the memoized require rather than conditionally getting the previously stored require value? For compat with modules whose value is falsey ? If always calling the memoized require's perf is on-par or better then I'll deprecate babel-plugin-transform-inline-imports-commonjs |
@zertosh I honestly didn't think on it too much. If anyone wants to do some benchmarks to compare, and your approach of having a separate variable is noticeably faster, we can certainly do it. I went this route because it seemed like it'd be easier for people debugging code to just have a single name to reference, and because it seemed like this approach would be pretty easy for engines to inline, since the function never changes and is statically guaranteed to always return the same value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really nice feature 🚀
76763fa
to
53826aa
Compare
@zertosh @loganfsmyth — I did some empirical benchmarking of both in my React Native app and witnessed the new It's possible that other changes in the Babel 7 version of the mainline commonjs plugin are to account for these improvements, but I'd say — from my limited perspective — that it's safe to sunset Thank you both for this feature. It's made a huge improvement in my cold-start time. (Addendum: if anybody would like to try using |
@jamesreggio Happy to hear it. Beware there's one known bug: #7176 |
@jamesreggio, interesting - I expected some difference one-way or the other, but not that stark. Curious to see this is a way I can repro (just for my know education). I'll deprecate |
@zertosh — I was surprised too. I thought your use of a local variable would be more performant. I wish I could supply a repro to you, but I'm measuring real-user scenarios in our closed-source app, and I don't have time to build a separate benchmark suite. |
Not inspired by the code from https://github.com/zertosh/babel-plugin-transform-inline-imports-commonjs itself, but certainly inspired by talking to @zertosh about it in the past, and now feeling like we might want to use it for Babel's own codebase. It's pretty trivial to include in our
helper-module-transforms
so I figured why not.Allow import bindings to lazy-initialize themselves the first time they are accessed, instead of up front at file load time.