diff --git a/package.json b/package.json index 200dbecacb..ace763fcf8 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "packages/frontend-shared", "packages/frontend", "packages/frontend-embed", + "packages/icons-subsetter", "packages/backend", "packages/sw", "packages/misskey-js", diff --git a/packages/frontend-embed/package.json b/packages/frontend-embed/package.json index 3d04c566b6..2429c5efc4 100644 --- a/packages/frontend-embed/package.json +++ b/packages/frontend-embed/package.json @@ -15,13 +15,13 @@ "@rollup/plugin-json": "6.1.0", "@rollup/plugin-replace": "5.0.7", "@rollup/pluginutils": "5.1.3", - "@tabler/icons-webfont": "3.3.0", "@twemoji/parser": "15.1.1", "@vitejs/plugin-vue": "5.2.0", "@vue/compiler-sfc": "3.5.12", "astring": "1.9.0", "buraha": "0.0.1", "estree-walker": "3.0.3", + "icons-subsetter": "workspace:*", "mfm-js": "0.24.0", "misskey-js": "workspace:*", "frontend-shared": "workspace:*", @@ -40,6 +40,7 @@ }, "devDependencies": { "@misskey-dev/summaly": "5.1.0", + "@tabler/icons-webfont": "3.3.0", "@testing-library/vue": "8.1.0", "@types/estree": "1.0.6", "@types/micromatch": "4.0.9", diff --git a/packages/frontend-embed/src/boot.ts b/packages/frontend-embed/src/boot.ts index c1b2b58beb..1624df9132 100644 --- a/packages/frontend-embed/src/boot.ts +++ b/packages/frontend-embed/src/boot.ts @@ -6,7 +6,12 @@ // https://vitejs.dev/config/build-options.html#build-modulepreload import 'vite/modulepreload-polyfill'; -import '@tabler/icons-webfont/dist/tabler-icons.scss'; +if (import.meta.env.DEV) { + await import('@tabler/icons-webfont/dist/tabler-icons.scss'); +} else { + await import('icons-subsetter/built/tabler-icons-classes.css'); + await import('icons-subsetter/built/tabler-icons-frontendEmbed.css'); +} import '@/style.scss'; import { createApp, defineAsyncComponent } from 'vue'; diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 8b20980d63..790f1abf0a 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -25,7 +25,6 @@ "@rollup/plugin-replace": "5.0.7", "@rollup/pluginutils": "5.1.3", "@syuilo/aiscript": "0.19.0", - "@tabler/icons-webfont": "3.3.0", "@twemoji/parser": "15.1.1", "@vitejs/plugin-vue": "5.2.0", "@vue/compiler-sfc": "3.5.12", @@ -46,6 +45,7 @@ "estree-walker": "3.0.3", "eventemitter3": "5.0.1", "frontend-shared": "workspace:*", + "icons-subsetter": "workspace:*", "idb-keyval": "6.2.1", "insert-text-at-cursor": "0.3.0", "is-file-animated": "1.0.2", @@ -95,6 +95,7 @@ "@storybook/types": "8.4.4", "@storybook/vue3": "8.4.4", "@storybook/vue3-vite": "8.4.4", + "@tabler/icons-webfont": "3.3.0", "@testing-library/vue": "8.1.0", "@types/canvas-confetti": "^1.6.4", "@types/estree": "1.0.6", diff --git a/packages/frontend/src/_boot_.ts b/packages/frontend/src/_boot_.ts index c90cc6bdd0..1fd366f0c4 100644 --- a/packages/frontend/src/_boot_.ts +++ b/packages/frontend/src/_boot_.ts @@ -6,7 +6,12 @@ // https://vitejs.dev/config/build-options.html#build-modulepreload import 'vite/modulepreload-polyfill'; -import '@tabler/icons-webfont/dist/tabler-icons.scss'; +if (import.meta.env.DEV) { + await import('@tabler/icons-webfont/dist/tabler-icons.scss'); +} else { + await import('icons-subsetter/built/tabler-icons-classes.css'); + await import('icons-subsetter/built/tabler-icons-frontend.css'); +} import '@/style.scss'; import { mainBoot } from '@/boot/main-boot.js'; diff --git a/packages/icons-subsetter/README.md b/packages/icons-subsetter/README.md new file mode 100644 index 0000000000..1249d65644 --- /dev/null +++ b/packages/icons-subsetter/README.md @@ -0,0 +1,15 @@ +## これは何 + +フロントエンドの各パッケージで使用されているtabler iconsのclassをスキャンし、使用されているiconのみを抽出するツールです。 + +なお、サブセット版に無いアイコンが呼び出された場合は本物のtabler icons フォントにフォールバックするようになっています。 + +このツールは本番ビルド時にのみ使用されます(開発モードでも最初の1回だけビルドが走りますが、これは型エラーを抑制するためにファイルを置いておく用の措置です) + +現時点では `src/generator.ts` の `filesToScan` にスキャン対象のファイルが書かれています。もしこれに当てはまらないファイルをサブセットのスキャン対象とする場合はこの部分を適宜修正してください。 + +## 使い方 + +```bash +pnpm build +``` diff --git a/packages/icons-subsetter/eslint.config.js b/packages/icons-subsetter/eslint.config.js new file mode 100644 index 0000000000..957100fd8c --- /dev/null +++ b/packages/icons-subsetter/eslint.config.js @@ -0,0 +1,18 @@ +import tsParser from '@typescript-eslint/parser'; +import sharedConfig from '../shared/eslint.config.js'; + +// eslint-disable-next-line import/no-default-export +export default [ + ...sharedConfig, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + parser: tsParser, + project: ['./tsconfig.json'], + sourceType: 'module', + tsconfigRootDir: import.meta.dirname, + }, + }, + }, +]; diff --git a/packages/icons-subsetter/package.json b/packages/icons-subsetter/package.json new file mode 100644 index 0000000000..1abcc04d29 --- /dev/null +++ b/packages/icons-subsetter/package.json @@ -0,0 +1,25 @@ +{ + "name": "icons-subsetter", + "version": "0.0.0", + "private": true, + "description": "Subset tabler-icons webfont", + "type": "module", + "scripts": { + "build": "tsx src/generator.ts", + "lint": "eslint" + }, + "devDependencies": { + "@tabler/icons-webfont": "https://github.com/misskey-dev/tabler-icons/archive/refs/tags/3.29.0-mi.1913+5921534bc.tar.gz", + "@types/node": "22.9.0", + "@typescript-eslint/eslint-plugin": "7.17.0", + "@typescript-eslint/parser": "7.17.0", + "harfbuzzjs": "0.4.4", + "tiny-glob": "^0.2.9", + "tsx": "4.4.0", + "typescript": "5.6.3", + "wasm-ttf2woff": "1.0.0" + }, + "files": [ + "built" + ] +} diff --git a/packages/icons-subsetter/src/generator.ts b/packages/icons-subsetter/src/generator.ts new file mode 100644 index 0000000000..035b6f42a9 --- /dev/null +++ b/packages/icons-subsetter/src/generator.ts @@ -0,0 +1,125 @@ +import { promises as fsp, existsSync } from 'fs'; +import path from 'path'; +import glob from 'tiny-glob'; +import { generateSubsettedFont } from './subsetter.js'; + +const filesToScan = { + frontend: 'packages/frontend/src/**/*.{ts,vue}', + //frontendShared: 'packages/frontend-shared/js/**/*.{ts}', // 現時点では該当がないのでスキップ。ここをコメントアウトするときは、各フロントエンドにこのチャンクのCSSのimportを追加すること + frontendEmbed: 'packages/frontend-embed/src/**/*.{ts,vue}', +}; + +async function main() { + const start = performance.now(); + + // 1. ビルドディレクトリを削除 + if (existsSync('./built')) { + await fsp.rm('./built', { recursive: true }); + } + await fsp.mkdir('./built'); + + // 2. tabler-icons.min.cssから、class名とUnicodeのマッピングを抽出 + const css = await fsp.readFile('node_modules/@tabler/icons-webfont/dist/tabler-icons.min.css', 'utf-8'); + const cssRegex = /\.(ti-[a-z0-9-]+)::?before\s*{\n?\s*content:\s*["']\\([a-fA-F0-9]+)["'];?\n?\s*}/g; + const rgMap = new Map(); + let matches: RegExpExecArray | null; + while ((matches = cssRegex.exec(css)) !== null) { + rgMap.set(matches[1], matches[2]); + } + + // 3. tabler-icons-classes.cssから、@font-faceを削除して書き出し + const cssWithoutFontFace = css.replace(/@font-face\s*{[^}]*}/g, ''); + await fsp.writeFile('./built/tabler-icons-classes.css', cssWithoutFontFace); + + // 4. フォールバック用のtabler-icons.woff2をコピー + const fontPath = 'node_modules/@tabler/icons-webfont/dist/fonts/'; + await fsp.copyFile(fontPath + 'tabler-icons.woff2', './built/tabler-icons.woff2'); + + // 5. 各チャンクごとにファイルをスキャンして、使用されているアイコンを抽出 + const unicodeRangeValues = new Map(); + for (const [key, dir] of Object.entries(filesToScan)) { + console.log(`Scanning ${key}...`); + + const iconsToPack = new Set(); + + const cwd = path.resolve(process.cwd(), '../../'); + const files = await glob(dir, { cwd }); + for (const file of files) { + //console.log(`Scanning ${file}`); + const content = await fsp.readFile(path.resolve(cwd, file), 'utf-8'); + const classRegex = /ti-[a-z0-9-]+/g; + let matches: RegExpExecArray | null; + while ((matches = classRegex.exec(content)) !== null) { + const icon = matches[0]; + if (rgMap.has(icon)) { + iconsToPack.add(icon); + } + } + } + + // 6. チャンク内で使用されているアイコンのUnicodeの配列を生成 + const unicodeValues = Array.from(iconsToPack).map((icon) => parseInt(rgMap.get(icon)!, 16)); + unicodeRangeValues.set(key, unicodeValues); + } + + // 7. Tabler Iconフォントをサブセット化 + const subsettedFonts = await generateSubsettedFont(fontPath + 'tabler-icons.ttf', unicodeRangeValues); + + // 8. サブセット化したフォント・CSSを書き出し + await Promise.allSettled(Array.from(subsettedFonts.entries()).map(async ([key, buffer]) => { + const cssRules = [`@font-face { + font-family: "tabler-icons"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("./tabler-icons.woff2") format("woff2"); +}`]; + + // サブセット化したフォントの中身がある(=unicodeRangeValuesの配列が殻ではない)場合のみ、サブセットしたものに関する情報を追記 + if (unicodeRangeValues.get(key)!.length > 0) { + await fsp.writeFile(`./built/tabler-icons-${key}.woff2`, buffer); + + const unicodeRangeString = (() => { + const values = unicodeRangeValues.get(key)!.sort((a, b) => a - b); + const ranges = []; + + for (let i = 0; i < values.length; i++) { + const start = values[i]; + let end = values[i]; + while (values[i + 1] === end + 1) { + end = values[i + 1]; + i++; + } + if (start === end) { + ranges.push(`U+${start.toString(16)}`); + } else if (start + 1 === end) { + ranges.push(`U+${start.toString(16)}`, `U+${end.toString(16)}`); + } else { + ranges.push(`U+${start.toString(16)}-${end.toString(16)}`); + } + } + + return ranges.join(', '); + })(); + + cssRules.push(`@font-face { + font-family: "tabler-icons"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("./tabler-icons-${key}.woff2") format("woff2"); + unicode-range: ${unicodeRangeString}; +}`); + } + + await fsp.writeFile(`./built/tabler-icons-${key}.css`, cssRules.join('\n\n') + '\n'); + })); + + const end = performance.now(); + console.log(`Done in ${Math.round((end - start) * 100) / 100}ms`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/packages/icons-subsetter/src/subsetter.ts b/packages/icons-subsetter/src/subsetter.ts new file mode 100644 index 0000000000..d959fb81a5 --- /dev/null +++ b/packages/icons-subsetter/src/subsetter.ts @@ -0,0 +1,76 @@ +import { promises as fsp } from 'fs'; +import { ttf2woff } from 'wasm-ttf2woff'; + +export async function generateSubsettedFont(ttfPath: string, unicodeRangeValues: Map) { + const ttf = await fsp.readFile(ttfPath); + + const { + instance: { exports: harfbuzzWasm }, + }: any = await WebAssembly.instantiate(await fsp.readFile('./node_modules/harfbuzzjs/hb-subset.wasm')); + + const heapu8 = new Uint8Array(harfbuzzWasm.memory.buffer); + + const subsetFonts = new Map(); + + let i = 0; + for (const [key, unicodeValues] of unicodeRangeValues) { + i++; + console.log(`Generating subset ${i} of ${unicodeRangeValues.size}...`); + + // サブセット入力を作成 + const input = harfbuzzWasm.hb_subset_input_create_or_fail(); + if (input === 0) { + throw new Error('hb_subset_input_create_or_fail (harfbuzz) returned zero'); + } + + // フォントバッファにフォントデータをセット + const fontBuffer = harfbuzzWasm.malloc(ttf.byteLength); + heapu8.set(new Uint8Array(ttf), fontBuffer); + + // フォントフェイスを作成 + const blob = harfbuzzWasm.hb_blob_create(fontBuffer, ttf.byteLength, 2, 0, 0); + const face = harfbuzzWasm.hb_face_create(blob, 0); + harfbuzzWasm.hb_blob_destroy(blob); + + // Unicodeセットに指定されたUnicodeポイントを追加 + const inputUnicodes = harfbuzzWasm.hb_subset_input_unicode_set(input); + for (const unicode of unicodeValues) { + harfbuzzWasm.hb_set_add(inputUnicodes, unicode); + } + + // サブセットを作成 + let subset; + try { + subset = harfbuzzWasm.hb_subset_or_fail(face, input); + if (subset === 0) { + harfbuzzWasm.hb_face_destroy(face); + harfbuzzWasm.free(fontBuffer); + throw new Error('hb_subset_or_fail (harfbuzz) returned zero'); + } + } finally { + harfbuzzWasm.hb_subset_input_destroy(input); + } + + // サブセットフォントデータを取得 + const result = harfbuzzWasm.hb_face_reference_blob(subset); + const offset = harfbuzzWasm.hb_blob_get_data(result, 0); + const subsetByteLength = harfbuzzWasm.hb_blob_get_length(result); + if (subsetByteLength === 0) { + harfbuzzWasm.hb_face_destroy(face); + harfbuzzWasm.hb_blob_destroy(result); + harfbuzzWasm.free(fontBuffer); + throw new Error('hb_blob_get_length (harfbuzz) returned zero'); + } + + // サブセットフォントをバッファに格納 + subsetFonts.set(key, Buffer.from(await ttf2woff(heapu8.slice(offset, offset + subsetByteLength)))); + + // メモリを解放 + harfbuzzWasm.hb_blob_destroy(result); + harfbuzzWasm.hb_face_destroy(subset); + harfbuzzWasm.hb_face_destroy(face); + harfbuzzWasm.free(fontBuffer); + } + + return subsetFonts; +} diff --git a/packages/icons-subsetter/tsconfig.json b/packages/icons-subsetter/tsconfig.json new file mode 100644 index 0000000000..08315a91cf --- /dev/null +++ b/packages/icons-subsetter/tsconfig.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "esModuleInterop": true, + "lib": [ + "esnext", + "dom" + ] + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37869da6a1..6973355f39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -142,7 +142,7 @@ importers: version: 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/testing': specifier: 10.4.7 - version: 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7)) + version: 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7)(@nestjs/platform-express@10.4.7) '@peertube/http-signature': specifier: 1.7.0 version: 1.7.0 @@ -709,9 +709,6 @@ importers: '@syuilo/aiscript': specifier: 0.19.0 version: 0.19.0 - '@tabler/icons-webfont': - specifier: 3.3.0 - version: 3.3.0 '@twemoji/parser': specifier: 15.1.1 version: 15.1.1 @@ -772,6 +769,9 @@ importers: frontend-shared: specifier: workspace:* version: link:../frontend-shared + icons-subsetter: + specifier: workspace:* + version: link:../icons-subsetter idb-keyval: specifier: 6.2.1 version: 6.2.1 @@ -914,6 +914,9 @@ importers: '@storybook/vue3-vite': specifier: 8.4.4 version: 8.4.4(storybook@8.4.4(bufferutil@4.0.8)(prettier@3.3.3)(utf-8-validate@6.0.4))(vite@5.4.11(@types/node@22.9.0)(sass@1.79.3)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3)) + '@tabler/icons-webfont': + specifier: 3.3.0 + version: 3.3.0 '@testing-library/vue': specifier: 8.1.0 version: 8.1.0(@vue/compiler-sfc@3.5.12)(@vue/server-renderer@3.5.12(vue@3.5.12(typescript@5.6.3)))(vue@3.5.12(typescript@5.6.3)) @@ -1055,9 +1058,6 @@ importers: '@rollup/pluginutils': specifier: 5.1.3 version: 5.1.3(rollup@4.26.0) - '@tabler/icons-webfont': - specifier: 3.3.0 - version: 3.3.0 '@twemoji/parser': specifier: 15.1.1 version: 15.1.1 @@ -1079,6 +1079,9 @@ importers: frontend-shared: specifier: workspace:* version: link:../frontend-shared + icons-subsetter: + specifier: workspace:* + version: link:../icons-subsetter json5: specifier: 2.2.3 version: 2.2.3 @@ -1125,6 +1128,9 @@ importers: '@misskey-dev/summaly': specifier: 5.1.0 version: 5.1.0 + '@tabler/icons-webfont': + specifier: 3.3.0 + version: 3.3.0 '@testing-library/vue': specifier: 8.1.0 version: 8.1.0(@vue/compiler-sfc@3.5.12)(@vue/server-renderer@3.5.12(vue@3.5.12(typescript@5.6.3)))(vue@3.5.12(typescript@5.6.3)) @@ -1157,7 +1163,7 @@ importers: version: 7.17.0(eslint@9.14.0)(typescript@5.6.3) '@vitest/coverage-v8': specifier: 1.6.0 - version: 1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0)) + version: 1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0)) '@vue/runtime-core': specifier: 3.5.12 version: 3.5.12 @@ -1244,6 +1250,36 @@ importers: specifier: 9.4.3 version: 9.4.3(eslint@9.14.0) + packages/icons-subsetter: + devDependencies: + '@tabler/icons-webfont': + specifier: https://github.com/misskey-dev/tabler-icons/archive/refs/tags/3.29.0-mi.1913+5921534bc.tar.gz + version: https://github.com/misskey-dev/tabler-icons/archive/refs/tags/3.29.0-mi.1913+5921534bc.tar.gz + '@types/node': + specifier: 22.9.0 + version: 22.9.0 + '@typescript-eslint/eslint-plugin': + specifier: 7.17.0 + version: 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.14.0)(typescript@5.6.3))(eslint@9.14.0)(typescript@5.6.3) + '@typescript-eslint/parser': + specifier: 7.17.0 + version: 7.17.0(eslint@9.14.0)(typescript@5.6.3) + harfbuzzjs: + specifier: 0.4.4 + version: 0.4.4 + tiny-glob: + specifier: ^0.2.9 + version: 0.2.9 + tsx: + specifier: 4.4.0 + version: 4.4.0 + typescript: + specifier: 5.6.3 + version: 5.6.3 + wasm-ttf2woff: + specifier: 1.0.0 + version: 1.0.0 + packages/misskey-bubble-game: dependencies: eventemitter3: @@ -4179,6 +4215,13 @@ packages: '@tabler/icons-webfont@3.3.0': resolution: {integrity: sha512-vMsxtwTXdC4QH4uDajZjBYThILEI0dP+Mn1s4XZkVtnJ793IF31i3596nYetWhAJnrED0UJ0HQWSbmqVxVQbxA==} + '@tabler/icons-webfont@https://github.com/misskey-dev/tabler-icons/archive/refs/tags/3.29.0-mi.1913+5921534bc.tar.gz': + resolution: {tarball: https://github.com/misskey-dev/tabler-icons/archive/refs/tags/3.29.0-mi.1913+5921534bc.tar.gz} + version: 3.29.0-mi.1913 + + '@tabler/icons@3.29.0': + resolution: {integrity: sha512-VWNINymdmhay3MDvWVREmRwuWLSrX3YiInKvs5L4AHRF4bAfJabLlEReE0BW/XFsBt22ff8/C8Eam/LXlF97mA==} + '@tabler/icons@3.3.0': resolution: {integrity: sha512-PLVe9d7b59sKytbx00KgeGhQG3N176Ezv8YMmsnSz4s0ifDzMWlp/h2wEfQZ0ZNe8e377GY2OW6kovUe3Rnd0g==} @@ -6882,10 +6925,16 @@ packages: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} engines: {node: '>= 0.4'} + globalyzer@0.1.0: + resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + google-protobuf@3.21.2: resolution: {integrity: sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==} @@ -6932,6 +6981,9 @@ packages: resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} engines: {node: '>=6'} + harfbuzzjs@0.4.4: + resolution: {integrity: sha512-8rRncykQxQsHJaPchDRNLYJqkt9YbQ/dZ/LkF64W3Qxx5tgHYAsjYq+TOH2PVRwrM405AWbLChWk8BKECmTgkA==} + has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -10246,6 +10298,9 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tiny-glob@0.2.9: + resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -10897,6 +10952,9 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + wasm-ttf2woff@1.0.0: + resolution: {integrity: sha512-JS4Xk/3TswEXBORjSEaXXTyw+YqCfpg3Ri6Gj/tDimrCeRirealWTckpf010k7mQyOo4ucYBDXLKcSXfVPh/VA==} + web-push@3.6.7: resolution: {integrity: sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==} engines: {node: '>= 16'} @@ -13074,7 +13132,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/testing@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7))': + '@nestjs/testing@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7)(@nestjs/platform-express@10.4.7)': dependencies: '@nestjs/common': 10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -14571,6 +14629,12 @@ snapshots: dependencies: '@tabler/icons': 3.3.0 + '@tabler/icons-webfont@https://github.com/misskey-dev/tabler-icons/archive/refs/tags/3.29.0-mi.1913+5921534bc.tar.gz': + dependencies: + '@tabler/icons': 3.29.0 + + '@tabler/icons@3.29.0': {} + '@tabler/icons@3.3.0': {} '@tensorflow/tfjs-backend-cpu@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))': @@ -15264,7 +15328,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0))': + '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0))': dependencies: '@ampproject/remapping': 2.2.1 '@bcoe/v8-coverage': 0.2.3 @@ -15279,7 +15343,7 @@ snapshots: std-env: 3.7.0 strip-literal: 2.1.0 test-exclude: 6.0.0 - vitest: 1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0) + vitest: 1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0) transitivePeerDependencies: - supports-color @@ -18036,6 +18100,8 @@ snapshots: dependencies: define-properties: 1.2.0 + globalyzer@0.1.0: {} + globby@11.1.0: dependencies: array-union: 2.1.0 @@ -18045,6 +18111,8 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + globrex@0.1.2: {} + google-protobuf@3.21.2: optional: true @@ -18121,6 +18189,8 @@ snapshots: hard-rejection@2.1.0: {} + harfbuzzjs@0.4.4: {} + has-bigints@1.0.2: {} has-flag@3.0.0: {} @@ -19046,35 +19116,6 @@ snapshots: jsdoc-type-pratt-parser@4.1.0: {} - jsdom@24.1.1: - dependencies: - cssstyle: 4.0.1 - data-urls: 5.0.0 - decimal.js: 10.4.3 - form-data: 4.0.1 - html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.12 - parse5: 7.2.1 - rrweb-cssom: 0.7.1 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 4.1.4 - w3c-xmlserializer: 5.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 3.1.1 - whatwg-mimetype: 4.0.0 - whatwg-url: 14.0.0 - ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) - xml-name-validator: 5.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - optional: true - jsdom@24.1.1(bufferutil@4.0.7)(utf-8-validate@6.0.3): dependencies: cssstyle: 4.0.1 @@ -21990,6 +22031,11 @@ snapshots: through@2.3.8: {} + tiny-glob@0.2.9: + dependencies: + globalyzer: 0.1.0 + globrex: 0.1.2 + tiny-invariant@1.3.3: {} tinybench@2.6.0: {} @@ -22532,7 +22578,7 @@ snapshots: - supports-color - terser - vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0): + vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -22557,7 +22603,7 @@ snapshots: optionalDependencies: '@types/node': 22.9.0 happy-dom: 10.0.3 - jsdom: 24.1.1 + jsdom: 24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - less - lightningcss @@ -22691,6 +22737,8 @@ snapshots: dependencies: makeerror: 1.0.12 + wasm-ttf2woff@1.0.0: {} + web-push@3.6.7: dependencies: asn1.js: 5.4.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d222614eda..7c563b437f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,6 +3,7 @@ packages: - 'packages/frontend-shared' - 'packages/frontend' - 'packages/frontend-embed' + - 'packages/icons-subsetter' - 'packages/sw' - 'packages/misskey-js' - 'packages/misskey-js/generator' diff --git a/scripts/build-assets.mjs b/scripts/build-assets.mjs index 421d4a6d1b..08cdf019a2 100644 --- a/scripts/build-assets.mjs +++ b/scripts/build-assets.mjs @@ -33,10 +33,6 @@ async function copyFrontendFonts() { await fs.cp('./packages/frontend/node_modules/three/examples/fonts', './built/_frontend_dist_/fonts', { dereference: true, recursive: true }); } -async function copyFrontendTablerIcons() { - await fs.cp('./packages/frontend/node_modules/@tabler/icons-webfont/dist', './built/_frontend_dist_/tabler-icons', { dereference: true, recursive: true }); -} - async function copyFrontendLocales() { generateDTS(); @@ -88,7 +84,6 @@ async function buildBackendStyle() { async function build() { await Promise.all([ copyFrontendFonts(), - copyFrontendTablerIcons(), copyFrontendLocales(), copyBackendViews(), buildBackendScript(), diff --git a/scripts/clean.js b/scripts/clean.js index 86c19281ea..69a8df76af 100644 --- a/scripts/clean.js +++ b/scripts/clean.js @@ -10,6 +10,7 @@ const fs = require('fs'); fs.rmSync(__dirname + '/../packages/frontend-shared/built', { recursive: true, force: true }); fs.rmSync(__dirname + '/../packages/frontend/built', { recursive: true, force: true }); fs.rmSync(__dirname + '/../packages/frontend-embed/built', { recursive: true, force: true }); + fs.rmSync(__dirname + '/../packages/icons-subsetter/built', { recursive: true, force: true }); fs.rmSync(__dirname + '/../packages/sw/built', { recursive: true, force: true }); fs.rmSync(__dirname + '/../packages/misskey-js/built', { recursive: true, force: true }); fs.rmSync(__dirname + '/../packages/misskey-reversi/built', { recursive: true, force: true }); diff --git a/scripts/dev.mjs b/scripts/dev.mjs index a4c82d46e1..424b29c7cc 100644 --- a/scripts/dev.mjs +++ b/scripts/dev.mjs @@ -32,6 +32,12 @@ await Promise.all([ stdout: process.stdout, stderr: process.stderr, }), + // icons-subsetterは開発段階では使用されないが、型エラーを抑制するためにはじめの一度だけビルドする + execa('pnpm', ['--filter', 'icons-subsetter', 'build'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, + }), ]); await Promise.all([