Initial commit.
This commit is contained in:
commit
01d5e2b210
11 changed files with 7248 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
dist
|
||||
node_modules
|
1
README.md
Symbolic link
1
README.md
Symbolic link
|
@ -0,0 +1 @@
|
|||
content/index.md
|
9
content/index.md
Normal file
9
content/index.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# How To Use
|
||||
|
||||
1. Clone the project into a new one.
|
||||
2. Update dependencies (`npm outdated`, `npm update` and `ncu` which
|
||||
can be deployed with `npm install -g npm-check-updates`).
|
||||
3. Fill in `package.json`.
|
||||
4. Install the dependencies (`npm install`).
|
||||
4. Configure `src/config.js`.
|
||||
5. Develop content, layouts and add-ons...
|
32
layouts/default.njk
Normal file
32
layouts/default.njk
Normal file
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ site.title }} | {{ page.title }}</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="author" content="David Soulayrol">
|
||||
<meta name="generator" content="Metalsmith" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<link rel="stylesheet" href="/css/mini.css" />
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{% block content %}
|
||||
{{ contents | safe }}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<footer id="page_footer">
|
||||
{% block footer %}{% endblock %}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
6815
package-lock.json
generated
Normal file
6815
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
62
package.json
Normal file
62
package.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"name": "metalsmith-starter-kit",
|
||||
"version": "2021.08.24",
|
||||
"private": true,
|
||||
"description": "A kit to deploy new projects using Metalsmith and some common dependencies",
|
||||
"main": "",
|
||||
"scripts": {
|
||||
"build": "DEBUG=metalsmith* npm run clean && npm run build:metalsmith",
|
||||
"build:prod": "NODE_ENV=production npm run build",
|
||||
"build:metalsmith": "nodejs scripts/run.js build",
|
||||
"clean": "rimraf dist",
|
||||
"dev": "npm run build && DEBUG=metalsmith* nodemon scripts/run.js serve",
|
||||
"server": "npm run build && http-server dist",
|
||||
"deploy": "npm run build:prod && cd dist && rsync -v -rlptz --relative * $SERVER_PATH",
|
||||
"lint": "npm run lint:js && npm run lint:css",
|
||||
"lint:js": "eslint src test",
|
||||
"lint:css": "stylelint src/assets/css/**/*.css",
|
||||
"test": "npm run lint && npm run build"
|
||||
},
|
||||
"author": "David Soulayrol <david@soulayrol.name>",
|
||||
"dependencies": {
|
||||
"mini.css": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browser-sync": "^2.27.5",
|
||||
"bs-fullscreen-message": "^1.1.0",
|
||||
"clean-css": "^5.1.5",
|
||||
"cli-table2": "^0.2.0",
|
||||
"debug": "^4.3.2",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-standard": "4.1.0",
|
||||
"filesize": "^8.0.0",
|
||||
"http-server": "^13.0.1",
|
||||
"jstransformer-nunjucks": "^1.0.0",
|
||||
"metalsmith": "^2.3.0",
|
||||
"metalsmith-assets": "^0.1.0",
|
||||
"metalsmith-clean-css": "^6.1.3",
|
||||
"metalsmith-layouts": "2.3.1",
|
||||
"metalsmith-markdownit": "^0.5.0",
|
||||
"metalsmith-rename": "^1.0.0",
|
||||
"metalsmith-sitemap": "^1.2.2",
|
||||
"nodemon": "^2.0.12",
|
||||
"rimraf": "^3.0.2",
|
||||
"slug": "^5.1.0",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-standard": "^22.0.0"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"delay": 2500,
|
||||
"ignore": [
|
||||
"test/*",
|
||||
"docs/*"
|
||||
],
|
||||
"watch": [
|
||||
"scripts"
|
||||
]
|
||||
}
|
||||
}
|
20
scripts/config.js
Normal file
20
scripts/config.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
const { resolve, join } = require('path')
|
||||
|
||||
const hostname = 'http://example.com'
|
||||
|
||||
const projectRoot = resolve(__dirname, '..')
|
||||
const distribution = join(projectRoot, 'dist')
|
||||
|
||||
module.exports = {
|
||||
hostname,
|
||||
paths: {
|
||||
projectRoot,
|
||||
/* Nodes */
|
||||
nodeModules: join(projectRoot, 'node_modules'),
|
||||
/* Metalsmith */
|
||||
metalsmithSource: 'content',
|
||||
metalsmithDestination: distribution,
|
||||
/* Server */
|
||||
serverRoot: distribution
|
||||
}
|
||||
}
|
108
scripts/metalsmith-helpers.js
Normal file
108
scripts/metalsmith-helpers.js
Normal file
|
@ -0,0 +1,108 @@
|
|||
const path = require('path')
|
||||
const Table = require('cli-table2')
|
||||
const filesize = require('filesize')
|
||||
|
||||
function generateFileMap (files) {
|
||||
return Object.keys(files).reduce((map, filename) => {
|
||||
const file = files[filename]
|
||||
const parsedFilename = path.parse(filename)
|
||||
const ext = parsedFilename.ext.substr(1)
|
||||
const extFiles = map[ext] || []
|
||||
return {
|
||||
...map,
|
||||
[ext]: [
|
||||
...extFiles,
|
||||
{
|
||||
file,
|
||||
filename
|
||||
}
|
||||
]
|
||||
}
|
||||
}, {})
|
||||
}
|
||||
|
||||
export function StatisticsPlugin (options) {
|
||||
return (files, metalsmith, done) => {
|
||||
const fileMap = generateFileMap(files)
|
||||
const fileTypes = Object.keys(fileMap)
|
||||
|
||||
// File overview table
|
||||
fileTypes.forEach((filetype) => {
|
||||
const fileTypeFiles = fileMap[filetype]
|
||||
const count = fileTypeFiles.length
|
||||
const size = fileTypeFiles.reduce((totalsize, entry) => {
|
||||
// Some plugins (eg. metalsmith-data-markdown) replace the Buffer by a string
|
||||
if (typeof entry.file.contents === 'string') {
|
||||
return totalsize + entry.file.contents.length
|
||||
} else {
|
||||
return totalsize + entry.file.contents.byteLength
|
||||
}
|
||||
}, 0)
|
||||
const filenamesTable = new Table({
|
||||
head: [`${count} ${filetype}-${count > 1 ? 'files' : 'file'} with total ${filesize(size)}`, 'File size'],
|
||||
wordWrap: true,
|
||||
colWidths: [process.stdout.columns - 16, 12]
|
||||
})
|
||||
fileTypeFiles.forEach((entry) => {
|
||||
let size = 0
|
||||
// Some plugins (eg. metalsmith-data-markdown) replace the Buffer by a string
|
||||
if (typeof entry.file.contents === 'string') {
|
||||
size = entry.file.contents.length
|
||||
} else {
|
||||
size = entry.file.contents.byteLength
|
||||
}
|
||||
filenamesTable.push([entry.filename, size])
|
||||
})
|
||||
console.log(filenamesTable.toString())
|
||||
})
|
||||
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
export function DebugPlugin (options) {
|
||||
function sanitizeTableContent (content) {
|
||||
const length = content.length
|
||||
content = content.replace(/\s+/g, ' ').slice(0, config.maxContentLength)
|
||||
if (length > config.maxContentLength) {
|
||||
content = content.trim() + '...'
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
maxContentLength: 1000
|
||||
}
|
||||
|
||||
const config = {
|
||||
...defaultOptions,
|
||||
...options
|
||||
}
|
||||
|
||||
return (files, metalsmith, done) => {
|
||||
const fileMap = generateFileMap(files)
|
||||
const fileTypes = Object.keys(fileMap)
|
||||
|
||||
fileTypes.forEach((filetype) => {
|
||||
const fileTypeFiles = fileMap[filetype]
|
||||
fileTypeFiles.forEach((entry) => {
|
||||
const content = sanitizeTableContent(entry.file.contents.toString())
|
||||
const size = filesize(entry.file.contents.byteLength)
|
||||
const metadata = {
|
||||
...entry.file
|
||||
}
|
||||
delete metadata.contents
|
||||
const fileTable = new Table({
|
||||
head: [`${entry.filename} @ ${size}`],
|
||||
wordWrap: true,
|
||||
colWidths: [process.stdout.columns - 2]
|
||||
})
|
||||
fileTable.push([JSON.stringify(metadata, null, 2)])
|
||||
fileTable.push([content])
|
||||
console.log(fileTable.toString())
|
||||
})
|
||||
})
|
||||
|
||||
done()
|
||||
}
|
||||
}
|
63
scripts/metalsmith-statistics-plugin.js
Normal file
63
scripts/metalsmith-statistics-plugin.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
const path = require('path')
|
||||
const Table = require('cli-table2')
|
||||
const filesize = require('filesize')
|
||||
|
||||
module.exports = plugin
|
||||
|
||||
function generateFileMap (files) {
|
||||
return Object.keys(files).reduce((map, filename) => {
|
||||
const file = files[filename]
|
||||
const parsedFilename = path.parse(filename)
|
||||
const ext = parsedFilename.ext.substr(1)
|
||||
const extFiles = map[ext] || []
|
||||
return {
|
||||
...map,
|
||||
[ext]: [
|
||||
...extFiles,
|
||||
{
|
||||
file,
|
||||
filename
|
||||
}
|
||||
]
|
||||
}
|
||||
}, {})
|
||||
}
|
||||
|
||||
function plugin () {
|
||||
return (files, metalsmith, done) => {
|
||||
const fileMap = generateFileMap(files)
|
||||
const fileTypes = Object.keys(fileMap)
|
||||
|
||||
// File overview table
|
||||
fileTypes.forEach((filetype) => {
|
||||
const fileTypeFiles = fileMap[filetype]
|
||||
const count = fileTypeFiles.length
|
||||
const size = fileTypeFiles.reduce((totalsize, entry) => {
|
||||
// Some plugins (eg. metalsmith-data-markdown) replace the Buffer by a string
|
||||
if (typeof entry.file.contents === 'string') {
|
||||
return totalsize + entry.file.contents.length
|
||||
} else {
|
||||
return totalsize + entry.file.contents.byteLength
|
||||
}
|
||||
}, 0)
|
||||
const filenamesTable = new Table({
|
||||
head: [`${count} ${filetype}-${count > 1 ? 'files' : 'file'} with total ${filesize(size)}`, 'File size'],
|
||||
wordWrap: true,
|
||||
colWidths: [process.stdout.columns - 16, 12]
|
||||
})
|
||||
fileTypeFiles.forEach((entry) => {
|
||||
var size = 0
|
||||
// Some plugins (eg. metalsmith-data-markdown) replace the Buffer by a string
|
||||
if (typeof entry.file.contents === 'string') {
|
||||
size = entry.file.contents.length
|
||||
} else {
|
||||
size = entry.file.contents.byteLength
|
||||
}
|
||||
filenamesTable.push([entry.filename, size])
|
||||
})
|
||||
console.log(filenamesTable.toString())
|
||||
})
|
||||
|
||||
done()
|
||||
}
|
||||
}
|
65
scripts/metalsmith.js
Normal file
65
scripts/metalsmith.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
/* This is the actual metalsmith configuration script. */
|
||||
const assets = require('metalsmith-assets')
|
||||
const cleanCSS = require('metalsmith-clean-css')
|
||||
const config = require('./config.js')
|
||||
const layouts = require('metalsmith-layouts')
|
||||
const Metalsmith = require('metalsmith')
|
||||
const markdown = require('metalsmith-markdownit')
|
||||
const rename = require('metalsmith-rename')
|
||||
const path = require('path')
|
||||
const sitemap = require('metalsmith-sitemap')
|
||||
const slug = require('slug')
|
||||
const statistics = require('./metalsmith-statistics-plugin')
|
||||
|
||||
const __PROD__ = process.env.NODE_ENV === 'production'
|
||||
|
||||
module.exports = new Metalsmith(config.paths.projectRoot)
|
||||
.clean(__PROD__)
|
||||
.metadata({
|
||||
remove_diatrics: function (e) { return slug(e, {mode: 'rfc3986' }) }
|
||||
})
|
||||
.source(config.paths.metalsmithSource)
|
||||
.destination(config.paths.metalsmithDestination)
|
||||
.use(cleanCSS({
|
||||
files: 'content/css/*.css',
|
||||
cleanCSS: {
|
||||
rebase: true
|
||||
}
|
||||
}))
|
||||
.use(assets({
|
||||
source: './assets/' + (process.env.NODE_ENV || 'dev'),
|
||||
destination: './'
|
||||
}))
|
||||
.use(markdown({
|
||||
html: true,
|
||||
typographer: true,
|
||||
quotes: ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'],
|
||||
plugin: {
|
||||
pattern: '**/*.md',
|
||||
// fields: ['contents', 'excerpt']
|
||||
extension: 'njk'
|
||||
}
|
||||
}))
|
||||
.use(layouts({
|
||||
default: 'default.njk',
|
||||
pattern: '**/*.njk',
|
||||
engineOptions: {
|
||||
filters: {
|
||||
setAttribute: (dictionary, key, value) => {
|
||||
dictionary[key] = value
|
||||
return dictionary
|
||||
}
|
||||
},
|
||||
globals: {
|
||||
production: __PROD__
|
||||
}
|
||||
}
|
||||
}))
|
||||
.use(rename([
|
||||
[/\.njk$/, '.html']
|
||||
]))
|
||||
.use(sitemap({
|
||||
changefreq: 'yearly',
|
||||
hostname: config.hostname
|
||||
}))
|
||||
.use(statistics())
|
71
scripts/run.js
Normal file
71
scripts/run.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
const bs = require('browser-sync').create('Metalsmith')
|
||||
const config = require('./config.js')
|
||||
const debug = require('debug')('Run')
|
||||
const metalsmith = require('./metalsmith')
|
||||
const path = require('path')
|
||||
const strip = require('strip-ansi')
|
||||
|
||||
function build (sync) {
|
||||
debug('Building Metalsmith')
|
||||
metalsmith.build((err) => {
|
||||
if (err) {
|
||||
debug('Metalsmith build error:')
|
||||
debug(err)
|
||||
if (sync) {
|
||||
return bs.sockets.emit('fullscreen:message', {
|
||||
title: 'Metalsmith Error:',
|
||||
body: strip(`${err.message}\n\n${err.stack}`),
|
||||
timeout: 100000
|
||||
})
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
debug('Metalsmith build finished!')
|
||||
if (sync) {
|
||||
bs.reload()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function serve () {
|
||||
bs.init({
|
||||
server: config.paths.serverRoot,
|
||||
port: 8080,
|
||||
ui: {
|
||||
port: 9000
|
||||
},
|
||||
open: false,
|
||||
logLevel: 'debug',
|
||||
logPrefix: 'BrowserSync',
|
||||
logConnections: true,
|
||||
logFileChanges: true,
|
||||
notify: true,
|
||||
files: [{
|
||||
match: [
|
||||
path.resolve(config.paths.projectRoot, 'content', '**', '*'),
|
||||
path.resolve(config.paths.projectRoot, 'layouts', '**', '*.njk')
|
||||
],
|
||||
fn: function (event, file) {
|
||||
build(true)
|
||||
},
|
||||
options: {
|
||||
ignored: ['**/.#*', '**/*~', '**/#*#']
|
||||
// /\.#|node_modules|~$/
|
||||
}
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2)
|
||||
|
||||
switch (args[0]) {
|
||||
case 'build':
|
||||
build()
|
||||
break
|
||||
case 'serve':
|
||||
serve()
|
||||
break
|
||||
default:
|
||||
console.log('Unknown arguments "' + args[0] + '"')
|
||||
}
|
Loading…
Reference in a new issue