├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── gulp-minify.d.ts
├── index.js
├── package.json
└── test
├── fixtures
├── demo1
│ └── index.js
├── demo2
│ └── index.js
├── demo3
│ └── index.js
├── demo4
│ ├── exclude
│ │ └── test.js
│ ├── ignore.js
│ └── index.js
├── demo5
│ ├── exclude
│ │ └── test.js
│ ├── ignore.js
│ └── index.js
└── demo6
│ ├── index.js
│ └── index.mjs
└── minifier.test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | package-lock.json
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - "6"
5 | - "7"
6 | - "8"
7 | - "9"
8 | - "10"
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018, 淘小杰
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 |
6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gulp-minify
2 |
3 | > Minify JavaScript with terser.
4 |
5 | [](https://travis-ci.org/hustxiaoc/gulp-minify)
6 | [](http://badge.fury.io/js/gulp-minify)
7 |
8 | ## Note
9 |
10 | The latest version of `gulp-minify` is using [terser](https://www.npmjs.com/package/terser) to minify files, this may cause some incompatible issues with earlier language versions for now, see https://github.com/hustxiaoc/gulp-minify/issues/27.
11 |
12 | So `gulp-minify@es5` is for the earlier language versions if your project is not ready for the ECMAScript 6 yet.
13 |
14 | ## Installation
15 |
16 | Install package with NPM and add it to your development dependencies:
17 |
18 | `npm install --save-dev gulp-minify`
19 |
20 | ## Usage
21 |
22 | _Basic usage_: the following minifies every `*.js` and `*.mjs` files to `*-min.js` and `*-min.mjs` respectively. Note that the original files are preserved.
23 |
24 | ```javascript
25 | const minify = require('gulp-minify');
26 |
27 | gulp.task('compress', function() {
28 | gulp.src(['lib/*.js', 'lib/*.mjs'])
29 | .pipe(minify())
30 | .pipe(gulp.dest('dist'))
31 | });
32 | ```
33 |
34 | Options can be added to control more finely what's happening, for example:
35 |
36 | ```javascript
37 | const minify = require('gulp-minify');
38 |
39 | gulp.task('compress', function() {
40 | gulp.src('lib/*.js')
41 | .pipe(minify({
42 | ext:{
43 | src:'-debug.js',
44 | min:'.js'
45 | },
46 | exclude: ['tasks'],
47 | ignoreFiles: ['.combo.js', '-min.js']
48 | }))
49 | .pipe(gulp.dest('dist'))
50 | });
51 | ```
52 |
53 | ## Options
54 |
55 | - `ext`
56 | An object that specifies output src and minified file extensions.
57 |
58 | - `src`
59 |
60 | The suffix string of the filenames that output source files ends with.
61 |
62 | - `min`
63 |
64 | - When **string**: The suffix string of the filenames that output minified files ends with.
65 | - When **Array**: The regex expressions to be replaced with input filenames. For example: `[/\.(.*)-source\.js$/, '$1.js']`
66 |
67 | - `exclude`
68 |
69 | Will not minify files in the dirs.
70 |
71 | - `noSource`
72 | Will not output the source code in the dest dirs.
73 |
74 | - `ignoreFiles`
75 |
76 | Will not minify files which matches the pattern.
77 |
78 | - `mangle`
79 |
80 | Pass `false` to skip mangling names.
81 |
82 | - `output`
83 |
84 | Pass an object if you wish to specify additional [output
85 | options](http://lisperator.net/uglifyjs/codegen). The defaults are
86 | optimized for best compression.
87 |
88 | - `compress`
89 |
90 | Pass an object to specify custom [compressor
91 | options](http://lisperator.net/uglifyjs/compress). Pass `false` to skip
92 | compression completely.
93 |
94 | - `preserveComments`
95 |
96 | A convenience option for `options.output.comments`. Defaults to preserving no
97 | comments.
98 |
99 | - `all`
100 |
101 | Preserve all comments in code blocks
102 |
103 | - `some`
104 |
105 | Preserve comments that start with a bang (`!`) or include a Closure
106 | Compiler directive (`@preserve`, `@license`, `@cc_on`)
107 |
108 | - `function`
109 |
110 | Specify your own comment preservation function. You will be passed the
111 | current node and the current comment and are expected to return either
112 | `true` or `false`.
113 |
--------------------------------------------------------------------------------
/gulp-minify.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare namespace GulpMinify {
4 | interface MinifyOptions {
5 | ext?: {
6 | src?: string;
7 | min?: string;
8 | },
9 | exclude?: Array,
10 | ignoreFiles?: Array
11 | }
12 | }
13 |
14 | declare module "gulp-minify" {
15 | import { Duplex } from 'stream';
16 |
17 | function minify(options?: GulpMinify.MinifyOptions): Duplex;
18 |
19 | export = minify;
20 | }
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const through = require("through2");
3 | const uglify = require('terser');
4 | const minimatch = require('minimatch');
5 | const path = require('path');
6 | const Vinyl = require('vinyl');
7 | const PluginError = require('plugin-error');
8 | const colors = require('ansi-colors');
9 | const reSourceMapComment = /\n\/\/# sourceMappingURL=.+?$/;
10 | const pathSeparatorRe = /[\/\\]/g;
11 | const jsExtensions = /\.m?js$/;
12 |
13 | function parseExt(ext, byDefault = ".js") {
14 |
15 | let _ext = {};
16 |
17 | if (!ext) {
18 | _ext = {
19 | min: "-min" + byDefault,
20 | src: byDefault
21 | }
22 | } else if (typeof ext === "string") {
23 | _ext = {
24 | min: ext,
25 | src: byDefault
26 | }
27 | } else {
28 | _ext = {
29 | min: ext.min || "-min" + byDefault,
30 | src: ext.src || byDefault
31 | }
32 | }
33 |
34 | return _ext;
35 |
36 | }
37 |
38 | function formatError(error, file) {
39 | let filePath = error.file === 'stdin' ? file.path : error.file;
40 | let message = '';
41 |
42 | filePath = filePath ? filePath : file.path;
43 | let relativePath = path.relative(process.cwd(), filePath);
44 |
45 | message += colors.underline(relativePath) + '\n';
46 | message += error.message + ' (line: ' + error.line + ', col: ' + error.col + ', pos: ' + error.pos;
47 | error.message = colors.red(message);
48 | return error;
49 | }
50 |
51 | module.exports = function(opt) {
52 |
53 | //Set the options to the one provided or an empty object.
54 | let options = opt || {};
55 |
56 | //Set options output to itself, or, if null an empty object.
57 | options.output = options.output || {};
58 |
59 | function minify(file, encoding, callback) {
60 |
61 | if (file.isNull()) {
62 | this.push(file);
63 | return callback();
64 | }
65 |
66 | if (file.isStream()) {
67 | this.emit('end');
68 | return new callback(PluginError('gulp-minify', 'Streaming not supported:' + file.path));
69 | }
70 |
71 | let ignore = false;
72 |
73 | if (options.exclude) {
74 | ignore = options.exclude.some(function(item) {
75 | return path.dirname(file.path).split(pathSeparatorRe).some(function(pathName) {
76 | return minimatch(pathName, item);
77 | });
78 | });
79 | }
80 |
81 | if (!ignore && options.ignoreFiles) {
82 | ignore = options.ignoreFiles.some(function(item) {
83 | return minimatch(path.basename(file.path), item);
84 | });
85 | }
86 |
87 | if (ignore || !path.extname(file.path).match(jsExtensions)) {
88 | this.push(file);
89 | return callback();
90 | }
91 |
92 | let mangled, originalSourceMap;
93 |
94 | if (file.sourceMap) {
95 | options.outSourceMap = file.relative;
96 | if (file.sourceMap.mappings !== '') {
97 | options.inSourceMap = file.sourceMap;
98 | }
99 | originalSourceMap = file.sourceMap;
100 | }
101 |
102 | if (options.preserveComments === 'all') {
103 | options.output.comments = true;
104 | } else if (options.preserveComments === 'some') {
105 |
106 | options.output.comments = /^!|@preserve|@license|@cc_on/i;
107 | } else if (typeof options.preserveComments === 'function') {
108 | options.output.comments = options.preserveComments;
109 | }
110 | options.fromString = options.hasOwnProperty("fromString") ? options.fromString : true;
111 |
112 | //Parse the extensions form the options.
113 | let ext = parseExt(options.ext, path.extname(file.path));
114 |
115 | let min_file = new Vinyl({
116 | base: file.base,
117 | path: Array.isArray(ext.min) ? file.path.replace(ext.min[0], ext.min[1]) : file.path.replace(jsExtensions, ext.min),
118 | });
119 |
120 | const uglifyOptions = {
121 | mangle : options.mangle !== undefined ? options.mangle : true,
122 | output : options.output !== undefined ? options.output : null,
123 | compress : options.compress !== undefined ? options.compress : {},
124 | sourceMap: !!file.sourceMap
125 | };
126 |
127 | try {
128 | mangled = uglify.minify(String(file.contents), uglifyOptions);
129 | min_file.contents = new Buffer(mangled.code.replace(reSourceMapComment, ''));
130 | } catch (e) {
131 | this.emit('end');
132 | return callback(new PluginError('gulp-minify', formatError(e, file)));
133 | }
134 |
135 | if (file.sourceMap) {
136 | min_file.sourceMap = JSON.parse(mangled.map);
137 | min_file.sourceMap.sourcesContent = originalSourceMap.sourcesContent;
138 | min_file.sourceMap.sources = originalSourceMap.sources;
139 | }
140 |
141 | this.push(min_file);
142 |
143 | if (options.noSource !== true) {
144 | file.path = file.path.replace(jsExtensions, ext.src);
145 | this.push(file);
146 | }
147 |
148 | callback();
149 | }
150 |
151 | return through.obj(minify);
152 | };
153 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gulp-minify",
3 | "version": "3.1.0",
4 | "description": "Js minify plugin for gulp",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "mocha"
8 | },
9 | "files": [
10 | "index.js"
11 | ],
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/hustxiaoc/gulp-minify.git"
15 | },
16 | "author": "308512341@qq.com",
17 | "dependencies": {
18 | "ansi-colors": "^1.0.1",
19 | "minimatch": "^3.0.2",
20 | "plugin-error": "^0.1.2",
21 | "terser": "^3.7.6",
22 | "through2": "^2.0.3",
23 | "vinyl": "^2.1.0"
24 | },
25 | "license": "ISC",
26 | "bugs": {
27 | "url": "https://github.com/hustxiaoc/gulp-minify/issues"
28 | },
29 | "homepage": "https://github.com/hustxiaoc/gulp-minify",
30 | "readmeFilename": "README.md",
31 | "devDependencies": {
32 | "gulp": "^4.0.0",
33 | "mocha": "^5.2.0",
34 | "rimraf": "^2.6.2"
35 | },
36 | "engines": {
37 | "node": ">= 6.0.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/test/fixtures/demo1/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = 'test';
3 |
--------------------------------------------------------------------------------
/test/fixtures/demo2/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = 'test';
3 |
--------------------------------------------------------------------------------
/test/fixtures/demo3/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = 'test';
3 |
--------------------------------------------------------------------------------
/test/fixtures/demo4/exclude/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = 'test';
3 |
--------------------------------------------------------------------------------
/test/fixtures/demo4/ignore.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = 'test';
3 |
--------------------------------------------------------------------------------
/test/fixtures/demo4/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = 'test';
3 |
--------------------------------------------------------------------------------
/test/fixtures/demo5/exclude/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = 'test';
3 |
--------------------------------------------------------------------------------
/test/fixtures/demo5/ignore.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = 'test';
3 |
--------------------------------------------------------------------------------
/test/fixtures/demo5/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = 'test';
3 |
--------------------------------------------------------------------------------
/test/fixtures/demo6/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = 'test';
3 |
--------------------------------------------------------------------------------
/test/fixtures/demo6/index.mjs:
--------------------------------------------------------------------------------
1 | // ES6 module with a .mjs extension name
2 | const test = "test";
3 | export default test;
4 |
--------------------------------------------------------------------------------
/test/minifier.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const assert = require('assert');
3 | const fs = require('fs');
4 | const gulp = require('gulp');
5 | const path = require('path');
6 | const minify = require('../');
7 | const rimraf = require('rimraf');
8 | const fixtures = path.join(__dirname, 'fixtures');
9 | const build = path.join(__dirname, 'build');
10 |
11 | function run(project, options) {
12 | return gulp.src(`${fixtures}/${project}/**/*js`).pipe(minify(options)).pipe(gulp.dest(`${build}/${project}`));
13 | }
14 | describe('test/minifier.test.js', () => {
15 | before(() => {
16 | rimraf.sync(build);
17 | });
18 |
19 | after(() => {
20 | rimraf.sync(build);
21 | });
22 |
23 | it('should minify ok', (done) => {
24 | const project = 'demo1';
25 | run(project).on('end', () => {
26 | const buildPath = `${build}/${project}`;
27 | assert(fs.existsSync(path.join(buildPath, 'index.js')));
28 | assert(fs.existsSync(path.join(buildPath, 'index-min.js')));
29 | done();
30 | });
31 | });
32 |
33 | it('should noSource work ok', (done) => {
34 | const project = 'demo2';
35 | run(project, {noSource: true}).on('end', () => {
36 | const buildPath = `${build}/${project}`;
37 | assert(fs.existsSync(path.join(buildPath, 'index.js')) === false);
38 | assert(fs.existsSync(path.join(buildPath, 'index-min.js')));
39 | done();
40 | });
41 | });
42 |
43 | it('should ext work ok', (done) => {
44 | const project = 'demo3';
45 | run(project, {
46 | ext:{
47 | src:'-debug.js',
48 | min:'-min.js'
49 | },
50 | }).on('end', () => {
51 | const buildPath = `${build}/${project}`;
52 | assert(fs.existsSync(path.join(buildPath, 'index-debug.js')));
53 | assert(fs.existsSync(path.join(buildPath, 'index-min.js')));
54 | done();
55 | });
56 | });
57 |
58 | it('should exclude work ok', (done) => {
59 | const project = 'demo4';
60 | run(project, {
61 | exclude: ['exclude']
62 | }).on('end', () => {
63 | const buildPath = `${build}/${project}`;
64 | assert(fs.existsSync(path.join(buildPath, 'exclude/test.js')));
65 | assert(fs.existsSync(path.join(buildPath, 'exclude/test-min.js')) === false);
66 | assert(fs.existsSync(path.join(buildPath, 'index.js')));
67 | assert(fs.existsSync(path.join(buildPath, 'index-min.js')));
68 | assert(fs.existsSync(path.join(buildPath, 'ignore.js')));
69 | assert(fs.existsSync(path.join(buildPath, 'ignore-min.js')));
70 | done();
71 | });
72 | });
73 |
74 | it('should ignoreFiles work ok', (done) => {
75 | const project = 'demo5';
76 | run(project, {
77 | ignoreFiles: ['ignore.js'],
78 | }).on('end', () => {
79 | const buildPath = `${build}/${project}`;
80 | assert(fs.existsSync(path.join(buildPath, 'exclude/test.js')));
81 | assert(fs.existsSync(path.join(buildPath, 'exclude/test-min.js')));
82 | assert(fs.existsSync(path.join(buildPath, 'index.js')));
83 | assert(fs.existsSync(path.join(buildPath, 'index-min.js')));
84 | assert(fs.existsSync(path.join(buildPath, 'ignore.js')));
85 | assert(fs.existsSync(path.join(buildPath, 'ignore-min.js')) === false);
86 | done();
87 | });
88 | });
89 |
90 | it('should minify .mjs files alongside .js files', (done) => {
91 | const project = 'demo6';
92 | run(project).on('end', () => {
93 | const buildPath = `${build}/${project}`;
94 | assert(fs.existsSync(path.join(buildPath, 'index.js')));
95 | assert(fs.existsSync(path.join(buildPath, 'index-min.js')));
96 | assert(fs.existsSync(path.join(buildPath, 'index.mjs')));
97 | assert(fs.existsSync(path.join(buildPath, 'index-min.mjs')));
98 | done();
99 | });
100 | });
101 |
102 | });
103 |
--------------------------------------------------------------------------------