Better handling of favicon generation

This commit is contained in:
Lucas Verney 2018-07-11 02:43:14 +02:00
parent 8bdd98a8bf
commit 037d4627e9
9 changed files with 993 additions and 292 deletions

View File

@ -95,7 +95,7 @@ exports.createNotifierCallback = () => {
title: packageConfig.name, title: packageConfig.name,
message: severity + ': ' + error.name, message: severity + ': ' + error.name,
subtitle: filename || '', subtitle: filename || '',
icon: path.join(__dirname, 'logo.png') icon: path.join(__dirname, 'static/icon.svg')
}) })
} }
} }

View File

@ -6,6 +6,7 @@ const merge = require('webpack-merge')
const path = require('path') const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf') const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin')
const AppManifestWebpackPlugin = require('app-manifest-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder') const portfinder = require('portfinder')
@ -14,99 +15,107 @@ const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT) const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, { const devWebpackConfig = merge(baseWebpackConfig, {
module: { module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
}, },
hot: true, // cheap-module-eval-source-map is faster for development
contentBase: false, // since we use CopyWebpackPlugin. devtool: config.dev.devtool,
compress: true,
host: HOST || config.dev.host, // these devServer options should be customized in /config/index.js
port: PORT || config.dev.port, devServer: {
open: config.dev.autoOpenBrowser, clientLogLevel: 'warning',
overlay: config.dev.errorOverlay historyApiFallback: {
? { warnings: false, errors: true } rewrites: [
: false, { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
publicPath: config.dev.assetsPublicPath, ],
proxy: config.dev.proxyTable, },
quiet: true, // necessary for FriendlyErrorsPlugin hot: true,
watchOptions: { contentBase: false, // since we use CopyWebpackPlugin.
poll: config.dev.poll, compress: true,
} host: HOST || config.dev.host,
}, port: PORT || config.dev.port,
plugins: [ open: config.dev.autoOpenBrowser,
new webpack.DefinePlugin({ overlay: config.dev.errorOverlay
'process.env': require('../config/dev.env') ? { warnings: false, errors: true }
}), : false,
new webpack.HotModuleReplacementPlugin(), publicPath: config.dev.assetsPublicPath,
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. proxy: config.dev.proxyTable,
new webpack.NoEmitOnErrorsPlugin(), quiet: true, // necessary for FriendlyErrorsPlugin
// https://github.com/ampedandwired/html-webpack-plugin watchOptions: {
new HtmlWebpackPlugin({ poll: config.dev.poll,
filename: 'index.html', }
template: 'index.html', },
ogURL: config.build.ogURL, plugins: [
ogImage: config.build.ogImage, new webpack.DefinePlugin({
inject: true 'process.env': require('../config/dev.env')
}), }),
// copy custom static assets new webpack.HotModuleReplacementPlugin(),
new CopyWebpackPlugin([ new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
{ new webpack.NoEmitOnErrorsPlugin(),
from: path.resolve(__dirname, '../static'), new AppManifestWebpackPlugin({
to: config.dev.assetsSubDirectory, logo: path.resolve(__dirname, '../static/icon.svg'),
ignore: ['.*'] prefix: '.',
}, output: '/static/icons-[hash:8]/',
{ inject: true,
from: path.resolve(__dirname, '../manifest.webmanifest'), persistentCache: true,
to: config.build.assetsRoot, config: {
ignore: ['.*'] appName: 'Cyclassist',
}, appDescription: "Track and share issues (work, interruption in routes, parked cars) on bike lanes in realtime.",
{ developerName: 'Phyks (Lucas Verney)',
from: path.resolve(__dirname, '../humans.txt'), developerURL: 'https://phyks.me',
to: config.build.assetsRoot, }
ignore: ['.*'] }),
}, // https://github.com/ampedandwired/html-webpack-plugin
{ new HtmlWebpackPlugin({
from: path.resolve(__dirname, '../robots.txt'), filename: 'index.html',
to: config.build.assetsRoot, template: 'index.html',
ignore: ['.*'] ogURL: config.build.ogURL,
} ogImage: config.build.ogImage,
]) inject: true
] }),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
},
{
from: path.resolve(__dirname, '../humans.txt'),
to: config.build.assetsRoot,
ignore: ['.*']
},
{
from: path.resolve(__dirname, '../robots.txt'),
to: config.build.assetsRoot,
ignore: ['.*']
}
])
]
}) })
module.exports = new Promise((resolve, reject) => { module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => { portfinder.getPort((err, port) => {
if (err) { if (err) {
reject(err) reject(err)
} else { } else {
// publish the new Port, necessary for e2e tests // publish the new Port, necessary for e2e tests
process.env.PORT = port process.env.PORT = port
// add port to devServer config // add port to devServer config
devWebpackConfig.devServer.port = port devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin // Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: { compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
}, },
onErrors: config.dev.notifyOnErrors onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback() ? utils.createNotifierCallback()
: undefined : undefined
})) }))
resolve(devWebpackConfig) resolve(devWebpackConfig)
} }
}) })
}) })

