Ember publish unique asset filename for CDN freshness? - javascript

In order to force updated static assets to load from a stubborn CDN, I'm looking for a method of publsihing each of my src="assets/... references in app/index.html to unique filename, in order to keep CDN distributions fresh.
Maybe in index.html:
<link rel="stylesheet" href="assets/vendor.css?version={{content-for 'version'}}">
And then somewhere else I might override a custom content-for value with a random number or a build tag.
Ideas? Thanks!

The answer is Ember fingerprinting.
It's enabled in production by default:
ember build --environment=production
This will result in (lines like) the following in index.html and corresponding asset file names:
<link rel="stylesheet" href="assets/vendor-d41d8cd98f00b204e9800998ecf8427e.css" integrity="sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= sha512-z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==" >
To enable fingerprinting by default for all environments, add this to your ember-cli-build.js:
// ember-cli-build.js
var app = new EmberApp({
fingerprint: {
enabled: false
}
});

I guess you can do this by configuring your ember-cli-build.js file. The defaults of this file is shown below:
// ember-cli-build.js
var app = new EmberApp({
outputPaths: {
app: {
html: 'index.html',
css: {
'app': '/assets/application-name.css'
},
js: '/assets/application-name.js'
},
vendor: {
css: '/assets/vendor.css',
js: '/assets/vendor.js'
}
}
});
Have a look at the configuring output paths section of the ember-cli user guide.

Related

How to change script tag url automatically on build

I'm running Backbone with node using the following code in index.html
<script src="js/api/require.js"></script>
<script>require(['js/require-cfg'],function(){require(['main'])});</script>
main.js looks like this:
require(['app'],
function(App){
App.initialize();
}
);
In production, I compile the files using r.js into main-build.js and redirect the link in the index.html file from main to main-build:
<script>require(['js/require-cfg'],function(){require(['main-build'])});</script>
Currently, if I want to deploy my code to production, I have to either manually change from main to main-build in index.html, or keep the link as main-build but change the contents of main-build.js to the same as main.js when I run a local or test environment, then switch back when deploying to production.
Is there a better (automatic) way of having the code use the compiled main-build.js when in production, and the content of main.js when in local or test environment?
eg: using node environment variables to either change the links in index.html (not sure how to change html!) or change the content of main-build.js but the content gets overwritten everytime r.js is run to compile for production
I personally use Gulp to process the index.html file with gulp-html-replace.
In development, you put the tags you need.
<script src="js/api/require.js"></script>
<!-- build:js -->
<script>require(['js/require-cfg'],function(){require(['main'])});</script>
<!-- endbuild -->
To make a build for production, create a gulp task which uses the gulp-html-replace plugin :
var gulp = require('gulp'),
htmlreplace = require('gulp-html-replace');
gulp.task('build', function() {
return gulp.src("index.html")
.pipe(htmlreplace({
js: {
src: [['main-build']],
tpl: '<script>require(["js/require-cfg"],function(){require(["%s"])});</script>'
},
}))
.pipe(gulp.dest("build/index.html"));
});
If you go the Gulp route, you could make all the build process through it. For example, here's a simple r.js task:
var rjs = require('requirejs');
gulp.task('optimize', function(done) {
rjs.optimize({
name: "main",
out: "build/js/main.min.js",
/* ...other options */
}, function(buildResponse) {
done();
}, done);
});

Using grunt for building prod/dev environment on frontend app

