mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
feat(desktop): bundled renderer, modules and instance accounts (#3254)
This commit is contained in:
1 parent
c748c8af4e
commit
7c5fa2180a
1275 files changed
+215074
-81246
No files matched your search
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
const LEGACY_FONT_SOURCE = /,\s*url\([^)]*\)\s*format\("(?:woff|truetype)"\)/g;
|
||||
|
||||
class MissingKatexWoff2SourceError extends Error {
|
||||
constructor(resource) {
|
||||
super(`${resource} declares no format("woff2") source, refusing to drop its legacy font sources`);
|
||||
this.name = 'MissingKatexWoff2SourceError';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function katexLegacyFontsLoader(source) {
|
||||
this.cacheable(true);
|
||||
if (!source.includes('format("woff2")')) {
|
||||
throw new MissingKatexWoff2SourceError(this.resourcePath);
|
||||
}
|
||||
return source.replace(LEGACY_FONT_SOURCE, '');
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
const UNUSED_WEIGHTS = ['thin', 'light'];
|
||||
const UNUSED_WEIGHT_ENTRY = /\[\s*"(?:thin|light)"\s*,/g;
|
||||
const REQUIRED_WEIGHTS = ['"bold"', '"duotone"', '"fill"', '"regular"'];
|
||||
|
||||
class UnreadablePhosphorWeightMapError extends Error {
|
||||
constructor(resource, detail) {
|
||||
super(`${resource} does not match the expected @phosphor-icons weight map shape (${detail})`);
|
||||
this.name = 'UnreadablePhosphorWeightMapError';
|
||||
}
|
||||
}
|
||||
|
||||
function findEntryEnd(source, start) {
|
||||
let depth = 0;
|
||||
let quote = null;
|
||||
for (let i = start; i < source.length; i++) {
|
||||
const char = source[i];
|
||||
if (quote != null) {
|
||||
if (char === '\\') {
|
||||
i++;
|
||||
} else if (char === quote) {
|
||||
quote = null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (char === '"' || char === "'" || char === '`') {
|
||||
quote = char;
|
||||
continue;
|
||||
}
|
||||
if (char === '[' || char === '(' || char === '{') {
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
if (char === ']' || char === ')' || char === '}') {
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
module.exports = function phosphorUnusedWeightsLoader(source) {
|
||||
this.cacheable(true);
|
||||
for (const weight of REQUIRED_WEIGHTS) {
|
||||
if (!source.includes(weight)) {
|
||||
throw new UnreadablePhosphorWeightMapError(this.resourcePath, `missing ${weight}`);
|
||||
}
|
||||
}
|
||||
const spans = [];
|
||||
UNUSED_WEIGHT_ENTRY.lastIndex = 0;
|
||||
for (let match = UNUSED_WEIGHT_ENTRY.exec(source); match != null; match = UNUSED_WEIGHT_ENTRY.exec(source)) {
|
||||
const end = findEntryEnd(source, match.index);
|
||||
if (end < 0) {
|
||||
throw new UnreadablePhosphorWeightMapError(this.resourcePath, `unterminated entry at ${match.index}`);
|
||||
}
|
||||
spans.push([match.index, end]);
|
||||
UNUSED_WEIGHT_ENTRY.lastIndex = end;
|
||||
}
|
||||
if (spans.length !== UNUSED_WEIGHTS.length) {
|
||||
throw new UnreadablePhosphorWeightMapError(this.resourcePath, `found ${spans.length} unused weight entries`);
|
||||
}
|
||||
let out = '';
|
||||
let cursor = 0;
|
||||
for (const [start, end] of spans) {
|
||||
out += source.slice(cursor, start);
|
||||
cursor = end;
|
||||
while (cursor < source.length && /[\s,]/.test(source[cursor])) {
|
||||
cursor++;
|
||||
}
|
||||
}
|
||||
return out + source.slice(cursor);
|
||||
};
|
||||
@@ -8,27 +8,11 @@ import * as esbuild from 'esbuild';
|
||||
|
||||
const PRECACHE_ROOT_FILES = ['manifest.json', 'browserconfig.xml', 'robots.txt', 'version.json'];
|
||||
|
||||
const NEVER_PRECACHED_EXTENSIONS = ['.woff', '.woff2', '.ttf', '.otf', '.eot'];
|
||||
|
||||
async function fileRevision(filePath: string): Promise<string> {
|
||||
const stat = await fs.stat(filePath);
|
||||
return `${stat.size}:${Math.trunc(stat.mtimeMs)}`;
|
||||
}
|
||||
|
||||
function isLocalAssetUrl(value: string): boolean {
|
||||
if (!value.startsWith('/')) {
|
||||
return false;
|
||||
}
|
||||
if (value.startsWith('//')) {
|
||||
return false;
|
||||
}
|
||||
const pathname = value.split(/[?#]/, 1)[0].toLowerCase();
|
||||
if (NEVER_PRECACHED_EXTENSIONS.some((extension) => pathname.endsWith(extension))) {
|
||||
return false;
|
||||
}
|
||||
return value.startsWith('/assets/') || PRECACHE_ROOT_FILES.some((file) => value === `/${file}`);
|
||||
}
|
||||
|
||||
async function collectPrecacheManifest(): Promise<Array<PrecacheEntry>> {
|
||||
const entries = new Map<string, string>();
|
||||
for (const file of PRECACHE_ROOT_FILES) {
|
||||
@@ -37,21 +21,6 @@ async function collectPrecacheManifest(): Promise<Array<PrecacheEntry>> {
|
||||
entries.set(`/${file}`, await fileRevision(filePath));
|
||||
} catch {}
|
||||
}
|
||||
try {
|
||||
const html = await fs.readFile(path.join(DIST_DIR, 'index.html'), 'utf8');
|
||||
const attributePattern = /\b(?:href|src)=["']([^"']+)["']/g;
|
||||
for (const match of html.matchAll(attributePattern)) {
|
||||
const url = match[1];
|
||||
if (!isLocalAssetUrl(url)) {
|
||||
continue;
|
||||
}
|
||||
const pathname = new URL(url, 'https://local.invalid').pathname;
|
||||
const filePath = path.join(DIST_DIR, pathname.slice(1));
|
||||
try {
|
||||
entries.set(pathname, await fileRevision(filePath));
|
||||
} catch {}
|
||||
}
|
||||
} catch {}
|
||||
return Array.from(entries, ([url, revision]) => ({url, revision}));
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user