adjust build setup for better production minification

This commit is contained in:
Evan You 2015-07-08 16:47:20 +08:00
parent 69edc1ae02
commit 0d3b26455d
34 changed files with 176 additions and 111 deletions

8
build/banner.js Normal file
View File

@ -0,0 +1,8 @@
var version =
process.env.VUE_VERSION ||
require('../package.json').version
module.exports =
'Vue.js v' + version + '\n' +
'(c) ' + new Date().getFullYear() + ' Evan You\n' +
'Released under the MIT License.'

View File

@ -1,5 +1,5 @@
/**
* Build, update component.json, uglify, and report size.
* Build and report size.
*/
module.exports = function (grunt) {
@ -9,66 +9,34 @@ module.exports = function (grunt) {
var fs = require('fs')
var zlib = require('zlib')
var webpack = require('webpack')
var uglifyjs = require('uglify-js')
var devConfig = require('../webpack.build.dev.config')
var prodConfig = require('../webpack.build.prod.config')
var banner =
'/**\n' +
' * Vue.js v' + grunt.config.get('version') + '\n' +
' * (c) ' + new Date().getFullYear() + ' Evan You\n' +
' * Released under the MIT License.\n' +
' */\n'
// build
webpack({
entry: './src/vue',
output: {
path: './dist',
filename: 'vue.js',
library: 'Vue',
libraryTarget: 'umd'
},
plugins: [
new webpack.BannerPlugin(banner, { raw: true })
]
}, function (err, stats) {
webpack(devConfig, function (err, stats) {
if (err) return done(err)
minify()
report('dist/vue.js')
webpack(prodConfig, function (err, stats) {
if (err) return done(err)
report('dist/vue.min.js')
zip()
})
})
function minify () {
var js = fs.readFileSync('dist/vue.js', 'utf-8')
report('dist/vue.js', js)
// uglify
var result = uglifyjs.minify(js, {
fromString: true,
output: {
comments: /License/
},
compress: {
pure_funcs: [
'require',
'_.log',
'_.warn',
'_.assertAsset',
'enableDebug'
]
}
function zip () {
fs.readFile('dist/vue.min.js', function (err, buf) {
if (err) return done(err)
zlib.gzip(buf, function (err, buf) {
if (err) return done(err)
report('dist/vue.min.js.gz', buf)
done()
})
})
// var min = grunt.config.get('banner') + result.code
write('dist/vue.min.js', result.code)
// report gzip size
zlib.gzip(result.code, function (err, buf) {
write('dist/vue.min.js.gz', buf)
done(err)
})
}
function write (path, file) {
fs.writeFileSync(path, file)
report(path, file)
}
function report (path, file) {
if (!file) {
file = fs.readFileSync(path)
}
console.log(
blue(path + ': ') +
(file.length / 1024).toFixed(2) + 'kb'

View File

@ -9,7 +9,7 @@ module.exports = function (grunt) {
*/
grunt.registerTask('version', function (version) {
var manifests = ['package', 'bower', 'component']
var manifests = ['package', 'component']
manifests.forEach(function (file) {
file = file + '.json'
var json = grunt.file.read(file)
@ -62,7 +62,8 @@ module.exports = function (grunt) {
}).question('Releasing version ' + next + '. Continue? (Y/n)', function (answer) {
if (!answer || answer.toLowerCase() === 'y') {
console.log(blue('Releasing: ' + next))
grunt.config.set('version', next)
// set version in env
process.env.VUE_VERSION = next
grunt.task.run([
'eslint',
'cover',

View File

@ -0,0 +1,20 @@
var webpack = require('webpack')
var banner = require('./banner')
module.exports = {
entry: './src/vue',
output: {
path: './dist',
filename: 'vue.js',
library: 'Vue',
libraryTarget: 'umd'
},
plugins: [
new webpack.BannerPlugin(banner),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"'
}
})
]
}

View File

@ -0,0 +1,25 @@
var webpack = require('webpack')
var banner = require('./banner')
module.exports = {
entry: './src/vue',
output: {
path: './dist',
filename: 'vue.min.js',
library: 'Vue',
libraryTarget: 'umd'
},
plugins: [
new webpack.BannerPlugin(banner),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
}

View File

@ -1,4 +1,4 @@
var sauceConfig = require('./build/saucelabs-config')
var sauceConfig = require('./build/saucelabs.config.js')
module.exports = function (grunt) {

View File

@ -18,7 +18,15 @@
"homepage": "http://vuejs.org",
"scripts": {
"test": "grunt ci",
"dev": "webpack --watch --config build/webpack-dev-config.js & webpack --watch --config build/webpack-test-config.js"
"dev": "webpack --watch --config build/webpack.dev.config.js & webpack --watch --config build/webpack.test.config.js"
},
"dependencies": {
"envify": "^3.4.0"
},
"browserify": {
"transform": [
"envify"
]
},
"devDependencies": {
"casperjs": "^1.1.0-beta3",

View File

@ -13,7 +13,9 @@ var compiler = require('../compiler')
exports.$mount = function (el) {
if (this._isCompiled) {
_.warn('$mount() should be called only once.')
process.env.NODE_ENV !== 'production' && _.warn(
'$mount() should be called only once.'
)
return
}
el = _.query(el)

View File

@ -71,7 +71,7 @@ exports.push = function (job) {
has[id]++
// detect possible infinite update loops
if (has[id] > config._maxUpdateCount) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'You may have an infinite update loop for the ' +
'watcher with expression: "' + job.expression + '".'
)

View File

@ -30,10 +30,11 @@ module.exports = function compileProps (el, propOptions) {
// so we need to camelize the path here
path = _.camelize(name.replace(dataAttrRE, ''))
if (!identRE.test(path)) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Invalid prop key: "' + name + '". Prop keys ' +
'must be valid identifiers.'
)
continue
}
value = el.getAttribute(_.hyphenate(name))
// create a prop descriptor
@ -68,7 +69,7 @@ module.exports = function compileProps (el, propOptions) {
if (settablePathRE.test(prop.parentPath)) {
prop.mode = propBindingModes.TWO_WAY
} else {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Cannot bind two-way prop with non-settable ' +
'parent path: ' + prop.parentPath
)
@ -76,7 +77,9 @@ module.exports = function compileProps (el, propOptions) {
}
}
} else if (options && options.required) {
_.warn('Missing required prop: ' + name)
process.env.NODE_ENV !== 'production' && _.warn(
'Missing required prop: ' + name
)
}
props.push(prop)
}
@ -120,7 +123,7 @@ function makePropsLinkFn (props) {
vm._bindDir('prop', el, prop, propDef)
}
} else {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Cannot bind dynamic prop on a root instance' +
' with no parent: ' + prop.name + '="' +
prop.raw + '"'

View File

@ -522,7 +522,9 @@ function compileDirectives (elOrAttrs, options) {
if (name.indexOf(config.prefix) === 0) {
dirName = name.slice(config.prefix.length)
dirDef = resolveAsset(options, 'directives', dirName)
_.assertAsset(dirDef, 'directive', dirName)
if (process.env.NODE_ENV !== 'production') {
_.assertAsset(dirDef, 'directive', dirName)
}
if (dirDef) {
dirs.push({
name: dirName,

View File

@ -59,15 +59,13 @@ exports.transclude = function (el, options) {
function transcludeTemplate (el, options) {
var template = options.template
var frag = templateParser.parse(template, true)
if (!frag) {
_.warn('Invalid template option: ' + template)
} else {
if (frag) {
var replacer = frag.firstChild
var tag = replacer.tagName && replacer.tagName.toLowerCase()
if (options.replace) {
/* istanbul ignore if */
if (el === document.body) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'You are mounting an instance with a template to ' +
'<body>. This will replace <body> entirely. You ' +
'should probably use `replace: false` here.'
@ -96,6 +94,10 @@ function transcludeTemplate (el, options) {
el.appendChild(frag)
return el
}
} else {
process.env.NODE_ENV !== 'production' && _.warn(
'Invalid template option: ' + template
)
}
}

View File

@ -50,7 +50,7 @@ module.exports = {
this.transMode = this._checkParam('transition-mode')
}
} else {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Do not create a component that only contains ' +
'a single other component - they will be mounted to ' +
'the same element and cause conflict. Wrap it with ' +

View File

@ -25,11 +25,11 @@ module.exports = {
true
)
} else {
this.invalid = true
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'v-if="' + this.expression + '" cannot be ' +
'used on an instance root element.'
)
this.invalid = true
}
},

View File

@ -29,7 +29,7 @@ module.exports = {
// friendly warning...
this.checkFilters()
if (this.hasRead && !this.hasWrite) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'It seems you are using a read-only filter with ' +
'v-model. You might want to use a two-way filter ' +
'to ensure correct behavior.'
@ -45,7 +45,9 @@ module.exports = {
} else if (tag === 'TEXTAREA') {
handler = handlers.text
} else {
_.warn('v-model does not support element type: ' + tag)
process.env.NODE_ENV !== 'production' && _.warn(
'v-model does not support element type: ' + tag
)
return
}
handler.bind.call(this)

View File

@ -82,7 +82,9 @@ function initOptions (expression) {
buildOptions(self.el, value)
self.forceUpdate()
} else {
_.warn('Invalid options value for v-model: ' + value)
process.env.NODE_ENV !== 'production' && _.warn(
'Invalid options value for v-model: ' + value
)
}
}
this.optionWatcher = new Watcher(

View File

@ -21,7 +21,7 @@ module.exports = {
update: function (handler) {
if (typeof handler !== 'function') {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Directive "v-on:' + this.expression + '" ' +
'expects a function value.'
)

View File

@ -7,7 +7,7 @@ module.exports = {
bind: function () {
var vm = this.el.__vue__
if (!vm) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'v-ref should only be used on a component root element.'
)
return

View File

@ -53,7 +53,7 @@ module.exports = {
checkIf: function () {
if (_.attr(this.el, 'if') !== null) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Don\'t use v-if with v-repeat. ' +
'Use v-show or the "filterBy" filter instead.'
)
@ -155,9 +155,11 @@ module.exports = {
}
var id = this.ctorGetter.call(context, context)
var Ctor = _.resolveAsset(this.vm.$options, 'components', id)
_.assertAsset(Ctor, 'component', id)
if (process.env.NODE_ENV !== 'production') {
_.assertAsset(Ctor, 'component', id)
}
if (!Ctor.options) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Async resolution is not supported for v-repeat ' +
'+ dynamic component. (component: ' + id + ')'
)
@ -380,7 +382,7 @@ module.exports = {
) {
vm.$watch(alias || '$value', function (val) {
if (dir.filters) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'You seem to be mutating the $value reference of ' +
'a v-repeat instance (likely through v-model) ' +
'and filtering the v-repeat at the same time. ' +
@ -449,7 +451,9 @@ module.exports = {
if (!cache[id]) {
cache[id] = vm
} else if (!primitive && idKey !== '$index') {
_.warn('Duplicate track-by key in v-repeat: ' + id)
process.env.NODE_ENV !== 'production' && _.warn(
'Duplicate track-by key in v-repeat: ' + id
)
}
} else {
id = this.id
@ -457,7 +461,7 @@ module.exports = {
if (data[id] === null) {
data[id] = vm
} else {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Duplicate objects are not supported in v-repeat ' +
'when using components or transitions.'
)

View File

@ -45,7 +45,9 @@ module.exports = {
insert: function (id) {
var partial = _.resolveAsset(this.vm.$options, 'partials', id)
_.assertAsset(partial, 'partial', id)
if (process.env.NODE_ENV !== 'production') {
_.assertAsset(partial, 'partial', id)
}
if (partial) {
var frag = templateParser.parse(partial, true)
// cache partials based on constructor id.

View File

@ -56,7 +56,7 @@ function register (vm, action, key, handler, options) {
if (method) {
vm[action](key, method, options)
} else {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Unknown method: "' + handler + '" when ' +
'registering callback for ' + action +
': "' + key + '".'

View File

@ -18,7 +18,9 @@ exports._applyFilters = function (value, oldValue, filters, write) {
for (i = 0, l = filters.length; i < l; i++) {
filter = filters[i]
fn = _.resolveAsset(this.$options, 'filters', filter.name)
_.assertAsset(fn, 'filter', filter.name)
if (process.env.NODE_ENV !== 'production') {
_.assertAsset(fn, 'filter', filter.name)
}
if (!fn) continue
fn = write ? fn.write : (fn.read || fn)
if (typeof fn !== 'function') continue
@ -50,7 +52,9 @@ exports._applyFilters = function (value, oldValue, filters, write) {
exports._resolveComponent = function (id, cb) {
var factory = _.resolveAsset(this.$options, 'components', id)
_.assertAsset(factory, 'component', id)
if (process.env.NODE_ENV !== 'production') {
_.assertAsset(factory, 'component', id)
}
// async component factory
if (!factory.options) {
if (factory.resolved) {
@ -73,7 +77,7 @@ exports._resolveComponent = function (id, cb) {
cbs[i](res)
}
}, function reject (reason) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Failed to resolve async component: ' + id + '. ' +
(reason ? '\nReason: ' + reason : '')
)

View File

@ -28,7 +28,7 @@ exports._initProps = function () {
var el = options.el
var props = options.props
if (props && !el) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Props will not be compiled if no `el` option is ' +
'provided at instantiation.'
)

View File

@ -106,7 +106,7 @@ function restore (str, i) {
function compileExpFns (exp, needSet) {
if (improperKeywordsRE.test(exp)) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Avoid using reserved keywords in expression: ' + exp
)
}
@ -175,7 +175,7 @@ function makeGetter (body) {
try {
return new Function('scope', 'return ' + body + ';')
} catch (e) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Invalid expression. ' +
'Generated function body: ' + body
)
@ -200,7 +200,9 @@ function makeSetter (body) {
try {
return new Function('scope', 'value', body + '=value;')
} catch (e) {
_.warn('Invalid setter function body: ' + body)
process.env.NODE_ENV !== 'production' && _.warn(
'Invalid setter function body: ' + body
)
}
}

View File

@ -299,9 +299,9 @@ exports.set = function (obj, path, val) {
if (i < l - 1) {
obj = obj[key]
if (!_.isObject(obj)) {
warnNonExistent(path)
obj = {}
last.$add(key, obj)
warnNonExistent(path)
}
} else {
if (_.isArray(obj)) {
@ -309,8 +309,8 @@ exports.set = function (obj, path, val) {
} else if (key in obj) {
obj[key] = val
} else {
obj.$add(key, val)
warnNonExistent(path)
obj.$add(key, val)
}
}
}
@ -318,7 +318,7 @@ exports.set = function (obj, path, val) {
}
function warnNonExistent (path) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'You are setting a non-existent path "' + path.raw + '" ' +
'on a vm instance. Consider pre-initializing the property ' +
'with the "data" option for more reliable reactivity ' +

View File

@ -87,7 +87,7 @@ exports.assertProp = function (prop, value) {
}
}
if (!valid) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Invalid prop: type check failed for ' +
prop.path + '="' + prop.raw + '".' +
' Expected ' + formatType(expectedType) +
@ -98,7 +98,7 @@ exports.assertProp = function (prop, value) {
var validator = options.validator
if (validator) {
if (!validator.call(null, value)) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Invalid prop: custom validator check failed for ' +
prop.path + '="' + prop.raw + '"'
)

View File

@ -1,15 +1,10 @@
var config = require('../config')
/**
* Enable debug utilities. The enableDebug() function and
* all _.log() & _.warn() calls will be dropped in the
* minified production build.
* Enable debug utilities.
*/
enableDebug()
function enableDebug () {
if (process.env.NODE_ENV !== 'production') {
var config = require('../config')
var hasConsole = typeof console !== 'undefined'
/**

View File

@ -13,7 +13,9 @@ exports.query = function (el) {
var selector = el
el = document.querySelector(el)
if (!el) {
_.warn('Cannot find element: ' + selector)
process.env.NODE_ENV !== 'production' && _.warn(
'Cannot find element: ' + selector
)
}
}
return el

View File

@ -44,7 +44,7 @@ strats.data = function (parentVal, childVal, vm) {
return parentVal
}
if (typeof childVal !== 'function') {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'The "data" option should be a function ' +
'that returns a per-instance value in component ' +
'definitions.'
@ -89,7 +89,7 @@ strats.data = function (parentVal, childVal, vm) {
strats.el = function (parentVal, childVal, vm) {
if (!vm && childVal && typeof childVal !== 'function') {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'The "el" option should be a function ' +
'that returns a per-instance value in component ' +
'definitions.'
@ -131,7 +131,7 @@ strats.props = function (parentVal, childVal) {
strats.paramAttributes = function () {
/* istanbul ignore next */
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'"paramAttributes" option has been deprecated in 0.12. ' +
'Use "props" instead.'
)
@ -218,10 +218,11 @@ function guardComponents (components) {
var def
for (var key in components) {
if (_.commonTagRE.test(key)) {
_.warn(
process.env.NODE_ENV !== 'production' && _.warn(
'Do not use built-in HTML elements as component ' +
'name: ' + key
)
continue
}
def = components[key]
if (_.isPlainObject(def)) {

View File

@ -86,7 +86,10 @@ p.get = function () {
try {
value = this.getter.call(vm, vm)
} catch (e) {
if (config.warnExpressionErrors) {
if (
process.env.NODE_ENV !== 'production' &&
config.warnExpressionErrors
) {
_.warn(
'Error when evaluating expression "' +
this.expression + '". ' +
@ -127,7 +130,10 @@ p.set = function (value) {
try {
this.setter.call(vm, vm, value)
} catch (e) {
if (config.warnExpressionErrors) {
if (
process.env.NODE_ENV !== 'production' &&
config.warnExpressionErrors
) {
_.warn(
'Error when evaluating setter "' +
this.expression + '"', e

View File

@ -15,3 +15,9 @@ scope.hasWarned = function (_, msg) {
return arg.indexOf(msg) > -1
}
}
scope.process = {
env: {
NODE_ENV: 'development'
}
}