View File

@ -6,6 +6,7 @@ const config = require('../config')
const merge = require('webpack-merge') const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf') const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin')
const AppManifestWebpackPlugin = require('app-manifest-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin') const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
@ -14,149 +15,158 @@ const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('../config/prod.env') const env = require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, { const webpackConfig = merge(baseWebpackConfig, {
module: { module: {
rules: utils.styleLoaders({ rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap, sourceMap: config.build.productionSourceMap,
extract: true, extract: true,
usePostCSS: true usePostCSS: true
}) })
}, },
devtool: config.build.productionSourceMap ? config.build.devtool : false, devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: { output: {
path: config.build.assetsRoot, path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'), filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
}, },
plugins: [ plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html // http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env': env 'process.env': env
}), }),
new UglifyJsPlugin({ new UglifyJsPlugin({
uglifyOptions: { uglifyOptions: {
compress: { compress: {
warnings: false warnings: false
} }
}, },
sourceMap: config.build.productionSourceMap, sourceMap: config.build.productionSourceMap,
parallel: true parallel: true
}), }),
// extract css into its own file // extract css into its own file
new ExtractTextPlugin({ new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'), filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks. // Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true, allChunks: true,
}), }),
// Compress extracted CSS. We are using this plugin so that possible // Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped. // duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({ new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } } ? { safe: true, map: { inline: false } }
: { safe: true } : { safe: true }
}), }),
// generate dist index.html with correct asset hash for caching. new AppManifestWebpackPlugin({
// you can customize output by editing /index.html logo: path.resolve(__dirname, '../static/icon.svg'),
// see https://github.com/ampedandwired/html-webpack-plugin prefix: '.',
new HtmlWebpackPlugin({ output: '/static/icons-[hash:8]/',
filename: config.build.index, inject: true,
template: 'index.html', persistentCache: true,
ogURL: config.build.ogURL, config: {
ogImage: config.build.ogImage, appName: 'Cyclassist',
inject: true, appDescription: "Track and share issues (work, interruption in routes, parked cars) on bike lanes in realtime.",
minify: { developerName: 'Phyks (Lucas Verney)',
removeComments: true, developerURL: 'https://phyks.me',
collapseWhitespace: true, }
removeAttributeQuotes: true }),
// more options: // generate dist index.html with correct asset hash for caching.
// https://github.com/kangax/html-minifier#options-quick-reference // you can customize output by editing /index.html
}, // see https://github.com/ampedandwired/html-webpack-plugin
// necessary to consistently work with multiple chunks via CommonsChunkPlugin //
chunksSortMode: 'dependency' new HtmlWebpackPlugin({
}), filename: config.build.index,
// keep module.id stable when vendor modules does not change template: 'index.html',
new webpack.HashedModuleIdsPlugin(), ogURL: config.build.ogURL,
// enable scope hoisting ogImage: config.build.ogImage,
new webpack.optimize.ModuleConcatenationPlugin(), inject: true,
// split vendor js into its own file minify: {
new webpack.optimize.CommonsChunkPlugin({ removeComments: true,
name: 'vendor', collapseWhitespace: true,
minChunks (module) { removeAttributeQuotes: true
// any required modules inside node_modules are extracted to vendor // more options:
return ( // https://github.com/kangax/html-minifier#options-quick-reference
module.resource && },
/\.js$/.test(module.resource) && // necessary to consistently work with multiple chunks via CommonsChunkPlugin
module.resource.indexOf( chunksSortMode: 'dependency'
path.join(__dirname, '../node_modules') }),
) === 0 // keep module.id stable when vendor modules does not change
) new webpack.HashedModuleIdsPlugin(),
} // enable scope hoisting
}), new webpack.optimize.ModuleConcatenationPlugin(),
// extract webpack runtime and module manifest to its own file in order to // split vendor js into its own file
// prevent vendor hash from being updated whenever app bundle is updated new webpack.optimize.CommonsChunkPlugin({
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor',
name: 'manifest', minChunks (module) {
minChunks: Infinity // any required modules inside node_modules are extracted to vendor
}), return (
// This instance extracts shared chunks from code splitted chunks and bundles them module.resource &&
// in a separate chunk, similar to the vendor chunk /\.js$/.test(module.resource) &&
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk module.resource.indexOf(
new webpack.optimize.CommonsChunkPlugin({ path.join(__dirname, '../node_modules')
name: 'app', ) === 0
async: 'vendor-async', )
children: true, }
minChunks: 3 }),
}), // extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets // copy custom static assets
new CopyWebpackPlugin([ new CopyWebpackPlugin([
{ {
from: path.resolve(__dirname, '../static'), from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory, to: config.build.assetsSubDirectory,
ignore: ['.*'] ignore: ['.*']
}, },
{ {
from: path.resolve(__dirname, '../manifest.webmanifest'), from: path.resolve(__dirname, '../humans.txt'),
to: config.build.assetsRoot, to: config.build.assetsRoot,
ignore: ['.*'] ignore: ['.*']
}, },
{ {
from: path.resolve(__dirname, '../humans.txt'), from: path.resolve(__dirname, '../robots.txt'),
to: config.build.assetsRoot, to: config.build.assetsRoot,
ignore: ['.*'] ignore: ['.*']
}, }
{ ])
from: path.resolve(__dirname, '../robots.txt'), ]
to: config.build.assetsRoot,
ignore: ['.*']
}
])
]
}) })
if (config.build.productionGzip) { if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin') const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push( webpackConfig.plugins.push(
new CompressionWebpackPlugin({ new CompressionWebpackPlugin({
asset: '[path].gz[query]', asset: '[path].gz[query]',
algorithm: 'gzip', algorithm: 'gzip',
test: new RegExp( test: new RegExp(
'\\.(' + '\\.(' +
config.build.productionGzipExtensions.join('|') + config.build.productionGzipExtensions.join('|') +
')$' ')$'
), ),
threshold: 10240, threshold: 10240,
minRatio: 0.8 minRatio: 0.8
}) })
) )
} }
if (config.build.bundleAnalyzerReport) { if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin()) webpackConfig.plugins.push(new BundleAnalyzerPlugin())
} }
module.exports = webpackConfig module.exports = webpackConfig

