feat: precompile assets on build (#2091)

This commit is contained in:
Michelle Tilley 2019-02-14 12:34:18 -08:00 коммит произвёл GitHub
Родитель 053e634f97
Коммит 7eea09be86
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 139 добавлений и 37 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -9,3 +9,4 @@ css/**/*.css
yarn.lock
cypress/videos/*
/public/styles/octicons/octicons.html
/precompiled/

Просмотреть файл

@ -2,7 +2,8 @@ require('require-yaml')
const i18n = require('electron-i18n')
const flat = require('flat')
const { get, set } = require('lodash')
const get = require('lodash/get')
const set = require('lodash/set')
const locales = Object.keys(i18n.locales)
const websiteStrings = require('../data/locale.yml')
const websiteKeys = Object.keys(flat(websiteStrings))

Просмотреть файл

@ -0,0 +1,25 @@
const nodeModulesToAvoidBabelifying = [
'lodash',
'lunr',
'prettydate'
]
const excludeRegex = new RegExp(`/node_modules/(${nodeModulesToAvoidBabelifying.join('|')})`)
module.exports = function doBrowserify (browserify) {
return function (entry) {
return browserify(entry, {
transform: [
['babelify', {
global: true,
exclude: excludeRegex,
presets: [
['@babel/preset-env', { targets: '> 0.25%, not dead' }]
]
}],
'brfs'
]
})
}
}

Просмотреть файл

@ -1,26 +1,4 @@
const browserify = require('browserify-middleware')
const browserifyOptions = require('./browserify-opts')
const nodeModulesToAvoidBabelifying = [
'lodash',
'lunr',
'prettydate'
]
const excludeRegex = new RegExp(`/node_modules/(${nodeModulesToAvoidBabelifying.join('|')})`)
function babelifyMiddleware (entry) {
return browserify(entry, {
transform: [
['babelify', {
global: true,
exclude: excludeRegex,
presets: [
['@babel/preset-env', { targets: '> 0.25%, not dead' }]
]
}],
'brfs'
]
})
}
module.exports = babelifyMiddleware
module.exports = browserifyOptions(browserify)

7
package-lock.json сгенерированный
Просмотреть файл

@ -13590,7 +13590,6 @@
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
"integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
"optional": true,
"requires": {
"commander": "~2.17.1",
"source-map": "~0.6.1"
@ -13599,14 +13598,12 @@
"commander": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
"optional": true
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"optional": true
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},

Просмотреть файл

@ -15,10 +15,12 @@
"release": "node script/release",
"dev": "cross-env NODE_PATH=. NODE_ENV=development nodemon server.js",
"prepack": "check-for-leaks",
"precompile-assets": "cross-env NODE_ENV=production node script/precompile-assets.js",
"linkschecker": "NODE_PATH=. NODE_ENV=test node scripts/links-checker.js",
"cypress": "cypress run",
"lint": "standard --fix",
"generate-octicons": "node ./script/generate-octicons.js"
"generate-octicons": "node ./script/generate-octicons.js",
"heroku-postbuild": "npm run precompile-assets"
},
"husky": {
"hooks": {
@ -99,6 +101,7 @@
"standard": "^12.0.1",
"supertest": "^3.4.2",
"supertest-session": "^3.3.0",
"uglify-js": "^3.4.9",
"wait-on": "^3.1.0",
"walk-sync": "^1.1.3"
},

Просмотреть файл

@ -1,3 +1,4 @@
npm run precompile-assets
npm start &
wait-on http://localhost:5000
cypress run --record --key a0cba5c6-0650-4abe-8d41-d990bb7a0a66

Просмотреть файл

@ -0,0 +1,90 @@
const path = require('path')
const stream = require('stream')
const fs = require('fs-extra')
const browserify = require('browserify')
const browserifyOptions = require('../middleware/browserify-opts')
const sass = require('node-sass')
const uglify = require('uglify-js')
function dir (...parts) {
return path.join(__dirname, '..', ...parts)
}
function uglifyStream () {
const buffers = []
return new stream.Transform({
transform (chunk, _encoding, callback) {
buffers.push(chunk)
callback()
},
flush (callback) {
const code = Buffer.concat(buffers).toString()
const compiled = uglify.minify(code)
this.push(compiled.code)
callback()
}
})
}
const PATHS = {
precompiled: dir('precompiled'),
scripts: dir('precompiled', 'scripts'),
styles: dir('precompiled', 'styles'),
nodeModules: dir('node_modules'),
jsEntry: dir('scripts', 'index.js'),
jsDestination: dir('precompiled', 'scripts', 'index.js'),
cssEntry: dir('public', 'styles', 'index.scss'),
cssDestination: dir('precompiled', 'styles', 'index.css')
}
async function precompileAssets () {
try {
console.log('Creating directories...')
await fs.remove(PATHS.precompiled)
await fs.ensureDir(PATHS.scripts)
await fs.ensureDir(PATHS.styles)
console.log('Precompiling JS...')
await precompileJavaScript()
console.log('Precompiling CSS...')
await precompileCss()
} catch (err) {
console.error(err)
process.exit(1)
}
}
function precompileJavaScript () {
return new Promise((resolve, reject) => {
const b = browserifyOptions(browserify)(PATHS.jsEntry)
const pipe = b.bundle()
.pipe(uglifyStream())
.pipe(fs.createWriteStream(PATHS.jsDestination))
pipe.on('error', reject)
pipe.on('finish', resolve)
})
}
function precompileCss () {
return new Promise((resolve, reject) => {
sass.render({
file: PATHS.cssEntry,
includePaths: [
PATHS.nodeModules
]
}, async function onSassCompiled (err, result) {
if (err) {
return reject(err)
}
await fs.writeFile(PATHS.cssDestination, result.css)
resolve()
})
})
}
if (require.main === module) {
precompileAssets()
}

Просмотреть файл

@ -1,4 +1,4 @@
const { escapeRegExp } = require('lodash')
const escapeRegExp = require('lodash/escapeRegExp')
module.exports = function applyActiveClassToActiveLinks () {
const topPath = `/${location.pathname.split('/')[1]}`

Просмотреть файл

@ -1,4 +1,4 @@
const { debounce } = require('lodash')
const debounce = require('lodash/debounce')
const lunr = require('lunr')
const queryString = require('query-string')
const setQueryString = require('set-query-string')

Просмотреть файл

@ -1,4 +1,4 @@
const { throttle } = require('lodash')
const throttle = require('lodash/throttle')
function inViewport (element) {
const { top, right, bottom, left } = element.getBoundingClientRect()

Просмотреть файл

@ -45,8 +45,14 @@ app.set('views', path.join(__dirname, '/views'))
app.use(nakedRedirect(true, 'www', 302))
app.use(compression())
app.use(helmet())
app.use(sass())
app.use('/scripts/index.js', browserify('scripts/index.js'))
if (process.env.NODE_ENV === 'production') {
console.log('Production app detected; serving JS and CSS from disk')
app.use(express.static(path.join(__dirname, 'precompiled'), { redirect: false }))
} else {
console.log('Dev app detected; compiling JS and CSS in memory')
app.use(sass())
app.use('/scripts/index.js', browserify('scripts/index.js'))
}
app.get('/service-worker.js', (req, res) => res.sendFile(path.resolve(__dirname, 'scripts', 'service-worker.js')))
app.use(cookieParser())
app.use(requestLanguage({

Просмотреть файл

@ -8,7 +8,7 @@ const fs = require('fs')
const path = require('path')
const walk = require('walk-sync')
const flat = require('flat')
const getProp = require('lodash').get
const getProp = require('lodash/get')
const locale = require(path.join(__dirname, '../data/locale.yml'))
const views = walk.entries(path.join(__dirname, '../views'))