UNPKG

21.8 kBMarkdownView Raw
1# cdnizer
2
3[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Support via Gratipay][gratipay-image]][gratipay-url]
4
5This library will replace local file references in HTML and other files with CDN locations. This allows you to work with local copies of libraries during development, and then automate switching to your CDN version when you deploy your application.
6
7For example, if you have a development file that looks like this:
8
9```html
10<html>
11<head>
12<script type="text/javascript" src="bower_components/angular/angular.js"></script>
13
14```
15
16You can use cdnizer to automatically convert it to this during your build process (*every* change here can be customized):
17
18```js
19<html>
20<head>
21<script>
22function cdnizerLoad(u) {
23 document.write('<scr'+'ipt src="'+encodeURIComponent(u)+'"></scr'+'ipt>';
24}
25</script>
26<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
27<script>if(!(angular)) cdnizerLoad("bower_components/angular/angular.js");</script>
28
29```
30
31If you want to use this library during a [gulp](http://gulpjs.com/) build, [gulp-cdnizer](https://npmjs.org/package/gulp-cdnizer) has you covered.
32
33
34### Features:
35* It's flexible without being overly complicated.
36* Handles private *and* multiple public CDNs in the same build.
37* It can use your Bower installation to determine the correct file versions—no more getting "upgraded" during your build.
38* Provides optional fallback scripts for failed file loading. (By default it can only handle failed JavaScript files, but it's easy to provide a custom solution.)
39
40
41### New in version 1.0
42
43cdnizer now can load CDN data from existing `*-cdn-data` packages, currently `google-cdn-data`, `cdnjs-cdn-data`, and `jsdelivr-cdn-data`. Now you can [configure common public CDNs with a single line](#optionsfilescommon-cdn)!
44
45
46### Possible breaking change in 1.0
47
48The `version` field has been changed in this release. Previously, it was the exact version as it existing within Bower. Now, `version` is the string `major(.minor)?(.patch)?`, with any trailing (`-beta*`, `-snapshot*`, etc) information removed. (Alpha-numeric characters that are attached to the version string, as in `1.0.0rc1`, are not stripped.)
49
50You can still access the full version string via `versionFull`, which is not modified at all.
51
52
53
54## Index
55
56* [Usage](#usage)
57* [API](#api)
58* [Support This Project](#help-support-this-project)
59* [License](#license)
60
61
62
63## Usage
64
65First, install `cdnizer`:
66
67```shell
68npm install --save cdnizer
69```
70
71Then, use it like so:
72
73```javascript
74var cdnizerFactory = require("cdnizer"),
75 cdnizer = cdnizerFactory({
76 defaultCDNBase: "//my.cdn.host/base",
77 allowRev: true,
78 allowMin: true,
79 files: [
80
81 // This file is on the default CDN, and will replaced with //my.cdn.host/base/js/app.js
82 'js/app.js',
83
84 // On Google's public CDN
85 {
86 file: 'vendor/angular/angular.js',
87 package: 'angular',
88 test: 'window.angular',
89 cdn: '//ajax.googleapis.com/ajax/libs/angularjs/${ version }/angular.min.js'
90 },
91
92 // On Firebase's public CDN
93 {
94 file: 'vendor/firebase/firebase.js',
95 test: 'window.Firebase',
96 cdn: '//cdn.firebase.com/v0/firebase.js'
97 }
98 ]
99 });
100
101// Load the file
102var contents = fs.readFileSync('./src/index.html', 'utf8');
103
104// Replace the file's contents
105contents = cdnizer(contents);
106```
107
108Alternatively, you can just pass in the files array if you don't need to provide any options, and only have custom files:
109
110```js
111var cdnizerFactory = require("cdnizer"),
112 cdnizer = cdnizerFactory([
113 {
114 file: 'vendor/angular/angular.js',
115 package: 'angular',
116 test: 'window.angular',
117 // use alternate providers easily
118 cdn: '//cdnjs.cloudflare.com/ajax/libs/angularjs/${ version }/angular.min.js'
119 },
120 {
121 file: 'vendor/firebase/firebase.js',
122 test: 'window.Firebase',
123 cdn: '//cdn.firebase.com/v0/firebase.js'
124 }
125 ]);
126```
127
128You can also use globs to define groups of file, and dynamic filename properties:
129
130```js
131var cdnizerFactory = require("cdnizer"),
132 cdnizer = cdnizerFactory([{
133 file: 'vendor/angular/*.js',
134 package: 'angular',
135 test: 'window.angular',
136 cdn: '//ajax.googleapis.com/ajax/libs/angularjs/${ version }/${ filenameMin }'
137 }]);
138```
139
140Works great on `url()`s in CSS files, too:
141
142```js
143var cdnizerFactory = require("cdnizer"),
144 cdnizer = cdnizerFactory({
145 defaultCDNBase: '//my.cdn.url/',
146 relativeRoot: 'css',
147 files: ['**/*.{gif,png,jpg,jpeg}']
148 });
149
150var cssFile = fs.readFileSync('./css/style.css', 'utf8');
151cssFile = cdnizer(cssFile);
152```
153
154**New in v1.0**, you can use simplified strings for common public CDNs, like so:
155
156```js
157var cdnizerFactory = require("cdnizer"),
158 cdnizer = cdnizerFactory([
159 'google:angular', // for most libraries, that's all you'll need to do!
160 'cdnjs:jquery',
161 {
162 cdn: 'jsdelivr:yui', // You can also use a known CDN, while…
163 package: 'yui3', // overriding the package name for Bower, and…
164 test: 'window.YUI' // providing a custom fallback test
165 },
166 // you can also specify alternate files within a package:
167 'jsdelivr:yui:anim-base/anim-base-min.js@3.17.2'
168 ]);
169```
170
171Need multiple files from the one package on a common CDN? Here's two solutions:
172
173```js
174// Manually list every file…
175var cdnizerFactory = require("cdnizer"),
176 cdnizer = cdnizerFactory([
177 'cdnjs:angular.js:angular.min.js',
178 'cdnjs:angular.js:angular-touch.min.js',
179 'cdnjs:angular.js:i18n/angular-locale_fr-fr.js'
180 ]);
181
182// Or wire up a pattern:
183var cdnizerFactory = require("cdnizer"),
184 cdnizer = cdnizerFactory([
185 // matches all root angular files
186 {
187 file: '**/angular/*.js',
188 cdn: 'cdnjs:angular.js:${ filenameMin }' // Yes, you can apply patterns to the filename!
189 },
190 // matches all i18n angular files
191 {
192 file: '**/angular/i18n/*.js',
193 cdn: 'cdnjs:angular.js:i18n/${ filename }' // These i18n files are not minified
194 }
195 ]);
196```
197
198
199
200## API
201
202* [cdnizer( options | files )](#cdnizer-options--files-)
203* Shared Options
204 * [defaultCDNBase](#optionsdefaultcdnbase)
205 * [defaultCDN](#optionsdefaultcdn)
206 * [relativeRoot](#optionsrelativeroot)
207 * [allowRev](#optionsallowrev)
208 * [allowMin](#optionsallowmin)
209 * [fallbackScript](#optionsfallbackscript)
210 * [fallbackTest](#optionsfallbacktest)
211 * [bowerComponents](#optionsbowercomponents)
212 * [matchers](#optionsmatchers)
213 * [cdnDataLibraries](#optionscdndatalibraries)
214* [Files Array](#optionsfiles)
215 * [Type: glob](#optionsfilesglob)
216 * [Type: common public cdn](#optionsfilescommon-cdn)
217 * [Type: custom hashmap](#optionsfileshashmap)
218 * [file](#optionsfilesfile)
219 * [package](#optionsfilespackage)
220 * [cdn](#optionsfilescdn)
221 * [test](#optionsfilestest)
222
223
224### cdnizer( options | files )
225
226Creates a new cdnizer function that can be used to process file contents. You can either pass in a configuration object, or you can pass in an array of files if you don't need to change the default shared options.
227
228See [Usage](#usage) above for examples.
229
230
231### Options
232
233#### options.defaultCDNBase
234
235Type: `String`
236Default: `''` *(disabled)*
237
238Used for a default, custom CDN, usually for your own files. This will be used in the defaultCDN property to define the default path for a CDN unless overridden.
239
240#### options.defaultCDN
241
242Type: `String`
243Default: `'${ defaultCDNBase }/${ filepathRel }'`
244
245This is the default pattern used for generating CDN links when one isn't provided by a specific file.
246
247#### options.relativeRoot
248
249Type: `String`
250Default: `''`
251
252If you are processing a file that references relative files, or is not rooted to the CDN, you **must** set `relativeRoot` to get correct results. This relative path will be appended to the file path and the path resolved to remove relative URLs.
253
254For example, if you have a CSS file at `style/main.css`, and you reference images as `../img/foo.png`, you should set `relativeRoot` to `'style/'`. Now if your `defaultCDNBase` is `//example/`, the image will be resolved to `//example/img/foo.png`.
255
256If you do *not* set `relativeRoot` when referencing relative files, your files will *not* match, and they will *not* be replaced with CDN URLs.
257
258#### options.allowRev
259Type: `Boolean`
260Default: `true`
261
262Allow for file names with `gulp-rev` appended strings, in the form of `<file>-XXXXXXXX.<ext>`. If you are using the `gulp-rev` plugin, this will automatically match filenames that have a rev string appeneded to them. If you are *not* using `gulp-rev`, then you can disable this by setting `allowRev` to `false`, which will prevent possible errors in accidentally matching similar file names.
263
264You can always manually configure your globs to include an optional rev string by using the form `?(<rev glob here>)`, such as `name?(-????????).ext` for appended revs.
265
266#### options.allowMin
267
268Type: `Boolean`
269Default: `true`
270
271Allow for file names that optionally have `.min` inserted before the file extension (but after rev, if enabled). This allows you to use the base name for a file, and have `cndizer` match the minified name.
272
273#### options.fallbackScript
274
275Type: `String`
276Default:
277
278```html
279<script>
280function cdnizerLoad(u) {
281 document.write('<scr'+'ipt src="'+encodeURIComponent(u)+'"></scr'+'ipt>';
282}
283</script>
284```
285
286Overwrite the default fallback script. If any of the inputs has a fallback, this script is injected before the first occurrence of `<link`, `<script`, or `</head>` in the HTML page. Ignored for files that don't contain `<head`.
287
288If you already have a script loader (such as yepnope or Modernizr), you can set this to an empty string and override the `fallbackTest` below to use that instead. Of course, this won't help if you are loading *those* scripts off a CDN and they fail!
289
290#### options.fallbackTest
291
292Type: `String`
293Default: `'<script>if(!(${ test })) cdnizerLoad("${ filepath }");</script>'`
294
295Overwrite the default fallback test. Note that all options availble to `options.files[].cdn` below are available to this template as well, along with the `options.files[].test` string.
296
297#### options.bowerComponents
298
299Type: `String`
300Default: null
301
302If provided, this is the directory to look for Bower components in. If not provided, cdnizer will attempt to look for the `.bowerrc` file, and if that is not found or does not specify a directory, it falls back to `'./bower_components'`.
303
304Once the directory is determined, the script will look for files in `<bowerComponents>/bower.json` or `<bowerComponents>/.bower.json` to try to determine the version of the installed component.
305
306#### options.matchers
307
308Type: `Array`
309Default: `[]`
310
311Array of custom matchers. Use this to add extra patterns within which you would like to cdn-ize URLs, for example if you have such URLs in data-attributes. The matchers should include regular expressions with three matching groups:
312
3131. Leading characters
3142. The actual URL to work on, and
3153. Trailing characters, which should include the end tag if you want a fallback script injected.
316
317Example (matches the ```data-src``` attribute in ```<img>``` tags):<br />
318```js
319matchers: [
320 {
321 pattern: /(<img\s.*?data-src=["'])(.+?)(["'].*?>)/gi,
322 //groups: ( leading )(url)(trailing)
323 fallback: false
324 }
325]
326```
327
328You can also specify just a regular expression. In that case, fallback will default to false.
329
330Equivalent example:<br />
331```js
332matchers: [
333 /(<img\s.*?data-src=["'])(.+?)(["'].*?>)/gi
334]
335```
336
337#### options.cdnDataLibraries
338
339Type: `Array`
340Default: `[]`
341
342Future-proof option to add additional `*-cdn-data` packages. These packages *must* be in the same format as [`google-cdn-data`](https://www.npmjs.org/package/google-cdn-data). The format is to only include the leading part of the package name, for example, `cdnjs-cdn-data` would be included as simply `'cdnjs'`.
343
344
345### options.files
346
347Type: `Array`
348Default: (none) **required**
349
350Array of sources or objects defining sources to cdnize. Each item in the array can be one of three types, a simple glob, a public CDN string, or object hashmap.
351
352##### options.files.«glob»
353
354When using a glob, if it matches a source, the `defaultCDN` template is applied. Because there is no `test`, the script will not have a fallback.
355
356*Examples:*
357```js
358'**/*.js' // matches any .js file anywhere
359'js/**/*.js' // matches any *.js file under the js folder
360'styles/main.css' // only matches styles/main.css, possibly rev'd or min'd based on options
361'img/icon/foo??.{png,gif,jpg}' // img/icon/foo10.png, img/icon/fooAA.gif, etc
362```
363
364##### options.files.«common-cdn»
365
366Public CDN strings make it easy to add known libraries from common public CDN repositories. They always take the form of `'<provider>:<package>(:filename)?(@version)?'`. Currently, cdnizer has built-in support for [Google](https://www.npmjs.org/package/google-cdn-data), [cdnjs](https://www.npmjs.org/package/cdnjs-cdn-data), and [jsDelivr](https://www.npmjs.org/package/jsdelivr-cdn-data), via the existing packages maintained by [Shahar Talmi](https://www.npmjs.org/~shahata).
367
368*Examples:*
369
370```js
371'google:jquery' // Note that it's all lowercase
372'google:jquery@1.0.0' // provide a version if you are not using Bower OR to override it
373'google:angular'
374// this loads the angular-touch file from angular on cdnjs
375'cdnjs:angular.js:angular-touch.min.js' // you need `.js` for angular on cdnjs, but not on Google!
376'jsdelivr:angularjs' // jsdelivr has it different still
377```
378
379The result of these patterns follows the following process:
380
3811. Look up the package (e.g.: `angular`) within the correct CDN's `cdn-data`.
3822. Get the URL for the package with the version set to `${ version }`, which allows dynamic replacement of the version later
3833. If we are provided an alternate filename, replace everything in the URL after `${ version }/` with the new filename. This is how you can select alternate files on the CDN.
3844. Set the `cdn` property to the URL.
3855. Grab everything after `${ version }/`—this is assumed to be the filename—and set the `file` property (i.e.: the glob pattern) to `**/«filename»`.
3866. Set the `package` property either to a known package (some known packages are mapped to their Bower package name) or to the passed-in package name.
3877. If we know the correct fallback `test` for a package, use that. Otherwise, don't set a `test`.
3888. If a version was passed in, store that as well under the `version` property.
389
390You can also use a common cdn while customizing the result by [using a common CDN with the `cdn` option within a hashmap](#optionsfilescdn). You will need to do this if the CDN provider uses a different package name than Bower, or if you want to provide a fallback test (excluding a few popular libraries).
391
392> Note: you can use template strings in the `:filename` portion of the string. However, if you do this, you'll need to overwrite the `file` property by using a hashmap (below), or it will try to match the literal string.
393> For example, you can this will match any file directly under an `angular` directory, and replace it with the CDN equivalent:
394
395> ```js
396> {
397> file: '**/angular/*.js',
398> cdn: 'cdnjs:angular.js:${ filenameMin }'
399> }
400> ```
401
402> If this solution gets a little too "magic-y", you can always just revert to manually specifying the hashmap, which might be easier. The hashmap alternative looks like this:
403
404> ```js
405> {
406> file: '**/angular/*.js',
407> package: 'angular',
408> test: 'window.angular',
409> cdn: '//cdnjs.cloudflare.com/ajax/libs/angular.js/${ version }/${ filenameMin }'
410> }
411> ```
412
413*Important Notes:*
414
4151. **Case matters** with these strings. Make sure you are not capitalizing the packages (such as `jQuery`, instead of `jquery`), or the output will be incorrect.
416
4172. The packages may have different names on different public CDNs. Make sure you look up the package name on the CDN website first. For example, AngularJS is `angular` on Google, `angular.js` on cdnjs, and `angularjs` on jsDelivr!
418
4193. You **must** provide the version if you are not using Bower to manage your packages, or if you want to override the Bower version (not recommended).
420
4214. Due to the way versions are calculated, `-beta*`, `-unstable*`, and `-snapshot*` releases will not work with common CDNs. This means only projects with standard 1-, 2-, or 3-part version strings will work.
422
4235. The CDN packages available are limited to those included in the `cdn-data` packages, which means they might not always be up-to-date with the latest public packages. However, the version information is not used at all by cdnizer, which means you can update to the latest version faster.
424
425##### options.files.«hashmap»
426
427The object hashmap gives you full control, using the following properties:
428
429> ##### options.files[].file
430
431> Type: `String`
432> Default: (none) **required**
433
434> Glob to match against for the file to cdnize. All properties within this object will be applied to all files that match the glob. Globs are matched in a first-come, first-served basis, where only the first matched object hashmap is applied.
435
436> ##### options.files[].package
437
438> Type: `String`
439> Default: (none)
440
441> Bower package name for this source or set of sources. By providing the package name, cdnizer will look up the version string of the *currently installed* Bower package, and provide it as a property to the `cdn` string. This is done by looking for either the `bower.json` or `.bower.json` file within your Bower components directory.
442
443> The benefit of doing it this way is that the version used from the CDN *always* matches your local copy. It will never automatically be updated to a newer patch version without being tested.
444
445> ##### options.files[].cdn
446
447> Type: `String`
448> Default: `options.defaultCDN`
449
450> This it the template for the replacement string. It can either be a custom CDN string, or it can be a common public CDN string, using the same format as a [public CDN string](#optionsfilescommon-cdn) above.
451
452> *Common Public CDN String:*
453
454> Load in the default data for an existing common public CDN, using the format `'<provider>:<package>(:filename)?(@version)?'`. You can then customize the settings for the package, by overriding any property in this section (e.g.: providing a fallback `test`, a different `package` name, or even matching a different `file`).
455
456> *Custom CDN String:*
457
458> Provide a custom CDN string, which can be a simple static string, or contain one or more underscore/lodash template properties to be injected into the string:
459
460> * `versionFull`: if [`package`](#optionsfilespackage) was provided, this is the complete version currently installed version from Bower.
461> * `version`: if `package` was provided, this is the `major(.minor)?(.patch)?` version number, minus any trailing information (such as `-beta*` or `-snapshot*`).
462> * `major`: if `package` was provided, this is the major version number.
463> * `minor`: if `package` was provided, this is the minor version number.
464> * `patch`: if `package` was provided, this is the patch version number.
465> * `defaultBase`: the default base provided above. Note that this will *never* end with a trailing slash.
466> * `filepath`: the full path of the source, as it exists currently. There is no guarantee about the whether this contains a leading slash or not, so be careful.
467> * `filepathRel`: the relative path of the source, guaranteed to *never* have a leading slash. The path is also processed against `options.relativeRoot` above, to try and remove any parent directory path elements.
468> * `filename`: the name of the file, without any parent directories
469> * `filenameMin`: the name of the file, *without* any rev tags (if `allowRev` is true), but *with* a `.min` extension added. This won't add a min if there is one already.
470> * `package`: the Bower package name, as provided above.
471
472> ##### options.files[].test
473
474> Type: `String`
475> Default: (none)
476
477> If provided, this string will be evaluated within a javascript block. If the result is truthy, then we assume the CDN resource loaded properly. If it isn't, then the original local file will be loaded. This is ignored for files that don't get the [fallback script](#optionsfallbackscript).
478
479> This snippet will be inserted *exactly* as provided. If the package fails to load from the CDN, the global variable won't exist, so you need to check for it's existence on an *existing* global object. Usually this will be `window`, which you'll see through most of the examples here.
480
481> When using a common public CDN, some popular packages come with fallback tests. The current packages that have a built-in fallback test are:
482
483> * AngularJS
484> * Backbone.js
485> * Dojo
486> * EmberJS
487> * jQuery
488> * jQuery UI
489> * Lo-Dash
490> * MooTools
491> * Prototype
492> * React
493> * SwfObject
494> * Underscore
495
496> For any other packages, you'll need to provide the fallback test yourself.
497
498> See [`options.fallbackScript`](#optionsfallbackscript) and [`options.fallbackTest`](#optionsfallbacktest) for more information.
499
500
501
502## Help Support This Project
503
504If you'd like to support this and other OverZealous Creations (Phil DeJarnett) projects, [donate via Gratipay][gratipay-url]!
505
506[![Support via Gratipay][gratipay-image]][gratipay-url]
507
508You can learn a little more about me and some of the [work I do for open source projects in an article at CDNify.](https://cdnify.com/blog/overzealous-creations/)
509
510
511
512## License
513
514[MIT License](http://en.wikipedia.org/wiki/MIT_License)
515
516
517
518
519
520[npm-url]: https://npmjs.org/package/cdnizer
521[npm-image]: https://badge.fury.io/js/cdnizer.png
522
523[travis-url]: http://travis-ci.org/OverZealous/cdnizer
524[travis-image]: https://secure.travis-ci.org/OverZealous/cdnizer.png?branch=master
525
526[gratipay-url]: https://www.gratipay.com/OverZealous/
527[gratipay-image]: https://img.shields.io/gratipay/OverZealous.svg