My application is currently only a frontend and I use grunt to do the following:
copy the necessary CSS and JS from bower_components into the right directories
run jshint
build documentation
I'd like to go a bit deeper into the deployment process by compiling my Handlebars templates, compiling the requirejs modules and minifying the resulting JS and all my CSS.
The problem is that I'd like to keep a development environment where the files arent't minified and the requirejs are not compiled.
How can I achieve this?
Specifically, should I template my index.html so that it uses either a single CSS for the prod environment and the multiple CSS for the dev env?
Here is the link to my sources: https://github.com/lilorox/darts/tree/dev (dev branch is the most recent).
I used to try using grunt, but when the configuration is getting bigger, I found myself pretty confused by the configuration over the grunt. I'd try to give a suggestion by using gulp for your front-end building tools. It's much simple, easier to read and faster. You can read the differences here.
Pretty much the same, while grunt specified all the configuration in gruntfile.js, gulp specified its configuration in gulpfile.js. Usually I would created my own configuration in extra file which I named gulpfile.config.js. It would looks like this :
gulpfile.config.js
module.exports = {
development: {
css: [
'./development/bower_components/bootstrap/dist/css/bootstrap.min.css',
'./development/bower_components/font-awesome/css/font-awesome.min.css'
],
js: [
'./development/bower_components/angular/angular.min.js',
'./development/app/components/*.js',
'./development/app/components/**/*.js'
],
ENV: {
name: 'development'
}
},
production: {
css: ['./production/dist/css/app.min.css'],
js: ['./production/dist/js/app.min.js'],
ENV: {
name: 'production'
}
}
}
And in gulpfile.js, I can simply run the task based on the environment that I've configured in gulpfile.config.js
var config = require('./gulpfile.config.js'),
gulp = require('gulp'),
cleancss = require('gulp-clean-css');
gulp.task('scripts', function() {
return gulp.src(config.development.js)
.pipe(concat('app.js'))
.pipe(ngannotate())
.pipe(rename({suffix: '.min'}))
.pipe(uglify())
.pipe(gulp.dest(config.ENV.name + '/dist/js'))
});
Just like grunt, gulp offers abundant of cool plugins for building your front-end apps. I myself usually use gulp-less, gulp-minify-css, gulp-ng-annotate, gulp-uglify, gulp-concat, gulp-server-livereload, gulp-rename, gulp-inject, gulp-imagemin. And of course you can explore much other plugins. Hope this helps!
[UPDATED - Build index.html based on environment]
First you need to configure the task and require all gulp plugins
var config = require('./gulpfile.config.js'),
gulp = require('gulp'),
del = require('del'),
inject = require('gulp-inject'),
rename = require('gulp-rename'),
gulpif = require('gulp-if'),
argv = require('yargs').argv;
if (argv.production) {
var selectedConfig = config.production;
} else {
var selectedConfig = config.development;
}
gulp.task('index', function() {
return gulp.src('./development/assets/templates/index.tpl.html')
.pipe(inject(gulp.src(selectedConfig.css.concat(selectedConfig.js), {read: false}), {ignorePath: selectedConfig.ENV.name}))
.pipe(rename('index.html'))
.pipe(gulp.dest(selectedConfig.ENV.name));
})
And provide the index.tpl.html
<!DOCTYPE html>
<html>
<head>
<!-- inject:css -->
<!-- endinject -->
</head>
<body>
<!-- inject:js -->
<!-- endinject -->
</body>
</html>
Then you can simply build the index.html by running gulp index --development or gulp index --production

How to change Ember.js location from where it serves the static assets

I am a beginner at Ember.js, so sorry if this is an easy question, but I couldn't locate the answer.
Basically, after running ember build, Ember's index.html will have these links for the built .js files:
<script src="assets/vendor-4d126b4b021a3ad999a0115386f5edf4.js" integrity=""></script>
<script src="assets/bsrs-ember-1906440e1018cb4d5bdbe009ff42b763.js" integrity=""></script>
I'd like to change these links in index.html to:
<script src="/static/assets/vendor-4d126b4b021a3ad999a0115386f5edf4.js" integrity=""></script>
<script src="/static/assets/bsrs-ember-1906440e1018cb4d5bdbe009ff42b763.js" integrity=""></script>
Is this possible? If so, how do you do this?
You need to alter the ember-cli-build file (previously called the Brocfile) to ensure a special prefix gets added to the front of your assets (note: only done for production builds)
var app = new EmberApp({
fingerprint: {
prepend: '/static/'
}
});
For changing path of vendor.js you can pass app.outputPaths.vendor.js property to EmberApp in ember-cli-build.js:
var app = new EmberApp({
outputPaths: {
vendor: {
js: '/static/assets/vendor.js'
}
}
});

RequireJS Optimizer - still attempting to load files from old dir - w/ Django

