optimize.ts 8.7 KB
Newer Older
I
isidor 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

J
Joao Moreno 已提交
6 7
'use strict';

8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
import * as path from 'path';
import * as gulp from 'gulp';
import * as sourcemaps from 'gulp-sourcemaps';
import * as filter from 'gulp-filter';
import * as minifyCSS from 'gulp-cssnano';
import * as uglify from 'gulp-uglify';
import * as es from 'event-stream';
import * as concat from 'gulp-concat';
import * as VinylFile from 'vinyl';
import * as bundle from './bundle';
import * as util from './util';
import * as i18n from './i18n';
import * as gulpUtil from 'gulp-util';
import * as flatmap from 'gulp-flatmap';
import * as pump from 'pump';
import * as sm from 'source-map';
24

25 26
const REPO_ROOT_PATH = path.join(__dirname, '../..');

27
function log(prefix:string, message:string): void {
28 29
	gulpUtil.log(gulpUtil.colors.cyan('[' + prefix + ']'), message);
}
D
Dirk Baeumer 已提交
30

31
export function loaderConfig(emptyPaths:string[]) {
J
Joao Moreno 已提交
32
	const result = {
I
isidor 已提交
33 34
		paths: {
			'vs': 'out-build/vs',
J
Joao Moreno 已提交
35
			'vscode': 'empty:'
I
isidor 已提交
36
		},
J
Joao Moreno 已提交
37
		nodeModules: emptyPaths||[]
I
isidor 已提交
38 39
	};

B
Benjamin Pasero 已提交
40 41
	result['vs/css'] = { inlineResources: true };

I
isidor 已提交
42
	return result;
43
}
I
isidor 已提交
44

J
Joao Moreno 已提交
45
const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i;
I
isidor 已提交
46

47 48 49 50 51
declare class FileSourceMap extends VinylFile {
	public sourceMap: sm.RawSourceMap;
}

function loader(bundledFileHeader:string, bundleLoader:boolean): NodeJS.ReadWriteStream {
A
Alex Dima 已提交
52 53 54 55 56 57 58 59 60 61
	let sources = [
		'out-build/vs/loader.js'
	];
	if (bundleLoader) {
		sources = sources.concat([
			'out-build/vs/css.js',
			'out-build/vs/nls.js'
		]);
	}

J
Joao Moreno 已提交
62
	let isFirst = true;
A
Alex Dima 已提交
63 64 65
	return (
		gulp
		.src(sources, { base: 'out-build' })
I
isidor 已提交
66 67 68
		.pipe(es.through(function(data) {
			if (isFirst) {
				isFirst = false;
69
				this.emit('data', new VinylFile({
I
isidor 已提交
70 71 72 73 74 75 76 77 78 79 80
					path: 'fake',
					base: '',
					contents: new Buffer(bundledFileHeader)
				}));
				this.emit('data', data);
			} else {
				this.emit('data', data);
			}
		}))
		.pipe(util.loadSourcemaps())
		.pipe(concat('vs/loader.js'))
81
		.pipe(es.mapSync<FileSourceMap,FileSourceMap>(function (f) {
82
			f.sourceMap.sourceRoot = util.toFileUri(path.join(REPO_ROOT_PATH, 'src'));
I
isidor 已提交
83
			return f;
A
Alex Dima 已提交
84 85
		}))
	);
I
isidor 已提交
86 87
}

88
function toConcatStream(bundledFileHeader:string, sources:bundle.IFile[], dest:string): NodeJS.ReadWriteStream {
J
Joao Moreno 已提交
89
	const useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest);
I
isidor 已提交
90 91 92

	// If a bundle ends up including in any of the sources our copyright, then
	// insert a fake source at the beginning of each bundle with our copyright
J
Joao Moreno 已提交
93 94 95
	let containsOurCopyright = false;
	for (let i = 0, len = sources.length; i < len; i++) {
		const fileContents = sources[i].contents;
I
isidor 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108
		if (IS_OUR_COPYRIGHT_REGEXP.test(fileContents)) {
			containsOurCopyright = true;
			break;
		}
	}

	if (containsOurCopyright) {
		sources.unshift({
			path: null,
			contents: bundledFileHeader
		});
	}

