I'm thinking of using bundling for my group's ASP.NET MVC3 project, and I can't break it cause I'm a junior dev.
Does JavaScript minification affect JavaScript function names - i.e. can I still call the functions that I minify by the same name from inline code? Also, does it ever effect how code behaves - or is JavaScript functionality and order fully preserved?
Bundling and Minification is not going to break your code by renaming your function names. The minification optimizes code through removing unnecessary whitespace, removing comments, and shortening variable names to one character.
Say you make this function:
function BigHonkingFunctionName(bigvariablename1, bigvariablename2) {
//I love this function
alert(bigvariablename1 + " " + bigvariablename2);
}
The minified result will be:
function BigHonkingFunctionName(n,t){alert(n+" "+t)}
So as you see, it won't impact how you call or use the function.
Reading on Bundling and Minification.
Normally Javascript minification does not change function names, and you can use them without worry. Javascript minification usually removes empty-spaces, new-line characters, shortens local variable names, shortens function parameter names.
Though you might get in trouble if your code uses eval statements which are referencing variables defined in its scope of access. Some minifier do not touch eval statements, which can be a bit problematic if the names of variables referenced by eval had been minified by the tool.
For example, following Javascript code block can be minified as following:
Source JS:
function eval_test(var_1, var_2) {
var_1 = "Updated var_1";
var_2 = "Updated var_2";
var a = eval("var_1 * var_2") + "<br>";
}
Minified JS:
function MyFunction(n,t){n="Updated var_1";t="Updated var_2";
var i=eval("var_1 * var_2")+"<br>"}
In this example the eval call will break after minification, because parameters var_1, var_2 of function eval_test are minified to n and t respectively. But the eval statement is still unchanged.
Still, some minfication tools do not minify parameter/variable names, in such a case eval statement will work perfectly fine. But keeping unminified parameter/variable names negatively affects the minification, because your code might not be as compressed/minified as it should.
You can try following online minification tools to test and verify minified output:
http://utilninja.com/Computing/JavascriptMinifier
https://javascript-minifier.com/
https://jscompress.com/
It is a valid concern. Probably depends on how your Javascript files are built, and now the bundler works. A lot of time, the bundlers do a great job of figuring everything out and leaving things in tact that need to be there.
A lot of bundlers do have configuration options to enable or disable options so you maybe able to configure the system to the exact way you want it, or if you run into problems.
Related
For a new project I am bound to keep things webpack-only, and thus need to find a way to efficiently compile our stylesheets. Basically in a very gulh-ish way:
gather all less-files including glob-patterns like src/**/*.less (css order may be arbitrary)
also allow import of css files like, say ../node_modules/vendor/**/*.css or 3rdparty/master.less
(If I have to setup a bogus.js entry point for that, fine...)
And with all of that, a typical gulp workflow:
transpile less to css
merge (concat) less and css
have cssnano do its optimization job, with specific css nano options like e.g. orderedValues: false, normalizeWhitespace: true ...
write styles.css as final output
And of course:
have source-maps survive that chain for a styles.css.map
efficient watching and the usual lazy/incremental compilation (as gulp and webpack have it)
Something I do not need is css modules (css imported into node modules for 'scoped' use, coming out as hash-scoped selectors...)
How can a 'typical' less|css-processing toolchain be done in Webpack?
This SO question has my first attempt where I got stuck in config hell right in the middle after combining...
considerations so far (helpful or not)
I know, to webpack, any ressource including css or even font and images is a "module"... Rather than merging my css 'modules' with with actual js (only to later painstakingly separate them again later again), it might be wiser, to have an entry point cssstub.js in parallel – also known as multi-compiler mode.
But that's really, where my wisdom ends... doing a sequence of $things on a set of files in webpack seems really hard (unless it's a connected javascript module graph). I did find something on globbing, but that's not enough (merge css, cssnano,...) and mostly I simply can't glue the pieces together...
I have used gulp to build less and create corresponding maps like below:
First step compiles less and generated css in tmp folder
gulp.task('compile-less', function () {
gulp.src('./*.less') // path to your file
.pipe(less().on('error', function(err) {
console.log(err);
}))
.pipe(gulp.dest('./tmp/'));
});
Second step minifies generated css and create map files
gulp.task('build-css', ['clean'], function() {
return gulp.src('./tmp/**/*.css')
.pipe(sourcemaps.init())
.pipe(cachebust.resources())
.pipe(sourcemaps.write('./maps'))
.pipe(gulp.dest('./compiled/css'));
});
If you want you can add additional step to conact generated css.
Until recentlly I was using the rather awkward dotless. I'm restricted to what I can use somewhat because I'm using VS2010, however I came across ServiceStack Bundler which seems to work great... apart from one thing.
My Less files are split using a directory structure - to simplify things, like this:
scaffolding/my-scaffolding.less
utilities/my-utilities.less
ui/my-ui.less
Each directories has many files, particularly with mixins etc.
In order to link them together, I've been using the #import function.
My main less file - main.less contains the following:
#import "utilities/all-utilities.less"
All utilities are listed here as it enables me to plug and play various file-sets. This code previously worked ok.
However I find ServiceStack won't pick up changes unless I reference all the less files individually and directly through the bundle file, which is a bit of a pain.
So, I can't use: main.less and import all sub files.
Instead, I have to use:
utilities/util1.less
utilities/util2.less
utilities/util3.less
and so on.
I'm using the ms build function to compile.
Any ideas?
/* UPDATE */
Tried the recommended answer below without success.
This is what I've tried so far:
Call a single less file that contains all #import declarations;
Does not trigger updates on compile.
Call all less files from bundle file and add #import statements to less files where necessary (note bundler won't compile without these);
Duplicates the #imported code as many times as the #import statement is used - even when (reference) directive is used, resulting in bloated/incorrect code.
You should be able to modify Bundler's bundler.js file to specify additional paths that less should search for when processing #import directives:
function compileLess(lessCss, lessPath, cb) {
var lessDir = path.dirname(lessPath),
fileName = path.basename(lessPath),
options = {
paths: ['.', lessDir], // Specify search paths for #import directives
filename: fileName
};
less.render(lessCss, options, function (err, css) {
if (err) throw err;
cb(css);
});
}
A mistake on my part, but one which wasn't easily spottable, so I'll post the reason for my problems so that others don't spend hours as I did chasing a solution to an unecessary problem.
I was using ServiceStack Bundler - I believe this issue would also have occured on on any solution using npm's less library.
My main issue was that none of my changes were triggered on compile. I use lots of #imports and numerous sub-directories for my less files so my first thoughts were the problem was due to subdirectories, and later, due to #import statements. However neither was correct.
I defined a bundle: main.css.bundle
Within the bundle I called my main less file that contained all the other #imported files: main.less
The issue was that less would normally reserve main.css, but the bundle also gives its output the same name based on the bundle name. So both were conflicting.
Change the bundle name or the main less file name and all should work.
I'm trying to take advantage of Google Closure Compiler minification by writing a database script of my own and compiling it with the pre-compiled ydn. To get a basic first version working I'm trying to rewrite the todo list demo from the project. Unfortunately, I don't understand how to keep namespaces for ydn functions preserved in the compiled output file.
Here's what I've written so far: http://pastebin.com/6YhnRuD5
When the code compiles in advanced mode, the "ydn.db.Storage" from "db = new ydn.db.Storage(dbName, Schema)" gets munged into "ydn.db.c$" making it unusable. The goog.exportSymbol at the bottom of the file doesn't seem to save the function names either.
Does anyone know how to rewrite this with Google Closure Compiler? Should this be compiled directly with the ydn source code instead?
The goog.exportSymbol at the bottom of the file doesn't seem to save the function names either.
It should.
goog.exportSymbol("ydn.db.Storage");
should be
goog.exportSymbol('ydn.db.Storage', ydn.db.Storage);
Is there a way to get multiple files from S3 (or any CDN) in one request?
For example, I have four files on S3:
example.com/cdn/one.js
example.com/cdn/two.js
example.com/cdn/three.js
example.com/cdn/four.js
I would like to be able to request any combination of them at a time. I currently have to include them separately:
<script src="example.com/cdn/one.js" />
<script src="example.com/cdn/two.js" />
But I would like to include them as one request:
example.com/cdn/code.js?one&two
I've considered combining the needed combinations into single files, but there will be way too many combinations for that to be realistic. I've also considered combining all of them into one file, but that would be ridiculously large.
First of all: Your question is more a JS thing than related to S3 / CDNs in general. And to answer your question: You can't "combine" them in one http request.
If you don't want to concatenate and minify them you should take a look at RequireJS, which could handle loading of your scripts – it's async and won't block the browser from rendering.
This is a really great article about async loading: css-tricks.com - Thinking Async which will give you a better insight.
EDIT after comment:
You can in fact use PHP to concatenate & minify your JS files on the fly, but this will put additional load on your server and you'll loose the benefits you get from a CDN … Another approach would be using a build system, which packs everything before it goes into production.
For further information on this topic take a look at the following links:
PHP5 - minify
PHP5 - Assetic
How do I concatenate JavaScript files into one file?
To do on the fly minifaction on your dev system (if it's a Mac) you may want to try CodeKit.
Here is one way you could do that (assuming you just use one&two&three and note file=one&file=two&file=3):
function getJs() {
var url = window.location.toString();
var query = url.split("?");
var params = query.split("&");
// iterate through array of params and write JS
for (i=0; i<params.length; i++) {
var name = params[i] + ".js";
document.write("<script defer language='javascript' type='text/javascript' src='" + name + "'><\/sc" + "ript>");
}
}
This should load after the page is loaded. Then in in your HTML perhaps. I haven't tested this but should be what you're looking for I reckon.
In AS3 you can pass a constant to the compiler
-define+=CONFIG::DEBUG,true
And use it for conditional compilation like so:
CONFIG::DEBUG {
trace("This only gets compiled when debug is true.");
}
I'm looking for something like #ifndef so I can negate the value of debug and use it to conditionally add release code. The only solution I've found so far was in the conditional compilation documentation at adobe and since my debug and release configurations are mutually exclusive I don't like the idea of having both DEBUG and RELEASE constants.
Also, this format works, but I'm assuming that it's running the check at runtime which is not what I want:
if (CONFIG::DEBUG) {
//debug stuff
}
else {
//release stuff
}
I also considered doing something like this but it's still not the elegant solution I was hoping for:
-define+=CONFIG::DEBUG,true -define+=CONFIG::RELEASE,!CONFIG::DEBUG
Thanks in advance :)
This works fine and will strip out code that won't run:
if (CONFIG::DEBUG) {
//debug stuff
}
else {
//release stuff
}
BUT this will be evaluated at runtime:
if (!CONFIG::DEBUG) {
//release stuff
}
else {
//debug stuff
}
mxmlc apparently can only evaluate a literal Boolean, and not any kind of expression, including a simple not.
Use the if / else construct : the dead code will be removed by the compiler and it will not be tested at runtime. You will have only one version of your code in your swf.
If you are not sure use a decompiler or a dump tool to see what really happens.
http://apparat.googlecode.com/files/dump.zip
http://www.swftools.org/
...
While Patrick's answer fulfills the question's criteria, it does not cover all use cases. If you are in an area of code that allows you to use an if/else statement then this is a good answer. But if you are in a place where you cannot then you will need a better solution. For example, you may want to do something like this to declare a constant in a class:
private var server:String = "http://localhost/mystagingenvironment";
or for a live release:
private var server:String = "http://productionserver.com";
(this is an example and I'm not advocating this as production code).
I use xml configs and use the loadConfig+="myconfig.xml" to do my configuration instead of passing large numbers of command line params. So in the <compiler> section of your xml config:
<define>
<name>CONFIG::debug</name>
<value>false</value>
</define>
<define>
<name>CONFIG::release</name>
<value>!CONFIG::debug</value>
</define>
This works well for all use cases:
CONFIG::debug
{
private var server:String = "http://localhost/mystagingenvironment";
}
CONFIG::release
{
private var server:String = "http://productionserver.com";
}
This has the additional benefit of working consistently across applications. It also does not rely on the 'optimize' flag being true, like Patrick's answer (although I think we can assume that 99.999999% of all swfs have optimize=true, I only set it to false when the optimizer breaks my AS3).
It does have the drawback that it doesn't compile all code paths, just the ones that are included. So if you're not using a build server to create release builds and tell you when things break, be prepared for surprise errors when you do your release build ("But it compiled in debug! Crap, I need this to launch now!").
Just my two cents about Chris Hill's answer (which is the solution I also use regularly): it seems that using the loadConfig+="myconfig.xml" option makes the compiler searching for the myconfig.xml file in the Flex SDK directory whereas the -load-config+=myconfig.xml option makes it searching for the myconfig.xml file in the project's directory, which is the behavior I strongly prefer as you can then easily distribute this file with your project sources...