rework build script to handle noscript, pre-loading version, and more opengraph tags
This commit is contained in:
parent
86f298d62c
commit
ba363bbd22
|
@ -49,9 +49,7 @@
|
||||||
"unused-translations": "node ./scripts/translations/unused-translations.js",
|
"unused-translations": "node ./scripts/translations/unused-translations.js",
|
||||||
"test": "node scripts/TestSelenium.js",
|
"test": "node scripts/TestSelenium.js",
|
||||||
"test-rpc": "cd scripts/tests && node test-rpc",
|
"test-rpc": "cd scripts/tests && node test-rpc",
|
||||||
"template": "cd customize.dist/src && for page in ../index.html ../contact.html ../features.html ../../www/login/index.html ../../www/register/index.html ../../www/user/index.html;do echo $page; cp template.html $page; done;",
|
|
||||||
"evict-inactive": "node scripts/evict-inactive.js",
|
"evict-inactive": "node scripts/evict-inactive.js",
|
||||||
"make-opengraph": "node scripts/build.js",
|
"build": "node scripts/build.js"
|
||||||
"clean-opengraph": "rm -rf customize/www/"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
311
scripts/build.js
311
scripts/build.js
|
@ -3,29 +3,8 @@
|
||||||
var Fs = require("fs");
|
var Fs = require("fs");
|
||||||
var Fse = require("fs-extra");
|
var Fse = require("fs-extra");
|
||||||
var Path = require("path");
|
var Path = require("path");
|
||||||
|
var OS = require("os");
|
||||||
|
|
||||||
if (process.env.CRYPTPAD_CONFIG) {
|
|
||||||
/* using Fs.existsSync() function and __dirname variable here won't filter
|
|
||||||
environment variables like CRYPTPAD_CONFIG='/../docs/config2.js', while require()
|
|
||||||
inside load-config.js will, outputing some non intellegible error messages.
|
|
||||||
(plus, it won't handle missing '.js' in file names neither) */
|
|
||||||
try {
|
|
||||||
require.resolve(process.env.CRYPTPAD_CONFIG);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`The configuration file ${process.env.CRYPTPAD_CONFIG} can not
|
|
||||||
be loaded. Please review your CRYPTPAD_CONFIG environment variable.`
|
|
||||||
.replace(/\s{2,}/g, ' '));
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!Fs.existsSync(__dirname + '/../config/config.js')) {
|
|
||||||
console.error(`This script needs the file config/config.js to work properly.
|
|
||||||
You can make one by copying config/config.example.js. Check the
|
|
||||||
value of httpUnsafeOrigin for this script to behave as expected.`
|
|
||||||
.replace(/\s{2,}/g, ' '));
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var config = require("../lib/load-config");
|
var config = require("../lib/load-config");
|
||||||
|
|
||||||
var swap = function (s, o) {
|
var swap = function (s, o) {
|
||||||
|
@ -50,41 +29,88 @@ var swap = function (s, o) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const ogData = `
|
var Messages = require("../www/common/translations/messages.json");
|
||||||
<meta property="og:url" content="{{rootUrl}}/{{app}}/">
|
|
||||||
<meta property="og:type" content="website">
|
|
||||||
<meta property="og:title" content="{{title}}">
|
|
||||||
<meta property="og:description" content="CryptPad: end-to-end encrypted collaboration suite">
|
|
||||||
<meta property="og:image" content="{{rootUrl}}/customize/images/opengraph_preview/{{image}}">
|
|
||||||
<meta property="twitter:card" content="summary_large_image">`;
|
|
||||||
|
|
||||||
var previewExists = function (name) {
|
var types = Messages.type;
|
||||||
if (Fs.existsSync(__dirname + '/../customize/images/opengraph_preview/')) {
|
[ 'calendar', 'notifications', ].forEach(k => { types[k] = Messages[k]; });
|
||||||
return Fs.existsSync(__dirname + `/../customize/images/opengraph_preview/${name}`);
|
|
||||||
|
// FIXME it would be better if these were just included in the translated list of types
|
||||||
|
types.settings = Messages.settings_title;
|
||||||
|
types.support = Messages.supportPage;
|
||||||
|
types.profile = Messages.profilePage;
|
||||||
|
|
||||||
|
var translations;
|
||||||
|
try {
|
||||||
|
translations = Fs.readdirSync('./www/common/translations/').filter(name => {
|
||||||
|
return /messages\..*\.json$/.test(name);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var preferredLanguage = config.preferredLanguage;
|
||||||
|
var Preferred;
|
||||||
|
|
||||||
|
var noScriptContent = [ Messages.ui_jsRequired ];
|
||||||
|
translations.forEach(name => {
|
||||||
|
var path = `./www/common/translations/${name}`;
|
||||||
|
var content;
|
||||||
|
try {
|
||||||
|
content = JSON.parse(Fs.readFileSync(path, 'utf-8'));
|
||||||
|
} catch (err) {
|
||||||
|
return void console.error(`Failed to parse ${path}`);
|
||||||
}
|
}
|
||||||
return Fs.existsSync(__dirname + `/../customize.dist/images/opengraph_preview/${name}`);
|
|
||||||
|
if (name === `messages.${preferredLanguage}.json`) { Preferred = content; }
|
||||||
|
|
||||||
|
if (typeof(content.ui_jsRequired) !== 'string') { return; }
|
||||||
|
noScriptContent.push(content.ui_jsRequired);
|
||||||
|
});
|
||||||
|
|
||||||
|
var makeNoscript = (indent) => {
|
||||||
|
var lines = noScriptContent.map(s => {
|
||||||
|
return `${indent + indent}<p class="noscript">${s}</p>`;
|
||||||
|
}).join('\n');
|
||||||
|
return `${indent}<noscript>\n${lines}\n${indent}</noscript>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
var templateOG = function (a, type) {
|
var getKey = function (key, args) {
|
||||||
return swap(ogData, {
|
var source;
|
||||||
rootUrl: config.httpUnsafeOrigin,
|
|
||||||
app: a,
|
if (Preferred && Preferred[key]) {
|
||||||
title: type && `Encrypted ${type}` || 'CryptPad',
|
source = Preferred[key];
|
||||||
image: previewExists(`og-${a}.png`) && `og-${a}.png` || `og-default.png`
|
} else if (Messages && Messages[key]) {
|
||||||
|
source = Messages[key];
|
||||||
|
} else {
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(source) !== 'string') { return '?'; }
|
||||||
|
if (!Array.isArray(args)) { return source; }
|
||||||
|
|
||||||
|
return source.replace(/\{(\d+)\}/g, (str, p1) => {
|
||||||
|
if (['string', 'number'].includes(typeof(p1))) { return args[p1]; }
|
||||||
|
console.error("Only strings and numbers can be used in _getKey params.\nAborting...");
|
||||||
|
process.exit(1);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var insert = function (src, template) {
|
var previewExists = function (name) {
|
||||||
var matchs = src.match(/(<meta .*>)|(.*<\/title>)/g);
|
if (Fs.existsSync('./customize/images/opengraph_preview/')) {
|
||||||
|
return Fs.existsSync(`./customize/images/opengraph_preview/${name}`);
|
||||||
if (!matchs || !matchs.length) {
|
|
||||||
return src;
|
|
||||||
}
|
}
|
||||||
return src.replace(matchs.at(-1), `$& ${template}`);
|
return Fs.existsSync(`./customize.dist/images/opengraph_preview/${name}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
var buildPath = __dirname + '/../customize/www';
|
var imagePath = `/customize/images/opengraph_preview/`;
|
||||||
var tmpPath = __dirname + '/../CRYPTPAD_TEMP_BUILD';
|
|
||||||
|
var appImagePath = a => {
|
||||||
|
var partial = previewExists(`og-${a}.png`) && `og-${a}.png` || `og-default.png`;
|
||||||
|
return new URL(imagePath + partial, config.httpUnsafeOrigin).href;
|
||||||
|
};
|
||||||
|
|
||||||
|
var buildPath = Path.resolve('./customize');
|
||||||
|
var tmpPath = Path.join(OS.tmpdir(), '/CRYPTPAD_TEMP_BUILD/');
|
||||||
|
|
||||||
var write = function (content, dest) {
|
var write = function (content, dest) {
|
||||||
console.log(`Creating ${dest}`);
|
console.log(`Creating ${dest}`);
|
||||||
|
@ -93,15 +119,12 @@ var write = function (content, dest) {
|
||||||
var dirPath = Path.dirname(path);
|
var dirPath = Path.dirname(path);
|
||||||
Fse.mkdirpSync(dirPath);
|
Fse.mkdirpSync(dirPath);
|
||||||
Fs.writeFileSync(path, content);
|
Fs.writeFileSync(path, content);
|
||||||
|
console.log();
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("Creating target directories");
|
console.log("Creating target directories");
|
||||||
// remove tmp path so we start fresh
|
// remove tmp path so we start fresh
|
||||||
Fse.removeSync(tmpPath);
|
Fse.removeSync(tmpPath);
|
||||||
Fse.mkdirpSync(tmpPath);
|
|
||||||
|
|
||||||
var srcAppTypes = Fs.readFileSync(__dirname + '/../www/common/translations/messages.json', 'utf8');
|
|
||||||
var types = JSON.parse(srcAppTypes).type;
|
|
||||||
|
|
||||||
var appIndexesToBuild = [
|
var appIndexesToBuild = [
|
||||||
'sheet',
|
'sheet',
|
||||||
|
@ -117,24 +140,180 @@ var appIndexesToBuild = [
|
||||||
'file',
|
'file',
|
||||||
'calendar',
|
'calendar',
|
||||||
'drive',
|
'drive',
|
||||||
'teams'
|
'teams',
|
||||||
|
'contacts',
|
||||||
|
|
||||||
|
'notifications',
|
||||||
|
'checkup',
|
||||||
|
'file',
|
||||||
|
'profile',
|
||||||
|
'settings',
|
||||||
|
'support',
|
||||||
|
// bounce ??
|
||||||
];
|
];
|
||||||
|
|
||||||
|
var baseAppPath = './www/';
|
||||||
|
const ogData = `
|
||||||
|
<meta property="og:url" content="{{url}}">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:title" content="{{title}}">
|
||||||
|
<meta property="og:description" content="{{description}}">
|
||||||
|
<meta property="og:image" content="{{image}}">
|
||||||
|
<meta property="twitter:card" content="summary_large_image">`;
|
||||||
|
|
||||||
|
|
||||||
|
var versionString = String(+new Date());
|
||||||
|
|
||||||
|
var processPage = (src) => {
|
||||||
|
return src
|
||||||
|
.replace(/(\s*)<noscript>([\s\S]*)<\/noscript>/, (all, space /*, content */) => {
|
||||||
|
var indent = space.split('\n').filter(Boolean);
|
||||||
|
if (indent && indent[0]) {
|
||||||
|
return '\n' + makeNoscript(indent[0] || ' ');
|
||||||
|
}
|
||||||
|
return space + makeNoscript(' ');
|
||||||
|
})
|
||||||
|
.replace(/pre\-loading\.js\?ver=([^"]+)"/, (all, content) => {
|
||||||
|
return all.replace(content, versionString);
|
||||||
|
})
|
||||||
|
.replace(/pre\-loading\.css\?ver=([^"]+)"/, (all, content) => {
|
||||||
|
return all.replace(content, versionString);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var checkPage = (built, srcPath) => {
|
||||||
|
if (!/pre\-loading\.js/.test(built)) {
|
||||||
|
console.log(`no preloading js in ${srcPath}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (!/pre\-loading\.css/.test(built)) {
|
||||||
|
console.log(`no preloading css in ${srcPath}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/noscript/i.test(built)) {
|
||||||
|
console.error(`NO NOSCRIPT TAG FOR ${srcPath}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/<\/html>/.test(built)) {
|
||||||
|
console.log(`weird html in ${srcPath}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
appIndexesToBuild.forEach(function (app) {
|
appIndexesToBuild.forEach(function (app) {
|
||||||
console.log(`Parsing www/${app}/index.html`);
|
var srcPath = Path.resolve(Path.join(baseAppPath, app, 'index.html'));
|
||||||
var src = Fs.readFileSync(__dirname + `/../www/${app}/index.html`, 'utf8');
|
console.log(`Parsing ${srcPath}`);
|
||||||
|
var src = Fs.readFileSync(srcPath, 'utf8');
|
||||||
|
|
||||||
// rename types for shared documents (ones in place can sound weird)
|
// rename types for shared documents (ones in place can sound weird)
|
||||||
if (app === 'drive') { types[app] = 'Drive'; }
|
if (app === 'drive') { types[app] = Messages.fm_rootName; }
|
||||||
if (app === 'teams') { types[app] = 'Team drive'; }
|
if (app === 'teams') { types[app] = Messages.og_teamDrive; }
|
||||||
|
|
||||||
write(
|
var patt = /<\/title>/;
|
||||||
insert(src, templateOG(app, types[app])),
|
var type = types[app];
|
||||||
`${app}/index.html`
|
var built = processPage(src.replace(patt, (current) => {
|
||||||
);
|
return current + swap(ogData, {
|
||||||
|
url: new URL(`/${app}/`, config.httpUnsafeOrigin).href,
|
||||||
|
title: type && `Encrypted ${type}` || 'CryptPad',
|
||||||
|
image: appImagePath(app),
|
||||||
|
description: Messages.og_default,
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!/noscript/i.test(built)) {
|
||||||
|
console.error(`NO NOSCRIPT TAG FOR ${srcPath}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
write(built, `./www/${app}/index.html`);
|
||||||
|
|
||||||
|
// XXX preloading version for inner.html
|
||||||
});
|
});
|
||||||
|
|
||||||
Fse.removeSync(buildPath);
|
var instance;
|
||||||
var dirPath = Path.dirname(buildPath);
|
try {
|
||||||
Fse.mkdirpSync(dirPath);
|
instance = new URL(config.httpUnsafeOrigin).hostname;
|
||||||
Fse.renameSync(tmpPath, buildPath);
|
} catch (err) {
|
||||||
|
console.error("Failed to parse instance domain name\nAborting...");
|
||||||
|
return void process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
src: './www/register/index.html',
|
||||||
|
dest: './www/register/index.html',
|
||||||
|
url: '/register/',
|
||||||
|
title: getKey('og_register', [instance]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './www/login/index.html',
|
||||||
|
dest: './www/login/index.html',
|
||||||
|
url: '/login/',
|
||||||
|
title: getKey('og_login', [instance]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './customize.dist/contact.html',
|
||||||
|
dest: './contact.html',
|
||||||
|
url: '/contact.html',
|
||||||
|
title: getKey('og_contact', [instance]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './customize.dist/features.html',
|
||||||
|
dest: './features.html',
|
||||||
|
url: '/features.html',
|
||||||
|
title: getKey((config.allow_subscriptions? 'og_pricing': 'og_features'), [instance]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './customize.dist/index.html',
|
||||||
|
dest: './index.html',
|
||||||
|
url: '/index.html',
|
||||||
|
title: getKey('og_default'),
|
||||||
|
}
|
||||||
|
// TODO 404 ?
|
||||||
|
// TODO 500 ?
|
||||||
|
// TODO down ?
|
||||||
|
].forEach(obj => {
|
||||||
|
var srcPath = obj.src;
|
||||||
|
var destPath = obj.dest;
|
||||||
|
|
||||||
|
console.log(`Parsing ${srcPath}`);
|
||||||
|
var src = Fs.readFileSync(srcPath, 'utf8');
|
||||||
|
var patt = /<\/title>/;
|
||||||
|
var href = new URL(obj.url, config.httpUnsafeOrigin).href;
|
||||||
|
var built = processPage(src.replace(patt, (current) => {
|
||||||
|
return current + swap(ogData, {
|
||||||
|
url: href,
|
||||||
|
title: obj.title || "CryptPad",
|
||||||
|
image: new URL(imagePath + 'og-default.png', config.httpUnsafeOrigin).href,
|
||||||
|
description: Messages.og_default,
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
checkPage(built, srcPath);
|
||||||
|
|
||||||
|
write(built, destPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`Copying built files to target directory (${buildPath})`);
|
||||||
|
Fse.copySync(tmpPath, buildPath, {
|
||||||
|
overwrite: true,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to copy generated content to ${buildPath}`);
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`Removing temporary build directory (${tmpPath})`);
|
||||||
|
Fse.rmSync(tmpPath, {
|
||||||
|
recursive: true,
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
console.log(`Successfully removed ${tmpPath}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user