Custom script and css versioning best practices and automation with gruntjs - javascript

I've decided to start versioning the custom css and js files i use in my WP themes so that when I update a site a page reload is not needed to display the changes to users(long overdue, I know).
Is just adding a version number as a query string at the end of the call and keeping the real file name static a good practice(as in "script.js?v=xyz")? How will this affect browser caching when the version number is not changed? Will it cache normally and only get a new version when i change the version number?
If the above is the way to go, i would like to automate this with Grunt so when I run my "build" command the PHP file where the files get enqueued in WP gets modified to contain a random hash + the date as the version parameter in the wp_enqueue_script command. I presume that when i run my development command with livereload and everything, the scripts having said version will not affect livereload so i do not need to remove the versioning, correct?
How would you do this, what npm module would you use and what would be the commands i need to add to my Gruntfile.js?
As a sidenote, i know that the version number added by wordpress by default to all scripts and css is considered a security risk as it exposes the WP version you're rolling and i've been removing it via functions.php, but, i would like to enable it, yet have it not use the WP version but something random that gets changed with each WP update. Any idea how one would implement this? I'm surprised that i was not able to find a plugin that does this.
Thank you ver much, community!

I have certainly done this, as a way of indicating to browsers that they should download a newer version of a stylesheet. This isn't particularly elegant, but one thing I've done is declared cache-busting parameter (xyz in your example) as a PHP constant:
// inc/constants.php
define('CACHEPARAMETER', '20161027');
And my grunt build task modifies this using grunt-text-replace:
replace: {
cacheparam: {
src: ['inc/constants.php'],
overwrite: true, // overwrite matched source files
replacements: [{
from: /'CACHEPARAMETER', '[0-9]{8}'/g,
to: "'CACHEPARAMETER', '<%= grunt.template.today('yyyymmdd') %>'"
}]
}
}
I'm enqueueing this in functions.php, which I do like this:
include "inc/constants.php";
wp_enqueue_style( 'my-style', get_stylesheet_uri(), array(), CACHEPARAMETER);
Hope this helps.

Related

how do I install an SDK using require, when I need to require an folder and this seems impossible?