J
Joao Moreno 已提交
109
	const treatedSources = sources.map(function(source) {
110
		const root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : '';
J
Joao Moreno 已提交
111
		const base = source.path ? root + '/out-build' : '';
I
isidor 已提交
112

113
		return new VinylFile({
I
isidor 已提交
114 115 116 117 118 119 120 121 122 123 124
			path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake',
			base: base,
			contents: new Buffer(source.contents)
		});
	});

	return es.readArray(treatedSources)
		.pipe(useSourcemaps ? util.loadSourcemaps() : es.through())
		.pipe(concat(dest));
}

125
function toBundleStream(bundledFileHeader:string, bundles:bundle.IConcatFile[]): NodeJS.ReadWriteStream {
I
isidor 已提交
126 127 128 129 130
	return es.merge(bundles.map(function(bundle) {
		return toConcatStream(bundledFileHeader, bundle.sources, bundle.dest);
	}));
}

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
export interface IOptimizeTaskOpts {
	/**
	 * (for AMD files, will get bundled and get Copyright treatment)
	 */
	entryPoints: bundle.IEntryPoint[];
	/**
	 * (for non-AMD files that should get Copyright treatment)
	 */
	otherSources: string[];
	/**
	 * (svg, etc.)
	 */
	resources: string[];
	loaderConfig: any;
	/**
	 * (true by default - append css and nls to loader)
	 */
	bundleLoader?: boolean;
	/**
	 * (basically the Copyright treatment)
	 */
	header: string;
	/**
	 * (emit bundleInfo.json file)
	 */
	bundleInfo: boolean;
	/**
	 * (out folder name)
	 */
	out: string;
}
export function optimizeTask(opts:IOptimizeTaskOpts):()=>NodeJS.ReadWriteStream {
J
Joao Moreno 已提交
163 164 165 166 167
	const entryPoints = opts.entryPoints;
	const otherSources = opts.otherSources;
	const resources = opts.resources;
	const loaderConfig = opts.loaderConfig;
	const bundledFileHeader = opts.header;
A
Alex Dima 已提交
168
	const bundleLoader = (typeof opts.bundleLoader === 'undefined' ? true : opts.bundleLoader);
J
Joao Moreno 已提交
169
	const out = opts.out;
I
isidor 已提交
170 171

	return function() {
J
Joao Moreno 已提交
172 173 174
		const bundlesStream = es.through(); // this stream will contain the bundled files
		const resourcesStream = es.through(); // this stream will contain the resources
		const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json
I
isidor 已提交
175 176 177 178

		bundle.bundle(entryPoints, loaderConfig, function(err, result) {
			if (err) { return bundlesStream.emit('error', JSON.stringify(err)); }

179 180 181
			toBundleStream(bundledFileHeader, result.files).pipe(bundlesStream);

			// Remove css inlined resources
J
Joao Moreno 已提交
182
			const filteredResources = resources.slice();
183
			result.cssInlinedResources.forEach(function(resource) {
J
Joao Moreno 已提交
184 185 186
				if (process.env['VSCODE_BUILD_VERBOSE']) {
					log('optimizer', 'excluding inlined: ' + resource);
				}
187 188 189
				filteredResources.push('!' + resource);
			});
			gulp.src(filteredResources, { base: 'out-build' }).pipe(resourcesStream);
190

191
			const bundleInfoArray:VinylFile[] = [];
192
			if (opts.bundleInfo) {
193
				bundleInfoArray.push(new VinylFile({
194 195 196 197 198 199
					path: 'bundleInfo.json',
					base: '.',
					contents: new Buffer(JSON.stringify(result.bundleData, null, '\t'))
				}));
			}
			es.readArray(bundleInfoArray).pipe(bundleInfoStream);
I
isidor 已提交
200 201
		});

J
Joao Moreno 已提交
202
		const otherSourcesStream = es.through();
203
		const otherSourcesStreamArr:NodeJS.ReadWriteStream[] = [];
I
isidor 已提交
204 205 206 207 208 209 210 211 212 213 214 215

		gulp.src(otherSources, { base: 'out-build' })
			.pipe(es.through(function (data) {
				otherSourcesStreamArr.push(toConcatStream(bundledFileHeader, [data], data.relative));
			}, function () {
				if (!otherSourcesStreamArr.length) {
					setTimeout(function () { otherSourcesStream.emit('end'); }, 0);
				} else {
					es.merge(otherSourcesStreamArr).pipe(otherSourcesStream);
				}
			}));

J
Joao Moreno 已提交
216
		const result = es.merge(
A
Alex Dima 已提交
217
			loader(bundledFileHeader, bundleLoader),
I
isidor 已提交
218 219
			bundlesStream,
			otherSourcesStream,
220 221
			resourcesStream,
			bundleInfoStream
I
isidor 已提交
222 223 224 225 226 227 228 229
		);

		return result
			.pipe(sourcemaps.write('./', {
				sourceRoot: null,
				addComment: true,
				includeContent: true
			}))
A
Alex Dima 已提交
230 231 232
			.pipe(i18n.processNlsFiles({
				fileHeader: bundledFileHeader
			}))
I
isidor 已提交
233 234 235 236
			.pipe(gulp.dest(out));
	};
};

