UNPKG

17.6 kBMarkdownView Raw
1# Jenkins JS Builder
2
3> __[JIRA](https://issues.jenkins-ci.org/browse/JENKINS/component/21132)__
4
5__Table of Contents__:
6<p>
7<ul>
8 <a href="#overview">Overview</a><br/>
9 <a href="#features">Features</a><br/>
10 <a href="#install">Install</a><br/>
11 <a href="#general-usage">General Usage</a><br/>
12 <a href="#predefined-gulp-tasks">Predefined Gulp Tasks</a><br/>
13 <a href="#redefining-one-of-the-predefined-gulp-tasks">Redefining one of the predefined Gulp tasks</a><br/>
14 <a href="#bundling-options">Bundling Options</a><br/>
15 <a href="#setting-src-and-test-spec-paths">Setting 'src' and 'test' (spec) paths</a><br/>
16 <a href="#command-line-options">Command line options</a><br/>
17 <a href="#maven-integration">Maven Integration</a><br/>
18 <a href="https://github.com/jenkinsci/js-samples">Sample Plugins (Jenkins - HPI)</a><br/>
19 <a href="https://issues.jenkins-ci.org/browse/JENKINS/component/21132">JIRA</a><br/>
20 <a href="#cli">CLI</a><br/>
21 <a href="#without-a-gulpfilejs">Without a `gulpfile.js`</a><br/>
22 <a href="#managing-bundle-size">Managing bundle size</a><br/>
23</ul>
24</p>
25
26<hr/>
27
28# Overview
29[NPM] utility for building [CommonJS] module [bundle]s (and optionally making them __[js-modules]__ compatible).
30
31> See __[js-modules]__.
32
33The following diagram illustrates the basic flow (and components used) in the process of building a [CommonJS] module [bundle].
34It uses a number of popular JavaScript and maven tools ([CommonJS]/[node.js], [Browserify], [Gulp], [frontend-maven-plugin] and more).
35
36<p align="center">
37 <a href="https://github.com/jenkinsci/js-modules" target="_blank">
38 <img src="res/build_workflow.png" alt="Jenkins Module Bundle Build Workflow">
39 </a>
40</p>
41
42The responsibilities of the components in the above diagram can be summarized as follows:
43
44* __[CommonJS]__: JavaScript module system (i.e. the expected format of JavaScript modules). This module system works with the nice/clean synchronous `require` syntax synonymous with [node.js] (for module loading) e.g. `var mathUtil = require('../util/mathUtil');`. This allows us to tap into the huge [NPM] JavaScript ecosystem.
45* __[Browserify]__: A build time utility ([NPM] package - executed as a [Gulp] "task") for "bundling" a graph of [CommonJS] style modules together, producing a single JavaScript file ([bundle]) that can be loaded (from a single request) in a browser. [Browserify] ensures that the `require` calls (see above) resolve properly to the correct module within the [bundle].
46* __[Gulp]__: A JavaScript build system ([NPM] package), analogous to what Maven is for Java i.e. executes "tasks" that eventually produce build artifacts. In this case, a JavaScript __[bundle]__ is produced via [Gulp]s execution of a [Browserify] "task".
47* __[frontend-maven-plugin]__: A Maven plugin that allows us to hook a [Gulp] "build" into a maven build e.g. for a Jenkins plugin. See <a href="#maven-integration">Maven Integration</a> below.
48
49# Features
50`js-builder` does a number of things:
51
521. Runs [Jasmine] tests/specs and produce a JUnit report that can be picked up by a top level Maven build.
531. Uses [Browserify] to produce a [CommonJS] module __[bundle]__ file from a "main" [CommonJS] module (see the `bundle` task below). The [bundle] file is typically placed somewhere on the filesystem that allows a higher level Maven build to pick it up and include it in e.g. a Jenkins plugin HPI file (so it can be loaded by the browser at runtime).
541. Pre-process a [LESS] fileset to a `.css` file that can be picked up by the top level Maven build and included in the e.g. a Jenkins plugin HPI file. See the Bundling Options section below.
55
56# Install
57
58```
59npm install --save-dev @jenkins-cd/js-builder
60```
61
62> This assumes you have [node.js] (minimum v4.0.0) installed on your local development environment.
63
64> Note this is only required if you intend developing [js-modules] compatible module bundles. Plugins using this should automatically handle all build aspects via maven (see later) i.e. __simple building of a plugin should require no machine level setup__.
65
66> Note you can also install `js-builder` globally and use it's <a href="#cli">CLI</a>.
67
68# General Usage
69
70Add a `gulpfile.js` (see [Gulp]) in the same folder as the `package.json`. Then use `js-builder` as follows:
71
72```javascript
73var builder = require('@jenkins-cd/js-builder');
74
75builder.bundle('./src/main/js/myappbundle.js');
76
77```
78
79After running the the `gulp` command from the command line, you will see an output something like the following.
80
81```
82[17:16:33] Javascript bundle "myappbundle" will be available in Jenkins as adjunct "org.jenkins.ui.jsmodules.myappbundle".
83```
84
85Or if run from a maven project where the `artifactId` is (e.g.) `jenkins-xyz-plugin`.
86
87```
88[17:16:33] Javascript bundle "myappbundle" will be available in Jenkins as adjunct "org.jenkins.ui.jsmodules.jenkins_xyz_plugin.myappbundle".
89```
90
91From this, you can deduce that the easiest way of using this JavaScript bundle in Jenkins is via the `<st:adjunct>` jelly tag.
92
93```xml
94<st:adjunct includes="org.jenkins.ui.jsmodules.jenkins_xyz_plugin.myappbundle"/>
95```
96
97The best place to learn how to use this utility as part of building Jenkins plugins is to see the
98[Sample Plugins](https://github.com/jenkinsci/js-samples) repository.
99
100# Predefined Gulp Tasks
101
102The following sections describe the available predefined [Gulp] tasks.
103
104> __Note__: If no task is specified (i.e. you just type `gulp` on its own), then the `bundle` and `test` tasks are auto-installed (i.e. auto-run) as the default tasks.
105
106## 'bundle' Task
107Run the 'bundle' task. See detail on this in the <a href="#bundling">dedicated section titled "Bundling"</a> (below).
108
109```
110gulp bundle
111```
112
113## 'test' Task
114
115Run tests. The default location for tests is the `spec` folder. The file names need to match the
116pattern "*-spec.js". The default location can be overridden by calling `builder.tests(<new-path>)`.
117
118```
119gulp test
120```
121
122> See [jenkins-js-test] for more on testing.
123> See <a href="#command-line-options">command line options</a> for `--skipTest` option.
124> See <a href="#command-line-options">command line options</a> for `--test` option (for running a single test spec).
125
126## 'bundle:watch' Task
127
128Watch module source files (`index.js`, `./lib/**/*.js` and `./lib/**/*.hbs`) for change, auto-running the
129`bundle` task whenever changes are detected.
130
131Note that this task will not be run by default, so you need to specify it explicitly on the gulp command in
132order to run it e.g.
133
134```
135gulp bundle:watch
136```
137
138## 'test:watch' Task
139
140Watch module source files changes (including test code) and rerun the tests e.g.
141
142```
143gulp test:watch
144```
145
146## 'lint' Task
147
148Run linting - ESLint or JSHint. ESlint is the default if no `.eslintrc` or `.jshintrc` file is found
149(using [eslint-config-jenkins](https://www.npmjs.com/package/@jenkins-cd/eslint-config-jenkins)) in the working
150directory (`.eslintrc` is also searched for in parent directories).
151
152```
153gulp lint
154```
155
156> See <a href="#command-line-options">command line options</a> for `--skipLint`, `--continueOnLint` and `--fixLint` options.
157
158# Redefining one of the predefined Gulp tasks
159
160There are times when you need to break out and redefine one of the predefined gulp tasks (see previous section).
161To redefine a task, you simply call `defineTask` again e.g. to redefine the `test` task to use mocha:
162
163```javascript
164builder.defineTask('test', function() {
165 var mocha = require('gulp-mocha');
166 var babel = require('babel-core/register');
167
168 builder.gulp.src('src/test/js/*-spec.js')
169 .pipe(mocha({
170 compilers: {js: babel}
171 })).on('error', function(e) {
172 if (builder.isRetest()) {
173 // ignore test failures if we are running test:watch.
174 return;
175 }
176 throw e;
177 });
178});
179```
180
181# Bundling Options
182
183The following sections outline some options that can be specified on a `bundle` instance.
184
185## Bundling CSS and LESS
186
187Note that bundling of __CSS__ or __LESS__ is also supported through a similar syntax e.g.
188
189```javascript
190builder.bundle('src/main/css/bootstrap336/bootstrap.css');
191```
192
193Or via [LESS]:
194
195```javascript
196builder.bundle('src/main/css/bootstrap336/bootstrap_tweaked.less');
197```
198
199The above commands will add all resources under `src/main/css/bootstrap336` to the plugin classpath, making
200them available as adjuncts e.g. using the bundled `bootstrap.css` referenced above would be as simple as
201adding the following to the relevant `.jelly` file (check the build output for the correct adjunct):
202
203```xml
204<st:adjunct includes="org.jenkins.ui.jsmodules.bootstrap336.bootstrap"/>
205```
206
207## Generating a bundle to a specific directory
208
209By default, the bundle command will output the bundle to the `target/classes/org/jenkins/ui/jsmodules`, making
210the bundle loadable in Jenkins as an adjunct. See the <a href="#general-usage">General Usage</a> section earlier
211in this document.
212
213Outputting the generated bundle to somewhere else is just a matter of specifying it on the `bundle` instance
214via the `inDir` function e.g.
215
216```javascript
217bundleSpec.inDir('<path-to-dir>');
218```
219
220## Minify bundle JavaScript
221
222This can be done by calling `minify` on `js-builder`:
223
224```javascript
225bundleSpec.minify();
226```
227
228Or, by passing `--minify` on the command line. This will result in the minification of all generated bundles.
229
230```sh
231$ gulp --minify
232```
233
234## onPreBundle listeners
235
236There are times when you will need access to the underlying [Browserify] `bundler` just before the
237bundling process is executed (e.g. for adding transforms etc).
238
239To do this, you call the `onPreBundle` function. This function takes a `listener` function as an argument.
240This `listener` function, when called, receives the `bundle` as `this` and the `bundler` as the only argument to
241the supplied `listener`.
242
243```javascript
244var builder = require('@jenkins-cd/js-builder');
245
246builder.onPreBundle(function(bundler) {
247 var bundle = this;
248
249 console.log('Adding the funky transform to bundler for bundle: ' + bundle.as);
250 bundler.transform(myFunkyTransform);
251});
252```
253
254# Setting 'src' and 'test' (spec) paths
255The default paths depend on whether or not running in a maven project.
256
257For a maven project, the default source and test/spec paths are:
258
259* __src__: `./src/main/js` and `./src/main/less` (used primarily by the `bundle:watch` task, watching these folders for source changes)
260* __test__: `./src/test/js` (used by the `test` task)
261
262Otherwise, they are:
263
264* __src__: `./js` and `./less` (used primarily by the `bundle:watch` task, watching these folders for source changes)
265* __test__: `./spec` (used by the `test` task)
266
267
268
269Changing these defaults is done through the `builder` instance e.g.:
270
271```javascript
272var builder = require('@jenkins-cd/js-builder');
273
274builder.src('src/main/js');
275builder.tests('src/test/js');
276```
277
278You can also specify an array of `src` folders e.g.
279
280```javascript
281builder.src(['src/main/js', 'src/main/less']);
282```
283
284# Command line options
285
286A number of `js-builder` options can be specified on the command line. If you are looking for
287
288
289## `--h` (or `--help`)
290
291Get a link to this documentation.
292
293```sh
294$ gulp --h
295```
296
297## `--minify`
298
299Passing `--minify` on the command line will result in the minification of all generated bundles.
300
301```sh
302$ gulp --minify
303```
304
305## `--test`
306
307Run a single test.
308
309```sh
310$ gulp --test configeditor
311```
312
313The above example would run test specs matching the `**/configeditor*-spec.js` pattern (in the test source directory).
314
315## Skip options: `--skipTest`, `--skipLint`, `--skipBundle`
316
317Skip one or more of the tasks/phases e.g.
318
319```sh
320$ gulp --skipTest --skipLint
321```
322
323## Lint options: `--skipLint`, `--continueOnLint`, `--fixLint`
324
325Many of the more irritating formatting rule errors/warnings can be fixed automatically by running
326with the `--fixLint` option, making them a little less irritating e.g.
327
328```sh
329$ gulp --fixLint
330```
331
332Or if you are just running the `lint` task on it's own (explicitly):
333
334```sh
335$ gulp lint --fixLint
336```
337
338Alternatively, if you wish to run `lint` and see all of the lint errors, but not fail the build:
339
340```sh
341$ gulp --continueOnLint
342```
343
344And to skip linting completely:
345
346```sh
347$ gulp --skipLint
348```
349
350# Maven Integration
351Hooking a [Gulp] based build into a Maven build involves adding a few Maven `<profile>`s to the
352Maven project's `pom.xml`. For Jenkins plugins, the easiest way to get this integration is to simply
353have the plugin `pom.xml` depend on the Jenkins [plugin-pom]. For other project types, you'll need
354to copy those profiles locally (see [plugin-pom]).
355
356These integrations hook the [Gulp] build into the maven build lifecycles. A few `mvn` build
357switches are supported, as described in the following sections.
358
359## `-DcleanNode`
360
361Cleans out the local node and NPM artifacts and resource (including the `node_modules` folder).
362
363```
364$ mvn clean -DcleanNode
365```
366
367## `-DskipTests`
368
369This switch is a standard `mvn` switch and is honoured by the profiles defined in the [plugin-pom].
370
371```
372$ mvn clean -DskipTests
373```
374
375`-DskipTests` also skips linting. See `-DskipLint`
376
377## `-DskipLint`
378
379Skip linting.
380
381```
382$ mvn clean -DskipLint
383```
384
385# CLI
386
387You can install `js-builder` globally and then use it as command-line tool.
388
389```
390sudo npm install -g @jenkins-cd/js-builder
391```
392
393Once installed, you do not need a globally installed [Gulp] in order to run `js-builder` builds. Instead, you can execute build tasks via
394the `jjsbuilder` command (or aliases `jjsbuild` and `jjsb`).
395
396E.g. to run a full build, simply run `jjsbuilder` with no args (equivalent to running `gulp` with no args).
397
398```
399jjsbuilder
400```
401
402To run specific tasks e.g. `test` and `lint`.
403
404```
405jjsbuilder --tasks test,lint
406```
407
408To run specific tasks and specify a switch e.g. the `lint` task with the `--fixList` switch.
409
410```
411jjsbuilder --tasks lint --fixLint
412```
413
414So as you can see, the CLI operates more or less the same as when executing via `gulp`.
415
416# Without a `gulpfile.js`
417
418The introduction of the <a href="#cli">CLI</a> has allowed us to remove the `gulpfile.js` in a lot of situation
419because the <a href="#cli">CLI</a> has it's own built in `gulpfile.js`.
420
421If your `gulpfile.js` is as simple as:
422
423```javascript
424var builder = require('@jenkins-cd/js-builder');
425
426//
427// Create a bundle.
428// See https://github.com/jenkinsci/js-builder
429//
430builder.bundle('src/main/js/rollbar.js');
431```
432
433This `gulpfile.js` can be deleted and the bundling command moved to the `jenkinscd.bundle` section of the `package.json` e.g.
434
435```
436{
437
438 "jenkinscd": {
439 "bundle": [
440 "src/main/js/rollbar.js"
441 ]
442 }
443}
444```
445
446Then you can add `scripts` to the `package.json` for executing build tasks. This allows the tasks to be executed without
447installing `js-builder` globally (which you would not want to require for e.g. a CI/CD build agent) e.g from a
448`package.json` generated by [`generator-blueocean-usain`](https://www.npmjs.com/package/generator-blueocean-usain).
449
450```
451{
452
453 "scripts": {
454 "build": "jjsbuilder",
455 "bundle": "jjsbuilder --tasks bundle",
456 "test": "jjsbuilder --tasks test",
457 "lint": "jjsbuilder --tasks lint",
458 "lint:fix": "jjsbuilder --tasks lint --fixLint",
459 "bundle:watch": "jjsbuilder --tasks bundle:watch",
460 "mvnbuild": "jjsbuilder --tasks bundle",
461 "mvntest": "jjsbuilder --tasks test,lint"
462 }
463}
464```
465
466# Managing bundle size
467
468Generated JavaScript bundles can become very big very fast if you don't manage them properly. This is a constant
469challenge and something you need to keep an eye on. We constantly do battle against this problem on [Blue Ocean].
470
471Once it has been identified that a bundle has become very large, then you need to analyse the bundle to find out
472what modules/packages are causing the bloat and if there's an opportunity to [externalize] modules/packages that are
473in use across a number of bundles.
474
475> Note: [browserify-tree](https://www.npmjs.com/package/browserify-tree) is a tool that we developed to help analyse [Browserify] generated bundles.
476> Note: [disc] is another useful tool for analysing Browserify generated bundles. [See this Jenkins Dev list thread](https://groups.google.com/forum/?hl=en#!searchin/jenkinsci-dev/bundle$20size%7Csort:relevance/jenkinsci-dev/UknJzFso3y8/jbqwZINtBAAJ) for some details on how we've used it in the past.
477
478[bundle]: https://github.com/jenkinsci/js-modules/blob/master/FAQs.md#what-is-the-difference-between-a-module-and-a-bundle
479[js-modules]: https://github.com/jenkinsci/js-modules
480[js-builder]: https://github.com/jenkinsci/js-builder
481[jenkins-js-test]: https://github.com/jenkinsci/js-test
482[NPM]: https://www.npmjs.com/
483[CommonJS]: http://www.commonjs.org/
484[node.js]: https://nodejs.org/en/
485[Browserify]: http://browserify.org/
486[Gulp]: http://gulpjs.com/
487[frontend-maven-plugin]: https://github.com/eirslett/frontend-maven-plugin
488[intra-bundle]: https://github.com/jenkinsci/js-modules/blob/master/FAQs.md#what-does-module-loading-mean
489[inter-bundle]: https://github.com/jenkinsci/js-modules/blob/master/FAQs.md#what-does-module-loading-mean
490[io.js]: https://iojs.org
491[Framework lib]: https://github.com/jenkinsci/js-libs
492[LESS]: http://lesscss.org/
493[Handlebars]: http://handlebarsjs.com/
494[Jasmine]: http://jasmine.github.io/
495[Moment.js]: http://momentjs.com/
496[plugin-pom]: https://github.com/jenkinsci/plugin-pom
497[externalize]: https://github.com/jenkinsci/js-samples/blob/master/step-04-externalize-libs/HOW-IT-WORKS.md#configure-node-build-to-externalize-dependencies
498[Blue Ocean]: https://github.com/jenkinsci/blueocean-plugin
499[disc]: https://github.com/hughsk/disc/