google / closure-compiler

A JavaScript checker and optimizer.
https://developers.google.com/closure/compiler/
Apache License 2.0
7.42k stars 1.15k forks source link

Promote types defined in a function to its parent's scope #1068

Closed woldie closed 9 years ago

woldie commented 9 years ago

I had a thought that a modified goog.scope(ftn) might allow me to bundle my code with Webpack and minify with Closure Compiler. The webpack bundler generates uber module bundles that look like this (simplified):

(function(modules) {
  function __webpack_require__(moduleId) {
    // logic to lookup moduleId in modules array and call module one time to get its exports
  }

  // Load entry module and return exports
  return __webpack_require__(0);
})([
  function(module, exports, __webpack_require__) {
    // contents of my Main.js CommonJS module
  },
  function(module, exports, __webpack_require__) {
    // contents of my Utils.js CommonJS module
  },
  ...
]); 

With some creative preprocessing, I can get Closure Compiler to minify the above code. However, when I start making JSDoc-annotated classes in my modules, I find myself unable to share types between the module functions in the generated webpack modules array. I get the common "WARNING - Bad type annotation. Unknown type ImageUtils" message, where ImageUtils is a class defined in Utils.js, but Main.js cannot see it so CC blows up when I use it as a @param {ImageUtils} imageUtils annotation.

My thought was, what if there was a goog utility sortof like goog.scope() where I could wrapper each of the CommonJS module functions in the webpack-generated array at the end of the bundle. Something like this:

(function(modules) {
   ...
})([
  goog.descopeTypes(function(module, exports, __webpack_require__) {
    // contents of my Main.js CommonJS module
  }),
  goog.descopeTypes(function(module, exports, __webpack_require__) {
    // contents of my Utils.js CommonJS module
  }),
  ...
]); 

where the effect of goog.descopeTypes() would be that Closure Compiler would semantically rescope declared types from the function's scope to the parent scope so that the types would become visible to all child scopes. In the case of webpack's generated bundles, the parent scope would be the top bundle scope, which I have noticed appears to be important to the compiler.

Having this scoping tool would allow all of my CommonJS modules to share the types they define with the rest of my modules - just like the jsdoc3 tool and IntelliJ allows. This will make it possible for me to transact type-safely across functions in separate CommonJS modules using custom typedef's/classes/enums that I create, all while staying in the Webpack plugin ecosystem.

Webpack's plugin system makes it easy for me to slip in the wrapper goog.* calls at the right places in the generated bundle. I have tried to do this with goog.scope(), but CC complained that my functions had parameters and were unsuitable.

I really am a fan of Closure Compiler, and am starting a greenfield project using WebPack and Gulp for builds. I would very much prefer to minify with Closure Compiler rather than UglifyJS. Please help me avoid uglifiness and to write great things about you in blogs! Thanks Google!

dimvar commented 9 years ago

For high-level questions/comments, please post on https://groups.google.com/forum/#!forum/closure-compiler-discuss. The github issue tracker is mostly for bug reports.

Regarding modules, the compiler currently handles AMD modules and commonjs modules (in addition to Google's goog.provide/require and goog.module), and we don't have plans to handle other non-standard module systems. Most module-related work these days is on ES6 modules.

I would very much prefer to minify with Closure Compiler rather than UglifyJS

The compiler's simple-optimizations level should give output of similar size to uglify, and it doesn't require type checking, so you can still use simple optimizations even if you don't manage to share the types across modules.

woldie commented 9 years ago

You're right, I can bundle most of my custom scripts using Closure Compiler's CommonJS support as long as I don't rely on any Webpack extensions in those scripts. I was just hoping to keep my integration with Closure Compiler simple and work entirely out of the Webpack ecosystem.

Thanks for replying. I'll try to make Advanced mode optimizations work with my code, if possible.