How can I make Google's closure library load faster? - javascript

I'm writing a simple phone number parser based on [libphonenumber]. Unfortunately, "http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js" takes forever to load, and when I wget the file and just include it as src="base.js", a bunch of errors pop up.
My guess is that this is because the library has not yet loaded yet, so the goog.require() statements are failing.
What can I do?
<!DOCTYPE html>
<html>
<head>
<title>Phone Number Parser</title>
<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script>
<script>
goog.require('goog.dom');
goog.require('goog.json');
goog.require('goog.proto2.ObjectSerializer');
goog.require('goog.string.StringBuffer');
</script>
<script src="phonemetadata.pb.js"></script>
<script src="phonenumber.pb.js"></script>
<script src="metadata.js"></script>
<script src="phonenumberutil.js"></script>
</head>
<body>
<script>
numbers = ['6509066389', '+40723875777', '720-935-6433', '914-262-7178', '7123040634'];
for (i in numbers) {
console.log(format_for_five9(numbers[i]));
}
function format_for_five9(phoneNumber) {
var $ = goog.dom.getElement;
var regionCode = 'US';
var output = new goog.string.StringBuffer();
try {
var phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
var number = phoneUtil.parseAndKeepRawInput(phoneNumber, regionCode);
number_json_serial = goog.json.serialize(new goog.proto2.ObjectSerializer(goog.proto2.ObjectSerializer.KeyOption.NAME).serialize(number));
number_json = goog.json.parse(number_json_serial);
if(phoneUtil.isValidNumberForRegion(number, regionCode)) {
five9_format = number_json.national_number.toString();
}
else {
five9_format = number_json.country_code.toString() + number_json.national_number.toString();
}
} catch (e) {
output.append('\n' + e);
console.log(e);
}
return five9_format;
}
</script>
</body>
</html>

The closure library is meant to be used in conjunction with the closure compiler to compile and minify your javascript for production. It's not intended to be used as a raw file in production. Even in your dev environment you can use http://plovr.com/ to dynamically compile and serve your javascript.
Closure is very verbose in raw form because of the type annotations and the java like structure, the closure compiler will not only minify the script, but also optimize and remove unused scripts to make things faster.
Here's an example of using plovr to dynamically serve your javascript code
java -Xmx256m -jar plovr.jar serve -p 9811 /path/to/your/closure/config.js
This will serve the compiled javascript files on localhost:9811. For production:
java -jar plovr.jar build /path/to/your/closure/config.js > production.js
Have a look at the documentation http://plovr.com/docs.html on how to configure plovr to compile or serve your javascript.