View File

@ -75,6 +75,6 @@ module.exports = {
// OpenGraph-related variables // OpenGraph-related variables
ogURL: 'https://cyclo.phyks.me', ogURL: 'https://cyclo.phyks.me',
ogImage: 'https://cyclo.phyks.me/static/icon192.png', ogImage: 'https://cyclo.phyks.me/static/icon.svg',
} }
} }

View File

@ -13,8 +13,6 @@
<meta property="og:image" content="<%= htmlWebpackPlugin.options.ogImage %>" /> <meta property="og:image" content="<%= htmlWebpackPlugin.options.ogImage %>" />
<link type="text/plain" rel="author" href="humans.txt" /> <link type="text/plain" rel="author" href="humans.txt" />
<link rel="manifest" href="manifest.webmanifest">
<link rel="shortcut icon" type="image/png" href="static/icon192.png">
<title>Cyclassist: your bike companion!</title> <title>Cyclassist: your bike companion!</title>
</head> </head>

View File

@ -1,39 +0,0 @@
{
"name": "Cyclassist",
"short_name": "Cyclassist",
"description": "Report and share issues with bike routes!",
"start_url": ".",
"display": "standalone",
"icons": [
{
"src": "static/icon48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "static/icon72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "static/icon96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "static/icon144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "static/icon168.png",
"sizes": "168x168",
"type": "image/png"
},
{
"src": "static/icon192.png",
"sizes": "192x192",
"type": "image/png"
}
]
}

View File

@ -11,6 +11,7 @@
"build": "node build/build.js" "build": "node build/build.js"
}, },
"dependencies": { "dependencies": {
"app-manifest-webpack-plugin": "^1.1.3",
"es6-promise": "^4.2.4", "es6-promise": "^4.2.4",
"isomorphic-fetch": "^2.2.1", "isomorphic-fetch": "^2.2.1",
"leaflet": "^1.3.1", "leaflet": "^1.3.1",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

772
yarn.lock

File diff suppressed because it is too large Load Diff