237 238 239
declare class FileWithCopyright extends VinylFile {
	public __hasOurCopyright: boolean;
}
I
isidor 已提交
240
/**
J
Joao Moreno 已提交
241 242
 * Wrap around uglify and allow the preserveComments function
 * to have a file "context" to include our copyright only once per file.
I
isidor 已提交
243
 */
244 245 246 247 248
function uglifyWithCopyrights():NodeJS.ReadWriteStream {
	const preserveComments = (f:FileWithCopyright) => {
		return (node, comment:{value:string;type:string;}) => {
			const text = comment.value;
			const type = comment.type;
I
isidor 已提交
249

250 251 252
			if (/@minifier_do_not_preserve/.test(text)) {
				return false;
			}
I
isidor 已提交
253

254
			const isOurCopyright = IS_OUR_COPYRIGHT_REGEXP.test(text);
I
isidor 已提交
255

256 257 258 259 260 261
			if (isOurCopyright) {
				if (f.__hasOurCopyright) {
					return false;
				}
				f.__hasOurCopyright = true;
				return true;
I
isidor 已提交
262 263
			}

264 265 266 267 268 269 270 271
			if ('comment2' === type) {
				// check for /*!. Note that text doesn't contain leading /*
				return (text.length > 0 && text[0] === '!') || /@preserve|license|@cc_on|copyright/i.test(text);
			} else if ('comment1' === type) {
				return /license|copyright/i.test(text);
			}
			return false;
		};
I
isidor 已提交
272 273
	};

274 275 276 277
	const input = es.through();
	const output = input
		.pipe(flatmap((stream, f) => {
			return stream
278
				.pipe(uglify({ preserveComments: preserveComments(<FileWithCopyright>f) }));
279
		}));
I
isidor 已提交
280

281
	return es.duplex(input, output);
I
isidor 已提交
282 283
}

284
export function minifyTask(src:string, sourceMapBaseUrl:string):(cb:any)=>void {
J
Joao Moreno 已提交
285 286
	const sourceMappingURL = sourceMapBaseUrl && (f => `${ sourceMapBaseUrl }/${ f.relative }.map`);

287
	return cb => {
J
Joao Moreno 已提交
288 289
		const jsFilter = filter('**/*.js', { restore: true });
		const cssFilter = filter('**/*.css', { restore: true });
I
isidor 已提交
290

291 292 293 294 295 296 297 298 299 300
		pump(
			gulp.src([src + '/**', '!' + src + '/**/*.map']),
			jsFilter,
			sourcemaps.init({ loadMaps: true }),
			uglifyWithCopyrights(),
			jsFilter.restore,
			cssFilter,
			minifyCSS({ reduceIdents: false }),
			cssFilter.restore,
			sourcemaps.write('./', {
J
Joao Moreno 已提交
301
				sourceMappingURL,
I
isidor 已提交
302 303
				sourceRoot: null,
				includeContent: true,
J
Joao Moreno 已提交
304
				addComment: true
305 306
			}),
			gulp.dest(src + '-min')
307
		, (err:any) => {
308 309 310 311 312 313
			if (err instanceof uglify.GulpUglifyError) {
				console.error(`Uglify error in '${ err.cause && err.cause.filename }'`);
			}

			cb(err);
		});
I
isidor 已提交
314
	};
315
};