You shouldn't be directly linking the library in the first place.
The solution is to download the entire library and host them it the same web server that hosts the above code. You should probably store the javascript in the same directory as phonemetadata.pb.js, metadata.js, etc. That would allow you to include the script just like all the others:
<script src="base.js">
You can download Closure via git (git clone https://github.com/google/closure-library.git), or as a zip file.

Related

Minify/uglify html page with embedded script tags in a nodejs app

I have an express app that, among other things, delivers some html code based on a single handlebars template.
It would be great for the use-case to deliver it with short latency. So I read and compile the template when the app starts and for now I simply call
res.send(
compiledTemplateIframeContent({ data: iframeData })
)
All of that works flawlessly.
The problem
Now I would like to minify the html and uglify the js code. I tried with html-minifier but the embedded javascript is not uglified (renaming variables), not even minified (eg remove whitespaces). I guess uglify-js is not called in the background because of some miss-configuration. I can't find my mistake by reading the docs.
I wrote some test html code and tried it in this online tool. It is just a simple gui infront of html-minifier and therefor potentially a good way to test my problem.
In the online version I can at least make it compress the javascript code when I add type="text/javascript" to the tag and text/javascript to the Process scripts option (even this does not work in my express app).
But also in the online tool the Minify JavaScript checkbox does not change anything.
I don't know the possibilities of uglifyjs yet (a dependency of html-minifier) but with that name I would assume it should not only compress but also uglify the js code.
Edit / Update
I did some more research. I extracted the js code by regex match and just called uglify-js directly with the code. It had some problems with the handlebar fragments {{ }}. After removing them uglif-js works on the embedded js part of the template.
As I have set the option ignoreCustomFragments: [/{{.*}}/g] for the html-minifier I guess this should not be the problem?!
Here is the part that directly uses uglify-js
function minifyWithUglifyJs(orig: string): string {
const re = /<script\b[^>]*>([\s\S]*?)<\/script>/gm;
const match = re.exec(orig);
if(match == null) {
throw new Error(`Can't minify js in iframe template as found no match for js script!`);
}
const origJs = match[1] as string;
console.log(origJs);
const minifyResult = uglifyjs.minify(origJs);
if(minifyResult.warnings != null) {
minifyResult.warnings.forEach(logger.warn);
}
if(minifyResult.error) {
throw new Error(`Can't minify the iframe template!`, {cause:minifyResult.error});
} else {
// replace orig js with minified js in code
return orig.replace(origJs, minifyResult.code);
}
}
Summing it up:
does anyone know what I do wrong with the http-minifier?
Here is my attempt to minify in my node app (using html-minifier#4.0.0)
import htmlMinifier from 'html-minifier';
function minifyWithHtmlMinifier(orig: string):string {
return htmlMinifier.minify(orig, {
minifyJS: true,
processScripts: ["text/javascript"],
ignoreCustomFragments: [/{{.*}}/g]
});
}
And here is some test code one can use in the online tool
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test ad</title>
</head>
<body onload="onLoad()">
<div id="img-container" style="width:100%;height:100%">test text in page</div>
<script type="text/javascript">
const longNameThatShouldBeUglified= 12;
console.log("test");
//{{another test}}
function onLoad() {
console.log("longNameThatShouldBeUglified (Not this time as it's a string) "+longNameThatShouldBeUglified);
}
</script>
</body>
</html>
The problem is, that html-minifier does not give you any warnings when uglify-js throws any errors. I thought I had misconfigured html-minifier becaue it did not minify.
After splitting my code to get only the content between the tags and using uglify-js directly I saw the problems and could fix the to-be-minified code. As soon as it worked I switched back to using html-minifier and it worked.
I now use the following code where I tell html-minifier to use uglify-js wrapped in some code to recognize any problems.
function minifyWithHtmlMinifier(orig: string):string {
return htmlMinifier.minify(orig, {
// I have replaced the default minifyJS usage of the html-minifier
// as this silently ignores any uglifyjs errors and just displays
// the non-minified code. Has cost me half a day to figure out
// that it was not a configuration problem but problem with the
// to-be-minified-code.
// By setting the call to uglifyjs myself I can catch the errors and
// rethrow them, failing fast, as this is much better than recognizing
// a not-minified code much later when I don't remember what I changed
minifyJS: (text, _inline) => {
const minifyResult = uglifyjs.minify(text, {
compress: {
drop_console: true // remove all console log entries
}
});
if(minifyResult.warnings != null) {
minifyResult.warnings.forEach(logger.warn);
}
if(minifyResult.error) {
// TODO maybe use terser. I think it supports
// newer ES versions than uglifyjs
throw new Error(`Can't minify the iframe template!`, {cause:minifyResult.error});
}
return minifyResult.code;
},
minifyCSS: true,
ignoreCustomFragments: [/{{.*}}/g], // handlebars fragments
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true,
collapseWhitespace: true
});
}
Important to note:
there is also html-minifier-terser that did not come up during my upfront research on what minifier to use with nodejs. Maybe it als does better reporting.

How to create QUnit tests with reference to another class?

I'm trying to add unit testing for JavaScript into my web site. I use VS2013 and my project is an ASP.NET web site.
Based on recommendations (http://www.rhyous.com/2013/02/20/creating-a-qunit-test-project-in-visual-studio-2010/) I've done so far:
Created new ASP.NET app
Imported QUnit (using NuGet)
Into "Scripts" added links to js-file in my original web site (files PlayerSkill.js - containts PlayerSkill class and trainings.js - contains Trainer and some other classes)
Created new folder "TestScripts"
Added TrainingTests.js file
Wrote simple test:
test( "Trainer should have non-empty group", function () {
var group = "group";
var trainer = new Trainer(123, "Name123", group, 123);
EQUAL(trainer.getTrainerGroup(), group);
});
Notice: my trainings.js file among others contains
function Trainer(id, name, group, level) {
...
var _group = group;
this.getTrainerGroup = function () { return _group ; }
};
When I execute my test I see error: Trainer is not defined.
It looks like reference to my class is not recognized. I feel like linking file is not enough, but what did I miss?
Please help add reference to the original file with class and run unit test.
Thank you.
P.S. Question 2: Can I add reference to 2 files (my unit test will require one more class which is in another file)? How?
You should add all the relevant logic of your application to your unit testing file so they all execute before you run your tests
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>QUnit Test Results</title>
<link rel="stylesheet" href="/Content/qunit.css">
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="/Scripts/qunit.js"></script>
<script src="/Scripts/PlayerSkill.js"></script>
<script src="/Scripts/trainings.js"></script>
<script src="/TestScripts/TrainingTests.js"></script>
</body>
</html>
You should not use linked files because they will not exist physically in the script folder.
If you really want to use them you should let the Visual Studio intellisense resolve the physical path of the file like this.
Type the script tag <script src=""></script>
Place the cursor inside the quotes in the src attribute and press CTRL + SPACE
Search your files and let the resolved path untouched
If your project location changes you must update the linked files and also the script references.
{Edit1}
Solution 2:
You could also use an MVC Controller and a Razor View to create your unit testing page and the linked files will work as expected with the only issue that you will have an extra controller in your project but this is not bad at all if for example you want to test the loading of content using ajax that is by default blocked by the browser if they are run from a local file.
Solution 3:
You can also setup a new MVC project just for your javascript unit testing just as you usually setup a new project for any server side code and this will help to prevent your testing to interfere with your production code
{Edit 2}
Solution 4:
As part of the javascript ecosystem you could use grunt or gulp to automate the copy of your scripts from anywhere to your project before running the tests. You could write a gulpfile.js like this
var sourcefiles = [/*you project file paths*/];
gulp.task('default', function () {
return gulp.src(sourcefiles).pipe(gulp.dest('Scripts'));
});
And then run it opening a console and running the command gulp or gulp default
Looks like trainings.js is not defined when calling TrainingTests.js . See this question for more details regarding why this happens! Once that is fixed it does work. And yes similar to trainings.js you can have any number of files in any folder as long as you reference them properly. I have created a sample fiddle accessible # http://plnkr.co/edit/PnqVebOzmPpGu7x2qWLs?p=preview
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="http://code.jquery.com/qunit/qunit-1.18.0.js"></script>
<script src="trainings.js"></script>
<script src="TrainingTests.js"></script>
</body>
In my case I wanted to run my tests from within my ASP.NET web application, and also on a CI server. In addition to the other information here I needed the following, otherwise I experienced the same error as the OP on my CI server:
Add one or more require() calls to test scripts.
Set the NODE_PATH environment variable to the root of my application.
Example of require()
Within my test scripts I include a requires block, the conditional allows me to use this script from a web browser without needing to adopt a third-party equivalent such as requirejs (which is convenient.)
if (typeof(require) !== 'undefined') {
require('lib/3rdparty/dist/3p.js');
require('js/my.js');
require('js/app.js');
}
Example of setting NODE_PATH
Below, 'wwwroot' is the path of where /lib/ and other application files are located. My test files are located within /tests/.
Using bash
#!/bin/bash
cd 'wwwroot'
export NODE_PATH=`pwd`
qunit tests
Using powershell
#!/usr/bin/pwsh
cd 'wwwroot'
$env:NODE_PATH=(pwd)
qunit tests
This allowed me to run tests both within my ASP.NET web application, and also from a CI server using a script.
HTH.
If you're wondering how to make your tests see your code when running from command line (not from browser!), here is a bit expanded version of Shaun Wilson's answer (which doesn't work out-of-the-box, but contains a good idea where to start)
Having following structure:
project
│ index.js <--- Your script with logic
└───test
tests.html <--- QUnit tests included in standard HTML page for "running" locally
tests.js <--- QUnit test code
And let's imagine that in your index.js you have following:
function doSomething(arg) {
// do smth
return arg;
}
And the test code in tests.js (not that it can be the whole content of the file - you don't need anything else to work):
QUnit.test( "test something", function( assert ) {
assert.ok(doSomething(true));
});
Running from command line
To make your code accessible from the tests you need to add two things to the scripts.
First is to explicitly "import" your script from tests. Since JS doesn't have sunch a functionality out-of-the box, we'll need to use require coming from NPM. And to keep our tests working from HTML (when you run it from browser, require is undefined) add simple check:
// Add this in the beginning of tests.js
// Use "require" only if run from command line
if (typeof(require) !== 'undefined') {
// It's important to define it with the very same name in order to have both browser and CLI runs working with the same test code
doSomething = require('../index.js').doSomething;
}
But if index.js does not expose anything, nothing will be accessible. So it's required to expose functions you want to test explicitly (read more about exports). Add this to index.js:
//This goes to the very bottom of index.js
if (typeof module !== 'undefined' && module.exports) {
exports.doSomething = doSomething;
}
When it's done, just type
qunit
And the output should be like
TAP version 13
ok 1 Testing index.js > returnTrue returns true
1..1
# pass 1
# skip 0
# todo 0
# fail 0
Well, due to help of two answers I did localize that problem indeed was in inability of VS to copy needed file into test project.
This can be probably resolved by multiple ways, I found one, idea copied from: http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml
Solution is simple: add tag dynamically
In order to achieve this, I've added the following code into tag:
<script>
var fileref = document.createElement('script');
fileref.setAttribute("type", "text/javascript");
var path = 'path'; // here is an absolute address to JS-file on my web site
fileref.setAttribute("src", path);
document.getElementsByTagName("head")[0].appendChild(fileref);
loadjscssfile(, "js") //dynamically load and add this .js file
</script>
And moved my tests into (required also reference to jquery before)
$(document).ready(function () {
QUnit.test("Test #1 description", function () { ... });
});
Similar approach also works for pure test files.

gulp: Automatically add version number to request for preventing browser cache

I deploy my project by building source files with gulp right on the server. To prevent caching issues, the best practice could be adding a unique number to request url, see: Preventing browser caching on web application upgrades;
In npm repositories, I couldn't find a tool for automatically adding version number to request. I'm asking if someone has invented such tool before.
Possible implementation could be the following:
I have a file index.html in src/ folder, with following script tag
<script src="js/app.js<!-- %nocache% -->"></script>
During build it is copied to dist/ folder, and comment is replaced by autoincrement number
<script src="js/app.js?t=1234"></script>
You can use gulp-version-number for this. It can add version numbers to linked scripts, stylesheets, and other files in you HTML documents, by appending an argument to the URLs. For example:
<link rel="stylesheet" href="main.css">
becomes:
<link rel="stylesheet" href="main.css?v=474dee2efac59e2dcac7bf6c37365ed0">
You don't even have to specify a placeholder, like you showed in your example implementation. And it's configurable.
Example usage:
const $ = gulpLoadPlugins();
const versionConfig = {
'value': '%MDS%',
'append': {
'key': 'v',
'to': ['css', 'js'],
},
};
gulp.task('html', () => {
return gulp.src('src/**/*.html')
.pipe($.htmlmin({collapseWhitespace: true}))
.pipe($.versionNumber(versionConfig))
.pipe(gulp.dest('docroot'));
});
NOTE:
I can no longer recommend this plugin. It is no longer maintained and there are some issues with it. I created a pull request some time ago, but there is no response from the author.
You can use the gulp-rev module. This will append a version number to the files, the version is a hash of the file content, so it will only change if the file changes.
You then output a manifest file containing the mapping between the file e.g. Scripts.js to Scripts-8wrefhn.js.
Then use a helper function when returning the page content to map the correct values.
I have used the above process. However there's another module gulp-rev-all which is an forked extension of gulp-rev which does a little more, e.g. automatic updating of file references in pages.
Documentation here:
gulp-rev: https://github.com/sindresorhus/gulp-rev
gulp-rev-all: https://www.npmjs.com/package/gulp-rev-all
I worked onto writing an regex, which in use along with gulp-replace works just fine.
Please find the code below. Following is a quick code for the image and css for views files codeigniter framework. But it should work fine for all the kinds of files given the source folder specified correctly.
You may customize the code as per your use.
You can call the tasks altogether, using gulp default or individual task at a time.
'use strict';
var gulp = require('gulp');
var replace = require('gulp-replace');
function makeid() {
return (Math.random() + 1).toString(36).substring(7);
}
gulp.task('versioningCss', () => {
return gulp.src('application/modules/**/views/*.php')
.pipe(replace(/(.*)\.css\?(_v=.+&)*(.*)/g, '$1.css?_v='+makeid()+'&$3'))
.pipe(replace(/(.*)\.css\"(.*)/g, '$1.css?_v='+makeid()+'"$2'))
.pipe(replace(/(.*)\.css\'(.*)/g, '$1.css?_v='+makeid()+'\'$2'))
.pipe(gulp.dest('application/modules'));
});
gulp.task('versioningJs', () => {
return gulp.src('application/modules/**/views/*.php')
.pipe(replace(/(.*)\.js\?(_v=.+&)*(.*)/g, '$1.js?_v='+makeid()+'&$3'))
.pipe(replace(/(.*)\.js\"(.*)/g, '$1.js?_v='+makeid()+'"$2'))
.pipe(replace(/(.*)\.js\'(.*)/g, '$1.js?_v='+makeid()+'\'$2'))
.pipe(gulp.dest('application/modules'));
});
gulp.task('versioningImage', () => {
return gulp.src('application/modules/**/views/*.php')
.pipe(replace(/(.*)\.(png|jpg|jpeg|gif)\?(_v=.+&)*(.*)/g, '$1.$2?_v='+makeid()+'&$4'))
.pipe(replace(/(.*)\.(png|jpg|jpeg|gif)\"(.*)/g, '$1.$2?_v='+makeid()+'"$3'))
.pipe(replace(/(.*)\.(png|jpg|jpeg|gif)\'(.*)/g, '$1.$2?_v='+makeid()+'\'$3'));
});
gulp.task('default', [ 'versioningCss', 'versioningJs', 'versioningImage']);
It looks like you may have quite a few options.
https://www.npmjs.com/package/gulp-cachebust
https://www.npmjs.com/package/gulp-buster
Hope this helps.
You can use
<script type="text/javascript" src="js/app.js?seq=<%=DateTime.Now.Ticks%>"></script>
or
<script type="text/javascript" src="js/app.js?seq=<%=DateTime.Now.ToString("yyyyMMddHHmm") %>"></script>

How to obfuscate client js file on http GET in browser in nodejs?

I have express application in nodejs:
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
app.use('/users', users);
My js files which go the browser are in /public folder. I want to obfuscate them automatically in runtime, so it'd be hard to read them in the browser. Is it possible to tune this app setup somehow?
Don't obfuscate at runtime. Create a build step which obfuscates your code and serve the obfuscated code in production.
Install UglifyJS:
npm install -g uglify-js
Then you can run the following command to obfuscate your code:
uglifyjs < src/script.js > dist/script.js
Alternatively, install grunt and the uglify plugin:
npm install -g grunt-cli
npm install --save-dev grunt grunt-contrib-uglify
Create a file called "Gruntfile" in the root of your project with the following contents:
'use strict';
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.initConfig({
uglify: {
all: {
files: {
'public/scripts.js': 'src/**/*.js'
}
}
}
});
grunt.registerTask('default', ['uglify']);
};
Run grunt from anywhere in your project to obfuscate your code. Update the index.html file in public/ with a <script src="scripts.js"></script> to load the obfuscated, concatenated scripts.
If order matters, list your scripts in order:
files: {
'public/scripts.js': [
'src/1.js',
'src/3.js',
'src/2.js'
]
}
So, you can't actually keep a user from reading your javascript when it's sent to the browser. You can make it very hard, but a determined user will always be able to transform it to something readable through a combination of automated tools and manual labor.
In short, there's no DRM for JavaScript (or any code, for that matter). If you want to protect the intellectual property in your scripts, put a copyright notice on them. If you think someone stole them, talk to a lawyer.
It's certainly worthwhile to minify your JavaScripts, but the only reason, in my opinion, to obfuscate them is because your stupid boss told you to.
If you want to obfuscate it, check out the answer to this question:
How can I obfuscate (protect) JavaScript?
Note that obfuscation is rather expensive, so you really don't want to do it at runtime. You should simply apply it to your javascripts when they change using a build tool like grunt or bower (personally I prefer grunt). These systems have plugins like this to perform the obfuscation.
You can use UglifyJs in your node application and do what you need using its API. Check out their documentation for further details.
Example from Unglify docs
var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify;
var orig_code = "... JS code here";
var ast = jsp.parse(orig_code); // parse code and get the initial AST
ast = pro.ast_mangle(ast); // get a new AST with mangled names
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
var final_code = pro.gen_code(ast); // compressed code here
P.S. People noting that it is slow and blah blah are of course all correct, but hey, if you need it here it is, I just don't understand why judge and post opinions in response to a clear question.
You can obfuscate the code using command line utility, such as this:
app.use('/javascripts',function(req,res,next){
if (process.env.OBFUSCATE != 1){
next();
}
var cmd = "java -jar ./gcc/compiler.jar ./public/javascripts" + req.url;// + "> ./tmp" + req.url + ".tmp";
exec(cmd,function (error, stdout, stderr){
res.write(stdout);
res.end();
if(error !== null){
console.log('exec error: ' + error);
}
});
});
My tips are easily bypassed, but without be careful, we can be trapped. And it is only client part, not nodejs part.
Only live view of the page
You can replace or remove script tag with javascript for hide this in live view of the page. But if you watch directly the network, you can see easily the javascript file/code.
<div id="RemoveMe0">
<script type="text/javascript">
//This code it is hidden to live view.
var my_var = 5 + 5;
$('#RemoveMe0').remove();
//or document.getElementById("RemoveMe0").innerHTML = "";
</script>
</div>
For include javascript :
<div id="RemoveMe1">
<script type="text/javascript" src="Javascript/MyJS.js"></script>
<script>
//Your include it is hidden to live view.
$('#RemoveMe1').remove();
</script>
</div>
Only direct view
Put your files in an HTML file (myfile.js to myfile.html), like this on a direct view you can execute a javascript function.
function Hello() {
alert("Hello");
}
Hello();
//<script>document.body.innerHTML = "";</script>
Conclusion :
For this tip, rename file or use .htaccess. And like first tip, if you watching the network you see full file.
Or minize/parse your JS
You can use tool like this :
minize your js : This tool use eval function, and try to complicate your script.
Javascript Obfuscator : Complicates the code for beginners, it's easy to by-pass.
Google Closure JS Compiler : Optimize, compress, and minify your code. It is a natural tool for the production environment.
I see you are using express and EJS. What I do is create another folder called jsobf that holds the obfucated files. Basically duplicate all your js files with the same name if you want.
In EJS you can do if else check in your ejs files like this :
<% if (process.env.NODE_ENV === "production") { %>
<script src="/jsobf/myjavascriptfile-prod.js"></script>
<% } else { %>
<script src="/js/myjavascriptfile-dev.js"></script>
<% } %>
At the top of the file where you do app.listen.. declare you a global variable:
const isProduction = true;
app.listen(PORT, function () {
if(isProduction){
process.env.NODE_ENV = 'production';
}
});
Then all you need to do before you deploy your code is manually obfuscate your code at a site like obfuscator.io
In your dev branch set the isProduction to false and in production branch to true. This way when you are developing everything is taken care of for you.
This may not be the cleanest solution but it works perfectly fine for me if you dont care to obfuscate manually before production deploys.

Include CDN sources in gulp-inject

I am trying to get CDN and other HTTP resources into a script that is modified by gulp-inject.
I created a corresponding issue at the repository.
The gist is that I would like something along these lines to work:
var sources = [
"http://cdnjs.cloudflare.com/ajax/libs/jasmine/1.3.1/jasmine.js",
"./spec/test.js"
]
gulp.task('source', function () {
gulp.src("src/my.html")
.pipe(inject(sources))
.dest("dest/")
})
With that result being the following included in dest/my.html:
<script src='http://cdnjs.cloudflare.com/ajax/libs/jasmine/1.3.1/jasmine.js'>
</script>
<script src='/spec/test.js'></script>
Anyone have any thoughts?
I wrote a plugin, gulp-cdnizer, specifically to help with this situation.
It's designed to allow you to keep all your CDN sources local during development, then replace the local path with a CDN path when you build your distribution.
Basically, you install your vendor scripts using bower or just copy-and-paste, and inject them into your HTML using the local path. Then, pipe the results of gulp-inject into gulp-cdnizer, and it will replace the local paths with the CDN path.
gulp.task('source', function () {
return gulp.src("src/my.html")
.pipe(inject(sources)) // only local sources
.pipe(cdnizer([
{
package: 'jasmine',
file: 'bower_components/jasmine/jasmine.js',
cdn: 'http://cdnjs.cloudflare.com/ajax/libs/jasmine/${version}/jasmine.js'
}
])
.dest("dest/")
});
I like doing it this way a lot better, because you can still develop offline. The cdnizer library can also handle local fallbacks, which makes sure your page still works if the CDN fails (for whatever reason).
I used gulp-replace for a similar use case:
html:
<!-- replace:google-places -->
gulp:
return gulp.src(path.join(conf.paths.src, '/*.html'))
.pipe($.inject(injectStyles, injectOptions))
.pipe($.inject(injectScripts, injectOptions))
.pipe($.replace('<!-- replace:google-places -->', replacePlaces)) // <-- gulp-replace
.pipe(wiredep(_.extend({}, conf.wiredep)))
.pipe(gulp.dest(path.join(conf.paths.tmp, '/serve')));
replacePlaces:
const replacePlaces = match => {
switch (process.env.NODE_ENV){
case 'dev':
return '<script src="https://maps.googleapis.com/maps/api/js....."></script>';
case 'production':
return '<script src="https://maps.googleapis.com/maps/api/js......"></script>';
default:
return match;
}
}

Categories

Resources