Skip to content

Instantly share code, notes, and snippets.

@panoply
Created June 19, 2019 07:46
Show Gist options
  • Save panoply/88a49a95633b342672d43df88252cbe8 to your computer and use it in GitHub Desktop.
Save panoply/88a49a95633b342672d43df88252cbe8 to your computer and use it in GitHub Desktop.
const { src, dest, watch, series, parallel, lastRun } = require('gulp')
const pipeline = require('stream').pipeline
const fs = require('fs')
const sass = require('gulp-sass')
const changed = require('gulp-changed')
const imagemin = require('gulp-imagemin')
const flatten = require('gulp-flatten')
const gulpif = require('gulp-if')
const cssnano = require('gulp-cssnano')
const rename = require('gulp-rename')
const svgsprite = require('gulp-svg-sprite')
const htmlmin = require('gulp-htmlmin')
const { rollup } = require('rollup')
const { uglify } = require('rollup-plugin-uglify')
const includepaths = require('rollup-plugin-includepaths')
const buble = require('rollup-plugin-buble')
const babel = require('rollup-plugin-babel')
const noderesolve = require('rollup-plugin-node-resolve')
const depinject = require('rollup-plugin-dep-inject')
const commonjs = require('rollup-plugin-commonjs')
const minimist = require('minimist')
const shopifysync = require('shopify-sync')
const del = require('del')
const bs = require('browser-sync').create()
const gulplogs = require('./build/logs/gulp-errors')
const rolluplogs = require('./build/logs/rollup-errors')
const print = require('./build/plugins/print')
const paths = require('./build/plugins/paths')
const icons = require('./build/plugins/icons')
/* -------------------------------------------- */
/* CONFIGURATION */
/* -------------------------------------------- */
const input = 'source'
const output = 'theme'
const config = {
scripts: {
src: `${input}/scripts/bundle.js`,
dist: `${output}/assets/bundle.js`,
watch: `${input}/scripts/**/*.js`,
external: [
'mithril',
'turbolinks',
'glider-js',
'stickybits'
],
globals: {
mithril: 'm',
turbolinks: 'Turbolinks',
'glider-js': 'Glider',
stickybits: 'stickybits'
}
},
styles: {
src: `${input}/styles/stylesheet.scss`,
dest: `${output}/assets/`,
watch: `${input}/styles/**/*.scss`,
include: [
'node_modules/',
'node_modules/bootstrap/scss/'
]
},
images: {
src: `${input}/images/**/**`,
dest: `${output}/assets/`
},
icons: {
src: {
inline: `${input}/icons/inline/*.svg`,
sprite: `${input}/icons/sprite/*.svg`
},
dest: `${output}/snippets`,
watch: `${input}/icons/**/*.svg`
},
schema: {
src: `${input}/schema/**/*.json`,
dest: `${output}`
},
views: {
src: `${input}/views/**/*.liquid`,
dest: `${output}`,
paths: {
prepend: {
layout: [
'theme.liquid'
]
},
skip: {
sections: [
'collection',
'index',
'layout',
'page',
'product'
]
}
}
}
}
/* -------------------------------------------- */
/* MINIMIST */
/* -------------------------------------------- */
const cli = minimist(process.argv.slice(2), {
boolean: [
'prod',
'dev',
'force'
],
alias: {
prod: 'production',
dev: 'development'
}
})
cli.dev = !cli.prod
/* -------------------------------------------- */
/* SERVE */
/* -------------------------------------------- */
function Serve () {
let domain, theme
const { targets } = JSON.parse(fs.readFileSync('.shopifysync'))
targets.map(target => {
if (target.target_name === 'development') {
domain = target.primary_domain
theme = target.theme_id
}
})
return bs.init({
proxy: `${domain}/?preview_theme_id=${theme}`,
logPrefix: print({
timestamp: true
}),
files: [
`${output}/assets/**`
],
serveStatic: [
`${output}/assets`
],
notify: true,
open: false,
https: {
key: './build/localhost.key',
cert: './build/localhost.crt'
},
reloadOnRestart: true,
ui: {
port: 4000
},
snippetOptions: {
rule: {
match: /<script/,
fn: (snippet, match) => snippet + match
}
},
rewriteRules: [
{
match: /(<link href=)(.*?)\/\/(cdn.shopify.com)(.*?)(stylesheet.css)/,
fn: () => '<link href="/stylesheet.css'
},
{
match: /(?:<script src="\/\/cdn.shopify.com.*?\/assets\/.*?.js)/,
fn: () => '<script src="/bundle.js'
}
]
})
}
/* -------------------------------------------- */
/* SHOPIFY SYNC */
/* -------------------------------------------- */
function Sync () {
return shopifysync('watch',
{
target: cli.prod ? 'production' : 'development',
ignore: [
'theme/assets/stylesheet.css.map',
'theme/assets/bundle.js.map'
]
},
function () {
return bs.reload()
})
}
/* -------------------------------------------- */
/* ICONS */
/* -------------------------------------------- */
function Icons (done) {
const task = config.icons
function sprite () {
return pipeline([
src(task.src.sprite, {
since: lastRun(sprite)
}),
icons({
task: 'sprite',
class: 'icon',
classPrefix: 'icon-'
},
{
plugins: [
{
removeUnknownsAndDefaults: {
keepDataAttrs: false
}
}
]
}),
svgsprite({
svg: {
xmlDeclaration: false,
doctypeDeclaration: false,
namespaceIDs: false,
dimensionAttributes: false,
rootAttributes: {
id: 'icons',
class: 'd-none'
},
transform: [
function (svg) {
let attr = 'data-turbolinks-permanent'
let match = `id="icons"`
let string = `${match} ${attr}`
let regex = new RegExp(match, 'gi')
svg = svg.replace(regex, string)
return svg
}
]
},
mode: {
shape: {
dimension: {
precision: 2,
attributes: false
}
},
symbol: {
inline: true,
dest: '.',
sprite: 'icons.liquid',
example: false
}
}
}),
changed(task.dest, {
hasChanged: changed.compareContents
}),
print(),
dest(task.dest)
],
gulplogs)
}
function inline () {
return pipeline([
src([
task.src.inline,
task.src.sprite
], {
since: lastRun(inline)
}),
icons({
class: 'icon',
classPrefix: 'icon-'
},
{
plugins: [
{
removeUnknownsAndDefaults: {
keepDataAttrs: false
}
},
{
addClassesToSVGElement: {
className: 'icon'
}
}
]
}),
rename({
prefix: 'icon.',
extname: '.liquid'
}),
changed(task.dest, {
hasChanged: changed.compareContents
}),
print(),
dest(task.dest)
],
gulplogs)
}
series(sprite, inline)(done)
}
/* -------------------------------------------- */
/* IMAGES */
/* -------------------------------------------- */
function Images () {
const task = config.images
return pipeline([
src(task.src, {
since: lastRun(Images)
}),
flatten(),
changed(task.src),
imagemin(),
changed(task.dest, {
hasChanged: changed.compareContents
}),
dest(task.dest)
],
gulplogs)
}
/* -------------------------------------------- */
/* SCRIPTS */
/* -------------------------------------------- */
async function Scripts () {
const task = config.scripts
const bundle = await rollup({
input: task.src,
external: cli.dev ? task.external : [],
plugins: [
cli.dev &&
depinject({
index: `${output}/layout/theme.liquid`,
attr: 'defer'
}),
includepaths({
paths: [
`${input}/scripts/`
]
}),
noderesolve(),
commonjs(),
babel({
exclude: 'node_modules/**'
}),
buble({
transforms: {
forOf: false
}
}),
cli.prod && uglify()
]
}).catch(rolluplogs)
await bundle
.write({
file: task.dist,
format: 'iife',
name: 'App',
globals: cli.dev ? task.globals : {},
sourcemap: cli.dev
})
.then(({ output }) => {
output.map(({ fileName }) => {
return print({
filename: fileName,
through2: false
})
})
})
}
/* -------------------------------------------- */
/* STYLES */
/* -------------------------------------------- */
function Styles () {
const task = config.styles
return pipeline([
src(task.src, {
sourcemaps: true
}),
sass({
outputStyle: 'compact',
includePaths: task.include
}),
gulpif(cli.prod,
cssnano({
autoprefixer: {
add: true,
browsers: []
}
})),
dest(task.dest, {
sourcemaps: '.'
}),
print(),
gulpif(cli.dev, bs.stream())
],
gulplogs)
}
/* -------------------------------------------- */
/* SCHEMA */
/* -------------------------------------------- */
function Schema () {
const task = config.schema
return pipeline([
src(task.src, {
since: lastRun(Schema)
}),
changed(task.dest, {
hasChanged: changed.compareContents
}),
print(),
dest(task.dest)
],
gulplogs)
}
/* -------------------------------------------- */
/* VIEWS */
/* -------------------------------------------- */
function Views () {
const task = config.views
return pipeline([
src(task.src, {
since: lastRun(Views),
sourcemaps: true
}),
paths(task.paths),
gulpif(cli.prod,
htmlmin({
minifyJS: true,
collapseWhitespace: true,
processScripts: 'text/template',
removeComments: true
})),
changed(task.dest, {
hasChanged: changed.compareContents
}),
print(),
dest(task.dest)
],
gulplogs).on('end', bs.reload)
}
/* -------------------------------------------- */
/* EXPORT */
/* -------------------------------------------- */
function Export () {
return pipeline([
src(`${output}/**/**`),
zip(cli.prod ? 'production.zip' : 'development.zip'),
dest('export')
],
gulplogs)
}
/* -------------------------------------------- */
/* CLEAN */
/* -------------------------------------------- */
function Clean () {
return del(`${output}/**/**`)
}
/* -------------------------------------------- */
/* WATCH */
/* -------------------------------------------- */
function Watch () {
watch(config.styles.watch, Styles)
watch(config.schema.src, Schema)
watch(config.views.src, Views)
watch(config.images.src, Images)
watch(config.scripts.watch, Scripts)
watch(config.icons.watch, Icons)
}
/* -------------------------------------------- */
/* EXPORT */
/* -------------------------------------------- */
exports.views = Export
exports.views = Views
exports.styles = Styles
exports.scripts = Scripts
exports.schema = Schema
exports.images = Images
exports.icons = Icons
exports.sync = Sync
exports.serve = Serve
exports.watch = Watch
exports.default = parallel(Watch, Serve, Sync)
exports.build = series(
Clean,
Styles,
Scripts,
Schema,
Views,
Images,
Icons,
Export
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment