Convert copy-res to typescript

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
pull/26141/head
Michael Telatynski 2023-09-08 10:33:57 +01:00
parent 501f01cdd5
commit c944a273d0
No known key found for this signature in database
GPG Key ID: A2B008A5F49F5D0D
8 changed files with 127 additions and 38 deletions

View File

@ -19,7 +19,7 @@ module.exports = {
},
overrides: [
{
files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}"],
files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "scripts/*.ts"],
extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"],
// NOTE: These rules are frozen and new rules should not be added here.
// New changes belong in https://github.com/matrix-org/eslint-plugin-matrix-org/

View File

@ -38,7 +38,7 @@
"build": "yarn clean && yarn build:genfiles && yarn build:bundle",
"build-stats": "yarn clean && yarn build:genfiles && yarn build:bundle-stats",
"build:jitsi": "ts-node scripts/build-jitsi.ts",
"build:res": "node scripts/copy-res.js",
"build:res": "ts-node scripts/copy-res.ts",
"build:genfiles": "yarn build:res && yarn build:jitsi && yarn build:module_system",
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
"build:bundle": "webpack --progress --bail --mode production",
@ -47,7 +47,7 @@
"dist": "scripts/package.sh",
"start": "yarn build:module_system && concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js\"",
"start:https": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js --https\"",
"start:res": "yarn build:jitsi && node scripts/copy-res.js -w",
"start:res": "yarn build:jitsi && ts-node scripts/copy-res.ts -w",
"start:js": "webpack-dev-server --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js -w --mode development --disable-host-check --hot",
"lint": "yarn lint:types && yarn lint:js && yarn lint:style",
"lint:js": "yarn lint:js:src && yarn lint:js:module_system",
@ -106,9 +106,11 @@
"@sentry/webpack-plugin": "^2.0.0",
"@svgr/webpack": "^5.5.0",
"@testing-library/react": "^12.1.5",
"@types/cpx": "1.5.0",
"@types/jest": "^29.0.0",
"@types/jitsi-meet": "^2.0.2",
"@types/jsrsasign": "^10.5.4",
"@types/loader-utils": "^2.0.4",
"@types/lodash": "^4.14.197",
"@types/modernizr": "^3.5.3",
"@types/node": "^16",
@ -123,7 +125,7 @@
"babel-loader": "^8.2.2",
"chokidar": "^3.5.1",
"concurrently": "^8.0.0",
"cpx": "^1.5.0",
"cpx": "1.5.0",
"css-loader": "^4",
"dotenv": "^16.0.2",
"eslint": "8.48.0",

View File

@ -1,16 +1,27 @@
#!/usr/bin/env node
const loaderUtils = require("loader-utils");
// copies the resources into the webapp directory.
import parseArgs from "minimist";
import * as chokidar from "chokidar";
import * as fs from "node:fs";
import * as _ from "lodash";
import * as Cpx from "cpx";
import * as loaderUtils from "loader-utils";
const I18N_BASE_PATH = "src/i18n/strings/";
const INCLUDE_LANGS = fs.readdirSync(I18N_BASE_PATH).filter((fn) => fn.endsWith(".json"));
// cpx includes globbed parts of the filename in the destination, but excludes
// common parents. Hence, "res/{a,b}/**": the output will be "dest/a/..." and
// "dest/b/...".
const COPY_LIST = [
const COPY_LIST: [
sourceGlob: string,
outputPath: string,
opts?: {
directwatch?: 1;
},
][] = [
["res/apple-app-site-association", "webapp"],
["res/manifest.json", "webapp"],
["res/sw.js", "webapp"],
@ -24,19 +35,12 @@ const COPY_LIST = [
["./config.json", "webapp", { directwatch: 1 }],
["contribute.json", "webapp"],
];
const parseArgs = require("minimist");
const Cpx = require("cpx");
const chokidar = require("chokidar");
const fs = require("fs");
const _ = require("lodash");
const argv = parseArgs(process.argv.slice(2), {});
const watch = argv.w;
const verbose = argv.v;
function errCheck(err) {
function errCheck(err?: Error): void {
if (err) {
console.error(err.message);
process.exit(1);
@ -52,7 +56,7 @@ if (!fs.existsSync("webapp/i18n/")) {
fs.mkdirSync("webapp/i18n/");
}
function next(i, err) {
function next(i: number, err?: Error): void {
errCheck(err);
if (i >= COPY_LIST.length) {
@ -63,13 +67,9 @@ function next(i, err) {
const source = ent[0];
const dest = ent[1];
const opts = ent[2] || {};
let cpx = undefined;
const cpx = new Cpx.Cpx(source, dest);
if (!opts.lang) {
cpx = new Cpx.Cpx(source, dest);
}
if (verbose && cpx) {
if (verbose) {
cpx.on("copy", (event) => {
console.log(`Copied: ${event.srcPath} --> ${event.dstPath}`);
});
@ -78,7 +78,7 @@ function next(i, err) {
});
}
const cb = (err) => {
const cb = (err?: Error): void => {
next(i + 1, err);
};
@ -88,7 +88,7 @@ function next(i, err) {
// which in the case of config.json is '.', which inevitably takes
// ages to crawl. So we create our own watcher on the files
// instead.
const copy = () => {
const copy = (): void => {
cpx.copy(errCheck);
};
chokidar.watch(source).on("add", copy).on("change", copy).on("ready", cb).on("error", errCheck);
@ -102,7 +102,7 @@ function next(i, err) {
}
}
function genLangFile(lang, dest) {
function genLangFile(lang: string, dest: string): string {
const reactSdkFile = "node_modules/matrix-react-sdk/src/i18n/strings/" + lang + ".json";
const riotWebFile = I18N_BASE_PATH + lang + ".json";
@ -120,7 +120,7 @@ function genLangFile(lang, dest) {
const json = JSON.stringify(translations, null, 4);
const jsonBuffer = Buffer.from(json);
const digest = loaderUtils.getHashDigest(jsonBuffer, null, null, 7);
const digest = loaderUtils.getHashDigest(jsonBuffer, null, "hex", 7);
const filename = `${lang}.${digest}.json`;
fs.writeFileSync(dest + filename, json);
@ -131,8 +131,8 @@ function genLangFile(lang, dest) {
return filename;
}
function genLangList(langFileMap) {
const languages = {};
function genLangList(langFileMap: Record<string, string>): void {
const languages: Record<string, string> = {};
INCLUDE_LANGS.forEach(function (lang) {
const normalizedLanguage = lang.toLowerCase().replace("_", "-");
const languageParts = normalizedLanguage.split("-");
@ -144,7 +144,7 @@ function genLangList(langFileMap) {
});
fs.writeFile("webapp/i18n/languages.json", JSON.stringify(languages, null, 4), function (err) {
if (err) {
console.error("Copy Error occured: " + err);
console.error("Copy Error occured: " + err.message);
throw new Error("Failed to generate languages.json");
}
});
@ -158,15 +158,15 @@ function genLangList(langFileMap) {
* regenerate the file, adding its content-hashed filename to langFileMap
* and regenerating languages.json with the new filename
*/
function watchLanguage(lang, dest, langFileMap) {
function watchLanguage(lang: string, dest: string, langFileMap: Record<string, string>): void {
const reactSdkFile = "node_modules/matrix-react-sdk/src/i18n/strings/" + lang + ".json";
const riotWebFile = I18N_BASE_PATH + lang + ".json";
// XXX: Use a debounce because for some reason if we read the language
// file immediately after the FS event is received, the file contents
// appears empty. Possibly https://github.com/nodejs/node/issues/6112
let makeLangDebouncer;
const makeLang = () => {
let makeLangDebouncer: number;
const makeLang = (): void => {
if (makeLangDebouncer) {
clearTimeout(makeLangDebouncer);
}
@ -184,7 +184,7 @@ function watchLanguage(lang, dest, langFileMap) {
// language resources
const I18N_DEST = "webapp/i18n/";
const I18N_FILENAME_MAP = INCLUDE_LANGS.reduce((m, l) => {
const I18N_FILENAME_MAP = INCLUDE_LANGS.reduce<Record<string, string>>((m, l) => {
const filename = genLangFile(l, I18N_DEST);
m[l] = filename;
return m;
@ -192,7 +192,7 @@ const I18N_FILENAME_MAP = INCLUDE_LANGS.reduce((m, l) => {
genLangList(I18N_FILENAME_MAP);
if (watch) {
INCLUDE_LANGS.forEach((l) => watchLanguage(l.value, I18N_DEST, I18N_FILENAME_MAP));
INCLUDE_LANGS.forEach((l) => watchLanguage(l, I18N_DEST, I18N_FILENAME_MAP));
}
// non-language resources

43
src/@types/cpx.d.ts vendored Normal file
View File

@ -0,0 +1,43 @@
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import "cpx";
import type EventEmitter from "events";
declare module "cpx" {
export class Cpx extends EventEmitter {
public constructor(source: string, outDir: string, options?: object);
/**
* Copy all files that matches `this.source` pattern to `this.outDir`.
*
* @param {function} [cb = null] - A callback function.
* @returns {void}
*/
public copy(cb: Function | null): void;
/**
* Copy all files that matches `this.source` pattern to `this.outDir`.
* And watch changes in `this.base`, and copy only the file every time.
*
* @returns {void}
* @throws {Error} This had been watching already.
*/
public watch(): void;
}
}
export as namespace Cpx;

28
src/@types/loader-utils.d.ts vendored Normal file
View File

@ -0,0 +1,28 @@
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as LoaderUtils from "loader-utils";
declare module "loader-utils" {
export function getHashDigest(
buffer: Buffer,
hashType: null,
digestType: LoaderUtils.DigestType,
maxLength: number,
): string;
}
export as namespace Cpx;

View File

@ -24,6 +24,7 @@
"./src/**/*.ts",
"./src/**/*.tsx",
"./test/**/*.ts",
"./test/**/*.tsx"
"./test/**/*.tsx",
"./scripts/*.ts"
]
}

View File

@ -507,7 +507,7 @@ module.exports = (env, argv) => {
},
{
// cache-bust languages.json file placed in
// element-web/webapp/i18n during build by copy-res.js
// element-web/webapp/i18n during build by copy-res.ts
test: /\.*languages.json$/,
type: "javascript/auto",
loader: "file-loader",

View File

@ -2398,6 +2398,13 @@
dependencies:
"@babel/types" "^7.20.7"
"@types/cpx@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@types/cpx/-/cpx-1.5.0.tgz#d2a44b0ab5ec32bfdd743f64aae84847858bce0c"
integrity sha512-kuGK3lZqEvHTSDbJcaA6tcPoEXV4/e88YrltZMcQUewZhzYQwNSTMGIiPBqeeFd4LCBo1CX5U6CV6LaHG3wXSg==
dependencies:
"@types/node" "*"
"@types/events@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
@ -2492,6 +2499,14 @@
resolved "https://registry.yarnpkg.com/@types/jsrsasign/-/jsrsasign-10.5.8.tgz#0d6c638505454b5e95c684d6f604d57641417336"
integrity sha512-1oZ3TbarAhKtKUpyrCIqXpbx3ZAfoSulleJs6/UzzyYty0ut+kjRX7zHLAaHwVIuw8CBjIymwW4J2LK944HoHQ==
"@types/loader-utils@^2.0.4":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@types/loader-utils/-/loader-utils-2.0.4.tgz#f1c9dd27392f163ee92394454563286dfc6e4778"
integrity sha512-I71X8yySVQW6DuXr78/McC+enpUYQ68JxAYlgVyuMvl5mb7jFUZpFAu1qURZcwvbITXwxPnrA7hbV0W3HHsbbg==
dependencies:
"@types/node" "*"
"@types/webpack" "^4"
"@types/lodash@^4.14.197":
version "4.14.198"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.198.tgz#4d27465257011aedc741a809f1269941fa2c5d4c"
@ -2651,7 +2666,7 @@
"@types/source-list-map" "*"
source-map "^0.7.3"
"@types/webpack@^4.41.8":
"@types/webpack@^4", "@types/webpack@^4.41.8":
version "4.41.33"
resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.33.tgz#16164845a5be6a306bcbe554a8e67f9cac215ffc"
integrity sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g==
@ -4449,7 +4464,7 @@ counterpart@^0.18.6:
pluralizers "^0.1.7"
sprintf-js "^1.0.3"
cpx@^1.5.0:
cpx@1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/cpx/-/cpx-1.5.0.tgz#185be018511d87270dedccc293171e37655ab88f"
integrity sha512-jHTjZhsbg9xWgsP2vuNW2jnnzBX+p4T+vNI9Lbjzs1n4KhOfa22bQppiFYLsWQKd8TzmL5aSP/Me3yfsCwXbDA==