Need some help with the requireJS optimizer.
My setup is as follows:
Django
JS inside the Django static folder
Folder structure
- dist
- copy of /static/ after optimizer (dir)
- django_app01
- django_app02
- django_app ...
- static
- bower_components
- js
What I'm doing:
Running r.js on the static folder.
r.js optmizes and moves the files to the "dist" folder as expected
Change the "static_url" settings in Django to get the static files from the "dist" folder.
Load the page and get a bunch of 404's as RequireJS is still trying to get modules from /static/...
Here's an example of the url's it's trying to fetch:
localhost/static/...
Rather than
localhost/dist/...
Any ideas why I'm getting all of these 404's after I run the optimizer. I expected the r.js optimizer to start to look for all the files in /dist.
build.js:
mainConfigFile : "static/js/require/common.js",
baseUrl: "static",
dir: "dist",
removeCombined: false,
findNestedDependencies: false,
modules: [
{
name: "js/require/common"
},
...
]
common.js:
requirejs.config({
baseUrl: '/static/',
paths: {
'jquery': 'bower_components/jquery/dist/jquery.min',
...
},
'shim': {
'blah': {
'deps': [...],
'exports': 'blah'
},
...
}
})
html:
<head>
<script src="//cdn.jsdelivr.net/requirejs/2.1.14/require.min.js"></script>
<script src="{% static 'js/require/common.js' %}"></script>
</head>
<script>
require(['{% static "js/interviews.js" %}']);
</script>
Judging by what you do show in your question, the runtime configuration you use when you do not optimize your files is the same as the one you use after optimization. That is, you have only one common.js file that sets your runtime for both cases. This file sets a baseUrl of /static/, so that's where RequireJS looks for the files. The optimization process does not override this setting behind the scenes.
For a project of mine, what I did was to put the sources subject to optimization into a directory that is just for the source (static-src). Django does not know anything about this directory. Then either one of two things happen when I run make to build my project:
I make a non-optimized build which copies all the files from static-src to a directory named build/static-build. (There are other subdirectories for other purposes under build.
I make an optimized build which runs r.js. r.js puts its output in build/static-build.
In either case, everything ends up in the same location. STATICFILES_DIRS is set in my settings to grab files from this directory when collectstatic is run. My server is set to serve the /static/ (the value of STATIC_URL) files from the location where collectstatic collected the static files.
This is just an example. You could use something else than make. You could certainly use different directory names. You could perhaps have a build process that is a bit simpler. (I created it when I was very new to Django and never bothered changing it.)
Okay, figured out how to do this without using a watcher, or make, or any other folder copy script madness.
requireJS allows you to set default variables before you load your require.js lib at the top of your HTML.
Changes I made from my previous configuration are:
- No longer define baseUrl in your module definitions (requirejs.config)
- Define baseUrl as a default depending on the environment.
build.js:
mainConfigFile : "static/js/require/common.js",
baseUrl: "static",
dir: "dist",
removeCombined: false,
findNestedDependencies: false,
modules: [
{
name: "js/require/common"
},
...
]
common.js:
requirejs.config({
// baseUrl: '/static/', DO NOT DEFINE baseUrl here anymore
paths: {
'jquery': 'bower_components/jquery/dist/jquery.min',
...
},
'shim': {
'blah': {
'deps': [...],
'exports': 'blah'
},
...
}
})
html:
<head>
<!-- Define the baseUrl depending on environment -->
<script>
var require = {
baseUrl: {% if debug %}'/static/'{% else %}'/dist/'{% endif %}
}
</script>
<script src="//cdn.jsdelivr.net/requirejs/2.1.14/require.min.js"></script>
<script src="{% static 'js/require/common.js' %}"></script>
</head>
<script>
require(['{% static "js/interviews.js" %}']);
</script>

RequireJS plugin( order.js )

http://requirejs.org/
I recently downloaded require.js 2.0 and I am getting error in my console:
Uncaught TypeError: Object function (){var g=ga.call(arguments,0),e;if(f&&v(e=g[g.length-1]))e.__requireJsBuild=!0;g.push(d);return b.apply(null,g)} has no method 'nameToUrl'
Is order.js plugin still supported by requirejs? I don't see its documentation in the website.
When I try to remove the file the script breaks.
In my index file, I included requirejs script in the head section:
<!DOCTYPE html>
<html>
<head>
<title>
My Mobile Application
</title>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<link rel="stylesheet" href="public/css/style.css" />
<script data-main="scripts/main.js" src="scripts/require.js"></script>
</head>
<body></body>
</html>
Then in my main.js file:
requirejs.config({
//By default load any module IDs from js/lib
baseUrl: 'js/lib',
//except, if the module ID starts with "app",
//load it from the js/app directory. paths
//config is relative to the baseUrl, and
//never includes a ".js" extension since
//the paths config could be for a directory.
paths: {
app: '../app',
assets: '../assets',
views: '../app/views',
templates: '../app/templates',
collections: '../app/collections',
models: '../app/models'
}
});
// Start the main app logic.
requirejs([
'jquery/jquery',
'assets/jqm.config',
'jquery/mobile',
'text'
]);
require([
'app'
],
function( App ){
$(document).ready( function(){
App.initialize();
});
}
);
I sees to it that App.initialize doesn't have any errors and what App.initialize is doing is just simple geo location. The requirejs simply ask for order.js, and when I put the code it's having the same error as mentioned above.
Thank you!
Your assumption that order is no longer supported is correct. It was removed in favour of the shim configuration option:
So, the the order plugin has been removed and following the lead of
Tim Branyen and Dave Geddes, of use and wrap respectively, requirejs
2.0 integrates that kind of dependency tree specification directly in requirejs.
Require 2.0 upgrade notes - https://github.com/jrburke/requirejs/wiki/Upgrading-to-RequireJS-2.0
Also, check the shim documentation on the RequireJS site - http://requirejs.org/docs/api.html#config-shim
Oh figured it out.
//This is our main applicatoon boot loader or bootstrap
//here we are loading necessary scripts dependencies like
//jquery, jqm.config, mobile, text
requirejs.config({
baseUrl: 'js/libs',
//except, if the module ID starts with "app",
//load it from the js/app directory. paths
//config is relative to the baseUrl, and
//never includes a ".js" extension since
//the paths config could be for a directory.
paths: {
app: '../app',
assets: '../assets',
views: '../app/views',
templates: '../app/templates',
collections: '../app/collections',
models: '../app/models'
}
});
// Start the main app logic.
require(["jquery","assets/jqm.config","jquery/mobile","text","app"],
function(
$,
config,
mobile,
text,
App
) {
//the jquery.alpha.js and jquery.beta.js plugins have been loaded.
$(function() {
App.initialize();
});
});

Categories

Resources