1 | var gulp = require('gulp');
|
2 | var fs = require('fs');
|
3 | var main = require('../index');
|
4 | var langConfig = require('./langConfig');
|
5 | var dependencies = require('./dependecies');
|
6 | var paths = require('./paths');
|
7 | var maven = require('./maven');
|
8 | var logger = require('./logger');
|
9 | var args = require('./args');
|
10 | var notifier = require('./notifier');
|
11 | var browserify = require('browserify');
|
12 | var source = require('vinyl-source-stream');
|
13 | var transformTools = require('browserify-transform-tools');
|
14 | var _string = require('underscore.string');
|
15 | var templates = require('./templates');
|
16 | var ModuleSpec = require('@jenkins-cd/js-modules/js/ModuleSpec');
|
17 | var entryModuleTemplate = templates.getTemplate('entry-module.hbs');
|
18 |
|
19 | var hasJenkinsJsModulesDependency = dependencies.hasJenkinsJsModulesDep();
|
20 | var preBundleListeners = [];
|
21 | var postBundleListeners = [];
|
22 | var globalImportMappings = [];
|
23 | var globalExportMappings = [];
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | exports.onPreBundle = function(listener) {
|
34 | preBundleListeners.push(listener);
|
35 | };
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | exports.onPostBundle = function(listener) {
|
46 | postBundleListeners.push(listener);
|
47 | };
|
48 |
|
49 | exports.addGlobalImportMapping = function(mapping) {
|
50 | globalImportMappings.push(mapping);
|
51 | };
|
52 |
|
53 | exports.addGlobalExportMapping = function(mapping) {
|
54 | globalExportMappings.push(mapping);
|
55 | };
|
56 |
|
57 | exports.doJSBundle = function(bundle, applyImports) {
|
58 | if (!bundle.bundleInDir) {
|
59 | var adjunctBase = setAdjunctInDir(bundle);
|
60 | logger.logInfo('Javascript bundle "' + bundle.as + '" will be available in Jenkins as adjunct "' + adjunctBase + '.' + bundle.as + '".')
|
61 | }
|
62 |
|
63 |
|
64 | if (!bundle.globalModuleMappingsApplied) {
|
65 | if (bundle.useGlobalImportMappings === true) {
|
66 | for (var i = 0; i < globalImportMappings.length; i++) {
|
67 | bundle._import(globalImportMappings[i]);
|
68 | }
|
69 | }
|
70 | if (bundle.useGlobalExportMappings === true) {
|
71 | if (main.bundleCount() > 1 && globalExportMappings.length > 0) {
|
72 | logger.logError('Unable to apply bundle dependency "export" configurations from package.json because there are multiple bundles being generated.');
|
73 | logger.logError(' (exporting the same package from multiple bundles is not permitted)');
|
74 | logger.logError(' TIP: From inside gulpfile.js, call bundle.export([package-name]) directly on the bundle performing the export.');
|
75 | } else {
|
76 | for (var i = 0; i < globalExportMappings.length; i++) {
|
77 | bundle.export(globalExportMappings[i]);
|
78 | }
|
79 | }
|
80 | }
|
81 | bundle.globalModuleMappingsApplied = true;
|
82 | }
|
83 |
|
84 | var bundleTo = bundle.bundleInDir;
|
85 |
|
86 | if (!applyImports) {
|
87 | bundleTo += '/no_imports';
|
88 | }
|
89 |
|
90 |
|
91 |
|
92 | if (applyImports && bundle.lessSrcPath) {
|
93 | var lessBundleTo = bundleTo;
|
94 |
|
95 | if (bundle.lessTargetDir) {
|
96 | lessBundleTo = bundle.lessTargetDir;
|
97 | }
|
98 |
|
99 | less(bundle.lessSrcPath, lessBundleTo);
|
100 | }
|
101 |
|
102 | var fileToBundle = bundle.bundleModule;
|
103 | if (bundle.bundleDependencyModule) {
|
104 |
|
105 | if (!fs.existsSync('target')) {
|
106 | fs.mkdirSync('target');
|
107 | }
|
108 | fileToBundle = 'target/' + bundle.bundleOutputFile;
|
109 | fs.writeFileSync(fileToBundle, "module.exports = require('" + bundle.module + "');");
|
110 | }
|
111 |
|
112 | var browserifyConfig = {
|
113 | entries: [fileToBundle],
|
114 | extensions: ['.js', '.es6', '.jsx', '.hbs'],
|
115 | cache: {},
|
116 | packageCache: {},
|
117 | fullPaths: true
|
118 | };
|
119 |
|
120 | if (bundle.minifyBundle === true) {
|
121 | browserifyConfig.debug = true;
|
122 | }
|
123 | var bundler = browserify(browserifyConfig);
|
124 |
|
125 | var hasJSX = paths.hasSourceFiles('jsx');
|
126 | var hasES6 = paths.hasSourceFiles('es6');
|
127 | var hasBabelRc = fs.existsSync('.babelrc');
|
128 |
|
129 | if (langConfig.ecmaVersion === 6 || hasJSX || hasES6 || hasBabelRc) {
|
130 | var babelify = require('babelify');
|
131 | var presets = [];
|
132 | var plugins = [];
|
133 |
|
134 | if (hasBabelRc) {
|
135 | logger.logInfo("Will use babel config from .babelrc");
|
136 | }
|
137 | else if (hasJSX) {
|
138 | presets.push('react');
|
139 | dependencies.warnOnMissingDependency('babel-preset-react', 'You have JSX sources in this project. Transpiling these will require the "babel-preset-react" package.');
|
140 | presets.push('es2015');
|
141 | dependencies.warnOnMissingDependency('babel-preset-es2015', 'You have JSX/ES6 sources in this project. Transpiling these will require the "babel-preset-es2015" package.');
|
142 | } else {
|
143 | presets.push('es2015');
|
144 | dependencies.warnOnMissingDependency('babel-preset-es2015', 'You have ES6 sources in this project. Transpiling these will require the "babel-preset-es2015" package.');
|
145 | }
|
146 |
|
147 | var babelConfig = {};
|
148 |
|
149 |
|
150 | if (!hasBabelRc) {
|
151 | babelConfig.presets = presets;
|
152 | babelConfig.plugins = plugins;
|
153 | }
|
154 |
|
155 |
|
156 | bundler.transform(babelify, babelConfig);
|
157 | }
|
158 |
|
159 | if (bundle.bundleTransforms) {
|
160 | for (var i = 0; i < bundle.bundleTransforms.length; i++) {
|
161 | bundler.transform(bundle.bundleTransforms[i]);
|
162 | }
|
163 | }
|
164 |
|
165 | if (applyImports) {
|
166 | addModuleMappingTransforms(bundle, bundler);
|
167 | }
|
168 |
|
169 | if (bundle.minifyBundle === true) {
|
170 | var sourceMap = bundle.as + '.map.json';
|
171 | bundler.plugin('minifyify', {
|
172 | map: sourceMap,
|
173 | output: bundleTo + '/' + sourceMap
|
174 | });
|
175 | }
|
176 |
|
177 | for (var i = 0; i < preBundleListeners.length; i++) {
|
178 | preBundleListeners[i].call(bundle, bundler);
|
179 | }
|
180 |
|
181 |
|
182 | bundler.transform(require('brfs'));
|
183 |
|
184 | var bundleOutput = bundler.bundle()
|
185 | .on('error', function (err) {
|
186 | logger.logError('Browserify bundle processing error');
|
187 | if (err) {
|
188 | logger.logError('\terror: ' + err.stack);
|
189 | }
|
190 | if (main.isRebundle() || main.isRetest()) {
|
191 | notifier.notify('bundle:watch failure', 'See console for details.');
|
192 |
|
193 | this.emit('end');
|
194 | } else {
|
195 | throw new Error('Browserify bundle processing error. See above for details.');
|
196 | }
|
197 | });
|
198 |
|
199 | if (applyImports) {
|
200 | var bufferedTextTransform = require('./pipeline-transforms/buffered-text-accumulator-transform');
|
201 | var requireStubTransform = require('./pipeline-transforms/require-stub-transform');
|
202 | var pack = require('browser-pack');
|
203 |
|
204 | bundleOutput = bundleOutput.pipe(bufferedTextTransform())
|
205 | .pipe(requireStubTransform.pipelinePlugin(bundle.moduleMappings))
|
206 | .pipe(pack());
|
207 | }
|
208 |
|
209 | var through = require('through2');
|
210 | var bundleOutFile = bundleTo + '/' + bundle.bundleOutputFile;
|
211 | return bundleOutput.pipe(source(bundle.bundleOutputFile))
|
212 | .pipe(gulp.dest(bundleTo))
|
213 | .pipe(through.obj(function (bundle, encoding, callback) {
|
214 | for (var i = 0; i < postBundleListeners.length; i++) {
|
215 | postBundleListeners[i].call(bundle, bundleOutFile);
|
216 | }
|
217 | callback();
|
218 | }));
|
219 | };
|
220 |
|
221 | exports.doCSSBundle = function(bundle, resource) {
|
222 | var ncp = require('ncp').ncp;
|
223 | var folder = paths.parentDir(resource);
|
224 |
|
225 | if (!bundle.bundleInDir) {
|
226 | var adjunctBase = setAdjunctInDir(bundle);
|
227 | logger.logInfo('CSS resource "' + resource + '" will be available in Jenkins as adjunct "' + adjunctBase + '.' + bundle.as + '".')
|
228 | }
|
229 |
|
230 | paths.mkdirp(bundle.bundleInDir);
|
231 | ncp(folder, bundle.bundleInDir, function (err) {
|
232 | if (err) {
|
233 | return logger.logError(err);
|
234 | }
|
235 | if (bundle.format === 'less') {
|
236 | less(resource, bundle.bundleInDir);
|
237 | }
|
238 |
|
239 | paths.walkDirs(bundle.bundleInDir, function(dir) {
|
240 | var dotAdjunct = dir + '/.adjunct';
|
241 | if (!fs.existsSync(dotAdjunct)) {
|
242 | fs.writeFileSync(dotAdjunct, '');
|
243 | }
|
244 | });
|
245 | });
|
246 | };
|
247 |
|
248 | function less(src, targetDir) {
|
249 | var less = require('gulp-less');
|
250 | gulp.src(src)
|
251 | .pipe(less().on('error', function (err) {
|
252 | logger.logError('LESS processing error:');
|
253 | if (err) {
|
254 | logger.logError('\tmessage: ' + err.message);
|
255 | logger.logError('\tline #: ' + err.line);
|
256 | if (err.extract) {
|
257 | logger.logError('\textract: ' + JSON.stringify(err.extract));
|
258 | }
|
259 | }
|
260 | if (main.isRebundle() || main.isRetest()) {
|
261 | notifier.notify('LESS processing error', 'See console for details.');
|
262 |
|
263 | this.emit('end');
|
264 | } else {
|
265 | throw new Error('LESS processing error. See above for details.');
|
266 | }
|
267 | }))
|
268 | .pipe(gulp.dest(targetDir));
|
269 | logger.logInfo("LESS CSS pre-processing completed to '" + targetDir + "'.");
|
270 | }
|
271 |
|
272 | function addModuleMappingTransforms(bundle, bundler) {
|
273 | var moduleMappings = bundle.moduleMappings;
|
274 | var requiredModuleMappings = [];
|
275 |
|
276 | if (moduleMappings.length > 0) {
|
277 | var requireSearch = transformTools.makeStringTransform("requireSearch", {},
|
278 | function(content, opts, cb) {
|
279 | for (var i = 0; i < moduleMappings.length; i++) {
|
280 | var mapping = moduleMappings[i];
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 | if (content.indexOf(mapping.fromSpec.moduleName) !== -1) {
|
288 | var toSpec = new ModuleSpec(mapping.to);
|
289 | var importAs = toSpec.importAs();
|
290 | if (requiredModuleMappings.indexOf(importAs) === -1) {
|
291 | requiredModuleMappings.push(importAs);
|
292 | }
|
293 | }
|
294 | }
|
295 | return cb(null, content);
|
296 | });
|
297 | bundler.transform({ global: true }, requireSearch);
|
298 | }
|
299 | var importExportApplied = false;
|
300 | var importExportTransform = transformTools.makeStringTransform("importExportTransform", {},
|
301 | function (content, opts, done) {
|
302 | if (!importExportApplied) {
|
303 | try {
|
304 | if(!hasJenkinsJsModulesDependency) {
|
305 | throw new Error("This module must have a dependency on the '@jenkins-cd/js-modules' package. Please run 'npm install --save @jenkins-cd/js-modules'.");
|
306 | }
|
307 |
|
308 | var exportNamespace = 'undefined';
|
309 | var exportModule = undefined;
|
310 |
|
311 | if (bundle.exportEmptyModule) {
|
312 | exportModule = '{}';
|
313 | }
|
314 |
|
315 | if (bundle.bundleExportNamespace) {
|
316 |
|
317 | exportNamespace = "'" + bundle.bundleExportNamespace + "'";
|
318 | }
|
319 | if (bundle.bundleExport) {
|
320 |
|
321 | exportModule = 'module';
|
322 | }
|
323 |
|
324 | var templateParams = {
|
325 | bundle: bundle,
|
326 | content: content,
|
327 | css: []
|
328 | };
|
329 |
|
330 | if(exportModule) {
|
331 |
|
332 |
|
333 |
|
334 | templateParams.entryExport = {
|
335 | namespace: exportNamespace,
|
336 | module: exportModule
|
337 | };
|
338 | }
|
339 |
|
340 | templateParams.dependencyExports = expandDependencyExports(bundle.moduleExports);
|
341 |
|
342 |
|
343 |
|
344 |
|
345 | for (var i = 0; i < moduleMappings.length; i++) {
|
346 | var mapping = moduleMappings[i];
|
347 | var addDefaultCSS = mapping.config.addDefaultCSS;
|
348 | if (addDefaultCSS && addDefaultCSS === true) {
|
349 | var parsedModuleQName = new ModuleSpec(mapping.to);
|
350 | templateParams.css.push(parsedModuleQName);
|
351 | }
|
352 | }
|
353 |
|
354 | var wrappedContent = entryModuleTemplate(templateParams);
|
355 |
|
356 | return done(null, wrappedContent);
|
357 | } finally {
|
358 | importExportApplied = true;
|
359 | }
|
360 | } else {
|
361 | return done(null, content);
|
362 | }
|
363 | });
|
364 |
|
365 | bundler.transform(importExportTransform);
|
366 |
|
367 | var through = require('through2');
|
368 | bundler.pipeline.get('deps').push(through.obj(function (row, enc, next) {
|
369 | if (row.entry) {
|
370 | row.source = "var ___$$$___requiredModuleMappings = " + JSON.stringify(requiredModuleMappings) + ";\n\n" + row.source;
|
371 | }
|
372 | this.push(row);
|
373 | next();
|
374 | }));
|
375 | }
|
376 |
|
377 | function expandDependencyExports(bundleExports) {
|
378 | if (!bundleExports || bundleExports.length === 0) {
|
379 | return undefined;
|
380 | }
|
381 |
|
382 | var dependencyExports = [];
|
383 | for (var i in bundleExports) {
|
384 | var packageName = bundleExports[i];
|
385 | dependencyExports.push(dependencies.externalizedVersionMetadata(packageName));
|
386 | }
|
387 |
|
388 | return dependencyExports;
|
389 | }
|
390 |
|
391 | function setAdjunctInDir(bundle) {
|
392 | var adjunctBase = 'org/jenkins/ui/jsmodules';
|
393 | if (bundle.bundleExportNamespace) {
|
394 | if (maven.isMavenProject && bundle.bundleExportNamespace === maven.getArtifactId()) {
|
395 | adjunctBase += '/' + maven.getArtifactId();
|
396 | } else {
|
397 | adjunctBase += '/' + normalizeForFilenameUse(bundle.bundleExportNamespace);
|
398 | }
|
399 | } else if (maven.isMavenProject) {
|
400 | adjunctBase += '/' + maven.getArtifactId();
|
401 | }
|
402 | bundle.bundleInDir = 'target/classes/' + adjunctBase;
|
403 | return _string.replaceAll(adjunctBase, '/', '\.');
|
404 | }
|
405 |
|
406 | function normalizeForFilenameUse(string) {
|
407 |
|
408 | return string.replace(/\W/g, '-');
|
409 | } |
\ | No newline at end of file |