commit
02e422f3ac
14 changed files with 2453 additions and 64 deletions
|
@ -10,7 +10,7 @@
|
||||||
"lint": "eslint --cache src/",
|
"lint": "eslint --cache src/",
|
||||||
"lint-ci": "eslint src/",
|
"lint-ci": "eslint src/",
|
||||||
"test": "impunity --entry-point src/main.js --force-esm-dirs lib/ src/",
|
"test": "impunity --entry-point src/main.js --force-esm-dirs lib/ src/",
|
||||||
"start": "node scripts/serve-local.js",
|
"start": "snowpack dev --port 3000",
|
||||||
"build": "node --experimental-modules scripts/build.mjs",
|
"build": "node --experimental-modules scripts/build.mjs",
|
||||||
"postinstall": "node ./scripts/post-install.js"
|
"postinstall": "node ./scripts/post-install.js"
|
||||||
},
|
},
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
"eslint": "^7.25.0",
|
"eslint": "^7.25.0",
|
||||||
"fake-indexeddb": "^3.1.2",
|
"fake-indexeddb": "^3.1.2",
|
||||||
"finalhandler": "^1.1.1",
|
"finalhandler": "^1.1.1",
|
||||||
"impunity": "^1.0.0",
|
"impunity": "^1.0.1",
|
||||||
"mdn-polyfills": "^5.20.0",
|
"mdn-polyfills": "^5.20.0",
|
||||||
"node-html-parser": "^4.0.0",
|
"node-html-parser": "^4.0.0",
|
||||||
"postcss": "^8.1.1",
|
"postcss": "^8.1.1",
|
||||||
|
@ -51,6 +51,8 @@
|
||||||
"rollup": "^2.26.4",
|
"rollup": "^2.26.4",
|
||||||
"rollup-plugin-cleanup": "^3.1.1",
|
"rollup-plugin-cleanup": "^3.1.1",
|
||||||
"serve-static": "^1.13.2",
|
"serve-static": "^1.13.2",
|
||||||
|
"snowpack": "^3.8.3",
|
||||||
|
"typescript": "^4.3.5",
|
||||||
"xxhashjs": "^0.2.2"
|
"xxhashjs": "^0.2.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -15,6 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {build as snowpackBuild, loadConfiguration} from "snowpack"
|
||||||
import cheerio from "cheerio";
|
import cheerio from "cheerio";
|
||||||
import fsRoot from "fs";
|
import fsRoot from "fs";
|
||||||
const fs = fsRoot.promises;
|
const fs = fsRoot.promises;
|
||||||
|
@ -45,8 +46,11 @@ import flexbugsFixes from "postcss-flexbugs-fixes";
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
const projectDir = path.join(__dirname, "../");
|
const projectDir = path.join(__dirname, "../");
|
||||||
|
const snowpackOutPath = path.join(projectDir, "snowpack-build-output");
|
||||||
const cssSrcDir = path.join(projectDir, "src/platform/web/ui/css/");
|
const cssSrcDir = path.join(projectDir, "src/platform/web/ui/css/");
|
||||||
const srcDir = path.join(projectDir, "src/");
|
const snowpackConfig = await loadConfiguration({buildOptions: {out: snowpackOutPath}}, "snowpack.config.js");
|
||||||
|
const snowpackOutDir = snowpackConfig.buildOptions.out.substring(projectDir.length);
|
||||||
|
const srcDir = path.join(projectDir, `${snowpackOutDir}/src/`);
|
||||||
const isPathInSrcDir = path => path.startsWith(srcDir);
|
const isPathInSrcDir = path => path.startsWith(srcDir);
|
||||||
|
|
||||||
const parameters = new commander.Command();
|
const parameters = new commander.Command();
|
||||||
|
@ -56,14 +60,27 @@ parameters
|
||||||
.option("--override-css <main css file>", "pass in an alternative main css file")
|
.option("--override-css <main css file>", "pass in an alternative main css file")
|
||||||
parameters.parse(process.argv);
|
parameters.parse(process.argv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We use Snowpack to handle the translation of TypeScript
|
||||||
|
* into JavaScript. We thus can't bundle files straight from
|
||||||
|
* the src directory, since some of them are TypeScript, and since
|
||||||
|
* they may import Node modules. We thus bundle files after they
|
||||||
|
* have been processed by Snowpack. This function returns paths
|
||||||
|
* to the files that have already been pre-processed in this manner.
|
||||||
|
*/
|
||||||
|
function srcPath(src) {
|
||||||
|
return path.join(snowpackOutDir, 'src', src);
|
||||||
|
}
|
||||||
|
|
||||||
async function build({modernOnly, overrideImports, overrideCss}) {
|
async function build({modernOnly, overrideImports, overrideCss}) {
|
||||||
|
await snowpackBuild({config: snowpackConfig});
|
||||||
// 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;
|
let importOverridesMap;
|
||||||
if (overrideImports) {
|
if (overrideImports) {
|
||||||
importOverridesMap = await readImportOverrides(overrideImports);
|
importOverridesMap = await readImportOverrides(overrideImports);
|
||||||
}
|
}
|
||||||
const devHtml = await fs.readFile(path.join(projectDir, "index.html"), "utf8");
|
const devHtml = await fs.readFile(path.join(snowpackOutPath, "index.html"), "utf8");
|
||||||
const doc = cheerio.load(devHtml);
|
const doc = cheerio.load(devHtml);
|
||||||
const themes = [];
|
const themes = [];
|
||||||
findThemes(doc, themeName => {
|
findThemes(doc, themeName => {
|
||||||
|
@ -77,13 +94,13 @@ async function build({modernOnly, overrideImports, overrideCss}) {
|
||||||
// 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"], importOverridesMap));
|
await assets.write(`hydrogen.js`, await buildJs(srcPath("main.js"), [srcPath("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(srcPath("main.js"), [
|
||||||
'src/platform/web/legacy-polyfill.js',
|
srcPath('platform/web/legacy-polyfill.js'),
|
||||||
'src/platform/web/LegacyPlatform.js'
|
srcPath('platform/web/LegacyPlatform.js')
|
||||||
], importOverridesMap));
|
], 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(srcPath("platform/web/worker/main.js"), [srcPath('platform/web/worker/polyfill.js')]));
|
||||||
}
|
}
|
||||||
// copy over non-theme assets
|
// copy over non-theme assets
|
||||||
const baseConfig = JSON.parse(await fs.readFile(path.join(projectDir, "assets/config.json"), {encoding: "utf8"}));
|
const baseConfig = JSON.parse(await fs.readFile(path.join(projectDir, "assets/config.json"), {encoding: "utf8"}));
|
||||||
|
@ -97,13 +114,14 @@ async function build({modernOnly, overrideImports, 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);
|
||||||
let swSource = await fs.readFile(path.join(projectDir, "src/platform/web/service-worker.js"), "utf8");
|
let swSource = await fs.readFile(path.join(snowpackOutPath, "service-worker.js"), "utf8");
|
||||||
assets.addToHashForAll("sw.js", swSource);
|
assets.addToHashForAll("service-worker.js", swSource);
|
||||||
|
|
||||||
const globalHash = assets.hashForAll();
|
const globalHash = assets.hashForAll();
|
||||||
|
|
||||||
await buildServiceWorker(swSource, version, globalHash, assets);
|
await buildServiceWorker(swSource, version, globalHash, assets);
|
||||||
await buildHtml(doc, version, baseConfig, globalHash, modernOnly, assets);
|
await buildHtml(doc, version, baseConfig, globalHash, modernOnly, assets);
|
||||||
|
await removeDirIfExists(snowpackOutPath);
|
||||||
console.log(`built hydrogen ${version} (${globalHash}) successfully with ${assets.size} files`);
|
console.log(`built hydrogen ${version} (${globalHash}) successfully with ${assets.size} files`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +174,7 @@ async function buildHtml(doc, version, baseConfig, globalHash, modernOnly, asset
|
||||||
const configJSON = JSON.stringify(Object.assign({}, baseConfig, {
|
const configJSON = JSON.stringify(Object.assign({}, baseConfig, {
|
||||||
worker: assets.has("worker.js") ? assets.resolve(`worker.js`) : null,
|
worker: assets.has("worker.js") ? assets.resolve(`worker.js`) : null,
|
||||||
downloadSandbox: assets.resolve("download-sandbox.html"),
|
downloadSandbox: assets.resolve("download-sandbox.html"),
|
||||||
serviceWorker: "sw.js",
|
serviceWorker: "service-worker.js",
|
||||||
olm: {
|
olm: {
|
||||||
wasm: assets.resolve("olm.wasm"),
|
wasm: assets.resolve("olm.wasm"),
|
||||||
legacyBundle: assets.resolve("olm_legacy.js"),
|
legacyBundle: assets.resolve("olm_legacy.js"),
|
||||||
|
@ -324,7 +342,7 @@ async function buildServiceWorker(swSource, version, globalHash, assets) {
|
||||||
swSource = replaceStringInSource("NOTIFICATION_BADGE_ICON", assets.resolve("icon.png"));
|
swSource = replaceStringInSource("NOTIFICATION_BADGE_ICON", assets.resolve("icon.png"));
|
||||||
|
|
||||||
// service worker should not have a hashed name as it is polled by the browser for updates
|
// service worker should not have a hashed name as it is polled by the browser for updates
|
||||||
await assets.writeUnhashed("sw.js", swSource);
|
await assets.writeUnhashed("service-worker.js", swSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildCssBundles(buildFn, themes, assets, mainCssFile = null) {
|
async function buildCssBundles(buildFn, themes, assets, mainCssFile = null) {
|
||||||
|
|
|
@ -75,7 +75,7 @@ async function populateLib() {
|
||||||
const olmDstDir = path.join(libDir, "olm/");
|
const olmDstDir = path.join(libDir, "olm/");
|
||||||
await fs.mkdir(olmDstDir);
|
await fs.mkdir(olmDstDir);
|
||||||
for (const file of ["olm.js", "olm.wasm", "olm_legacy.js"]) {
|
for (const file of ["olm.js", "olm.wasm", "olm_legacy.js"]) {
|
||||||
await fs.symlink(path.join(olmSrcDir, file), path.join(olmDstDir, file));
|
await fs.copyFile(path.join(olmSrcDir, file), path.join(olmDstDir, file));
|
||||||
}
|
}
|
||||||
// transpile node-html-parser to esm
|
// transpile node-html-parser to esm
|
||||||
await fs.mkdir(path.join(libDir, "node-html-parser/"));
|
await fs.mkdir(path.join(libDir, "node-html-parser/"));
|
||||||
|
@ -83,12 +83,6 @@ async function populateLib() {
|
||||||
require.resolve('node-html-parser/dist/index.js'),
|
require.resolve('node-html-parser/dist/index.js'),
|
||||||
path.join(libDir, "node-html-parser/index.js")
|
path.join(libDir, "node-html-parser/index.js")
|
||||||
);
|
);
|
||||||
// Symlink dompurify
|
|
||||||
await fs.mkdir(path.join(libDir, "dompurify/"));
|
|
||||||
await fs.symlink(
|
|
||||||
require.resolve('dompurify/dist/purify.es.js'),
|
|
||||||
path.join(libDir, "dompurify/index.js")
|
|
||||||
);
|
|
||||||
// transpile another-json to esm
|
// transpile another-json to esm
|
||||||
await fs.mkdir(path.join(libDir, "another-json/"));
|
await fs.mkdir(path.join(libDir, "another-json/"));
|
||||||
await commonjsToESM(
|
await commonjsToESM(
|
||||||
|
|
38
snowpack.config.js
Normal file
38
snowpack.config.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Snowpack Configuration File
|
||||||
|
// See all supported options: https://www.snowpack.dev/reference/configuration
|
||||||
|
|
||||||
|
/** @type {import("snowpack").SnowpackUserConfig } */
|
||||||
|
module.exports = {
|
||||||
|
mount: {
|
||||||
|
// More specific paths before less specific paths (if they overlap)
|
||||||
|
"src/platform/web/docroot": "/",
|
||||||
|
"src": "/src",
|
||||||
|
"lib": {url: "/lib", static: true },
|
||||||
|
"assets": "/assets",
|
||||||
|
/* ... */
|
||||||
|
},
|
||||||
|
exclude: [
|
||||||
|
/* Avoid scanning scripts which use dev-dependencies and pull in babel, rollup, etc. */
|
||||||
|
'**/node_modules/**/*',
|
||||||
|
'**/scripts/**',
|
||||||
|
'**/target/**',
|
||||||
|
'**/prototypes/**',
|
||||||
|
'**/src/matrix/storage/memory/**',
|
||||||
|
'**/src/platform/web/legacy-polyfill.js',
|
||||||
|
'**/src/platform/web/worker/polyfill.js'
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
/* ... */
|
||||||
|
],
|
||||||
|
packageOptions: {
|
||||||
|
/* ... */
|
||||||
|
},
|
||||||
|
devOptions: {
|
||||||
|
open: "none",
|
||||||
|
hmr: false,
|
||||||
|
/* ... */
|
||||||
|
},
|
||||||
|
buildOptions: {
|
||||||
|
/* ... */
|
||||||
|
},
|
||||||
|
};
|
|
@ -18,20 +18,26 @@ limitations under the License.
|
||||||
import {EventKey} from "../EventKey.js";
|
import {EventKey} from "../EventKey.js";
|
||||||
export const PENDING_FRAGMENT_ID = Number.MAX_SAFE_INTEGER;
|
export const PENDING_FRAGMENT_ID = Number.MAX_SAFE_INTEGER;
|
||||||
|
|
||||||
|
interface FragmentIdComparer {
|
||||||
|
compare: (a: number, b: number) => number
|
||||||
|
}
|
||||||
|
|
||||||
export class BaseEntry {
|
export class BaseEntry {
|
||||||
constructor(fragmentIdComparer) {
|
protected _fragmentIdComparer: FragmentIdComparer
|
||||||
|
|
||||||
|
constructor(fragmentIdComparer: FragmentIdComparer) {
|
||||||
this._fragmentIdComparer = fragmentIdComparer;
|
this._fragmentIdComparer = fragmentIdComparer;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fragmentId() {
|
get fragmentId(): number {
|
||||||
throw new Error("unimplemented");
|
throw new Error("unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
get entryIndex() {
|
get entryIndex(): number {
|
||||||
throw new Error("unimplemented");
|
throw new Error("unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
compare(otherEntry) {
|
compare(otherEntry: BaseEntry): number {
|
||||||
if (this.fragmentId === otherEntry.fragmentId) {
|
if (this.fragmentId === otherEntry.fragmentId) {
|
||||||
return this.entryIndex - otherEntry.entryIndex;
|
return this.entryIndex - otherEntry.entryIndex;
|
||||||
} else if (this.fragmentId === PENDING_FRAGMENT_ID) {
|
} else if (this.fragmentId === PENDING_FRAGMENT_ID) {
|
||||||
|
@ -44,9 +50,9 @@ export class BaseEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
asEventKey() {
|
asEventKey(): EventKey {
|
||||||
return new EventKey(this.fragmentId, this.entryIndex);
|
return new EventKey(this.fragmentId, this.entryIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFrom() {}
|
updateFrom(other: BaseEntry) {}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseEntry} from "./BaseEntry.js";
|
import {BaseEntry} from "./BaseEntry";
|
||||||
import {REDACTION_TYPE} from "../../common.js";
|
import {REDACTION_TYPE} from "../../common.js";
|
||||||
import {createAnnotation, ANNOTATION_RELATION_TYPE, getRelationFromContent} from "../relations.js";
|
import {createAnnotation, ANNOTATION_RELATION_TYPE, getRelationFromContent} from "../relations.js";
|
||||||
import {PendingAnnotation} from "../PendingAnnotation.js";
|
import {PendingAnnotation} from "../PendingAnnotation.js";
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseEntry} from "./BaseEntry.js";
|
import {BaseEntry} from "./BaseEntry";
|
||||||
import {Direction} from "../Direction.js";
|
import {Direction} from "../Direction.js";
|
||||||
import {isValidFragmentId} from "../common.js";
|
import {isValidFragmentId} from "../common.js";
|
||||||
import {KeyLimits} from "../../../storage/common.js";
|
import {KeyLimits} from "../../../storage/common.js";
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {PENDING_FRAGMENT_ID} from "./BaseEntry.js";
|
import {PENDING_FRAGMENT_ID} from "./BaseEntry";
|
||||||
import {BaseEventEntry} from "./BaseEventEntry.js";
|
import {BaseEventEntry} from "./BaseEventEntry.js";
|
||||||
|
|
||||||
export class PendingEventEntry extends BaseEventEntry {
|
export class PendingEventEntry extends BaseEventEntry {
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
downloadSandbox: "assets/download-sandbox.html",
|
downloadSandbox: "assets/download-sandbox.html",
|
||||||
defaultHomeServer: "matrix.org",
|
defaultHomeServer: "matrix.org",
|
||||||
// NOTE: uncomment this if you want the service worker for local development
|
// NOTE: uncomment this if you want the service worker for local development
|
||||||
// serviceWorker: "sw.js",
|
// serviceWorker: "service-worker.js",
|
||||||
// NOTE: provide push config if you want push notifs for local development
|
// NOTE: provide push config if you want push notifs for local development
|
||||||
// see assets/config.json for what the config looks like
|
// see assets/config.json for what the config looks like
|
||||||
// push: {...},
|
// push: {...},
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import DOMPurify from "../../../lib/dompurify/index.js"
|
import DOMPurify from "dompurify"
|
||||||
|
|
||||||
class HTMLParseResult {
|
class HTMLParseResult {
|
||||||
constructor(bodyNode) {
|
constructor(bodyNode) {
|
||||||
|
|
1
sw.js
1
sw.js
|
@ -1 +0,0 @@
|
||||||
src/platform/web/service-worker.js
|
|
8
tsconfig.json
Normal file
8
tsconfig.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"target": "es6"
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
}
|
Reference in a new issue