Merge pull request #299 from vector-im/bwindels/skinning
Basic skinning support
This commit is contained in:
commit
f4bb95f459
2 changed files with 83 additions and 11 deletions
22
doc/SKINNING.md
Normal file
22
doc/SKINNING.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Replacing javascript files
|
||||||
|
|
||||||
|
Any source file can be replaced at build time by mapping the path in a JSON file passed in to the build command, e.g. `yarn build --override-imports customizations.json`. The file should be written like so:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"src/platform/web/ui/session/room/timeline/TextMessageView.js": "src/platform/web/ui/session/room/timeline/MyTextMessageView.js"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The paths are relative to the location of the mapping file, but the mapping file should be in a parent directory of the files you want to replace.
|
||||||
|
|
||||||
|
You should see a "replacing x with y" line (twice actually, for the normal and legacy build).
|
||||||
|
|
||||||
|
# Injecting CSS
|
||||||
|
|
||||||
|
You can override the location of the main css file with the `--override-css <file>` option to the build script. The default is `src/platform/web/ui/css/main.css`, which you probably want to import from your custom css file like so:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import url('src/platform/web/ui/css/main.css');
|
||||||
|
|
||||||
|
/* additions */
|
||||||
|
```
|
|
@ -50,12 +50,17 @@ const cssSrcDir = path.join(projectDir, "src/platform/web/ui/css/");
|
||||||
const parameters = new commander.Command();
|
const parameters = new commander.Command();
|
||||||
parameters
|
parameters
|
||||||
.option("--modern-only", "don't make a legacy build")
|
.option("--modern-only", "don't make a legacy build")
|
||||||
|
.option("--override-imports <json file>", "pass in a file to override import paths, see doc/SKINNING.md")
|
||||||
|
.option("--override-css <main css file>", "pass in an alternative main css file")
|
||||||
parameters.parse(process.argv);
|
parameters.parse(process.argv);
|
||||||
|
|
||||||
async function build({modernOnly}) {
|
async function build({modernOnly, overrideImports, overrideCss}) {
|
||||||
// get version number
|
// get version number
|
||||||
const version = JSON.parse(await fs.readFile(path.join(projectDir, "package.json"), "utf8")).version;
|
const version = JSON.parse(await fs.readFile(path.join(projectDir, "package.json"), "utf8")).version;
|
||||||
|
let importOverridesMap;
|
||||||
|
if (overrideImports) {
|
||||||
|
importOverridesMap = await readImportOverrides(overrideImports);
|
||||||
|
}
|
||||||
const devHtml = await fs.readFile(path.join(projectDir, "index.html"), "utf8");
|
const devHtml = await fs.readFile(path.join(projectDir, "index.html"), "utf8");
|
||||||
const doc = cheerio.load(devHtml);
|
const doc = cheerio.load(devHtml);
|
||||||
const themes = [];
|
const themes = [];
|
||||||
|
@ -70,12 +75,12 @@ async function build({modernOnly}) {
|
||||||
// copy olm assets
|
// copy olm assets
|
||||||
const olmAssets = await copyFolder(path.join(projectDir, "lib/olm/"), assets.directory);
|
const olmAssets = await copyFolder(path.join(projectDir, "lib/olm/"), assets.directory);
|
||||||
assets.addSubMap(olmAssets);
|
assets.addSubMap(olmAssets);
|
||||||
await assets.write(`hydrogen.js`, await buildJs("src/main.js", ["src/platform/web/Platform.js"]));
|
await assets.write(`hydrogen.js`, await buildJs("src/main.js", ["src/platform/web/Platform.js"], importOverridesMap));
|
||||||
if (!modernOnly) {
|
if (!modernOnly) {
|
||||||
await assets.write(`hydrogen-legacy.js`, await buildJsLegacy("src/main.js", [
|
await assets.write(`hydrogen-legacy.js`, await buildJsLegacy("src/main.js", [
|
||||||
'src/platform/web/legacy-polyfill.js',
|
'src/platform/web/legacy-polyfill.js',
|
||||||
'src/platform/web/LegacyPlatform.js'
|
'src/platform/web/LegacyPlatform.js'
|
||||||
]));
|
], importOverridesMap));
|
||||||
await assets.write(`worker.js`, await buildJsLegacy("src/platform/web/worker/main.js", ['src/platform/web/worker/polyfill.js']));
|
await assets.write(`worker.js`, await buildJsLegacy("src/platform/web/worker/main.js", ['src/platform/web/worker/polyfill.js']));
|
||||||
}
|
}
|
||||||
// copy over non-theme assets
|
// copy over non-theme assets
|
||||||
|
@ -86,7 +91,7 @@ async function build({modernOnly}) {
|
||||||
// creates the directories where the theme css bundles are placed in,
|
// creates the directories where the theme css bundles are placed in,
|
||||||
// and writes to assets, so the build bundles can translate them, so do it first
|
// and writes to assets, so the build bundles can translate them, so do it first
|
||||||
await copyThemeAssets(themes, assets);
|
await copyThemeAssets(themes, assets);
|
||||||
await buildCssBundles(buildCssLegacy, themes, assets);
|
await buildCssBundles(buildCssLegacy, themes, assets, overrideCss);
|
||||||
await buildManifest(assets);
|
await buildManifest(assets);
|
||||||
// all assets have been added, create a hash from all assets name to cache unhashed files like index.html
|
// all assets have been added, create a hash from all assets name to cache unhashed files like index.html
|
||||||
assets.addToHashForAll("index.html", devHtml);
|
assets.addToHashForAll("index.html", devHtml);
|
||||||
|
@ -177,11 +182,15 @@ async function buildHtml(doc, version, baseConfig, globalHash, modernOnly, asset
|
||||||
await assets.writeUnhashed("index.html", doc.html());
|
await assets.writeUnhashed("index.html", doc.html());
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildJs(mainFile, extraFiles = []) {
|
async function buildJs(mainFile, extraFiles, importOverrides) {
|
||||||
// create js bundle
|
// create js bundle
|
||||||
|
const plugins = [multi(), removeJsComments({comments: "none"})];
|
||||||
|
if (importOverrides) {
|
||||||
|
plugins.push(overridesAsRollupPlugin(importOverrides));
|
||||||
|
}
|
||||||
const bundle = await rollup({
|
const bundle = await rollup({
|
||||||
input: extraFiles.concat(mainFile),
|
input: extraFiles.concat(mainFile),
|
||||||
plugins: [multi(), removeJsComments({comments: "none"})]
|
plugins
|
||||||
});
|
});
|
||||||
const {output} = await bundle.generate({
|
const {output} = await bundle.generate({
|
||||||
format: 'es',
|
format: 'es',
|
||||||
|
@ -192,7 +201,7 @@ async function buildJs(mainFile, extraFiles = []) {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildJsLegacy(mainFile, extraFiles = []) {
|
async function buildJsLegacy(mainFile, extraFiles, importOverrides) {
|
||||||
// compile down to whatever IE 11 needs
|
// compile down to whatever IE 11 needs
|
||||||
const babelPlugin = babel.babel({
|
const babelPlugin = babel.babel({
|
||||||
babelHelpers: 'bundled',
|
babelHelpers: 'bundled',
|
||||||
|
@ -212,13 +221,18 @@ async function buildJsLegacy(mainFile, extraFiles = []) {
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
const plugins = [multi(), commonjs()];
|
||||||
|
if (importOverrides) {
|
||||||
|
plugins.push(overridesAsRollupPlugin(importOverrides));
|
||||||
|
}
|
||||||
|
plugins.push(nodeResolve(), babelPlugin);
|
||||||
// create js bundle
|
// create js bundle
|
||||||
const rollupConfig = {
|
const rollupConfig = {
|
||||||
// important the extraFiles come first,
|
// important the extraFiles come first,
|
||||||
// so polyfills are available in the global scope
|
// so polyfills are available in the global scope
|
||||||
// if needed for the mainfile
|
// if needed for the mainfile
|
||||||
input: extraFiles.concat(mainFile),
|
input: extraFiles.concat(mainFile),
|
||||||
plugins: [multi(), commonjs(), nodeResolve(), babelPlugin]
|
plugins
|
||||||
};
|
};
|
||||||
const bundle = await rollup(rollupConfig);
|
const bundle = await rollup(rollupConfig);
|
||||||
const {output} = await bundle.generate({
|
const {output} = await bundle.generate({
|
||||||
|
@ -298,8 +312,11 @@ async function buildServiceWorker(swSource, version, globalHash, assets) {
|
||||||
await assets.writeUnhashed("sw.js", swSource);
|
await assets.writeUnhashed("sw.js", swSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildCssBundles(buildFn, themes, assets) {
|
async function buildCssBundles(buildFn, themes, assets, mainCssFile = null) {
|
||||||
const bundleCss = await buildFn(path.join(cssSrcDir, "main.css"));
|
if (!mainCssFile) {
|
||||||
|
mainCssFile = path.join(cssSrcDir, "main.css");
|
||||||
|
}
|
||||||
|
const bundleCss = await buildFn(mainCssFile);
|
||||||
await assets.write(`hydrogen.css`, bundleCss);
|
await assets.write(`hydrogen.css`, bundleCss);
|
||||||
for (const theme of themes) {
|
for (const theme of themes) {
|
||||||
const themeRelPath = `themes/${theme}/`;
|
const themeRelPath = `themes/${theme}/`;
|
||||||
|
@ -488,4 +505,37 @@ class AssetMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function readImportOverrides(filename) {
|
||||||
|
const json = await fs.readFile(filename, "utf8");
|
||||||
|
const mapping = new Map(Object.entries(JSON.parse(json)));
|
||||||
|
return {
|
||||||
|
basedir: path.dirname(path.resolve(filename))+path.sep,
|
||||||
|
mapping
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function overridesAsRollupPlugin(importOverrides) {
|
||||||
|
const {mapping, basedir} = importOverrides;
|
||||||
|
return {
|
||||||
|
name: "rewrite-imports",
|
||||||
|
resolveId (source, importer) {
|
||||||
|
let file;
|
||||||
|
if (source.startsWith(path.sep)) {
|
||||||
|
file = source;
|
||||||
|
} else {
|
||||||
|
file = path.join(path.dirname(importer), source);
|
||||||
|
}
|
||||||
|
if (file.startsWith(basedir)) {
|
||||||
|
const searchPath = file.substr(basedir.length);
|
||||||
|
const replacingPath = mapping.get(searchPath);
|
||||||
|
if (replacingPath) {
|
||||||
|
console.info(`replacing ${searchPath} with ${replacingPath}`);
|
||||||
|
return path.join(basedir, replacingPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
build(parameters).catch(err => console.error(err));
|
build(parameters).catch(err => console.error(err));
|
||||||
|
|
Reference in a new issue