Uglify unexpectedly changes Angularjs code load order - javascript

My Angular 1 code works fine in development. But when I compress my Angular project to dist with Grunt, my directives run before my services and app.run(), which results in breaking the page.
I traced the issue to uglify which changes the code execution order. I don't know how to prevent my uglify from changing the execution order.

Below solution worked for me. May be this will help you too. I refered it from here.
Three solutions:
Make the grunt task do not include unwanted files, and include those files already minified in your app.
If problem still occurs after first solution, your angular modules are not "uglify friendly". There's a special way to declare your dependencies so that minifying goes smooth, as described on this page ("a note on minification" paragraph) for example. Please note that instead of doing this declaration "by hand", you can use the "ng-min" task that automatize the process and keep your code clean.
Well, in my case I still had issue, so I ended turning off the "mangle" option in uglify (that very option that make long strings into smaller to compress response size). You can turn off the option like this :
uglify: {
options: {
mangle: false
},
}

Related

WebPack sourcemaps confusing (duplicated files)

I decided to try out WebPack on a new project I'm spinning up today and I'm getting really strange behavior from the sourcemaps. I can't find anything about it in the documentation, nor can I find anyone else having this issue when skimming StackOverflow.
I'm currently looking at the HelloWorld app produced by Vue-CLI's WebPack template -- no changes have been made to the code, the build environment, or anything.
I installed everything and ran it like so:
vue init webpack test && cd test && npm install && npm run dev
Looking at my sourcemaps, I see the following:
This is a hot mess. Why are there three version of HelloWorld.vue and App.vue? Worse yet, each version has a slightly different version of the code and none of them match the original source. The HellowWorld.vue sitting in the root directory does match the original source, but what's it doing down there instead of in the ./src/components folder? Finally, why isn't there a fourth App.vue that has the original source for it?
As far as I can tell this may have something to do with the WebPack loaders. I've never gotten these kinds of issues with any other bundler, though. Below is an example of the exact same steps using the Browserify Vue-CLI template:
No webpack:// schema, only one copy of every file, the files actually contain the original source code (kind of important for source maps), no unexpected (webpack)/buildin or (webpack)-hot-middleware, no . subdirectory,.... just the source code.
I haven't worked with Vue so can't really describe how exactly this is happening but it seems to be related to Vue Loader. Looking at the documentation I did not really find anything that clarifies why it would create three different files for one component. But it does seem logical considering that a .vue file might contain three types of top-level language blocks: <template>, <script>, and <style>.
Also, looking at two of those files you do see a comment at end of each file that suggests it was modified in some way by a Vue loader. Either this
//////////////////
// WEBPACK FOOTER
// ./node_modules/vue-loader/lib/template-compiler
or
//////////////////
// WEBPACK FOOTER
// ./node_modules/vue-style-loader!./node_modules/css-loader
The third file is different but it still does have code that identifies it as being modified by Vue loader. Here is some of that code
function injectStyle (ssrContext) {
if (disposed) return
require("!!vue-style-loader...")
}
/* script */
import __vue_script__ from "!!babel-loader!../../node_modules/vue-loader/..."
/* template */
import __vue_template__ from "!!../../node_modules/vue-loader/..."
/* styles */
var __vue_styles__ = injectStyle
The document also says this:
vue-loader is a loader for Webpack that can transform Vue components written in the following format into a plain JavaScript module:
Which explains why you might not see the same type of behaviour with other bundlers.
Now, This might not be the answer you were looking for but just wanted to share what I had found.
This is actually a feature of webpack.
webpack has HMR (Hot Module Reloading). If you look in your network tab, go ahead and make an update to your HelloWorld.vue file. You'll see a js chunk come thru as well as an updated JSON manifest. Both of these will have a unique hash at the end for each time you make a change to the application. It does this so the browser does not have to do a full reload.
For a better explanation of this I would highly recommend reading through https://webpack.js.org/concepts/hot-module-replacement/

Where to specify module dependencies?

I'm following the standard practice of organizing my angular assets by feature; e.g. AngularJS Folder Structure and AngularJS Best Practices: Directory Structure.
Which file should I put my module / dependency declaration in?
I'm trying to solve the following problems:
I'd like to be able to sort my <script> references alphabetically for maintenance reasons, but I can't because that breaks my Angular bootstrap (for some modules).
I've tried keeping them in the alphabetically-first *.js file in the module, but I spend a lot of time as my app grows moving my dependency declarations around.
I often have to hunt around to find module declarations.
I end up staring at Angular's relatively uninformative module error too often for related reasons.
Regardless, attaching the module declaration to a specific controller seems to imply a direct correlation that doesn't exist.
Here's an example:
metric/
_module.js // Should I create this file?
detail-controller.js
detail.html
search-filter.js
selector-controller.js
selector-directive.js
selector.html
Currently, for this module, that line of code exists in one of my module's controllers, you guess which one! ;)
As a possible solution that I'm not entirely happy with, should I put each module definition in its own tiny, one-line file?
angular.module('metric', ['lib', 'ngSanitize', 'ui.select', 'data']);
How do you do this? Am I missing some other clever or obvious solution?
p.s. as a related problem, if you feel like it, how do to track which components of your module are the source(s) of the dependency?
I would break it up even further.
metric/
metric.js
controllers/
detail-controller.js
selector-controller.js
directives/
selector-directive.js
filters/
search-filter.js
templates/
detail.html
selector.html
Now that I've been working with it for a while, and because I've started pre-compiling my javascript with gulp, the one-line module declaration file seems to be the best solution for me.
I name that file <special-character>module.js, so that it sorts visually and at compile-time to the top. Because my layout convention is one folder = one module, this works schematically. My special character is dash, YMMV. My individual .js file names don't show up in the production compiled version anyway.
It initially bothered me that there was a one-line file in my project, but now I appreciate it. It gets compiled in to my application javascript with gulp, so it's not a performance issue. Also, there's an obvious place to look for dependencies, clear trail in revision control logs of dependency changes, and simple process to document dependencies from my sources with my own custom tools.

NodeJS 'Require' loading library in coffeescript, empty object in Javascript

I've come across an interesting problem in NodeJS that pops up now and then at work, and I haven't been able to figure it out.
I've written a back-end in CoffeeScript, which I then compile with grunt-contrib-coffee into Javascript in a ~/bin directory. I also include a library that I privately host on Bitbucket with the appropriate private keys, and install through npm. This library too is written in coffeescript.
Usually I'm able to include this library in Javascript without any headaches, using a simple require just like I would for any other library. However, occasionally one of the servers that's using the back-end gets updates, and it stops working. When I go check the error, it's always the same - 'require' passes, but instead of loading the actual library in JavaScript, it returns an empty object ({}). Running the code in coffeescript still works, but regardless of what I do - recompile, reinstall all dependencies, remove and clone the repository, the problem persists.
I've run out of ideas of what it might be myself, so I'm hoping that someone here has come across the problem before and might be able to point me in the right direction.
In the library package.json:
{
"name": "graph-engine",
"main": "./lib/graph"
}
In the library's graph.coffee
class Graph
constructor: () ->
# Perform init
module.exports = Graph
Then in the app's package.json:
{
"graph-engine": "git+ssh://git#bitbucket.org:<team>/graph-engine.git"
}
Finally, in the app itself:
GraphEngine = require "graph-engine"
engineInstance = new GraphEngine()
This works fine in coffeescript, but when compiling the app using grunt with the following setup for grunt-contrib-coffee:
coffee:
glob_to_multiple:
expand: true
flatten: false
cwd: 'src'
src: ['**/*.coffee']
dest: 'bin'
ext: '.js'
It fails to load the library correctly when running the compiled application, instead returning an empty object. Again, I'd like to emphasise that this does not always happen, and as such I didn't include any code or json files as I believed that it was unrelated.
Although the exact reason for the randomness of the behaviour eludes me to this day, I have found that it is related to these libraries being written in CoffeeScript.
It seems that depending on the load order, occasionally third party libraries would register coffee script and my own libraries would load correctly, whereas other times these libraries would load after my own libraries had loaded, resulting in an inability to load coffeescript. Therefore, the solution turned out to be fairly straightforward - register coffeescript. This can be done by putting the following at the start of the node.js app:
require('coffee-script/register')
Refer to the documentation for further information.

What's the best way to concatenate vendor js files?

