Files
roll20-character-sheets/backbone/source/scaffold/index.js
T

148 lines
5.2 KiB
JavaScript

const path = require('path');
const {pathToFileURL} = require('url');
const fs = require('fs/promises');
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
const pug = require('pug');
const sass = require('sass-embedded');
// Object to hold the last edited times of each file to prevent extraneous renders
const getTemplate = (filePath) => fs.readFile(filePath,'utf8')
.then(text => text.replace(/include k-scaffold/g,'include /node_modules/k-scaffold/_k.pug'));
const outputPug = async (html,destination) => {
if(!destination) return;
const destDir = path.dirname(destination);
await fs.mkdir(destDir,{recursive:true});
await fs.writeFile(destination,html);
console.log(`HTML written to ${destination}`);
const dom = new JSDOM(html);
const { window } = dom;
const { document } = window;
const transArr = [...document.querySelectorAll('[data-i18n]')];
const attrTransKeys = ['title','placeholder','label','aria-label','alt','list']
const attrTransArr = attrTransKeys.reduce((memo,key) => [...new Set([
...memo,
...document.querySelectorAll(`[data-i18n-${key}]`)
])] ,[]);
const translations = transArr.reduce((memo,el)=>{
memo[el.dataset.i18n] = el.textContent?.trim() || el.dataset.i18n;
return memo;
},{});
const capitalizeI18n = string => string.replace(/(?:^|\s+|\/|-)[a-z]/ig,(letter)=>letter.toUpperCase()).replace(/-/g,'');
attrTransArr.forEach(el => {
attrTransKeys.forEach(key => {
if(el.dataset[`i18n${capitalizeI18n(key)}`]){
translations[el.dataset[`i18n${capitalizeI18n(key)}`]] = el[key] || el.dataset[`i18n${capitalizeI18n(key)}`];
}
});
});
if(translations){
const transPath = path.resolve(path.dirname(destination),'translation.json');
const currTranslation = await fs.readFile(transPath,'utf8')
.then(t => JSON.parse(t))
.catch( e => {return {}});
Object.entries(translations)
.forEach(([key,val]) => {
if(!currTranslation[key]){
currTranslation[key] = val;
}
});
await fs.writeFile(transPath,JSON.stringify(currTranslation,null,2));
console.log(`${transPath} updated`);
}
};
const renderPug = async ({source,destination,options={suppressStack:true}}) => {
const template = await getTemplate(source);
try{
console.log('original source',source);
console.log('manipulated filename',source);
const html = pug.render(template,{pretty:true,...options,filename:source,basedir:path.dirname(process.argv[1])})
await outputPug(html,destination);
return html;
}catch(err){
if(err.message.endsWith('kScript mixin already used. Kscript should be the final mixin used in the sheet code.')){
console.log('\x1b[31m%s\x1b[0m',
'================================\n==== K-scaffold PARSE ERROR ====\n================================');
console.log('kScript mixin already used. Kscript should be the final mixin used in the sheet code.');
}else{
console.log('\x1b[31m%s\x1b[0m',
'=========================\n==== PUG PARSE ERROR ====\n=========================');
if(options.suppressStack){
console.error(err.message)
}else{
console.log('showing full err');
console.error(err);
}
}
}
};
const renderSASS = async ({source,destination,options={}}) => {
const dirname = path.dirname(process.argv[1]);
const compileOptions = {
charset:false,
importers: [
{
findFileUrl(url) {
if(!url.startsWith('k/')) return null;
const sub = url.substring(2);
const fileURL = pathToFileURL('node_modules/k-scaffold');
const newURL = new URL(sub, pathToFileURL('node_modules/k-scaffold/'));
return newURL;
}
}
]
};
const currOptions = {...options};
if(currOptions.importers){
compileOptions.importers.push(...currOptions.importers);
delete currOptions.importers;
}
Object.assign(compileOptions,currOptions);
const {css} = await sass.compileAsync(source,compileOptions);
if(destination){
await fs.writeFile(destination,css);
console.log(`CSS written to ${destination}`);
}
return css
};
const renderAll = async ({source ='./',destination,pugOptions={suppressStack:true},scssOptions={}}) => {
const sourceDir = path.dirname(source);
const destDir = path.dirname(destination);
const files = await fs.opendir(source);
const pugOutput = [];
const scssOutput = [];
for await (entry of files){
if(entry.isFile()){
const resSource = path.resolve(source,entry.name);
const resDest = destination ?
path.resolve(destination,entry.name.replace(/\.pug$/,'.html').replace(/\.scss$/,'.css')) :
undefined;
if(entry.name.endsWith('.scss')){
console.log(`Processing ${entry.name}`);
const newSass = await renderSASS({source:resSource,destination:resDest,options:scssOptions});
scssOutput.push(newSass);
}
if(entry.name.endsWith('.pug')){
console.log(`Processing ${entry.name}`);
const newPug = await renderPug({source:resSource,destination:resDest,options:pugOptions});
pugOutput.push(newPug);
}
}
}
return [pugOutput,scssOutput];
};
module.exports = {
pug:renderPug,
scss:renderSASS,
all:renderAll
};