diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 000000000..d1dfac4f2 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,4 @@ +{ + "undef": true, + "unused": true +} diff --git a/Makefile b/Makefile index 34fb57883..adc6f2a68 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,6 @@ imports_src/globals/globals.js := \ imports_src/config/Config.coffee := \ src/Archive/archives.json imports_src/css/CSS.js := \ - tools/style.js \ node_modules/font-awesome/package.json imports_src/Monitoring/Favicon.coffee := \ src/meta/icon128.png diff --git a/src/css/CSS.js b/src/css/CSS.js index fc17f4e7f..2bb0209df 100644 --- a/src/css/CSS.js +++ b/src/css/CSS.js @@ -1,7 +1,16 @@ -CSS = { +<% + var inc = require['style']; + var faCSS = read('/node_modules/font-awesome/css/font-awesome.css'); + var faWebFont = readBase64('/node_modules/font-awesome/fonts/fontawesome-webfont.woff'); + var mainCSS = ['font-awesome', 'style', 'yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon'].map(x => read(`${x}.css`)).join(''); + var iconNames = files.filter(f => /^linkify\.[^.]+\.png$/.test(f)); + var icons = iconNames.map(readBase64); +%>CSS = { boards: -<%= multiline(require['style']()()) %>, +<%= multiline( + inc.fa(faCSS, faWebFont) + mainCSS + inc.icons(iconNames, icons) + read('supports.css') +) %>, report: <%= multiline(read('report.css')) %>, diff --git a/src/css/style.inc b/src/css/style.inc new file mode 100644 index 000000000..ac391e658 --- /dev/null +++ b/src/css/style.inc @@ -0,0 +1,40 @@ +/* jshint esnext: true */ + +// == Reprocess Font Awesome CSS == // +var fa = (css, font) => ( + +// Font Awesome CSS attribution and license +css.match(/\/\*\![^]*?\*\//)[0] + '\n' + + +// Font Awesome web font +`@font-face { + font-family: FontAwesome; + src: url('data:application/font-woff;base64,${font}') format('woff'); + font-weight: 400; + font-style: normal; +} +` + + +// fa-[icon name] classes +css + .match(/(\.fa-[^{]*{\s*content:[^}]*}\s*)+/)[0] + .replace(/([,{;])\s+/g, '$1') + .replace(/,/g, ', ') + +); + +// == Create CSS for Link Title Favicons == // +var icons = (names, data) => ( + +'/* Link Title Favicons */\n' + +names.map((file, i) => +`.linkify.${file.split('.')[1]} { + background: transparent url('data:image/png;base64,${data[i]}') center left no-repeat!important; + padding-left: 18px; +} +` +).join('') + +); + +return {fa, icons}; diff --git a/tools/.jshintrc b/tools/.jshintrc index 427373449..0c9613612 100644 --- a/tools/.jshintrc +++ b/tools/.jshintrc @@ -2,6 +2,5 @@ "esnext": true, "undef": true, "unused": true, - "node": true, - "-W083": true + "node": true } diff --git a/tools/style.js b/tools/style.js deleted file mode 100644 index 6642ba3d8..000000000 --- a/tools/style.js +++ /dev/null @@ -1,48 +0,0 @@ -var fs = require('fs'); - -var read = filename => fs.readFileSync(filename, 'utf8').replace(/\r\n/g, '\n'); -var readBase64 = filename => fs.readFileSync(filename).toString('base64'); - -module.exports = () => ( - -// Font Awesome CSS attribution and license -read('node_modules/font-awesome/css/font-awesome.css').match(/\/\*\![^]*?\*\//)[0] + '\n' + - -// Font Awesome web font -`@font-face { - font-family: FontAwesome; - src: url('data:application/font-woff;base64,${readBase64('node_modules/font-awesome/fonts/fontawesome-webfont.woff')}') format('woff'); - font-weight: 400; - font-style: normal; -} -` + - -// fa-[icon name] classes -read('node_modules/font-awesome/css/font-awesome.css') - .match(/(\.fa-[^{]*{\s*content:[^}]*}\s*)+/)[0] - .replace(/([,{;])\s+/g, '$1') - .replace(/,/g, ', ') + - -[ - 'font-awesome', - 'style', - 'yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon' -].map( - name => read(`src/css/${name}.css`) -).join('') + - -'/* Link Title Favicons */\n' + -fs.readdirSync('src/css').filter(file => - /^linkify\.[^.]+\.png$/.test(file) -).map(file => -`.linkify.${file.split('.')[1]} { - background: transparent url('data:image/png;base64,${readBase64(`src/css/${file}`)}') center left no-repeat!important; - padding-left: 18px; -} -` -).join('') + - -// XXX Moved to end of stylesheet to avoid breaking whole stylesheet in Maxthon. -read('src/css/supports.css') - -); diff --git a/tools/template.js b/tools/template.js index c93ba7351..4c62011a7 100644 --- a/tools/template.js +++ b/tools/template.js @@ -1,3 +1,4 @@ +/* jshint evil: true */ var fs = require('fs'); var path = require('path'); var _template = require('lodash.template'); @@ -9,11 +10,6 @@ var _templateSettings = {interpolate: /<%=([\s\S]+?)%>/g}; // Functions used in templates. var tools = {}; -tools.require = {}; -for (var m of ['style']) { - tools.require[m] = () => require(`./${m}`); -} - var read = tools.read = filename => fs.readFileSync(filename, 'utf8').replace(/\r\n/g, '\n'); var readJSON = tools.readJSON = filename => JSON.parse(read(filename)); tools.readBase64 = filename => fs.readFileSync(filename).toString('base64'); @@ -179,25 +175,43 @@ tools.assert = function(statement) { return `throw new Error 'Assertion failed: ' + ${constExpression(statement)} unless ${statement}`; }; -function resolvePath(filename, templateName) { - if (filename[0] === '/') { - return path.join(process.cwd(), filename); - } +function includesDir(templateName) { var dir = path.dirname(templateName); var subdir = path.basename(templateName).replace(/\.[^.]+$/, ''); if (fs.readdirSync(dir).indexOf(subdir) >= 0) { - return path.join(dir, subdir, filename); + return path.join(dir, subdir); } else { - return path.join(dir, filename); + return dir; } } +function resolvePath(includeName, templateName) { + var dir; + if (includeName[0] === '/') { + dir = process.cwd(); + } else { + dir = includesDir(templateName); + } + return path.join(dir, includeName); +} + function wrapTool(tool, templateName) { - return function(filename) { - return tool(resolvePath(filename, templateName)); + return function(includeName) { + return tool(resolvePath(includeName, templateName)); }; } +function loadModules(templateName) { + var dir = includesDir(templateName); + var moduleNames = fs.readdirSync(dir).filter(f => /\.inc$/.test(f)); + var modules = {}; + for (var name of moduleNames) { + var code = read(path.join(dir, name)); + modules[name.replace(/\.inc$/, '')] = new Function(code)(); + } + return modules; +} + // Import variables from package.json. var pkg = readJSON('package.json'); @@ -214,6 +228,8 @@ function interpolate(text, data, filename) { context[key] = data[key]; } } + context.files = fs.readdirSync(includesDir(filename)); + context.require = loadModules(filename); return _template(text, _templateSettings)(context); }