PROBLEM DESCRIPTION.
I'm trying to follow https://github.com/lucascosta/facebook-js-ads-sdk to install the Javascript SDK for Facebook. Before anyone objects, I am absolutely aware of the compact version (https://developers.facebook.com/docs/javascript/quickstart), but that API is absolutely useless to me. I need the one produced by Lucas Costa, in order to be able to make changes to my ads account, which the recommended script by Facebook does not enable.
As usual installation instructions (see github.com/lucascosta/facebook-js-ads-sdk) are horribly predicated on mysterious conditions, that are unbeknown to me. I cannot get the SDK to work and would like someone to tell me explicitly what to do. The crux of the problem is the paradoxical situation: I am supposed to use a require('...') command (where??) to include the SDK (where is this supposed to be saved??) and yet require cannot be used on folders, but on scripts. Maybe somewhere there is a require.config file, I'm supposed to set up, but I have no idea, as, like I said, the instructions completely bypass any mention of the necessary starting conditions.
Here is what I have done so far. My folder-structure looks like this (I don't even know if this is right, as no-one explains it anywhere!):
[Serverroot]
— [folder with my website]
– facebook-ads-sdk (the folder one gets by downloading)
– css — pagestyles.css
– js — lib
require.js
— app
( some header scripts )
– img
( some images )
index.php
In index.php I have a block of html followed by some javascript. It is here, that I try to insert the setup / example code from . The browser cannot even get past the line const adsSdk = require('facebook-ads-sdk');. I have tried a number of things: require('./facebook-ads-sdk');, moving all this to the folder ./js/app in a script main.js and then writing in my html in index.php where main and require are located. Setting up a require.config (or requirejs.config) replacing require by requirejs, etc. and including the appropriate scripts in the <head> part of index.php. Nothing helps. Here are the errors: first with const adsSdk = require('facebook-ads-sdk'); I get
Error: Module name "facebook-ads-sdk" has not been loaded yet for context: _. Use require([])
Okay. How do I ‘load the Module for the context _’?? Reading requirejs.org/docs/start.html is of no help here. I tried require([], function() {require('facebook-ads-sdk')}); Same error. I tried require(['facebook-ads-sdk']);
I tried using the following commands in my script in index.php:
require.config({
shim: {
'facebook': {
exports: 'adsSdk',
},
},
paths: {
'sdk': './facebook-ads-sdk',
}
});
var adsSdk = require(['sdk']);
Then I get the error
Failed to load resource: http:// .... /facebook-ads-sdk.js the server responded with a status of 404 (Not Found)
Ie, the browser thinks I'm trying to include a script facebook-ads-sdk.js, but I’m supposed to(???) ‘require’ the entire folder! What is going on? Can someone please give me thorough instructions about the necessary folder structure and command, in order to get this SDK working?
Why is this so infuriatingly vaguely described online? Is there some kind of mysterious way of installing SDKs, about which ‘everyone’ knows, but never mentions?
UPDATE: SOLUTION. For any future google-searches for this same problem: via the following simple methods, one can install & embed the Javascript-FB-Ads-SDK:
install via npm install --save facebook-ads-sdk a copy of the npm modul Lucas Costa’s facebook-ads SDK.
Copy this folder to your website’s architecture (actually you possibly just need one subfolder / file).
in your HTML include the script (via <script type='text/javascript' src='...'></script>) with the src pointing to the file in the facebook-ads-sdk folder: /dist/iife.js.
In your page’s script, you now have access to then API via the global variable fb.
alternatively to 3:
3’. in your HTML headers make sure to include the require.js script via a <script>-tag. Then in your website’s javascript apply the following commands anywhere:
require.config({
paths: {
'sdk': './[FOLDERNAME OF SDK]/dist/iife',
}
});
require(['sdk']);
Credit and special thanks goes to #SLaks and #KhauriMcClain (I would give you up-points if I could) for this solution.
The instructions are assuming that you're using a bundling system like Webpack or Browserify.
If you are, the instructions "just work"; you can require() it and everything will work fine.
If you're building a non-trivial codebase, you really should use such a system.
If you aren't, you can reference iife.js in a <script> tag and that will create global variables.
You need to change the way you are using require.
You cannot use the synchronous require method to load dependencies if they have not been loaded before. Hence const adsSdk = require('facebook-ads-sdk'); will not work. You will need to use the form require(['name-of-script'], callback).
Require does not allow you to load an entire folder. You have to find the exact script you are trying to work with so var adsSdk = require(['sdk']); will not work.
Ultimately your code should look something like:
require(['some-facebook-script'], function(someFacebookScript) {
// Do some work here
});
The parameter passed to the callback function will be the loaded module you are trying to consume. When using the asynchronous version (as I just demonstrated) the return from require is not useful.

Javascript - Load new cache on deploy

I want to load a new version of my javascript,css,html etc.. every time i deploy my page. I saw that i could add ?<version> to the script's src link, Is there any other new/more reliable way of doing this?
UPDATE I'm looking for something like a variable that would allow me to update the URL automatically if a new file is loaded rather than me renaming the files manually. Is this possible?
I think that an elegant way is to use a gulpfile which rename your *.css and *.js filename to include a hash of file content at the end.
1) Gulp library : deal with filename
https://github.com/sindresorhus/gulp-rev
Example of what it does : transform filename unicorn.css into unicorn-d41d8cd98f.css
2) Gulp library : deal with index.html include
To inject css and js, you could use another gulp librairie :
https://www.npmjs.com/package/gulp-inject
This one allow you to include automatically css and js in your index.html using file directories.
3) What you have to do
All you have to do is to create a gulp task which use these two library. I swear it's not to hard. I don't have enough time to do a demonstration (I'll do it in the next few days).
This solution presents two advantage :
Only changed files are cache reloaded on the user side
You don't need to use an external js library inside your project,
which increase a risk of failure.
Add the resource's last-edited time to the src/href of the resource's element, or add an MD5 of all related times if you're linking to an aggregation script that might include sub-directories, etc.
http://verens.com/2008/04/09/javascript-cache-problem-solved/

How do I use Jangaroo to convert a single As3 function to javascript?

I've stumbled upon Jangaroo, and it seems to provide what i need. The problem is that to use it the docs say that i need to setup maven.
I really have a single function, so all that is a bit of an overkill.
The ideal solution would be something similar to the Telerik Code Converter(http://converter.telerik.com), but for AS3.
I just updated the documentation on how to use Jangaroo as a command line tool:
https://github.com/CoreMedia/jangaroo-tools/wiki/Stand-Alone-Compiler
After following steps 1 through 6, you can compile your single class like so:
mkdir joo\classes
jooc -v -g SOURCE -classpath %JOOLIBS%\jangaroo-runtime.jar -sourcepath . -d joo\classes GACodec.as
Note that the generated JavaScript file GACodec.js only works together with the jangaroo runtime. The Wiki page continues with instructions on how to end up with a working Webapp. For your class, you just have to unpack jangaroo-runtime.jar:
"%JAVA_HOME%\bin\jar" -xf %JOOLIBS%\jangaroo-runtime.jar
Then, you can run your class from a tiny HTML file that looks like so:
<script src="joo/jangaroo-runtime.module.js"></script>
<script>
joo.classLoader.import_("GACodec");
joo.classLoader.complete(function() {
alert(new GACodec().encode("FOOBAR!"));
});
</script>
When trying out your code, I noticed that it needs a minor change to work: Jangaroo does not generate implicit initialization code for typed local variables. There are at least two lines in your code where an integer variable is declared but not initialized explicitly. ActionScript would set it to 0, but Jangaroo does not. Anyway, it is better style to do explicit initialization, and if you do so, i.e. in your source code replace
var i:int;
by
var i:int = 0;
as far as I can tell, it seems works!
Last thing, I find using Maven easier than installing the Jangaroo SDK, since you just have to install Maven once and it takes care of all needed downloads and makes updating to the latest Jangaroo version a breeze: Just increase the Jangaroo version number in your pom.xml, and Maven takes care of everything else.

How can I access images from Javascript using Grails Asset-Pipeline plugin?

I have just upgraded to Grails 2.4 and am using the Asset-Pipeline1.8.7 plugin. I am wondering how to access the images from Javascript. I am using the Google Maps Javascript V3 API and need to set some marker icons in Javascript. Is there a way to create some Javascript vars on a GSP using the tag and then access the file in my app.js code? If that is not possible, how do a reference the compiled images in assets?
You could define a globally available object that holds the root-path to your assets dir and use this to build-up URLs to your assets.
Add this snippet to your layouts head-section
<g:javascript>
window.grailsSupport = {
assetsRoot : '${ raw(asset.assetPath(src: '')) }'
};
</g:javascript>
Then use it elsewhere like this:
<g:javascript>
var pathToMyImg = window.grailsSupport.assetsRoot + 'images/google_maps_marker.png';
</g:javascript>
Update 2015-08-06
While checking the release notes of the asset-pipeline plugin I noticed that non-digest versions of assets are no longer stored in the WAR-file. This would mean that my proposed solution breaks when the application is deployed as a WAR:
May 31, 2015
2.2.3 Release - No longer storing non digest versions in war file, cutting overhead in half. Also removed Commons i/o dependency. Faster byte stream.
This means that you have to explicitly define all your images beforehand and are no longer able to construct the path dynamically in your scripts:
<g:javascript>
window.grailsSupport = {
myImage1 : '${assetPath(src: 'myImage1.jpg')}',
myImage2 : '${assetPath(src: 'myImage2.jpg')}'
};
</g:javascript>
Update 2016-05-25
It is now possible to configure if non-digest versions of the assets are included in the built war-file by setting grails.assets.skipNonDigests (default is false):
It is normally not necessary to turn off 'skipNonDigests'. Tomcat will automatically still serve files by non digest name and will copy them out using storagePath via the manifest.properties alias map. This simply cuts storage in half. However, if you are attempting to do things like upload to a cdn outside of the cdn-asset-pipeline plugin and via the contents of 'target/assets'. This may still be useful.
Note that you still might use the proposed solution to define all required images beforehand to work around caching issues in the browser (as the digest-version of the asset has its content-hash in the filename).
Yes you can by putting a ${assetPath(src: 'img.png')} in your gsp
I don't know what the ideal solution is in your case but a solution might be:
Use relative paths like '../assets/use-control.png' in your js code.
Add the img in your dom and reference it from your js code.
Add a data-imgpath="${asset.assetPath(src: 'use-control.png')}" attribute on an appropriate element in your dom and use this link.
As an alternative you can use HTML5's data-* Attribute.
I've explained a bit further here:
load images from javascript

require.js - How can I set a version on required modules as part of the URL?

I am using require.js to require JS modules in my application.
I need a way to bust client cache on new JS modules, by way of a different requested URL.
i.e., if the file hello/there.js has already been cached on the client, I can change the file name to force the browser to get the new file.
In other words, for the module hello/there, I'd like require.js to request the url hello/there___v1234___.js (the file name can look different, it's just an example), according to a version string which is accessible on the client.
What is the best way to achieve that?
I got really frustrated with the urlArgs solution and finally gave up and implemented my own fix directly into require.js. This fix implements your ideal solution, if you are willing to modify your version of the library.
You can see the patch here:
https://github.com/jbcpollak/requirejs/commit/589ee0cdfe6f719cd761eee631ce68eee09a5a67
Once added, you can do something like this in your require config:
var require = {
baseUrl: "/scripts/",
cacheSuffix: ".buildNumber"
}
Use your build system or server environment to replace buildNumber with a revision id or software version.
Using require like this:
require(["myModule"], function() {
// no-op;
});
Will cause require to request this file:
http://yourserver.com/scripts/myModule.buildNumber.js
The patch will ignore any script that specifies a protocol, and it will not affect any non-JS files.
On our server environment, we use url rewrite rules to strip out the buildNumber, and serve the correct JS file. This way we don't actually have to worry about renaming all our JS files.
This works well for my environment, but I realize some users would prefer a prefix rather than a suffix, it should be easy to modify my commit to suit your needs.
Here are some possible duplicate questions:
RequireJS and proxy caching
Prevent RequireJS from Caching Required Scripts
OK, I googled "requirejs cache bust" for you and found this existing SO answer, which says you can configure requireJS with an urlArgs parameter, which is only a partial solution, but it might be enough to meet your immediate needs.
That said, the cachebusting problem is full of challenges and many "solutions" don't actually solve the problem in its entirety. The only maintainable way to do this (IMHO as of now) is with a full-on asset management system like the Ruby on Rails asset pipeline or connect-assets or the equivalent for your server side framework of choice. Those can correct compute a checksum (usually MD5 or SHA1) of the content of each file and give you the file names you need to put as URLs in your HTML script tags. So, don't bother with manually changing filenames based on version numbers, just use checksums since they are easily automated and foolproof.
From what I can tell, out of the box requirejs can't do the cachebusting aspect for you. You might want to read this google groups thread. Otherwise, you may need to pair requirejs with an additional tool/script to get you good cachebuster checksums.
Just do it like the creator of requirejs suggests:
var load = requirejs.load;
requirejs.load = function (context, moduleId, url) {
// modify url here
url = url.substring(0, url.lastIndexOf('.')) + '.' + VERSION + url.substring(url.lastIndexOf('.'));
return load(context, moduleId, url);
};
https://github.com/jrburke/requirejs/wiki/Fine-grained-URL-control
HTML5 Boilerplate has ant-build-script that renames your files and any reference to them for this exact reason and can do alot more. It's worth checking out if you haven't already.

Categories

Resources