Last active
January 15, 2021 12:52
-
-
Save tenthree/3ae5f0bb1fad7099be0c0d22d236e773 to your computer and use it in GitHub Desktop.
@vue/cli project configure example (with default scss settings)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -------------------------------------------------- | |
// require modules | |
// -------------------------------------------------- | |
const pkg = require('./package.json') | |
const path = require('path').posix | |
const exec = require('child_process').execSync | |
const BannerPlugin = require('webpack/lib/BannerPlugin.js') | |
// -------------------------------------------------- | |
// Prerender routes | |
// -------------------------------------------------- | |
const prerenderRoutes = [ | |
// '/', | |
// '/about' | |
] | |
// -------------------------------------------------- | |
// Basic configuration | |
// -------------------------------------------------- | |
const publicPath = (process.env.NODE_ENV !== 'production') ? '/' : '/' | |
const outputDir = !prerenderRoutes.length ? 'dist' : path.join('dist', publicPath) | |
const filenameHashing = false | |
const runtimeCompiler = false | |
const disableHtmlMinify = true | |
// -------------------------------------------------- | |
// Build destinations by types | |
// -------------------------------------------------- | |
const dest = { | |
script: 'js', | |
style: 'css', | |
image: 'images', | |
media: 'media', | |
font: 'fonts' | |
} | |
// -------------------------------------------------- | |
// SASS auto injection data | |
// -------------------------------------------------- | |
const sassInjectionData = ` | |
// @import "@/scss/settings/variables.scss"; | |
// @import "@/scss/settings/mixins.scss"; | |
` | |
// -------------------------------------------------- | |
// Webpack DevServer | |
// -------------------------------------------------- | |
const devServer = { | |
historyApiFallback: true, | |
disableHostCheck: false, | |
allowedHosts: ['.ngrok.io'], | |
proxy: { | |
// [doc] https://github.com/chimurai/http-proxy-middleware#http-proxy-options | |
// e.g. proxy github api | |
// from: https://api.github.com/repos/f2etw/jobs/issues | |
// to: /api-github/repos/f2etw/jobs/issues | |
'/api-github/': { | |
target: 'https://api.github.com', | |
changeOrigin: true, | |
pathRewrite: { '^/api-github/': '' } | |
} | |
} | |
} | |
// -------------------------------------------------- | |
// PWA configuration | |
// -------------------------------------------------- | |
// const pwa = { | |
// workboxPluginMode: 'InjectManifest', | |
// workboxOptions: { | |
// swSrc: 'src/service-worker.js', | |
// importWorkboxFrom: 'disabled' | |
// // To use the latest workbox version(v4.3.0) | |
// // You can disable default workbox version(v3.6.3), and import it manually | |
// // e.g. importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.0/workbox-sw.js') | |
// }, | |
// iconPaths: { | |
// favicon32: `${dest.image}/icons/favicon-32x32.png`, | |
// favicon16: `${dest.image}/icons/favicon-16x16.png`, | |
// appleTouchIcon: `${dest.image}/icons/apple-touch-icon-152x152.png`, | |
// maskIcon: `${dest.image}/icons/safari-pinned-tab.svg`, | |
// msTileImage: `${dest.image}/icons/msapplication-icon-144x144.png` | |
// } | |
// } | |
// -------------------------------------------------- | |
// BannerPlugin configuration | |
// -------------------------------------------------- | |
let user, email, branch, commit | |
try { | |
user = exec('git config user.name').toString().trim() | |
email = exec('git config user.email').toString().trim() | |
branch = exec('git rev-parse --abbrev-ref HEAD').toString().trim() | |
commit = exec('git rev-parse --short HEAD').toString().trim() | |
} catch (err) { | |
user = '' | |
email = '' | |
branch = '' | |
commit = '' | |
} | |
const now = new Date().toLocaleString('default', { hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }) | |
const banner = `/*! | |
@build with webpack | |
------------------------------ | |
project : ${pkg.name} | |
author : ${user} <${email}> | |
branch : ${branch} | |
commit : ${commit} | |
file: : [file] | |
hash : [hash] | |
chunk : [chunkhash] | |
update : ${now} | |
------------------------------ | |
*/` | |
// -------------------------------------------------- | |
// Configure webpack | |
// -------------------------------------------------- | |
const configureWebpack = config => { | |
// production mode only | |
if (process.env.NODE_ENV === 'production') { | |
return { | |
/* | |
// prevent xlsx|canvg|pdfmake files generated from amcharts4 | |
// treat them as external libraries in webpack | |
// [doc] https://www.amcharts.com/docs/v4/getting-started/integrations/using-webpack/#Large_file_sizes | |
// [doc] https://webpack.js.org/configuration/externals/#function | |
externals (context, request, callback) { | |
if (/xlsx|canvg|pdfmake/.test(request)) { | |
return callback(null, `commonjs ${request}`) | |
} | |
callback() | |
} | |
*/ | |
/* | |
// performance warning bundle size | |
performance: { | |
maxAssetSize: 500000, | |
maxEntrypointSize: 614400 | |
} | |
*/ | |
} | |
} | |
return {} | |
} | |
// -------------------------------------------------- | |
// Chain webpack | |
// -------------------------------------------------- | |
const chainWebpack = config => { | |
// filename pattern | |
const assetHash = !filenameHashing ? '' : '.[hash:8]' | |
const assetNamePattern = `[name]${assetHash}.[ext]` | |
// common | |
config.module.rule('images') | |
.use('url-loader') | |
.loader('url-loader') | |
.tap(options => { | |
options = { | |
limit: 4096, | |
fallback: { | |
loader: 'file-loader', | |
options: { | |
name: path.join(dest.image, assetNamePattern) | |
} | |
} | |
} | |
return options | |
}) | |
config.module.rule('svg') | |
.use('file-loader') | |
.loader('file-loader') | |
.tap(options => { | |
options.name = path.join(dest.image, assetNamePattern) | |
return options | |
}) | |
config.module.rule('media') | |
.use('url-loader') | |
.loader('url-loader') | |
.tap(options => { | |
options = { | |
limit: 4096, | |
fallback: { | |
loader: 'file-loader', | |
options: { | |
name: path.join(dest.media, assetNamePattern) | |
} | |
} | |
} | |
return options | |
}) | |
config.module.rule('fonts') | |
.use('url-loader') | |
.loader('url-loader') | |
.tap(options => { | |
options = { | |
limit: 4096, | |
fallback: { | |
loader: 'file-loader', | |
options: { | |
name: path.join(dest.font, assetNamePattern) | |
} | |
} | |
} | |
return options | |
}) | |
// support csv module | |
// [cmd] npm i -D csv-loader papaparse | |
config.module.rule('csv') | |
.test(/\.csv$/) | |
.use('csv') | |
.loader('csv-loader') | |
.options({ | |
dynamicTyping: true, | |
header: true, | |
skipEmptyLines: true | |
}) | |
// production mode only | |
if (process.env.NODE_ENV === 'production') { | |
const hash = !filenameHashing ? '' : '.[contenthash:8]' | |
const scriptFullPath = path.join(dest.script, `[name]${hash}.js`) | |
const styleFullPath = path.join(dest.style, `[name]${hash}.css`) | |
config | |
.output | |
.filename(scriptFullPath) | |
.chunkFilename(scriptFullPath) | |
config | |
.plugin('extract-css') | |
.tap(args => { | |
args[0] = { | |
filename: styleFullPath, | |
chunkFilename: styleFullPath | |
} | |
return [...args] | |
}) | |
config | |
.plugin('banner') | |
.use(BannerPlugin, [{ banner, raw: true }]) | |
if (disableHtmlMinify) { | |
// Disable HtmlWebpackPlugin minify | |
const entries = Object.keys(config.entryPoints.entries() || []) | |
if (entries.length > 1) { | |
entries.forEach(entry => { | |
config | |
.plugin(`html-${entry}`) | |
.tap(args => { | |
args[0].minify = false | |
return [...args] | |
}) | |
}) | |
} else { | |
config | |
.plugin('html') | |
.tap(args => { | |
args[0].minify = false | |
return [...args] | |
}) | |
} | |
} | |
if (prerenderRoutes.length) { | |
// add Prerender SPA Plugin | |
// [cmd] npm i -D prerender-spa-plugin | |
// [doc] https://github.com/chrisvfritz/prerender-spa-plugin | |
const PrerenderSPAPlugin = require('prerender-spa-plugin') | |
config | |
.plugin('prerender') | |
.use(PrerenderSPAPlugin, [ | |
{ | |
staticDir: path.join(__dirname, 'dist'), | |
indexPath: path.join(__dirname, 'dist', publicPath, 'index.html'), | |
routes: prerenderRoutes.map(route => `${publicPath}${route}`.replace(/\/+/g, '/')), | |
server: { | |
port: 8080 | |
}, | |
renderer: new PrerenderSPAPlugin.PuppeteerRenderer({ | |
// WSL ubuntu | |
// [issue] https://github.com/puppeteer/puppeteer/issues/1837 | |
// [Dependencies] https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#chrome-headless-doesnt-launch-on-unix | |
// args: [ | |
// '--no-sandbox', | |
// // '--disable-setuid-sandbox', | |
// // '--disable-gpu', | |
// '--single-process' | |
// ], | |
inject: {}, | |
headless: false, | |
// renderAfterDocumentEvent: 'prerender' // document.dispatchEvent(new Event('prerender')) | |
renderAfterElementExists: '[prerender]' // add "prerender" attribute on the root element of the vue route component | |
}), | |
postProcess (renderedRoute) { | |
// [issue] https://github.com/chrisvfritz/prerender-spa-plugin/issues/198 | |
// [issue] https://github.com/chrisvfritz/prerender-spa-plugin/issues/335 | |
const html = renderedRoute.html.split('</head>') | |
html[0] = html[0].replace(/<script.*src="[^<]([0-9a-zA-Z/-])+\.js".*><\/script>/g, function (target) { | |
console.log(`\n\n[clean unscript] ${target}`) | |
return '' | |
}) | |
renderedRoute.html = html.join('</head>') | |
return renderedRoute | |
} | |
} | |
]) | |
} | |
} | |
} | |
// -------------------------------------------------- | |
// Export vue configuration | |
// -------------------------------------------------- | |
module.exports = { | |
publicPath, | |
outputDir, | |
filenameHashing, | |
runtimeCompiler, | |
css: { | |
loaderOptions: { | |
sass: { | |
prependData: sassInjectionData // use "data" option if sass-loader < v8.0.0 | |
} | |
} | |
}, | |
devServer, | |
// pwa, | |
configureWebpack, | |
chainWebpack | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -------------------------------------------------- | |
// RWD media query generator | |
// -------------------------------------------------- | |
@mixin rwd ($device) { | |
@media only screen and (#{$device}) { | |
@content; | |
} | |
} | |
// -------------------------------------------------- | |
// inject pseudo element ":before" for vertical-align | |
// -------------------------------------------------- | |
@mixin use-vertical-align ($align: middle) { | |
@if ($align == "none") { | |
&:before { | |
content: normal; | |
width: auto; | |
height: auto; | |
} | |
} @else { | |
&:before { | |
content: ""; | |
display: inline-block; | |
width: 0; | |
height: 100%; | |
vertical-align: $align; | |
} | |
} | |
} | |
// -------------------------------------------------- | |
// inject pseudo element ":before" as space placeholder | |
// -------------------------------------------------- | |
@mixin use-fixed-ratio ($width: none, $height: none) { | |
@if ($width == "none") { | |
&:before { | |
content: normal; | |
display: inline; | |
padding: 0; | |
} | |
} @else if ($width != "none") and ($height == "none") { | |
&:before { | |
content: ""; | |
display: block; | |
width: 100%; | |
height: 0; | |
padding-top: 100%; | |
} | |
} @else { | |
&:before { | |
content: ""; | |
display: block; | |
width: 100%; | |
height: 0; | |
padding-top: ($height / $width) * 100%; | |
} | |
} | |
} | |
// -------------------------------------------------- | |
// apply multi-formats blur for browsers compatibility | |
// -------------------------------------------------- | |
@mixin use-filter-blur($radius: 4) { | |
// filter: url("data:image/svg+xml;utf8,<svg height=\"0\" xmlns=\"http://www.w3.org/2000/svg\"><filter id=\"svg-filter-blur\" x=\"-5%\" y=\"-5%\" width=\"110%\" height=\"110%\"><feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"#{$radius}\"/></filter></svg>#svg-filter-blur"); | |
filter: url("data:image/svg+xml;utf-8;base64,PHN2ZyBoZWlnaHQ9IjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGZpbHRlciBpZD0ic3ZnLWZpbHRlci1ibHVyIiB4PSItNSUiIHk9Ii01JSIgd2lkdGg9IjExMCUiIGhlaWdodD0iMTEwJSI+PGZlR2F1c3NpYW5CbHVyIGluPSJTb3VyY2VHcmFwaGljIiBzdGREZXZpYXRpb249IjQiLz48L2ZpbHRlcj48L3N2Zz4=#svg-filter-blur"); | |
filter: blur(#{$radius}px); | |
filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius="#{$radius}"); | |
} | |
// -------------------------------------------------- | |
// auto inject z-index from $AUTO_LAYERS in variables.scss | |
// -------------------------------------------------- | |
@mixin use-layer($self) { | |
@if not index($AUTO_LAYERS, "#{$self}") { | |
@error "#{$self} is not defined in $AUTO_LAYERS." | |
} | |
z-index: index($AUTO_LAYERS, "#{$self}") | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -------------------------------------------------- | |
// RWD devices information | |
// -------------------------------------------------- | |
$RWD_MOBILE: "min-width: 0"; | |
$RWD_MOBILE_ONLY: "max-width: 519px"; | |
$RWD_TABLET_SMALL: "min-width: 520px"; | |
$RWD_TABLET: "min-width: 760px"; | |
$RWD_DESKTOP: "min-width: 960px"; | |
$RWD_DESKTOP_WIDE: "min-width: 1280px"; | |
// -------------------------------------------------- | |
// Generate z-index automatically in order with "@use-layer" mixin | |
// -------------------------------------------------- | |
$AUTO_LAYERS: ( | |
'.the-content', | |
'.the-header', | |
'.the-menu' | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment