Skip to content
Draft
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
31 changes: 31 additions & 0 deletions lib/metalsmith-cache/cache.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const fs = require('fs')

const Metalsmith = require('metalsmith')

const plugin = require('./index.js')

describe('metalsmith-cache plugin', () => {
const cacheDir = 'lib/metalsmith-cache/fixtures/.cache'
let callCount = 0
const dummyPlugin = (files, metalsmith, done) => {
callCount++
done()
}

it('runs plugin on first build and creates a cache marker', () => {
callCount = 0
fs.rmSync(cacheDir, { recursive: true, force: true })

Metalsmith('lib/metalsmith-cache/fixtures')
.use(plugin('initial-test', '**/*.md', dummyPlugin, { cacheDir }))
.build((err) => {
if (err) {
throw err
}
expect(callCount).toBe(1)

const cached = fs.readdirSync(cacheDir)
expect(cached).toHaveLength(1)
})
})
})
2 changes: 2 additions & 0 deletions lib/metalsmith-cache/fixtures/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build
.cache
1 change: 1 addition & 0 deletions lib/metalsmith-cache/fixtures/src/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## Test file
49 changes: 49 additions & 0 deletions lib/metalsmith-cache/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const { createHash } = require('crypto')
const { existsSync, rmSync, mkdirSync, writeFileSync } = require('fs')
const { join } = require('path')

const { root } = require('../../config/paths.js')

/**
*
* @param {string} name - The name for the cached process
* @param {string | string[]} pattern - Minimatch pattern
* @param {Function} plugin - A Metalsmith plugin to run with caching
* @param {object} [options] - Plugin options
* @param {boolean} [options.isProduction] - Whether the build is in production mode
* @param {string} [options.cacheDir] - Directory to store cache files, defaults to '.cache'
* @returns {Function} - The caching plugin function
*/
module.exports = function metalsmithCache(name, pattern, plugin, options = {}) {
const cacheDir = options.cacheDir || '.cache'

return function (files, metalsmith, done) {
// never cache in production
if (options.isProduction) {
return plugin(files, metalsmith, done)
}

const cachePath = join(root, cacheDir, name)

const hash = createHash('md5')
.update(
metalsmith
.match(pattern)
.sort()
.map((filename) => filename + files[filename].contents)
.join('')
)
.digest('hex')

const marker = join(cachePath, `metalsmith-cache-${name}-${hash}`)

if (!existsSync(marker)) {
rmSync(cachePath, { recursive: true, force: true })
mkdirSync(cachePath, { recursive: true })
writeFileSync(marker, '')

return plugin(files, metalsmith, done)
}
return done()
}
}
70 changes: 43 additions & 27 deletions lib/metalsmith.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const sass = require('@metalsmith/sass') // convert Sass files to CSS using Dart
const { glob } = require('glob') // Match files using glob patterns
const Metalsmith = require('metalsmith') // static site generator
const canonical = require('metalsmith-canonical') // add a canonical url property to pages
const { default: tracer } = require('metalsmith-tracer') // debug logging

// Helpers and config
const { paths, navigation: menuItems } = require('../config')
Expand All @@ -26,14 +27,15 @@ const rebrandedNunjucksOptions = {
// Local metalsmith plugins
const { hashAssets } = require('./fingerprints') // rename files with hash fingerprints
const generateSitemap = require('./generate-sitemap.js') // generate sitemap
const cache = require('./metalsmith-cache') // cache plugin to skip expensive tasks
const lunr = require('./metalsmith-lunr-index') // generate search index
const renderMarkdown = require('./metalsmith-render-markdown')
const titleChecker = require('./metalsmith-title-checker.js')
const navigation = require('./navigation.js') // navigation plugin
const rollup = require('./rollup') // used to build GOV.UK Frontend JavaScript

// Static site generator
const metalsmith = Metalsmith(resolve(__dirname, '../'))
const metalsmith = tracer(Metalsmith(resolve(__dirname, '../')))

// Flag production mode (to skip plugins in development)
const isProduction = process.env.NODE_ENV !== 'development'
Expand Down Expand Up @@ -183,22 +185,28 @@ module.exports = metalsmith
done()
})

// render legacy templating syntax in source files
.use(
inPlace({
pattern: ['**/*.{md,njk}', '!**/branded.njk'],
transform: 'jstransformer-nunjucks',
engineOptions: nunjucksOptions
})
cache(
'rebrand',
['**/branded.njk'],
inPlace({
pattern: '**/branded.njk',
transform: 'jstransformer-nunjucks',
engineOptions: rebrandedNunjucksOptions
})
)
)

// render rebranded templating syntax in source files
.use(
inPlace({
pattern: '**/branded.njk',
transform: 'jstransformer-nunjucks',
engineOptions: rebrandedNunjucksOptions
})
cache(
'nunjucks',
['**/*.{md,njk}', '!**/branded.njk'],
inPlace({
pattern: ['**/*.{md,njk}', '!**/branded.njk'],
transform: 'jstransformer-nunjucks',
engineOptions: nunjucksOptions
})
)
)

// render markdown in source files and extract page headings
Expand Down Expand Up @@ -241,24 +249,32 @@ module.exports = metalsmith

// apply layouts to source files
.use(
layouts({
default: 'layout.njk',
directory: join(paths.views, 'layouts'),
pattern: ['**/*.html', '!**/branded/**/*.html'],
engineOptions: nunjucksOptions,
transform: 'nunjucks'
})
cache(
'layouts',
['**/*.html', '!**/branded/**/*.html'],
layouts({
default: 'layout.njk',
directory: join(paths.views, 'layouts'),
pattern: ['**/*.html', '!**/branded/**/*.html'],
engineOptions: nunjucksOptions,
transform: 'nunjucks'
})
)
)

// apply layouts to source files
.use(
layouts({
default: 'layout.njk',
directory: join(paths.views, 'layouts'),
pattern: '**/branded/**/*.html',
engineOptions: rebrandedNunjucksOptions,
transform: 'nunjucks'
})
cache(
'rebranded-layouts',
['**/branded/**/*.html'],
layouts({
default: 'layout.njk',
directory: join(paths.views, 'layouts'),
pattern: '**/branded/**/*.html',
engineOptions: rebrandedNunjucksOptions,
transform: 'nunjucks'
})
)
)

// generate a sitemap.xml in public/ folder
Expand Down
Loading