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 |
|
33 | The following diagram illustrates the basic flow (and components used) in the process of building a [CommonJS] module [bundle].
|
34 | It 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 |
|
42 | The 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 |
|
52 | 1. Runs [Jasmine] tests/specs and produce a JUnit report that can be picked up by a top level Maven build.
|
53 | 1. 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).
|
54 | 1. 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 | ```
|
59 | npm 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 |
|
70 | Add a `gulpfile.js` (see [Gulp]) in the same folder as the `package.json`. Then use `js-builder` as follows:
|
71 |
|
72 | ```javascript
|
73 | var builder = require('@jenkins-cd/js-builder');
|
74 |
|
75 | builder.bundle('./src/main/js/myappbundle.js');
|
76 |
|
77 | ```
|
78 |
|
79 | After 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 |
|
85 | Or 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 |
|
91 | From 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 |
|
97 | The 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 |
|
102 | The 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
|
107 | Run the 'bundle' task. See detail on this in the <a href="#bundling">dedicated section titled "Bundling"</a> (below).
|
108 |
|
109 | ```
|
110 | gulp bundle
|
111 | ```
|
112 |
|
113 | ## 'test' Task
|
114 |
|
115 | Run tests. The default location for tests is the `spec` folder. The file names need to match the
|
116 | pattern "*-spec.js". The default location can be overridden by calling `builder.tests(<new-path>)`.
|
117 |
|
118 | ```
|
119 | gulp 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 |
|
128 | Watch module source files (`index.js`, `./lib/**/*.js` and `./lib/**/*.hbs`) for change, auto-running the
|
129 | `bundle` task whenever changes are detected.
|
130 |
|
131 | Note that this task will not be run by default, so you need to specify it explicitly on the gulp command in
|
132 | order to run it e.g.
|
133 |
|
134 | ```
|
135 | gulp bundle:watch
|
136 | ```
|
137 |
|
138 | ## 'test:watch' Task
|
139 |
|
140 | Watch module source files changes (including test code) and rerun the tests e.g.
|
141 |
|
142 | ```
|
143 | gulp test:watch
|
144 | ```
|
145 |
|
146 | ## 'lint' Task
|
147 |
|
148 | Run 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
|
150 | directory (`.eslintrc` is also searched for in parent directories).
|
151 |
|
152 | ```
|
153 | gulp 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 |
|
160 | There are times when you need to break out and redefine one of the predefined gulp tasks (see previous section).
|
161 | To redefine a task, you simply call `defineTask` again e.g. to redefine the `test` task to use mocha:
|
162 |
|
163 | ```javascript
|
164 | builder.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 |
|
183 | The following sections outline some options that can be specified on a `bundle` instance.
|
184 |
|
185 | ## Bundling CSS and LESS
|
186 |
|
187 | Note that bundling of __CSS__ or __LESS__ is also supported through a similar syntax e.g.
|
188 |
|
189 | ```javascript
|
190 | builder.bundle('src/main/css/bootstrap336/bootstrap.css');
|
191 | ```
|
192 |
|
193 | Or via [LESS]:
|
194 |
|
195 | ```javascript
|
196 | builder.bundle('src/main/css/bootstrap336/bootstrap_tweaked.less');
|
197 | ```
|
198 |
|
199 | The above commands will add all resources under `src/main/css/bootstrap336` to the plugin classpath, making
|
200 | them available as adjuncts e.g. using the bundled `bootstrap.css` referenced above would be as simple as
|
201 | adding 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 |
|
209 | By default, the bundle command will output the bundle to the `target/classes/org/jenkins/ui/jsmodules`, making
|
210 | the bundle loadable in Jenkins as an adjunct. See the <a href="#general-usage">General Usage</a> section earlier
|
211 | in this document.
|
212 |
|
213 | Outputting the generated bundle to somewhere else is just a matter of specifying it on the `bundle` instance
|
214 | via the `inDir` function e.g.
|
215 |
|
216 | ```javascript
|
217 | bundleSpec.inDir('<path-to-dir>');
|
218 | ```
|
219 |
|
220 | ## Minify bundle JavaScript
|
221 |
|
222 | This can be done by calling `minify` on `js-builder`:
|
223 |
|
224 | ```javascript
|
225 | bundleSpec.minify();
|
226 | ```
|
227 |
|
228 | Or, 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 |
|
236 | There are times when you will need access to the underlying [Browserify] `bundler` just before the
|
237 | bundling process is executed (e.g. for adding transforms etc).
|
238 |
|
239 | To do this, you call the `onPreBundle` function. This function takes a `listener` function as an argument.
|
240 | This `listener` function, when called, receives the `bundle` as `this` and the `bundler` as the only argument to
|
241 | the supplied `listener`.
|
242 |
|
243 | ```javascript
|
244 | var builder = require('@jenkins-cd/js-builder');
|
245 |
|
246 | builder.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
|
255 | The default paths depend on whether or not running in a maven project.
|
256 |
|
257 | For 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 |
|
262 | Otherwise, 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 |
|
269 | Changing these defaults is done through the `builder` instance e.g.:
|
270 |
|
271 | ```javascript
|
272 | var builder = require('@jenkins-cd/js-builder');
|
273 |
|
274 | builder.src('src/main/js');
|
275 | builder.tests('src/test/js');
|
276 | ```
|
277 |
|
278 | You can also specify an array of `src` folders e.g.
|
279 |
|
280 | ```javascript
|
281 | builder.src(['src/main/js', 'src/main/less']);
|
282 | ```
|
283 |
|
284 | # Command line options
|
285 |
|
286 | A number of `js-builder` options can be specified on the command line. If you are looking for
|
287 |
|
288 |
|
289 | ## `--h` (or `--help`)
|
290 |
|
291 | Get a link to this documentation.
|
292 |
|
293 | ```sh
|
294 | $ gulp --h
|
295 | ```
|
296 |
|
297 | ## `--minify`
|
298 |
|
299 | Passing `--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 |
|
307 | Run a single test.
|
308 |
|
309 | ```sh
|
310 | $ gulp --test configeditor
|
311 | ```
|
312 |
|
313 | The 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 |
|
317 | Skip 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 |
|
325 | Many of the more irritating formatting rule errors/warnings can be fixed automatically by running
|
326 | with the `--fixLint` option, making them a little less irritating e.g.
|
327 |
|
328 | ```sh
|
329 | $ gulp --fixLint
|
330 | ```
|
331 |
|
332 | Or if you are just running the `lint` task on it's own (explicitly):
|
333 |
|
334 | ```sh
|
335 | $ gulp lint --fixLint
|
336 | ```
|
337 |
|
338 | Alternatively, 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 |
|
344 | And to skip linting completely:
|
345 |
|
346 | ```sh
|
347 | $ gulp --skipLint
|
348 | ```
|
349 |
|
350 | # Maven Integration
|
351 | Hooking a [Gulp] based build into a Maven build involves adding a few Maven `<profile>`s to the
|
352 | Maven project's `pom.xml`. For Jenkins plugins, the easiest way to get this integration is to simply
|
353 | have the plugin `pom.xml` depend on the Jenkins [plugin-pom]. For other project types, you'll need
|
354 | to copy those profiles locally (see [plugin-pom]).
|
355 |
|
356 | These integrations hook the [Gulp] build into the maven build lifecycles. A few `mvn` build
|
357 | switches are supported, as described in the following sections.
|
358 |
|
359 | ## `-DcleanNode`
|
360 |
|
361 | Cleans 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 |
|
369 | This 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 |
|
379 | Skip linting.
|
380 |
|
381 | ```
|
382 | $ mvn clean -DskipLint
|
383 | ```
|
384 |
|
385 | # CLI
|
386 |
|
387 | You can install `js-builder` globally and then use it as command-line tool.
|
388 |
|
389 | ```
|
390 | sudo npm install -g @jenkins-cd/js-builder
|
391 | ```
|
392 |
|
393 | Once installed, you do not need a globally installed [Gulp] in order to run `js-builder` builds. Instead, you can execute build tasks via
|
394 | the `jjsbuilder` command (or aliases `jjsbuild` and `jjsb`).
|
395 |
|
396 | E.g. to run a full build, simply run `jjsbuilder` with no args (equivalent to running `gulp` with no args).
|
397 |
|
398 | ```
|
399 | jjsbuilder
|
400 | ```
|
401 |
|
402 | To run specific tasks e.g. `test` and `lint`.
|
403 |
|
404 | ```
|
405 | jjsbuilder --tasks test,lint
|
406 | ```
|
407 |
|
408 | To run specific tasks and specify a switch e.g. the `lint` task with the `--fixList` switch.
|
409 |
|
410 | ```
|
411 | jjsbuilder --tasks lint --fixLint
|
412 | ```
|
413 |
|
414 | So as you can see, the CLI operates more or less the same as when executing via `gulp`.
|
415 |
|
416 | # Without a `gulpfile.js`
|
417 |
|
418 | The introduction of the <a href="#cli">CLI</a> has allowed us to remove the `gulpfile.js` in a lot of situation
|
419 | because the <a href="#cli">CLI</a> has it's own built in `gulpfile.js`.
|
420 |
|
421 | If your `gulpfile.js` is as simple as:
|
422 |
|
423 | ```javascript
|
424 | var builder = require('@jenkins-cd/js-builder');
|
425 |
|
426 | //
|
427 | // Create a bundle.
|
428 | // See https://github.com/jenkinsci/js-builder
|
429 | //
|
430 | builder.bundle('src/main/js/rollbar.js');
|
431 | ```
|
432 |
|
433 | This `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 |
|
446 | Then you can add `scripts` to the `package.json` for executing build tasks. This allows the tasks to be executed without
|
447 | installing `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 |
|
468 | Generated JavaScript bundles can become very big very fast if you don't manage them properly. This is a constant
|
469 | challenge and something you need to keep an eye on. We constantly do battle against this problem on [Blue Ocean].
|
470 |
|
471 | Once it has been identified that a bundle has become very large, then you need to analyse the bundle to find out
|
472 | what modules/packages are causing the bloat and if there's an opportunity to [externalize] modules/packages that are
|
473 | in 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/
|