diff --git a/.gitignore b/.gitignore index e9307c18..b6c3cebc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules /__sapper__ /mastodon /mastodon.log +/src/template.html /static/*.css /static/robots.txt /static/inline-script.js.map diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe31756a..0a324a40 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,6 +21,10 @@ To run a dev server with hot reloading: Now it's running at `localhost:4002`. +**Linux users:** for file changes to work, +you'll probably want to run `export CHOKIDAR_USEPOLLING=1` +because of [this issue](https://github.com/paulmillr/chokidar/issues/237). + ## Linting Pinafore uses [JavaScript Standard Style](https://standardjs.com/). @@ -106,4 +110,4 @@ Check `mastodon.log` if you have any issues. ## Unit tests There are also some unit tests that run in Node using Mocha. You can find them in `tests/unit` and -run them using `npm run test-unit`. \ No newline at end of file +run them using `npm run test-unit`. diff --git a/bin/build-inline-script.js b/bin/build-inline-script.js index 547bfd8d..789fe24b 100644 --- a/bin/build-inline-script.js +++ b/bin/build-inline-script.js @@ -10,12 +10,11 @@ import replace from 'rollup-plugin-replace' import fromPairs from 'lodash-es/fromPairs' import { themes } from '../src/routes/_static/themes' -const readFile = pify(fs.readFile.bind(fs)) const writeFile = pify(fs.writeFile.bind(fs)) const themeColors = fromPairs(themes.map(_ => ([_.name, _.color]))) -async function main () { +export async function buildInlineScript () { let inlineScriptPath = path.join(__dirname, '../inline-script.js') let bundle = await rollup({ @@ -36,25 +35,13 @@ async function main () { sourcemap: true }) - let fullCode = `${code}\n//# sourceMappingURL=/inline-script.js.map` - + let fullCode = `${code}//# sourceMappingURL=/inline-script.js.map` let checksum = crypto.createHash('sha256').update(fullCode).digest('base64') - let checksumFilepath = path.join(__dirname, '../inline-script-checksum.json') - await writeFile(checksumFilepath, JSON.stringify({ checksum }), 'utf8') + await writeFile(path.resolve(__dirname, '../inline-script-checksum.json'), + JSON.stringify({ checksum }), 'utf8') + await writeFile(path.resolve(__dirname, '../static/inline-script.js.map'), + map.toString(), 'utf8') - let htmlTemplateFilepath = path.join(__dirname, '../src/template.html') - let htmlTemplateFile = await readFile(htmlTemplateFilepath, 'utf8') - htmlTemplateFile = htmlTemplateFile.replace( - /[\s\S]+/, - '' - ) - await writeFile(htmlTemplateFilepath, htmlTemplateFile, 'utf8') - - await writeFile(path.resolve(__dirname, '../static/inline-script.js.map'), map.toString(), 'utf8') + return '' } - -main().catch(err => { - console.error(err) - process.exit(1) -}) diff --git a/bin/build-sass.js b/bin/build-sass.js index dc4177d6..77cbef23 100755 --- a/bin/build-sass.js +++ b/bin/build-sass.js @@ -1,40 +1,21 @@ #!/usr/bin/env node -const sass = require('node-sass') -const chokidar = require('chokidar') -const path = require('path') -const debounce = require('lodash/debounce') -const fs = require('fs') -const pify = require('pify') +import sass from 'node-sass' +import path from 'path' +import fs from 'fs' +import pify from 'pify' + const writeFile = pify(fs.writeFile.bind(fs)) const readdir = pify(fs.readdir.bind(fs)) -const readFile = pify(fs.readFile.bind(fs)) const render = pify(sass.render.bind(sass)) -const now = require('performance-now') const globalScss = path.join(__dirname, '../scss/global.scss') const defaultThemeScss = path.join(__dirname, '../scss/themes/_default.scss') const offlineThemeScss = path.join(__dirname, '../scss/themes/_offline.scss') const customScrollbarScss = path.join(__dirname, '../scss/custom-scrollbars.scss') -const htmlTemplateFile = path.join(__dirname, '../src/template.html') -const scssDir = path.join(__dirname, '../scss') const themesScssDir = path.join(__dirname, '../scss/themes') const assetsDir = path.join(__dirname, '../static') -function doWatch () { - let start = now() - chokidar.watch(scssDir).on('change', debounce(() => { - console.log('Recompiling SCSS...') - Promise.all([ - compileGlobalSass(), - compileThemesSass() - ]).then(() => { - console.log('Recompiled SCSS in ' + (now() - start) + 'ms') - }) - }, 500)) - chokidar.watch() -} - async function renderCss (file) { return (await render({ file, outputStyle: 'compressed' })).css } @@ -44,16 +25,9 @@ async function compileGlobalSass () { let offlineStyle = (await renderCss(offlineThemeScss)) let scrollbarStyle = (await renderCss(customScrollbarScss)) - let html = await readFile(htmlTemplateFile, 'utf8') - html = html.replace(/[\s\S]+/, - `\n` + - `\n` + + return `\n` + `\n` + - `\n` + - `` - ) - - await writeFile(htmlTemplateFile, html, 'utf8') + `\n` } async function compileThemesSass () { @@ -65,14 +39,7 @@ async function compileThemesSass () { })) } -async function main () { - await Promise.all([compileGlobalSass(), compileThemesSass()]) - if (process.argv.includes('--watch')) { - doWatch() - } +export async function buildSass () { + let [ result ] = await Promise.all([compileGlobalSass(), compileThemesSass()]) + return result } - -Promise.resolve().then(main).catch(err => { - console.error(err) - process.exit(1) -}) diff --git a/bin/build-svg.js b/bin/build-svg.js index 9d89fc8b..de7c9245 100755 --- a/bin/build-svg.js +++ b/bin/build-svg.js @@ -1,17 +1,16 @@ #!/usr/bin/env node -const svgs = require('./svgs') -const path = require('path') -const fs = require('fs') -const pify = require('pify') -const SVGO = require('svgo') +import svgs from './svgs' +import path from 'path' +import fs from 'fs' +import pify from 'pify' +import SVGO from 'svgo' +import $ from 'cheerio' + const svgo = new SVGO() -const $ = require('cheerio') - const readFile = pify(fs.readFile.bind(fs)) -const writeFile = pify(fs.writeFile.bind(fs)) -async function main () { +export async function buildSvg () { let result = (await Promise.all(svgs.map(async svg => { let filepath = path.join(__dirname, '../', svg.src) let content = await readFile(filepath, 'utf8') @@ -25,18 +24,5 @@ async function main () { return $.xml($symbol) }))).join('\n') - result = `\n${result}\n` - - let htmlTemplateFilepath = path.join(__dirname, '../src/template.html') - let htmlTemplateFile = await readFile(htmlTemplateFilepath, 'utf8') - htmlTemplateFile = htmlTemplateFile.replace( - /[\s\S]+/, - '' + result + '' - ) - await writeFile(htmlTemplateFilepath, htmlTemplateFile, 'utf8') + return `\n${result}\n` } - -main().catch(err => { - console.error(err) - process.exit(1) -}) diff --git a/bin/build-template-html.js b/bin/build-template-html.js new file mode 100644 index 00000000..8d3e0dd3 --- /dev/null +++ b/bin/build-template-html.js @@ -0,0 +1,106 @@ +import chokidar from 'chokidar' +import fs from 'fs' +import path from 'path' +import pify from 'pify' +import { buildSass } from './build-sass' +import { buildInlineScript } from './build-inline-script' +import { buildSvg } from './build-svg' +import now from 'performance-now' +import debounce from 'lodash-es/debounce' + +const writeFile = pify(fs.writeFile.bind(fs)) + +const DEBOUNCE = 500 + +const builders = [ + { + watch: 'scss', + comment: '', + rebuild: buildSass + }, + { + watch: 'inline-script.js', + comment: '', + rebuild: buildInlineScript + }, + { + watch: 'bin/svgs.js', + comment: '', + rebuild: buildSvg + } +] + +// array of strings and builder functions, we build this on-the-fly +const partials = buildPartials() + +function buildPartials () { + let rawTemplate = fs.readFileSync(path.resolve(__dirname, '../src-build/template.html'), 'utf8') + + let partials = [rawTemplate] + + builders.forEach(builder => { + for (let i = 0; i < partials.length; i++) { + let partial = partials[i] + if (typeof partial !== 'string') { + continue + } + let idx = partial.indexOf(builder.comment) + if (idx !== -1) { + partials.splice( + i, + 1, + partial.substring(0, idx), + builder, + partial.substring(idx + builder.comment.length) + ) + break + } + } + }) + + return partials +} + +function doWatch () { + // rebuild each of the partials on-the-fly if something changes + partials.forEach(partial => { + if (typeof partial === 'string') { + return + } + + chokidar.watch(partial.watch).on('change', debounce(path => { + console.log(`Detected change in ${path}...`) + delete partial.result + buildAll() + }), DEBOUNCE) + }) +} + +async function buildAll () { + let start = now() + let html = (await Promise.all(partials.map(async partial => { + if (typeof partial === 'string') { + return partial + } + if (!partial.result) { + partial.result = partial.comment + '\n' + (await partial.rebuild()) + } + return partial.result + }))).join('') + + await writeFile(path.resolve(__dirname, '../src/template.html'), html, 'utf8') + let end = now() + console.log(`Built template.html in ${(end - start).toFixed(2)}ms`) +} + +async function main () { + await buildAll() + if (process.argv.includes('--watch')) { + doWatch() + } +} + +main().catch(err => { + console.error(err) + process.exit(1) +}) diff --git a/package.json b/package.json index 0be8fd59..8c6c1253 100644 --- a/package.json +++ b/package.json @@ -5,19 +5,16 @@ "scripts": { "lint": "standard && standard --plugin html 'src/routes/**/*.html'", "lint-fix": "standard --fix && standard --fix --plugin html 'src/routes/**/*.html'", - "dev": "run-s build-svg build-inline-script serve-dev", - "serve-dev": "run-p --race build-sass-watch sapper-dev", + "dev": "run-p --race build-template-html-watch sapper-dev", "sapper-dev": "cross-env NODE_ENV=development PORT=4002 sapper dev", "sapper-prod": "cross-env PORT=4002 node __sapper__/build", "build": "cross-env NODE_ENV=production npm run build-steps", - "build-steps": "run-s build-sass build-svg build-inline-script sapper-build", + "build-steps": "run-s build-template-html sapper-build", "sapper-build": "sapper build", "start": "cross-env NODE_ENV=production npm run sapper-prod", "build-and-start": "run-s build start", - "build-svg": "node ./bin/build-svg.js", - "build-inline-script": "node -r esm ./bin/build-inline-script.js", - "build-sass": "node ./bin/build-sass.js", - "build-sass-watch": "node ./bin/build-sass.js --watch", + "build-template-html": "node -r esm ./bin/build-template-html.js", + "build-template-html-watch": "node -r esm ./bin/build-template-html.js --watch", "run-mastodon": "node -r esm ./bin/run-mastodon.js", "test": "cross-env BROWSER=chrome:headless npm run test-browser", "test-browser": "run-p --race run-mastodon build-and-start test-mastodon", @@ -163,6 +160,7 @@ "original-static", "scss", "src", + "src-build", "static", "package.json", "package-lock.json", diff --git a/scss/custom-scrollbars.scss b/scss/custom-scrollbars.scss index 52866158..c323dd36 100644 --- a/scss/custom-scrollbars.scss +++ b/scss/custom-scrollbars.scss @@ -31,4 +31,4 @@ html { ::-webkit-scrollbar-corner { background: var(--scrollbar-background-color); -} \ No newline at end of file +} diff --git a/src-build/template.html b/src-build/template.html new file mode 100644 index 00000000..ed1444fe --- /dev/null +++ b/src-build/template.html @@ -0,0 +1,58 @@ + + + + + + + + + %sapper.base% + + + + + + + + + + + + + + + %sapper.styles% + + + %sapper.head% + + + + + +
%sapper.html%
+ + +
+ + + + + + + + %sapper.scripts% + + diff --git a/src/template.html b/src/template.html deleted file mode 100644 index 85e667bd..00000000 --- a/src/template.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - %sapper.base% - - - - - - - - - - - - - - - - - - - %sapper.styles% - - - %sapper.head% - - - - - - -
%sapper.html%
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %sapper.scripts% - -