diff --git a/package.json b/package.json index bb44951e8..3057c5d80 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "check:connect": "cd packages/backend && pnpm check:connect", "migrateandstart": "pnpm migrate && pnpm start", "watch": "pnpm dev", - "dev": "pnpm -r dev", + "dev": "node scripts/dev.mjs", "lint": "pnpm -r lint", "cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts", "cy:run": "pnpm cypress run", diff --git a/packages/backend/package.json b/packages/backend/package.json index 17113a5e0..504cc882f 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -16,7 +16,8 @@ "watch:swc": "swc src -d built -D -w", "build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json", "watch": "node watch.mjs", - "dev": "node ./built/boot/entry.js", + "restart": "pnpm build && pnpm start", + "dev": "nodemon -w src -e ts,js,mjs,cjs,json --exec \"cross-env NODE_ENV=development pnpm run restart\"", "typecheck": "tsc --noEmit", "eslint": "eslint --quiet \"src/**/*.ts\"", "lint": "pnpm typecheck && pnpm eslint", @@ -226,6 +227,7 @@ "execa": "8.0.1", "jest": "29.7.0", "jest-mock": "29.7.0", + "nodemon": "3.0.2", "simple-oauth2": "5.0.0" } } diff --git a/packages/frontend/@types/global.d.ts b/packages/frontend/@types/global.d.ts index 390f63990..7d9335cc5 100644 --- a/packages/frontend/@types/global.d.ts +++ b/packages/frontend/@types/global.d.ts @@ -13,3 +13,6 @@ declare const _PERF_PREFIX_: string; declare const _DATA_TRANSFER_DRIVE_FILE_: string; declare const _DATA_TRANSFER_DRIVE_FOLDER_: string; declare const _DATA_TRANSFER_DECK_COLUMN_: string; + +// for dev-mode +declare const _LANGS_FULL_: string[][]; diff --git a/packages/frontend/src/_dev_boot_.ts b/packages/frontend/src/_dev_boot_.ts index 2e95a0357..d01a95704 100644 --- a/packages/frontend/src/_dev_boot_.ts +++ b/packages/frontend/src/_dev_boot_.ts @@ -8,4 +8,88 @@ // (pnpm start時はpugファイルの中で静的リソースとして読み込むようになっており、この問題は起こっていない) import '@tabler/icons-webfont/tabler-icons.scss'; +await main(); + import('@/_boot_.js'); + +/** + * backend/src/server/web/boot.jsで差し込まれている起動処理のうち、最低限必要なものを模倣するための処理 + */ +async function main() { + const forceError = localStorage.getItem('forceError'); + if (forceError != null) { + renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.'); + } + + //#region Detect language & fetch translations + + // dev-modeの場合は常に取り直す + const supportedLangs = _LANGS_.map(it => it[0]); + 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 + if (lang == null) lang = 'en-US'; + } + } + + // TODO:今のままだと言語ファイル変更後はpnpm devをリスタートする必要があるので、chokidarを使ったり等で対応できるようにする + const locale = _LANGS_FULL_.find(it => it[0] === lang); + localStorage.setItem('lang', lang); + localStorage.setItem('locale', JSON.stringify(locale[1])); + localStorage.setItem('localeVersion', _VERSION_); + //#endregion + + //#region Theme + const theme = localStorage.getItem('theme'); + if (theme) { + for (const [k, v] of Object.entries(JSON.parse(theme))) { + document.documentElement.style.setProperty(`--${k}`, v.toString()); + + // HTMLの theme-color 適用 + if (k === 'htmlThemeColor') { + for (const tag of document.head.children) { + if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') { + tag.setAttribute('content', v); + break; + } + } + } + } + } + const colorScheme = localStorage.getItem('colorScheme'); + if (colorScheme) { + document.documentElement.style.setProperty('color-scheme', colorScheme); + } + //#endregion + + const fontSize = localStorage.getItem('fontSize'); + if (fontSize) { + document.documentElement.classList.add('f-' + fontSize); + } + + const useSystemFont = localStorage.getItem('useSystemFont'); + if (useSystemFont) { + document.documentElement.classList.add('useSystemFont'); + } + + const wallpaper = localStorage.getItem('wallpaper'); + if (wallpaper) { + document.documentElement.style.backgroundImage = `url(${wallpaper})`; + } + + const customCss = localStorage.getItem('customCss'); + if (customCss && customCss.length > 0) { + const style = document.createElement('style'); + style.innerHTML = customCss; + document.head.appendChild(style); + } +} + +function renderError(code: string, details?: string) { + console.log(code, details); +} diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts index 6b6394a72..aea46f423 100644 --- a/packages/frontend/vite.config.local-dev.ts +++ b/packages/frontend/vite.config.local-dev.ts @@ -1,5 +1,6 @@ import dns from 'dns'; import { defineConfig } from 'vite'; +import locales from '../../locales'; import { getConfig } from './vite.config.js'; dns.setDefaultResultOrder('ipv4first'); @@ -49,6 +50,11 @@ const devConfig = { input: 'index.html', }, }, + + define: { + ...defaultConfig.define, + _LANGS_FULL_: JSON.stringify(Object.entries(locales)), + }, }; export default defineConfig(({ command, mode }) => devConfig); diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 6a7712af9..0b54c6a59 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -6,6 +6,7 @@ "types": "./built/index.d.ts", "scripts": { "build": "tsc", + "watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run build\"", "tsd": "tsd", "api": "pnpm api-extractor run --local --verbose", "api-prod": "pnpm api-extractor run --verbose", @@ -32,9 +33,10 @@ "jest-fetch-mock": "3.0.3", "jest-websocket-mock": "2.5.0", "mock-socket": "9.3.1", + "ncp": "2.0.0", + "nodemon": "3.0.2", "tsd": "0.29.0", - "typescript": "5.3.3", - "ncp": "2.0.0" + "typescript": "5.3.3" }, "files": [ "built" diff --git a/packages/sw/package.json b/packages/sw/package.json index 039a6887a..990e99196 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -2,7 +2,7 @@ "name": "sw", "private": true, "scripts": { - "watch": "node build.js watch", + "watch": "nodemon -w ../../package.json -e json --exec \"node build.js watch\"", "build": "node build.js", "typecheck": "tsc --noEmit", "eslint": "eslint --quiet src/**/*.ts", @@ -18,6 +18,7 @@ "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67", "eslint": "8.55.0", "eslint-plugin-import": "2.29.0", + "nodemon": "3.0.2", "typescript": "5.3.3" }, "type": "module" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5afc58334..7c0bc470b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -643,6 +643,9 @@ importers: jest-mock: specifier: 29.7.0 version: 29.7.0 + nodemon: + specifier: 3.0.2 + version: 3.0.2 simple-oauth2: specifier: 5.0.0 version: 5.0.0 @@ -1041,6 +1044,9 @@ importers: ncp: specifier: 2.0.0 version: 2.0.0 + nodemon: + specifier: 3.0.2 + version: 3.0.2 tsd: specifier: 0.29.0 version: 0.29.0 @@ -1105,6 +1111,9 @@ importers: eslint-plugin-import: specifier: 2.29.0 version: 2.29.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0) + nodemon: + specifier: 3.0.2 + version: 3.0.2 typescript: specifier: 5.3.3 version: 5.3.3 diff --git a/scripts/build-assets.mjs b/scripts/build-assets.mjs index f8f09ec2f..2e1268130 100644 --- a/scripts/build-assets.mjs +++ b/scripts/build-assets.mjs @@ -95,6 +95,7 @@ if (process.argv.includes("--watch")) { for await (const event of watcher) { const filename = event.filename?.replaceAll('\\', '/'); if (/^[a-z]+-[A-Z]+\.yml/.test(filename)) { + console.log(`update ${filename} ...`) locales = buildLocales(); await copyFrontendLocales() } diff --git a/scripts/build-pre.js b/scripts/build-pre.js index bf3e355b5..ed75aa655 100644 --- a/scripts/build-pre.js +++ b/scripts/build-pre.js @@ -4,7 +4,24 @@ */ const fs = require('fs'); -const meta = require('../package.json'); +const packageJsonPath = __dirname + '/../package.json' -fs.mkdirSync(__dirname + '/../built', { recursive: true }); -fs.writeFileSync(__dirname + '/../built/meta.json', JSON.stringify({ version: meta.version }), 'utf-8'); +function build() { + try { + const json = fs.readFileSync(packageJsonPath, 'utf-8') + const meta = JSON.parse(json); + fs.mkdirSync(__dirname + '/../built', { recursive: true }); + fs.writeFileSync(__dirname + '/../built/meta.json', JSON.stringify({ version: meta.version }), 'utf-8'); + } catch (e) { + console.error(e) + } +} + +build(); + +if (process.argv.includes("--watch")) { + fs.watch(packageJsonPath, (event, filename) => { + console.log(`update ${filename} ...`) + build() + }) +} diff --git a/scripts/dev.mjs b/scripts/dev.mjs index 1d06aa541..30dbebcf0 100644 --- a/scripts/dev.mjs +++ b/scripts/dev.mjs @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import fs from 'node:fs'; import { dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import { execa } from 'execa'; @@ -11,8 +10,6 @@ import { execa } from 'execa'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); -const vitePort = process.env.VITE_PORT ? ["--strictPort", "--port", process.env.VITE_PORT] : ["--strictPort"]; - await execa('pnpm', ['clean'], { cwd: _dirname + '/../', stdout: process.stdout, @@ -31,19 +28,25 @@ await execa('pnpm', ['build-assets'], { stderr: process.stderr, }); +execa('pnpm', ['build-pre', '--watch'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +}); + execa('pnpm', ['build-assets', '--watch'], { cwd: _dirname + '/../', stdout: process.stdout, stderr: process.stderr, }); -execa('pnpm', ['--filter', 'backend', 'watch'], { +execa('pnpm', ['--filter', 'backend', 'dev'], { cwd: _dirname + '/../', stdout: process.stdout, stderr: process.stderr, }); -execa('pnpm', ['--filter', 'frontend', 'watch', ...vitePort], { +execa('pnpm', ['--filter', 'frontend', 'dev'], { cwd: _dirname + '/../', stdout: process.stdout, stderr: process.stderr, @@ -55,27 +58,8 @@ execa('pnpm', ['--filter', 'sw', 'watch'], { stderr: process.stderr, }); -const start = async () => { - try { - const stat = fs.statSync(_dirname + '/../packages/backend/built/boot/entry.js'); - if (!stat) throw new Error('not exist yet'); - if (stat.size === 0) throw new Error('not built yet'); - - const subprocess = await execa('pnpm', ['start'], { - cwd: _dirname + '/../', - stdout: process.stdout, - stderr: process.stderr, - }); - - // なぜかworkerだけが終了してmasterが残るのでその対策 - process.on('SIGINT', () => { - subprocess.kill('SIGINT'); - process.exit(0); - }); - } catch (e) { - await new Promise(resolve => setTimeout(resolve, 3000)); - start(); - } -}; - -start(); +execa('pnpm', ['--filter', 'misskey-js', 'watch'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +});