This commit is contained in:
anatawa12 2025-09-14 05:43:21 +05:30 committed by GitHub
commit b815b16ec4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 139 additions and 135 deletions

View File

@ -185,8 +185,10 @@ export type Config = {
driveUrl: string;
userAgent: string;
frontendEntry: { file: string | null };
frontendBootLoader: { file: string };
frontendManifestExists: boolean;
frontendEmbedEntry: { file: string | null };
frontendEmbedBootLoader: { file: string };
frontendEmbedManifestExists: boolean;
mediaProxy: string;
externalMediaProxyEnabled: boolean;
@ -235,10 +237,10 @@ export function loadConfig(): Config {
const frontendEmbedManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_embed_vite_/manifest.json');
const frontendManifest = frontendManifestExists ?
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_vite_/manifest.json`, 'utf-8'))
: { 'src/_boot_.ts': { file: null } };
: { 'src/_boot_.ts': { file: null }, 'src/_bootloader.ts': { file: 'src/_bootloader.ts' } };
const frontendEmbedManifest = frontendEmbedManifestExists ?
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_embed_vite_/manifest.json`, 'utf-8'))
: { 'src/boot.ts': { file: null } };
: { 'src/boot.ts': { file: null }, 'src/_bootloader.ts': { file: 'src/_bootloader.ts' } };
const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
@ -312,8 +314,10 @@ export function loadConfig(): Config {
: null,
userAgent: `Misskey/${version} (${config.url})`,
frontendEntry: frontendManifest['src/_boot_.ts'],
frontendBootLoader: frontendManifest['src/_bootloader.ts'],
frontendManifestExists: frontendManifestExists,
frontendEmbedEntry: frontendEmbedManifest['src/boot.ts'],
frontendEmbedBootLoader: frontendEmbedManifest['src/_bootloader.ts'],
frontendEmbedManifestExists: frontendEmbedManifestExists,
perChannelMaxNoteCacheCount: config.perChannelMaxNoteCacheCount ?? 1000,
perUserNotificationsMaxCount: config.perUserNotificationsMaxCount ?? 500,

View File

@ -2,6 +2,7 @@ block vars
block loadClientEntry
- const entry = config.frontendEmbedEntry;
- const bootLoader = config.frontendEmbedBootLoader;
doctype html
@ -47,8 +48,7 @@ html(class='embed')
script(type='application/json' id='misskey_embedCtx' data-generated-at=now)
!= embedCtx
script
include ../boot.embed.js
script(type='module', src=`/embed_vite/${bootLoader.file}`)
body
noscript: p

View File

@ -2,6 +2,7 @@ block vars
block loadClientEntry
- const entry = config.frontendEntry;
- const bootLoader = config.frontendBootLoader;
- const baseUrl = config.url;
doctype html
@ -76,8 +77,7 @@ html
script(type='application/json' id='misskey_clientCtx' data-generated-at=now)
!= clientCtx
script
include ../boot.js
script(type='module', src=`/vite/${bootLoader.file}`)
body
noscript: p

View File

@ -6,6 +6,7 @@
type FIXME = any;
declare const _LANGS_: string[][];
declare const _LANG_IDS_: string[];
declare const _VERSION_: string;
declare const _ENV_: string;
declare const _DEV_: boolean;

View File

@ -5,18 +5,24 @@
'use strict';
import { addStyle, bootloaderLocales, detectLanguage } from '@@/js/bootloader';
interface Window {
CLIENT_ENTRY: string | undefined;
}
// ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
(async () => {
window.onerror = (e) => {
console.error(e);
window.onerror = (error) => {
console.error(error);
renderError('SOMETHING_HAPPENED');
};
window.onunhandledrejection = (e) => {
console.error(e);
window.onunhandledrejection = (error) => {
console.error(error);
renderError('SOMETHING_HAPPENED_IN_PROMISE');
};
let forceError = localStorage.getItem('forceError');
const forceError = localStorage.getItem('forceError');
if (forceError != null) {
renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.');
return;
@ -31,33 +37,13 @@
document.documentElement.classList.add('noborder');
}
//#region Detect language & fetch translations
const supportedLangs = LANGS;
/** @type { string } */
let lang = localStorage.getItem('lang');
if (lang == null || !supportedLangs.includes(lang)) {
if (supportedLangs.includes(navigator.language)) {
lang = navigator.language;
} else {
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
// Fallback
if (lang == null) lang = 'en-US';
}
}
// for https://github.com/misskey-dev/misskey/issues/10202
if (lang == null || lang.toString == null || lang.toString() === 'null') {
console.error('invalid lang value detected!!!', typeof lang, lang);
lang = 'en-US';
}
//#endregion
const lang = detectLanguage();
//#region Script
async function importAppScript() {
await import(CLIENT_ENTRY ? `/embed_vite/${CLIENT_ENTRY.replace('scripts', lang)}` : '/embed_vite/src/_boot_.ts')
.catch(async e => {
console.error(e);
await import(`/embed_vite/${(window.CLIENT_ENTRY ?? 'src/boot.ts').replace('scripts', lang)}`)
.catch(async error => {
console.error(error);
renderError('APP_IMPORT');
});
}
@ -72,44 +58,19 @@
}
//#endregion
async function addStyle(styleText) {
let css = document.createElement('style');
css.appendChild(document.createTextNode(styleText));
document.head.appendChild(css);
}
async function renderError(code) {
async function renderError(code: string, _details?) {
// Cannot set property 'innerHTML' of null を回避
if (document.readyState === 'loading') {
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
}
let messages = null;
const bootloaderLocales = localStorage.getItem('bootloaderLocales');
if (bootloaderLocales) {
messages = JSON.parse(bootloaderLocales);
}
if (!messages) {
// older version of misskey does not store bootloaderLocales, stores locale as a whole
const legacyLocale = localStorage.getItem('locale');
if (legacyLocale) {
const parsed = JSON.parse(legacyLocale);
messages = {
...(parsed._bootErrors ?? {}),
reload: parsed.reload,
};
}
}
if (!messages) messages = {};
const title = messages?.title || 'Failed to initialize Misskey';
const reload = messages?.reload || 'Reload';
const messages = bootloaderLocales();
document.body.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M12 9v4" /><path d="M12 16v.01" /></svg>
<div class="message">${title}</div>
<div class="message">${messages.title}</div>
<div class="submessage">Error Code: ${code}</div>
<button onclick="location.reload(!0)">
<div>${reload}</div>
<div>${messages.reload}</div>
</button>`;
addStyle(`
#misskey_app,

View File

@ -121,6 +121,7 @@ export function getConfig(): UserConfig {
define: {
_VERSION_: JSON.stringify(meta.version),
_LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]) => [k, v._lang_])),
_LANG_IDS_: JSON.stringify(Object.keys(locales)),
_ENV_: JSON.stringify(process.env.NODE_ENV),
_DEV_: process.env.NODE_ENV !== 'production',
_PERF_PREFIX_: JSON.stringify('Misskey:'),
@ -139,6 +140,7 @@ export function getConfig(): UserConfig {
input: {
i18n: './src/i18n.ts',
entry: './src/boot.ts',
bootloader: './src/_bootloader.ts',
},
external: externalPackages.map(p => p.match),
preserveEntrySignatures: 'allow-extension',

View File

@ -7,6 +7,7 @@
type FIXME = any;
declare const _LANGS_: string[][];
declare const _LANG_IDS_: string[];
declare const _VERSION_: string;
declare const _ENV_: string;
declare const _DEV_: boolean;

View File

@ -0,0 +1,74 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
// common portion of bootloader for frontend and frontend-embed
import type { Locale } from '../../../locales/index.js';
export function detectLanguage(): string {
const supportedLangs = _LANG_IDS_;
let lang: string | null | undefined = localStorage.getItem('lang');
if (lang == null || !supportedLangs.includes(lang)) {
if (supportedLangs.includes(navigator.language)) {
lang = navigator.language;
} else {
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
// Fallback
lang ??= 'en-US';
}
}
// for https://github.com/misskey-dev/misskey/issues/10202
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (lang == null || lang.toString == null || lang.toString() === 'null') {
console.error('invalid lang value detected!!!', typeof lang, lang);
lang = 'en-US';
}
return lang;
}
type BootLoaderLocales = Locale['_bootErrors'] & Pick<Locale, 'reload'>;
export function bootloaderLocales(): BootLoaderLocales {
let messages: Partial<BootLoaderLocales> | null = null;
const bootloaderLocalesJson = localStorage.getItem('bootloaderLocales');
if (bootloaderLocalesJson) {
messages = JSON.parse(bootloaderLocalesJson);
}
if (!messages) {
// older version of misskey does not store bootloaderLocales, stores locale as a whole
const legacyLocale = localStorage.getItem('locale');
if (legacyLocale) {
const parsed = JSON.parse(legacyLocale);
messages = {
...(parsed._bootErrors ?? {}),
reload: parsed.reload,
};
}
}
return Object.assign({
title: 'Failed to initialize Misskey',
solution: 'The following actions may solve the problem.',
solution1: 'Update your os and browser',
solution2: 'Disable an adblocker',
solution3: 'Clear the browser cache',
solution4: '(Tor Browser) Set dom.webaudio.enabled to true',
otherOption: 'Other options',
otherOption1: 'Clear preferences and cache',
otherOption2: 'Start the simple client',
otherOption3: 'Start the repair tool',
otherOption4: 'Start Misskey in safe mode',
reload: 'Reload',
}, messages) as BootLoaderLocales;
}
export function addStyle(styleText: string) {
const styleElement = document.createElement('style');
styleElement.appendChild(document.createTextNode(styleText));
document.head.appendChild(styleElement);
}

View File

@ -6,6 +6,7 @@
type FIXME = any;
declare const _LANGS_: string[][];
declare const _LANG_IDS_: string[];
declare const _VERSION_: string;
declare const _ENV_: string;
declare const _DEV_: boolean;

View File

@ -3,50 +3,40 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint no-restricted-globals: off */
'use strict';
import { addStyle, bootloaderLocales, detectLanguage } from '@@/js/bootloader';
interface Window {
CLIENT_ENTRY: string | undefined;
}
// ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
(async () => {
window.onerror = (e) => {
console.error(e);
renderError('SOMETHING_HAPPENED', e);
const CLIENT_ENTRY = window.CLIENT_ENTRY;
window.onerror = (error) => {
console.error(error);
renderError('SOMETHING_HAPPENED', error);
};
window.onunhandledrejection = (e) => {
console.error(e);
renderError('SOMETHING_HAPPENED_IN_PROMISE', e);
window.onunhandledrejection = (error) => {
console.error(error);
renderError('SOMETHING_HAPPENED_IN_PROMISE', error);
};
let forceError = localStorage.getItem('forceError');
const forceError = localStorage.getItem('forceError');
if (forceError != null) {
renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.');
return;
}
//#region Detect language
const supportedLangs = LANGS;
/** @type { string } */
let lang = localStorage.getItem('lang');
if (lang == null || !supportedLangs.includes(lang)) {
if (supportedLangs.includes(navigator.language)) {
lang = navigator.language;
} else {
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
// Fallback
if (lang == null) lang = 'en-US';
}
}
// for https://github.com/misskey-dev/misskey/issues/10202
if (lang == null || lang.toString == null || lang.toString() === 'null') {
console.error('invalid lang value detected!!!', typeof lang, lang);
lang = 'en-US';
}
//#endregion
const lang = detectLanguage();
//#region Script
async function importAppScript() {
await import(CLIENT_ENTRY ? `/vite/${CLIENT_ENTRY.replace('scripts', lang)}` : '/vite/src/_boot_.ts')
await import(`/vite/${(CLIENT_ENTRY ?? 'src/_boot_.ts').replace('scripts', lang)}`)
.catch(async e => {
console.error(e);
renderError('APP_IMPORT', e);
@ -78,7 +68,7 @@
if (!isSafeMode) {
const theme = localStorage.getItem('theme');
if (theme) {
for (const [k, v] of Object.entries(JSON.parse(theme))) {
for (const [k, v] of Object.entries(JSON.parse(theme) as Record<string, string>)) {
document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
// HTMLの theme-color 適用
@ -119,50 +109,13 @@
}
}
async function addStyle(styleText) {
let css = document.createElement('style');
css.appendChild(document.createTextNode(styleText));
document.head.appendChild(css);
}
async function renderError(code, details) {
// Cannot set property 'innerHTML' of null を回避
if (document.readyState === 'loading') {
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
}
let messages = null;
const bootloaderLocales = localStorage.getItem('bootloaderLocales');
if (bootloaderLocales) {
messages = JSON.parse(bootloaderLocales);
}
if (!messages) {
// older version of misskey does not store bootloaderLocales, stores locale as a whole
const legacyLocale = localStorage.getItem('locale');
if (legacyLocale) {
const parsed = JSON.parse(legacyLocale);
messages = {
...(parsed._bootErrors ?? {}),
reload: parsed.reload,
};
}
}
if (!messages) messages = {};
messages = Object.assign({
title: 'Failed to initialize Misskey',
solution: 'The following actions may solve the problem.',
solution1: 'Update your os and browser',
solution2: 'Disable an adblocker',
solution3: 'Clear the browser cache',
solution4: '(Tor Browser) Set dom.webaudio.enabled to true',
otherOption: 'Other options',
otherOption1: 'Clear preferences and cache',
otherOption2: 'Start the simple client',
otherOption3: 'Start the repair tool',
otherOption4: 'Start Misskey in safe mode',
reload: 'Reload',
}, messages);
const messages = bootloaderLocales();
const safeModeUrl = new URL(window.location.href);
safeModeUrl.searchParams.set('safemode', 'true');
@ -214,7 +167,7 @@
<br>
<div id="errors"></div>
`;
errorsElement = document.getElementById('errors');
errorsElement = document.getElementById('errors')!;
}
const detailsElement = document.createElement('details');
detailsElement.id = 'errorInfo';

View File

@ -25,3 +25,8 @@ declare module 'search-index:settings' {
declare module 'search-index:admin' {
export const searchIndexes: XGeneratedSearchIndexItem[];
}
declare module 'virtual:supported-langs' {
const value: string[];
export default value;
}

View File

@ -160,6 +160,7 @@ export function getConfig(): UserConfig {
define: {
_VERSION_: JSON.stringify(meta.version),
_LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]) => [k, v._lang_])),
_LANG_IDS_: JSON.stringify(Object.keys(locales)),
_ENV_: JSON.stringify(process.env.NODE_ENV),
_DEV_: process.env.NODE_ENV !== 'production',
_PERF_PREFIX_: JSON.stringify('Misskey:'),
@ -178,6 +179,7 @@ export function getConfig(): UserConfig {
input: {
i18n: './src/i18n.ts',
entry: './src/_boot_.ts',
bootloader: './src/_bootloader.ts',
},
external: externalPackages.map(p => p.match),
preserveEntrySignatures: 'allow-extension',

View File

@ -53,8 +53,8 @@ async function buildBackendScript() {
await fs.mkdir('./packages/backend/built/server/web', { recursive: true });
for (const file of [
'./packages/backend/src/server/web/boot.js',
'./packages/backend/src/server/web/boot.embed.js',
//'./packages/backend/src/server/web/boot.js',
//'./packages/backend/src/server/web/boot.embed.js',
'./packages/backend/src/server/web/bios.js',
'./packages/backend/src/server/web/cli.js',
'./packages/backend/src/server/web/error.js',