Skip to content
This repository was archived by the owner on Nov 29, 2022. It is now read-only.

feat: precompile assets on build #2091

Merged
merged 15 commits into from
Feb 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ css/**/*.css
yarn.lock
cypress/videos/*
/public/styles/octicons/octicons.html
/precompiled/
3 changes: 2 additions & 1 deletion lib/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
25 changes: 25 additions & 0 deletions middleware/browserify-opts.js
Original file line number Diff line number Diff line change
@@ -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'
]
})
}
}

26 changes: 2 additions & 24 deletions middleware/browserify.js
Original file line number Diff line number Diff line change
@@ -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 changes: 2 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -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"
},
Expand Down
1 change: 1 addition & 0 deletions script/integration.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
npm run precompile-assets
npm start &
wait-on http://localhost:5000
cypress run --record --key a0cba5c6-0650-4abe-8d41-d990bb7a0a66
90 changes: 90 additions & 0 deletions script/precompile-assets.js
Original file line number Diff line number Diff line change
@@ -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()
}
2 changes: 1 addition & 1 deletion scripts/apply-active-class-to-active-links.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { escapeRegExp } = require('lodash')
const escapeRegExp = require('lodash/escapeRegExp')

module.exports = function applyActiveClassToActiveLinks () {
const topPath = `/${location.pathname.split('/')[1]}`
Expand Down
2 changes: 1 addition & 1 deletion scripts/create-filter-list.js
Original file line number Diff line number Diff line change
@@ -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')
Expand Down
2 changes: 1 addition & 1 deletion scripts/lazy-load-images.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { throttle } = require('lodash')
const throttle = require('lodash/throttle')

function inViewport (element) {
const { top, right, bottom, left } = element.getBoundingClientRect()
Expand Down
10 changes: 8 additions & 2 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
2 changes: 1 addition & 1 deletion test/localization.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'))
Expand Down