In my Angular JS app, I'm using a lot of third party packages, mainly maintained via Bower.
When I use Grunt to concatenate all of them into one mega file, I'm getting errors when I load my page, for example that
Uncaught ReferenceError: angular is not defined and
GET http://localhost:8080/theproj/v4/dist/app/bootstrap.css.map 404 (Not Found)
What is the best way to properly concatenate all these files to ensure that everything loads in the right order and doesn't cause problems?
First issue: A lot of times third party libraries must be loaded in a particular order. That looks like like it's the source of your first issue. Something is trying to use angular and it's getting loaded before the angular code. You should refactor your grunt task to use a pre-defined order for third party libraries.
Second issue: You probably are missing the .map file. This is a file used by Chrome dev tools to show you the original source for the css (sass or less). Either provide the map file, or delete the reference to it from bootstrap.css. Or just ignore the error, it's only an error when you have chrome dev tools open, and doesn't actually affect your application.
For the issue of the correct order for your javascript files, i had that problem in a larger project where noone really had a clue which was the correct order.
Luckily we found that the Google Closure Compiler does exactly this: https://github.com/google/closure-compiler
You give it all your js files and it analyzes them all and concatenates them in order
$ java -jar compiler.jar --js_output_file=out.js in1.js in2.js in3.js ...
There is even a grunt plugin for the connection: https://github.com/gmarty/grunt-closure-compiler
'closure-compiler': {
frontend: {
closurePath: '/src/to/closure-compiler',
js: 'static/src/frontend.js',
jsOutputFile: 'static/js/frontend.min.js',
maxBuffer: 500,
options: {
compilation_level: 'ADVANCED_OPTIMIZATIONS',
language_in: 'ECMASCRIPT5_STRICT'
}
}
},
Another way would be to change your javascripts into AMD or CommonJS modules, this way you don't have to worry about the correct order. RequireJS (http://requirejs.org/docs/start.html) is a possibility for AMD for example or Webpack (http://webpack.github.io/) ...or many many others.

how to minify js files in order via grunt-contrib-uglify?

I have a directory like below:
/folder/b.js
/folder/jQuery.js
/folder/a.js
/folder/sub/c.js
I want to minify all these js files in one js file in order:
jQuery.js -> a.js -> b.js -> c.js
Q:
1.How can I do it via grunt-contrib-uglify?(In fact, there are lots of files, it is impractical to specify all source filepaths individually)
2.btw, How can I get unminified files when debug and get minified single file when release and no need to change script tag in html(and how to write the script tag)?
Good questions!
1) Uglify will reorder the functions in the destination file so that function definitions are on top and function execution on bottom but it seems that it will preserve the order of the function executions.
This means that the function jQuery runs to define its global functions will be put first if you make sure jQuery is mentioned first in Uglify's config in the Gruntfile.
I use this config:
uglify: {
options: {
sourceMap: true
},
build: {
files: {
'public/all.min.js': ['public/js/vendor/jquery-1.10.2.min.js', 'public/js/*.js'],
}
}
}
2) I don't think there is one definite way to accomplish this. It depends on what web framework, templating framework and what kind of requirements you have. I use express + jade and in my main jade layout I have:
if process.env.NODE_ENV === 'production'
script(src='/all.min.js')
else
script(src='/js/vendor/jquery-1.10.2.min.js')
script(src='/js/someScript.js')
script(src='/js/otherScript.js')
In my package.json I have:
"scripts": {
"postinstall": "grunt"
},
This means that when I run npm install on deploy (on Heroku) grunt is run to minify/concat files and when the app is started with NODE_ENV=production the minified client side javascript is used. Locally I get served the original client side javascripts for easy debugging.
The two downsides are:
I have to keep the two lists of script files in sync (in the Gruntfile and in the layout.js) I solve this by using *.js in the Gruntfile but this may not suite everyone. You could put the list of javascripts in the Gruntfile and create a jade-template from this but it seems overkill for most projects.
If you don't trust your Grunt config you basically have to test running the application using NODE_ENV=production locally to verify that the minification worked the way you intended.
This can be done using the following Grunt tasks:
https://github.com/gruntjs/grunt-contrib-concat concatenates
files
https://github.com/gruntjs/grunt-contrib-uglify minifies
concatenated files
EDIT
I usually run all my files through a Grunt concatenation task using grunt-contrib-concat. Then I have another task to uglify the concatenated file using grunt-contrib-uglify.
You're probably not going to like this, but the best way is to define your js source files as AMD modules and use Requirejs to manage the order in which they load. The grunt-contrib-requirejs task will recurse your dependency tree and concatenate the js files in the necessary order into one big js file. You will then use uglify (actually r.js has uglify built-in) to minify the big file.
https://github.com/danheberden/yeoman-generator-requirejs has a good example gruntfile and template js files to work from.
EDIT
I've recently started using CommonJS modules instead of AMD since it's much closer to the ES6 module spec. You can achieve the same results (1 big complied+concatenated js file) by running commonjs modules through Browserify. There are plugins for both grunt and gulp to manage the task for you.
EDIT
I'd like to add that if your site is written using ES6 that Rollup is the best new concatenating package. In addition to bundling your files, it will also perform tree shaking, removing parts of libraries you use if included via an import statement. This reduces your codebase to just what you need without the bloat of code you'll never use.
I don't think you can do this with the uglify task alone, but you have a multitude of choices which might lead to your desired outcome.
A possible workflow would be first concatenating (grunt-contrib-concat) the files in order into one single file, and put this concatenated file through uglify. You can either define the order for concat in your Gruntfile, or you use on of those plugins:
First one would be https://github.com/yeoman/grunt-usemin, where you can specify the order in your HTML file, put some comments around your script block. The Google guys made it and it's pretty sweet to use.
Second one would be https://github.com/trek/grunt-neuter, where you can define some dependencies with require, but without the bulk of require.js. It requires changes in your JS code, so might not like it. I'd go with option one.
I ran into the same issue. A quick fix is just to change the filenames - I used 1.jquery.min.js, 2.bootstrap.min.js, etc.
This might be only remotely related to your question but I wanted something similar. Only my order was important in the following way:
I was loading all vendor files (angular, jquery, and their respective related plugins) with a wildcard (['vendor/**/*.js']). But some plugins had names that made them load before angular and jquery. A solution is to manually load them first.
['vendor/angular.js', 'vendor/jquery.js', 'vendor/**/*.js]
Luckily angular and jquery handle being loaded twice well enough. Edit: Although it's not really the best practice to load such large libraries twice, causing your minified file unnecessary bloat. (thanks #Kano for pointing this out!)
Another issue was client-js the order was important in a way that it required the main app file to be loaded last, after all its dependencies have been loaded. Solution to that was to exclude and then include:
['app/**/*.js', '!app/app.js', 'app/app.js']
This prevents app.js from being loaded along with all the other files, and only then includes it at the end.
Looks like the second part of your question is still unanswered. But let me try one by one.
Firstly you can join and uglify a large number of js files into one as explained by the concat answer earlier. It should also be possible to use https://github.com/gruntjs/grunt-contrib-uglify because it does seem to have wildcards. You may have to experiment with 'expand = true' option and wildcards. That takes care of your first question.
For the second part, say you joined and uglified into big-ugly.js
Now in your html you can add following directives:
<!-- build:js:dist big-ugly.js -->
<script src="js1.js"></script>
<script src="js2.js"></script>
<!-- etc etc -->
<script src="js100.js"></script>
<!-- /build -->
And then pass it through the grunt html preprocessor at https://www.npmjs.com/package/grunt-processhtml as part of your grunt jobs.
This preprocessor will replace the entire block with
<script src="big-ugly.js"></script>
Which means that the html file with be semantically equivalent - before and after the grunt jobs; i.e. if the page works correctly in the native form (for debugging) - then the transformed page must work correctly after the grunt - without requiring you to manually change any tags.
This was #1469's answer but he didn't make it clear why this works. Use concat to put all js files into one, this module does this in the order of file names, so I put a prefix to the file names based on orders. I believe it even has other options for ordering.
concat: {
js: {
options: {
block: true,
line: true,
stripBanners: true
},
files: {
'library/dist/js/scripts.js' : 'library/js/*.js',
}
}
},
Then use uglify to create the minified ugly version:
uglify: {
dist: {
files: {
'library/dist/js/scripts.min.js': [
'library/js/scripts.js'
]
},
options: {
}
}
},
If your problem was that you had vendors which needed to be loaded in order (let's say jquery before any jquery plugins). I solved it by putting jquery in its own folder called '!jquery', effectively putting it on top of the stack.
Then I just used concat as you normally would:
concat: {
options: {
separator: ';',
},
build: {
files: [
{
src: ['js/vendor/**/*.js', 'js/main.min.js'],
dest: 'js/global.min.js'
}
]
}
},

Categories