Merge branch 'misskey-develop' into merge/2025-03-24

# Conflicts:
#	.github/workflows/storybook.yml
#	package.json
#	packages/frontend/src/utility/autogen/settings-search-index.ts
#	pnpm-lock.yaml
This commit is contained in:
Hazelnoot 2025-04-05 12:20:19 -04:00
commit 7e7350eab5
65 changed files with 486 additions and 1651 deletions

View file

@ -1888,6 +1888,8 @@ _role:
descriptionOfIsExplorable: "La línia de temps d'aquest rol i la llista d'usuaris seran públics si s'activa." descriptionOfIsExplorable: "La línia de temps d'aquest rol i la llista d'usuaris seran públics si s'activa."
displayOrder: "Posició " displayOrder: "Posició "
descriptionOfDisplayOrder: "Com més gran és el número, més dalt la seva posició a la interfície." descriptionOfDisplayOrder: "Com més gran és el número, més dalt la seva posició a la interfície."
preserveAssignmentOnMoveAccount: "L'estat de l'assignació també es trasllada amb el compte migrat"
preserveAssignmentOnMoveAccount_description: "Si s'activa quan es migra un compte amb aquest rol, el compte migrat també heretarà aquest rol."
canEditMembersByModerator: "Permetre que els moderadors editin la llista d'usuaris en aquest rol" canEditMembersByModerator: "Permetre que els moderadors editin la llista d'usuaris en aquest rol"
descriptionOfCanEditMembersByModerator: "Quan s'activa, els moderadors, així com els administradors, podran afegir i treure usuaris d'aquest rol. Si es troba desactivat, només els administradors poden assignar usuaris." descriptionOfCanEditMembersByModerator: "Quan s'activa, els moderadors, així com els administradors, podran afegir i treure usuaris d'aquest rol. Si es troba desactivat, només els administradors poden assignar usuaris."
priority: "Prioritat" priority: "Prioritat"

View file

@ -1888,6 +1888,8 @@ _role:
descriptionOfIsExplorable: "This role's timeline and the list of users with this will be made public if enabled." descriptionOfIsExplorable: "This role's timeline and the list of users with this will be made public if enabled."
displayOrder: "Position" displayOrder: "Position"
descriptionOfDisplayOrder: "The higher the number, the higher its UI position." descriptionOfDisplayOrder: "The higher the number, the higher its UI position."
preserveAssignmentOnMoveAccount: "Preserve role assignment during migration"
preserveAssignmentOnMoveAccount_description: "When turned on, this role will be carried over to the destination account when an account with this role is migrated."
canEditMembersByModerator: "Allow moderators to edit the list of members for this role" canEditMembersByModerator: "Allow moderators to edit the list of members for this role"
descriptionOfCanEditMembersByModerator: "When turned on, moderators as well as administrators will be able to assign and unassign users to this role. When turned off, only administrators will be able to assign users." descriptionOfCanEditMembersByModerator: "When turned on, moderators as well as administrators will be able to assign and unassign users to this role. When turned off, only administrators will be able to assign users."
priority: "Priority" priority: "Priority"

View file

@ -1888,6 +1888,8 @@ _role:
descriptionOfIsExplorable: "Selezionandolo, la timeline del ruolo diventerà accessibile pubblicamente. Tranne se il ruolo non è pubblico." descriptionOfIsExplorable: "Selezionandolo, la timeline del ruolo diventerà accessibile pubblicamente. Tranne se il ruolo non è pubblico."
displayOrder: "Ordine di visualizzazione" displayOrder: "Ordine di visualizzazione"
descriptionOfDisplayOrder: "I valori più alti vengono visualizzati per primi" descriptionOfDisplayOrder: "I valori più alti vengono visualizzati per primi"
preserveAssignmentOnMoveAccount: "Mantenere l'assegnazione alla migrazione del profilo"
preserveAssignmentOnMoveAccount_description: "Attivando, il ruolo verrà portato sul profilo destinatario, durante la migrazione."
canEditMembersByModerator: "Anche i Moderatori assegnano profili a questo ruolo" canEditMembersByModerator: "Anche i Moderatori assegnano profili a questo ruolo"
descriptionOfCanEditMembersByModerator: "Se disattivo, potranno farlo solamente gli Amministratori." descriptionOfCanEditMembersByModerator: "Se disattivo, potranno farlo solamente gli Amministratori."
priority: "Priorità" priority: "Priorità"

View file

@ -1887,6 +1887,8 @@ _role:
descriptionOfIsExplorable: "打开后将公开角色时间线。如果角色不是公开的,就无法公开时间线。" descriptionOfIsExplorable: "打开后将公开角色时间线。如果角色不是公开的,就无法公开时间线。"
displayOrder: "显示顺序" displayOrder: "显示顺序"
descriptionOfDisplayOrder: "数字越大,显示位置越靠前。" descriptionOfDisplayOrder: "数字越大,显示位置越靠前。"
preserveAssignmentOnMoveAccount: "将分配状态继承到目标账户"
preserveAssignmentOnMoveAccount_description: "启用后,当迁移具有该角色的账户时,目标账户也会继承该角色。"
canEditMembersByModerator: "允许监察员编辑成员" canEditMembersByModerator: "允许监察员编辑成员"
descriptionOfCanEditMembersByModerator: "如果选中,监察员和管理员都能够为用户分配/取消分配角色。如果未选中,则只有管理员可以执行此操作。" descriptionOfCanEditMembersByModerator: "如果选中,监察员和管理员都能够为用户分配/取消分配角色。如果未选中,则只有管理员可以执行此操作。"
priority: "优先级" priority: "优先级"

View file

@ -1,6 +1,6 @@
{ {
"name": "sharkey", "name": "sharkey",
"version": "2025.4.0-rc.0", "version": "2025.4.0-rc.1",
"codename": "shonk", "codename": "shonk",
"repository": { "repository": {
"type": "git", "type": "git",
@ -24,7 +24,6 @@
"build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
"build-storybook": "pnpm --filter frontend build-storybook", "build-storybook": "pnpm --filter frontend build-storybook",
"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api", "build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
"build-frontend-search-index": "pnpm --filter frontend build-search-index",
"start": "pnpm check:connect && cd packages/backend && MK_WARNED_ABOUT_CONFIG=true node ./built/boot/entry.js", "start": "pnpm check:connect && cd packages/backend && MK_WARNED_ABOUT_CONFIG=true node ./built/boot/entry.js",
"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
"init": "pnpm migrate", "init": "pnpm migrate",

View file

@ -15,8 +15,6 @@
focus: ':alpha<0.3<@accent', focus: ':alpha<0.3<@accent',
bg: '#000', bg: '#000',
fg: '#dadada', fg: '#dadada',
fgTransparentWeak: ':alpha<0.75<@fg',
fgTransparent: ':alpha<0.5<@fg',
fgHighlighted: ':lighten<3<@fg', fgHighlighted: ':lighten<3<@fg',
fgOnAccent: '#fff', fgOnAccent: '#fff',
fgOnWhite: '#333', fgOnWhite: '#333',
@ -26,7 +24,6 @@
panelHighlight: ':lighten<3<@panel', panelHighlight: ':lighten<3<@panel',
panelHeaderBg: ':lighten<3<@panel', panelHeaderBg: ':lighten<3<@panel',
panelHeaderFg: '@fg', panelHeaderFg: '@fg',
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
panelBorder: '" solid 1px var(--MI_THEME-divider)', panelBorder: '" solid 1px var(--MI_THEME-divider)',
thread: ':lighten<12<@panel', thread: ':lighten<12<@panel',
windowHeader: ':alpha<0.85<@panel', windowHeader: ':alpha<0.85<@panel',

View file

@ -15,8 +15,6 @@
focus: ':alpha<0.3<@accent', focus: ':alpha<0.3<@accent',
bg: '#fff', bg: '#fff',
fg: '#5f5f5f', fg: '#5f5f5f',
fgTransparentWeak: ':alpha<0.75<@fg',
fgTransparent: ':alpha<0.5<@fg',
fgHighlighted: ':darken<3<@fg', fgHighlighted: ':darken<3<@fg',
fgOnAccent: '#fff', fgOnAccent: '#fff',
fgOnWhite: '#333', fgOnWhite: '#333',
@ -26,7 +24,6 @@
panelHighlight: ':darken<3<@panel', panelHighlight: ':darken<3<@panel',
panelHeaderBg: ':lighten<3<@panel', panelHeaderBg: ':lighten<3<@panel',
panelHeaderFg: '@fg', panelHeaderFg: '@fg',
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
panelBorder: '" solid 1px var(--MI_THEME-divider)', panelBorder: '" solid 1px var(--MI_THEME-divider)',
thread: ':darken<12<@panel', thread: ':darken<12<@panel',
windowHeader: ':alpha<0.85<@panel', windowHeader: ':alpha<0.85<@panel',

View file

@ -46,7 +46,6 @@
fgOnWhite: '@accent', fgOnWhite: '@accent',
panelHighlight: ':lighten<3<@panel', panelHighlight: ':lighten<3<@panel',
scrollbarHandle: 'rgba(255, 255, 255, 0.2)', scrollbarHandle: 'rgba(255, 255, 255, 0.2)',
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)', scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)',
}, },
} }

View file

@ -14,7 +14,6 @@
fgOnWhite: '@accent', fgOnWhite: '@accent',
divider: 'rgba(255, 255, 255, 0.14)', divider: 'rgba(255, 255, 255, 0.14)',
panel: 'rgb(47, 47, 44)', panel: 'rgb(47, 47, 44)',
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
header: ':alpha<0.7<@panel', header: ':alpha<0.7<@panel',
navBg: '#363636', navBg: '#363636',
renote: '@accent', renote: '@accent',

View file

@ -14,7 +14,6 @@
fgOnWhite: '@accent', fgOnWhite: '@accent',
divider: 'rgba(255, 255, 255, 0.14)', divider: 'rgba(255, 255, 255, 0.14)',
panel: '#2d2d2d', panel: '#2d2d2d',
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
header: ':alpha<0.7<@panel', header: ':alpha<0.7<@panel',
navBg: '#363636', navBg: '#363636',
renote: '@accent', renote: '@accent',

View file

@ -15,7 +15,6 @@
fgOnWhite: '@accent', fgOnWhite: '@accent',
divider: 'rgba(255, 255, 255, 0.1)', divider: 'rgba(255, 255, 255, 0.1)',
panel: '#18181c', panel: '#18181c',
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
renote: '@accent', renote: '@accent',
mention: '#f2c97d', mention: '#f2c97d',
mentionMe: '@accent', mentionMe: '@accent',

View file

@ -15,7 +15,6 @@
fgOnWhite: '@accent', fgOnWhite: '@accent',
divider: '#e7fffb24', divider: '#e7fffb24',
panel: '#192320', panel: '#192320',
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
popup: '#293330', popup: '#293330',
renote: '@accent', renote: '@accent',
mentionMe: '#ffaa00', mentionMe: '#ffaa00',

View file

@ -15,7 +15,6 @@
fgOnWhite: '@accent', fgOnWhite: '@accent',
divider: '#e7fffb24', divider: '#e7fffb24',
panel: '#192320', panel: '#192320',
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
popup: '#293330', popup: '#293330',
renote: '@accent', renote: '@accent',
mentionMe: '#b4e900', mentionMe: '#b4e900',

View file

@ -49,7 +49,6 @@
navIndicator: '@indicator', navIndicator: '@indicator',
driveFolderBg: ':alpha<0.3<@accent', driveFolderBg: ':alpha<0.3<@accent',
fgHighlighted: ':lighten<3<@fg', fgHighlighted: ':lighten<3<@fg',
fgTransparent: ':alpha<0.5<@fg',
panelHeaderBg: ':lighten<3<@panel', panelHeaderBg: ':lighten<3<@panel',
panelHeaderFg: '@fg', panelHeaderFg: '@fg',
buttonGradateA: '@accent', buttonGradateA: '@accent',
@ -58,8 +57,6 @@
panelHighlight: ':lighten<3<@panel', panelHighlight: ':lighten<3<@panel',
scrollbarHandle: 'rgba(255, 255, 255, 0.2)', scrollbarHandle: 'rgba(255, 255, 255, 0.2)',
inputBorderHover: 'rgba(255, 255, 255, 0.2)', inputBorderHover: 'rgba(255, 255, 255, 0.2)',
fgTransparentWeak: ':alpha<0.75<@fg',
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)', scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)',
deckBg: '#142022', deckBg: '#142022',
}, },

View file

@ -15,7 +15,6 @@
divider: '#cfcfcf', divider: '#cfcfcf',
panel: '#ebe7e5', panel: '#ebe7e5',
panelHeaderBg: '@panel', panelHeaderBg: '@panel',
panelHeaderDivider: '@divider',
header: ':alpha<0.7<@panel', header: ':alpha<0.7<@panel',
navBg: '#ebe7e5', navBg: '#ebe7e5',
renote: '#229e92', renote: '#229e92',

View file

@ -15,7 +15,6 @@
header: ':alpha<0.7<@panel', header: ':alpha<0.7<@panel',
navBg: '#fff', navBg: '#fff',
panel: '#fff', panel: '#fff',
panelHeaderDivider: '@divider',
mentionMe: 'rgb(0, 179, 70)', mentionMe: 'rgb(0, 179, 70)',
}, },
} }

View file

@ -13,7 +13,6 @@
fgOnWhite: '@accent', fgOnWhite: '@accent',
panel: '#fff', panel: '#fff',
divider: 'rgb(230 233 234)', divider: 'rgb(230 233 234)',
panelHeaderDivider: '@divider',
renote: '@accent', renote: '@accent',
link: '@accent', link: '@accent',
mention: '@accent', mention: '@accent',

View file

@ -51,7 +51,6 @@
buttonHoverBg: '#0000001a', buttonHoverBg: '#0000001a',
driveFolderBg: ':alpha<0.3<@accent', driveFolderBg: ':alpha<0.3<@accent',
fgHighlighted: ':lighten<3<@fg', fgHighlighted: ':lighten<3<@fg',
fgTransparent: ':alpha<0.5<@fg',
panelHeaderBg: ':lighten<3<@panel', panelHeaderBg: ':lighten<3<@panel',
panelHeaderFg: '@fg', panelHeaderFg: '@fg',
buttonGradateA: '@accent', buttonGradateA: '@accent',
@ -60,8 +59,6 @@
panelHighlight: ':lighten<3<@panel', panelHighlight: ':lighten<3<@panel',
scrollbarHandle: '#74747433', scrollbarHandle: '#74747433',
inputBorderHover: 'rgba(255, 255, 255, 0.2)', inputBorderHover: 'rgba(255, 255, 255, 0.2)',
fgTransparentWeak: ':alpha<0.75<@fg',
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)', scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)',
}, },
} }

View file

@ -41,15 +41,12 @@
navIndicator: '@accent', navIndicator: '@accent',
driveFolderBg: ':alpha<0.3<@accent', driveFolderBg: ':alpha<0.3<@accent',
fgHighlighted: ':darken<3<@fg', fgHighlighted: ':darken<3<@fg',
fgTransparent: ':alpha<0.5<@fg',
fgOnWhite: '@accent', fgOnWhite: '@accent',
panelHeaderBg: ':lighten<3<@panel', panelHeaderBg: ':lighten<3<@panel',
panelHeaderFg: '@fg', panelHeaderFg: '@fg',
htmlThemeColor: '@bg', htmlThemeColor: '@bg',
panelHighlight: ':darken<3<@panel', panelHighlight: ':darken<3<@panel',
scrollbarHandle: 'rgba(0, 0, 0, 0.2)', scrollbarHandle: 'rgba(0, 0, 0, 0.2)',
fgTransparentWeak: ':alpha<0.75<@fg',
panelHeaderDivider: '@divider',
scrollbarHandleHover: 'rgba(0, 0, 0, 0.4)', scrollbarHandleHover: 'rgba(0, 0, 0, 0.4)',
}, },
} }

View file

@ -4,77 +4,68 @@
*/ */
import { parse as vueSfcParse } from 'vue/compiler-sfc'; import { parse as vueSfcParse } from 'vue/compiler-sfc';
import type { LogOptions, Plugin } from 'vite'; import {
createLogger,
EnvironmentModuleGraph,
normalizePath,
type LogErrorOptions,
type LogOptions,
type Plugin,
type PluginOption
} from 'vite';
import fs from 'node:fs'; import fs from 'node:fs';
import { glob } from 'glob'; import { glob } from 'glob';
import JSON5 from 'json5'; import JSON5 from 'json5';
import MagicString from 'magic-string'; import MagicString, { SourceMap } from 'magic-string';
import path from 'node:path' import path from 'node:path'
import { hash, toBase62 } from '../vite.config'; import { hash, toBase62 } from '../vite.config';
import { createLogger } from 'vite'; import { minimatch } from 'minimatch';
import type {
AttributeNode, CompoundExpressionNode, DirectiveNode,
ElementNode,
RootNode, SimpleExpressionNode,
TemplateChildNode,
} from '@vue/compiler-core';
import { NodeTypes } from '@vue/compiler-core';
interface VueAstNode { export type AnalysisResult<T = SearchIndexItem> = {
type: number;
tag?: string;
loc?: {
start: { offset: number, line: number, column: number },
end: { offset: number, line: number, column: number },
source?: string
};
props?: Array<{
name: string;
type: number;
value?: { content?: string };
arg?: { content?: string };
exp?: { content?: string; loc?: any };
}>;
children?: VueAstNode[];
content?: any;
__markerId?: string;
__children?: string[];
}
export type AnalysisResult = {
filePath: string; filePath: string;
usage: SearchIndexItem[]; usage: T[];
} }
export type SearchIndexItem = { export type SearchIndexItem = SearchIndexItemLink<SearchIndexItem>;
export type SearchIndexStringItem = SearchIndexItemLink<string>;
export interface SearchIndexItemLink<T> {
id: string; id: string;
path?: string; path?: string;
label: string; label: string;
keywords: string | string[]; keywords: string | string[];
icon?: string; icon?: string;
inlining?: string[]; inlining?: string[];
children?: SearchIndexItem[]; children?: T[];
}; }
export type Options = { export type Options = {
targetFilePaths: string[], targetFilePaths: string[],
exportFilePath: string, mainVirtualModule: string,
modulesToHmrOnUpdate: string[],
fileVirtualModulePrefix?: string,
fileVirtualModuleSuffix?: string,
verbose?: boolean, verbose?: boolean,
}; };
// 関連するノードタイプの定数化
const NODE_TYPES = {
ELEMENT: 1,
EXPRESSION: 2,
TEXT: 3,
INTERPOLATION: 5, // Mustache
};
// マーカー関係を表す型 // マーカー関係を表す型
interface MarkerRelation { interface MarkerRelation {
parentId?: string; parentId?: string;
markerId: string; markerId: string;
node: VueAstNode; node: ElementNode;
} }
// ロガー // ロガー
let logger = { let logger = {
info: (msg: string, options?: LogOptions) => { }, info: (msg: string, options?: LogOptions) => { },
warn: (msg: string, options?: LogOptions) => { }, warn: (msg: string, options?: LogOptions) => { },
error: (msg: string, options?: LogOptions) => { }, error: (msg: string, options?: LogErrorOptions) => { },
}; };
let loggerInitialized = false; let loggerInitialized = false;
@ -99,14 +90,11 @@ function initLogger(options: Options) {
} }
} }
/** function collectSearchItemIndexes(analysisResults: AnalysisResult<SearchIndexStringItem>[]): SearchIndexItem[] {
* TypeScriptファイルとして出力する
*/
function outputAnalysisResultAsTS(outputPath: string, analysisResults: AnalysisResult[]): void {
logger.info(`Processing ${analysisResults.length} files for output`); logger.info(`Processing ${analysisResults.length} files for output`);
// 新しいツリー構造を構築 // 新しいツリー構造を構築
const allMarkers = new Map<string, SearchIndexItem>(); const allMarkers = new Map<string, SearchIndexStringItem>();
// 1. すべてのマーカーを一旦フラットに収集 // 1. すべてのマーカーを一旦フラットに収集
for (const file of analysisResults) { for (const file of analysisResults) {
@ -115,10 +103,9 @@ function outputAnalysisResultAsTS(outputPath: string, analysisResults: AnalysisR
for (const marker of file.usage) { for (const marker of file.usage) {
if (marker.id) { if (marker.id) {
// キーワードとchildren処理を共通化 // キーワードとchildren処理を共通化
const processedMarker = { const processedMarker: SearchIndexStringItem = {
...marker, ...marker,
keywords: processMarkerProperty(marker.keywords, 'keywords'), keywords: processMarkerProperty(marker.keywords, 'keywords'),
children: processMarkerProperty(marker.children || [], 'children')
}; };
allMarkers.set(marker.id, processedMarker); allMarkers.set(marker.id, processedMarker);
@ -143,14 +130,13 @@ function outputAnalysisResultAsTS(outputPath: string, analysisResults: AnalysisR
const { totalMarkers, totalChildren } = countMarkers(resolvedRootMarkers); const { totalMarkers, totalChildren } = countMarkers(resolvedRootMarkers);
logger.info(`Total markers in tree: ${totalMarkers} (${resolvedRootMarkers.length} roots + ${totalChildren} nested children)`); logger.info(`Total markers in tree: ${totalMarkers} (${resolvedRootMarkers.length} roots + ${totalChildren} nested children)`);
// 6. 結果をTS形式で出力 return resolvedRootMarkers;
writeOutputFile(outputPath, resolvedRootMarkers);
} }
/** /**
* keywordsやchildren * keywordsやchildren
*/ */
function processMarkerProperty(propValue: any, propType: 'keywords' | 'children'): any { function processMarkerProperty(propValue: string | string[], propType: 'keywords' | 'children'): string | string[] {
// 文字列の配列表現を解析 // 文字列の配列表現を解析
if (typeof propValue === 'string' && propValue.startsWith('[') && propValue.endsWith(']')) { if (typeof propValue === 'string' && propValue.startsWith('[') && propValue.endsWith(']')) {
try { try {
@ -169,7 +155,7 @@ function processMarkerProperty(propValue: any, propType: 'keywords' | 'children'
/** /**
* IDを収集する * IDを収集する
*/ */
function collectChildIds(allMarkers: Map<string, SearchIndexItem>): Set<string> { function collectChildIds(allMarkers: Map<string, SearchIndexStringItem>): Set<string> {
const childIds = new Set<string>(); const childIds = new Set<string>();
allMarkers.forEach((marker, id) => { allMarkers.forEach((marker, id) => {
@ -232,10 +218,10 @@ function collectChildIds(allMarkers: Map<string, SearchIndexItem>): Set<string>
* *
*/ */
function identifyRootMarkers( function identifyRootMarkers(
allMarkers: Map<string, SearchIndexItem>, allMarkers: Map<string, SearchIndexStringItem>,
childIds: Set<string> childIds: Set<string>
): SearchIndexItem[] { ): SearchIndexStringItem[] {
const rootMarkers: SearchIndexItem[] = []; const rootMarkers: SearchIndexStringItem[] = [];
allMarkers.forEach((marker, id) => { allMarkers.forEach((marker, id) => {
if (!childIds.has(id)) { if (!childIds.has(id)) {
@ -251,12 +237,12 @@ function identifyRootMarkers(
* IDから実際のオブジェクトに解決する * IDから実際のオブジェクトに解決する
*/ */
function resolveChildReferences( function resolveChildReferences(
rootMarkers: SearchIndexItem[], rootMarkers: SearchIndexStringItem[],
allMarkers: Map<string, SearchIndexItem> allMarkers: Map<string, SearchIndexStringItem>
): SearchIndexItem[] { ): SearchIndexItem[] {
function resolveChildrenForMarker(marker: SearchIndexItem): SearchIndexItem { function resolveChildrenForMarker(marker: SearchIndexStringItem): SearchIndexItem {
// マーカーのディープコピーを作成 // マーカーのディープコピーを作成
const resolvedMarker = { ...marker }; const resolvedMarker: SearchIndexItem = { ...marker, children: [] };
// 明示的に子マーカー配列を作成 // 明示的に子マーカー配列を作成
const resolvedChildren: SearchIndexItem[] = []; const resolvedChildren: SearchIndexItem[] = [];
@ -351,55 +337,19 @@ function countMarkers(markers: SearchIndexItem[]): { totalMarkers: number, total
return { totalMarkers, totalChildren }; return { totalMarkers, totalChildren };
} }
/**
* TypeScriptファイルを出力
*/
function writeOutputFile(outputPath: string, resolvedRootMarkers: SearchIndexItem[]): void {
try {
const tsOutput = generateTypeScriptCode(resolvedRootMarkers);
fs.writeFileSync(outputPath, tsOutput, 'utf-8');
// 強制的に出力させるためにViteロガーを使わない
console.log(`Successfully wrote search index to ${outputPath} with ${resolvedRootMarkers.length} root entries`);
} catch (error) {
logger.error('[create-search-index]: error writing output: ', error);
}
}
/** /**
* TypeScriptコード生成 * TypeScriptコード生成
*/ */
function generateTypeScriptCode(resolvedRootMarkers: SearchIndexItem[]): string { function generateJavaScriptCode(resolvedRootMarkers: SearchIndexItem[]): string {
return ` return `import { i18n } from '@/i18n.js';\n`
/* + `export const searchIndexes = ${customStringify(resolvedRootMarkers)};\n`;
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
// This file was automatically generated by create-search-index.
// Do not edit this file.
import { i18n } from '@/i18n.js';
export type SearchIndexItem = {
id: string;
path?: string;
label: string;
keywords: string[];
icon?: string;
children?: SearchIndexItem[];
};
export const searchIndexes: SearchIndexItem[] = ${customStringify(resolvedRootMarkers)} as const;
export type SearchIndex = typeof searchIndexes;
`;
} }
/** /**
* *
* i18n参照を保持しつつ適切な形式に変換 * i18n参照を保持しつつ適切な形式に変換
*/ */
function customStringify(obj: any, depth = 0): string { function customStringify(obj: unknown, depth = 0): string {
const INDENT_STR = '\t'; const INDENT_STR = '\t';
// 配列の処理 // 配列の処理
@ -441,7 +391,6 @@ function customStringify(obj: any, depth = 0): string {
.filter(([key, value]) => { .filter(([key, value]) => {
if (value === undefined) return false; if (value === undefined) return false;
if (key === 'children' && Array.isArray(value) && value.length === 0) return false; if (key === 'children' && Array.isArray(value) && value.length === 0) return false;
if (key === 'inlining') return false;
return true; return true;
}) })
// 各プロパティを変換 // 各プロパティを変換
@ -462,7 +411,7 @@ function customStringify(obj: any, depth = 0): string {
/** /**
* *
*/ */
function formatSpecialProperty(key: string, value: any): string { function formatSpecialProperty(key: string, value: unknown): string {
// 値がundefinedの場合は空文字列を返す // 値がundefinedの場合は空文字列を返す
if (value === undefined) { if (value === undefined) {
return '""'; return '""';
@ -499,7 +448,7 @@ function formatSpecialProperty(key: string, value: any): string {
/** /**
* *
*/ */
function formatArrayForOutput(items: any[]): string { function formatArrayForOutput(items: unknown[]): string {
return items.map(item => { return items.map(item => {
// i18n.ts. 参照の文字列はそのままJavaScript式として出力 // i18n.ts. 参照の文字列はそのままJavaScript式として出力
if (typeof item === 'string' && isI18nReference(item)) { if (typeof item === 'string' && isI18nReference(item)) {
@ -516,17 +465,18 @@ function formatArrayForOutput(items: any[]): string {
* *
* *
*/ */
function extractElementText(node: VueAstNode): string | null { function extractElementText(node: TemplateChildNode): string | null {
if (!node) return null; if (!node) return null;
if (node.type === NodeTypes.COMPOUND_EXPRESSION) throw new Error("Unexpected COMPOUND_EXPRESSION");
logger.info(`Extracting text from node type=${node.type}, tag=${node.tag || 'unknown'}`); logger.info(`Extracting text from node type=${node.type}, tag=${'tag' in node ? node.tag : 'unknown'}`);
// 1. 直接コンテンツの抽出を試行 // 1. 直接コンテンツの抽出を試行
const directContent = extractDirectContent(node); const directContent = extractDirectContent(node);
if (directContent) return directContent; if (directContent) return directContent;
// 子要素がない場合は終了 // 子要素がない場合は終了
if (!node.children || !Array.isArray(node.children)) { if (!('children' in node) || !Array.isArray(node.children)) {
return null; return null;
} }
@ -548,12 +498,13 @@ function extractElementText(node: VueAstNode): string | null {
/** /**
* *
*/ */
function extractDirectContent(node: VueAstNode): string | null { function extractDirectContent(node: TemplateChildNode): string | null {
if (!node.content) return null; if (!('content' in node)) return null;
if (typeof node.content == 'object' && node.content.type === NodeTypes.COMPOUND_EXPRESSION) throw new Error("Unexpected COMPOUND_EXPRESSION");
const content = typeof node.content === 'string' const content = typeof node.content === 'string' ? node.content.trim()
? node.content.trim() : node.content.type !== NodeTypes.INTERPOLATION ? node.content.content.trim()
: (node.content.content ? node.content.content.trim() : null); : null;
if (!content) return null; if (!content) return null;
@ -582,9 +533,9 @@ function extractDirectContent(node: VueAstNode): string | null {
/** /**
* Mustache * Mustache
*/ */
function extractInterpolationContent(children: VueAstNode[]): string | null { function extractInterpolationContent(children: TemplateChildNode[]): string | null {
for (const child of children) { for (const child of children) {
if (child.type === NODE_TYPES.INTERPOLATION) { if (child.type === NodeTypes.INTERPOLATION) {
logger.info(`Found interpolation node (Mustache): ${JSON.stringify(child.content).substring(0, 100)}...`); logger.info(`Found interpolation node (Mustache): ${JSON.stringify(child.content).substring(0, 100)}...`);
if (child.content && child.content.type === 4 && child.content.content) { if (child.content && child.content.type === 4 && child.content.content) {
@ -595,6 +546,7 @@ function extractInterpolationContent(children: VueAstNode[]): string | null {
return content; return content;
} }
} else if (child.content && typeof child.content === 'object') { } else if (child.content && typeof child.content === 'object') {
if (child.content.type == NodeTypes.COMPOUND_EXPRESSION) throw new Error("Unexpected COMPOUND_EXPRESSION");
// オブジェクト形式のcontentを探索 // オブジェクト形式のcontentを探索
logger.info(`Complex interpolation node: ${JSON.stringify(child.content).substring(0, 100)}...`); logger.info(`Complex interpolation node: ${JSON.stringify(child.content).substring(0, 100)}...`);
@ -616,10 +568,10 @@ function extractInterpolationContent(children: VueAstNode[]): string | null {
/** /**
* *
*/ */
function extractExpressionContent(children: VueAstNode[]): string | null { function extractExpressionContent(children: TemplateChildNode[]): string | null {
// i18n.ts. 参照パターンを持つものを優先 // i18n.ts. 参照パターンを持つものを優先
for (const child of children) { for (const child of children) {
if (child.type === NODE_TYPES.EXPRESSION && child.content) { if (child.type === NodeTypes.TEXT && child.content) {
const expr = child.content.trim(); const expr = child.content.trim();
if (isI18nReference(expr)) { if (isI18nReference(expr)) {
@ -631,7 +583,7 @@ function extractExpressionContent(children: VueAstNode[]): string | null {
// その他の式 // その他の式
for (const child of children) { for (const child of children) {
if (child.type === NODE_TYPES.EXPRESSION && child.content) { if (child.type === NodeTypes.TEXT && child.content) {
const expr = child.content.trim(); const expr = child.content.trim();
logger.info(`Found expression: ${expr}`); logger.info(`Found expression: ${expr}`);
return expr; return expr;
@ -644,9 +596,9 @@ function extractExpressionContent(children: VueAstNode[]): string | null {
/** /**
* *
*/ */
function extractTextContent(children: VueAstNode[]): string | null { function extractTextContent(children: TemplateChildNode[]): string | null {
for (const child of children) { for (const child of children) {
if (child.type === NODE_TYPES.TEXT && child.content) { if (child.type === NodeTypes.COMMENT && child.content) {
const text = child.content.trim(); const text = child.content.trim();
if (text) { if (text) {
@ -672,16 +624,16 @@ function extractTextContent(children: VueAstNode[]): string | null {
/** /**
* *
*/ */
function extractNestedContent(children: VueAstNode[]): string | null { function extractNestedContent(children: TemplateChildNode[]): string | null {
for (const child of children) { for (const child of children) {
if (child.children && Array.isArray(child.children) && child.children.length > 0) { if ('children' in child && Array.isArray(child.children) && child.children.length > 0) {
const nestedContent = extractElementText(child); const nestedContent = extractElementText(child);
if (nestedContent) { if (nestedContent) {
logger.info(`Found nested content: ${nestedContent}`); logger.info(`Found nested content: ${nestedContent}`);
return nestedContent; return nestedContent;
} }
} else if (child.type === NODE_TYPES.ELEMENT) { } else if (child.type === NodeTypes.ELEMENT) {
// childrenがなくても内部を調査 // childrenがなくても内部を調査
const nestedContent = extractElementText(child); const nestedContent = extractElementText(child);
@ -699,16 +651,16 @@ function extractNestedContent(children: VueAstNode[]): string | null {
/** /**
* SearchLabelとSearchKeywordを探して抽出する関数 * SearchLabelとSearchKeywordを探して抽出する関数
*/ */
function extractLabelsAndKeywords(nodes: VueAstNode[]): { label: string | null, keywords: any[] } { function extractLabelsAndKeywords(nodes: TemplateChildNode[]): { label: string | null, keywords: string[] } {
let label: string | null = null; let label: string | null = null;
const keywords: any[] = []; const keywords: string[] = [];
logger.info(`Extracting labels and keywords from ${nodes.length} nodes`); logger.info(`Extracting labels and keywords from ${nodes.length} nodes`);
// 再帰的にSearchLabelとSearchKeywordを探索ネストされたSearchMarkerは処理しない // 再帰的にSearchLabelとSearchKeywordを探索ネストされたSearchMarkerは処理しない
function findComponents(nodes: VueAstNode[]) { function findComponents(nodes: TemplateChildNode[]) {
for (const node of nodes) { for (const node of nodes) {
if (node.type === NODE_TYPES.ELEMENT) { if (node.type === NodeTypes.ELEMENT) {
logger.info(`Checking element: ${node.tag}`); logger.info(`Checking element: ${node.tag}`);
// SearchMarkerの場合は、その子要素は別スコープなのでスキップ // SearchMarkerの場合は、その子要素は別スコープなのでスキップ
@ -730,11 +682,12 @@ function extractLabelsAndKeywords(nodes: VueAstNode[]): { label: string | null,
logger.info(`SearchLabel found but extraction failed, trying direct children inspection`); logger.info(`SearchLabel found but extraction failed, trying direct children inspection`);
// バックアップ: 子直接確認 - type=5のMustacheインターポレーションを重点的に確認 // バックアップ: 子直接確認 - type=5のMustacheインターポレーションを重点的に確認
if (node.children && Array.isArray(node.children)) { {
for (const child of node.children) { for (const child of node.children) {
// Mustacheインターポレーション // Mustacheインターポレーション
if (child.type === NODE_TYPES.INTERPOLATION && child.content) { if (child.type === NodeTypes.INTERPOLATION && child.content) {
// content内の式を取り出す // content内の式を取り出す
if (child.content.type == NodeTypes.COMPOUND_EXPRESSION) throw new Error("unexpected COMPOUND_EXPRESSION");
const expression = child.content.content || const expression = child.content.content ||
(child.content.type === 4 ? child.content.content : null) || (child.content.type === 4 ? child.content.content : null) ||
JSON.stringify(child.content); JSON.stringify(child.content);
@ -747,13 +700,13 @@ function extractLabelsAndKeywords(nodes: VueAstNode[]): { label: string | null,
} }
} }
// 式ノード // 式ノード
else if (child.type === NODE_TYPES.EXPRESSION && child.content && isI18nReference(child.content)) { else if (child.type === NodeTypes.TEXT && child.content && isI18nReference(child.content)) {
label = child.content.trim(); label = child.content.trim();
logger.info(`Found i18n in expression: ${label}`); logger.info(`Found i18n in expression: ${label}`);
break; break;
} }
// テキストードでもMustache構文を探す // テキストードでもMustache構文を探す
else if (child.type === NODE_TYPES.TEXT && child.content) { else if (child.type === NodeTypes.COMMENT && child.content) {
const mustacheMatch = child.content.trim().match(/^\s*{{\s*(.*?)\s*}}\s*$/); const mustacheMatch = child.content.trim().match(/^\s*{{\s*(.*?)\s*}}\s*$/);
if (mustacheMatch && mustacheMatch[1] && isI18nReference(mustacheMatch[1])) { if (mustacheMatch && mustacheMatch[1] && isI18nReference(mustacheMatch[1])) {
label = mustacheMatch[1].trim(); label = mustacheMatch[1].trim();
@ -778,11 +731,12 @@ function extractLabelsAndKeywords(nodes: VueAstNode[]): { label: string | null,
logger.info(`SearchKeyword found but extraction failed, trying direct children inspection`); logger.info(`SearchKeyword found but extraction failed, trying direct children inspection`);
// バックアップ: 子直接確認 - type=5のMustacheインターポレーションを重点的に確認 // バックアップ: 子直接確認 - type=5のMustacheインターポレーションを重点的に確認
if (node.children && Array.isArray(node.children)) { {
for (const child of node.children) { for (const child of node.children) {
// Mustacheインターポレーション // Mustacheインターポレーション
if (child.type === NODE_TYPES.INTERPOLATION && child.content) { if (child.type === NodeTypes.INTERPOLATION && child.content) {
// content内の式を取り出す // content内の式を取り出す
if (child.content.type == NodeTypes.COMPOUND_EXPRESSION) throw new Error("unexpected COMPOUND_EXPRESSION");
const expression = child.content.content || const expression = child.content.content ||
(child.content.type === 4 ? child.content.content : null) || (child.content.type === 4 ? child.content.content : null) ||
JSON.stringify(child.content); JSON.stringify(child.content);
@ -796,14 +750,14 @@ function extractLabelsAndKeywords(nodes: VueAstNode[]): { label: string | null,
} }
} }
// 式ノード // 式ノード
else if (child.type === NODE_TYPES.EXPRESSION && child.content && isI18nReference(child.content)) { else if (child.type === NodeTypes.TEXT && child.content && isI18nReference(child.content)) {
const keyword = child.content.trim(); const keyword = child.content.trim();
keywords.push(keyword); keywords.push(keyword);
logger.info(`Found i18n keyword in expression: ${keyword}`); logger.info(`Found i18n keyword in expression: ${keyword}`);
break; break;
} }
// テキストードでもMustache構文を探す // テキストードでもMustache構文を探す
else if (child.type === NODE_TYPES.TEXT && child.content) { else if (child.type === NodeTypes.COMMENT && child.content) {
const mustacheMatch = child.content.trim().match(/^\s*{{\s*(.*?)\s*}}\s*$/); const mustacheMatch = child.content.trim().match(/^\s*{{\s*(.*?)\s*}}\s*$/);
if (mustacheMatch && mustacheMatch[1] && isI18nReference(mustacheMatch[1])) { if (mustacheMatch && mustacheMatch[1] && isI18nReference(mustacheMatch[1])) {
const keyword = mustacheMatch[1].trim(); const keyword = mustacheMatch[1].trim();
@ -834,23 +788,22 @@ function extractLabelsAndKeywords(nodes: VueAstNode[]): { label: string | null,
function extractUsageInfoFromTemplateAst( function extractUsageInfoFromTemplateAst(
templateAst: any, templateAst: RootNode | undefined,
id: string, id: string,
): SearchIndexItem[] { ): SearchIndexStringItem[] {
const allMarkers: SearchIndexItem[] = []; const allMarkers: SearchIndexStringItem[] = [];
const markerMap = new Map<string, SearchIndexItem>(); const markerMap = new Map<string, SearchIndexItemLink<string>>();
const childrenIds = new Set<string>(); const childrenIds = new Set<string>();
const normalizedId = id.replace(/\\/g, '/'); const normalizedId = id.replace(/\\/g, '/');
if (!templateAst) return allMarkers; if (!templateAst) return allMarkers;
// マーカーの基本情報を収集 // マーカーの基本情報を収集
function collectMarkers(node: VueAstNode, parentId: string | null = null) { function collectMarkers(node: TemplateChildNode | RootNode, parentId: string | null = null) {
if (node.type === 1 && node.tag === 'SearchMarker') { if (node.type === NodeTypes.ELEMENT && node.tag === 'SearchMarker') {
// マーカーID取得 // マーカーID取得
const markerIdProp = node.props?.find((p: any) => p.name === 'markerId'); const markerIdProp = node.props?.find(p => p.name === 'markerId');
const markerId = markerIdProp?.value?.content || const markerId = markerIdProp?.type == NodeTypes.ATTRIBUTE ? markerIdProp.value?.content : null;
node.__markerId;
// SearchMarkerにマーカーIDがない場合はエラー // SearchMarkerにマーカーIDがない場合はエラー
if (markerId == null) { if (markerId == null) {
@ -859,7 +812,7 @@ function extractUsageInfoFromTemplateAst(
} }
// マーカー基本情報 // マーカー基本情報
const markerInfo: SearchIndexItem = { const markerInfo: SearchIndexStringItem = {
id: markerId, id: markerId,
children: [], children: [],
label: '', // デフォルト値 label: '', // デフォルト値
@ -882,7 +835,7 @@ function extractUsageInfoFromTemplateAst(
if (bindings.path) markerInfo.path = bindings.path; if (bindings.path) markerInfo.path = bindings.path;
if (bindings.icon) markerInfo.icon = bindings.icon; if (bindings.icon) markerInfo.icon = bindings.icon;
if (bindings.label) markerInfo.label = bindings.label; if (bindings.label) markerInfo.label = bindings.label;
if (bindings.children) markerInfo.children = bindings.children; if (bindings.children) markerInfo.children = processMarkerProperty(bindings.children, 'children') as string[];
if (bindings.inlining) { if (bindings.inlining) {
markerInfo.inlining = bindings.inlining; markerInfo.inlining = bindings.inlining;
logger.info(`Added inlining ${JSON.stringify(bindings.inlining)} to marker ${markerId}`); logger.info(`Added inlining ${JSON.stringify(bindings.inlining)} to marker ${markerId}`);
@ -946,19 +899,19 @@ function extractUsageInfoFromTemplateAst(
} }
// 子ノードを処理 // 子ノードを処理
if (node.children && Array.isArray(node.children)) { for (const child of node.children) {
node.children.forEach((child: VueAstNode) => {
collectMarkers(child, markerId); collectMarkers(child, markerId);
});
} }
return markerId; return markerId;
} }
// SearchMarkerでない場合は再帰的に子ードを処理 // SearchMarkerでない場合は再帰的に子ードを処理
else if (node.children && Array.isArray(node.children)) { else if ('children' in node && Array.isArray(node.children)) {
node.children.forEach((child: VueAstNode) => { for (const child of node.children) {
if (typeof child == 'object' && child.type !== NodeTypes.SIMPLE_EXPRESSION) {
collectMarkers(child, parentId); collectMarkers(child, parentId);
}); }
}
} }
return null; return null;
@ -969,16 +922,22 @@ function extractUsageInfoFromTemplateAst(
return allMarkers; return allMarkers;
} }
type SpecialBindings = {
inlining: string[];
keywords: string[] | string;
};
type Bindings = Partial<Omit<Record<keyof SearchIndexItem, string>, keyof SpecialBindings> & SpecialBindings>;
// バインドプロパティの処理を修正する関数 // バインドプロパティの処理を修正する関数
function extractNodeBindings(node: VueAstNode): Record<keyof SearchIndexItem, any> { function extractNodeBindings(node: TemplateChildNode | RootNode): Bindings {
const bindings: Record<string, any> = {}; const bindings: Bindings = {};
if (!node.props || !Array.isArray(node.props)) return bindings; if (node.type !== NodeTypes.ELEMENT) return bindings;
// バインド式を収集 // バインド式を収集
for (const prop of node.props) { for (const prop of node.props) {
if (prop.type === 7 && prop.name === 'bind' && prop.arg?.content) { if (prop.type === NodeTypes.DIRECTIVE && prop.name === 'bind' && prop.arg && 'content' in prop.arg) {
const propName = prop.arg.content; const propName = prop.arg.content;
if (prop.exp?.type === NodeTypes.COMPOUND_EXPRESSION) throw new Error('unexpected COMPOUND_EXPRESSION');
const propContent = prop.exp?.content || ''; const propContent = prop.exp?.content || '';
logger.info(`Processing bind prop ${propName}: ${propContent}`); logger.info(`Processing bind prop ${propName}: ${propContent}`);
@ -1055,7 +1014,7 @@ function extractNodeBindings(node: VueAstNode): Record<keyof SearchIndexItem, an
} }
// 配列式をパースする補助関数(文字列リテラル処理を改善) // 配列式をパースする補助関数(文字列リテラル処理を改善)
function parseArrayExpression(expr: string): any[] { function parseArrayExpression(expr: string): string[] {
try { try {
// 単純なケースはJSON5でパースを試みる // 単純なケースはJSON5でパースを試みる
return JSON5.parse(expr.replace(/'/g, '"')); return JSON5.parse(expr.replace(/'/g, '"'));
@ -1067,7 +1026,7 @@ function parseArrayExpression(expr: string): any[] {
const content = expr.substring(1, expr.length - 1).trim(); const content = expr.substring(1, expr.length - 1).trim();
if (!content) return []; if (!content) return [];
const result: any[] = []; const result: string[] = [];
let currentItem = ''; let currentItem = '';
let depth = 0; let depth = 0;
let inString = false; let inString = false;
@ -1138,37 +1097,16 @@ function parseArrayExpression(expr: string): any[] {
} }
} }
export async function analyzeVueProps(options: Options & { export function collectFileMarkers(files: [id: string, code: string][]): AnalysisResult<SearchIndexStringItem> {
transformedCodeCache: Record<string, string>, const allMarkers: SearchIndexStringItem[] = [];
}): Promise<void> { for (const [id, code] of files) {
initLogger(options);
const allMarkers: SearchIndexItem[] = [];
// 対象ファイルパスを glob で展開
const filePaths = options.targetFilePaths.reduce<string[]>((acc, filePathPattern) => {
const matchedFiles = glob.sync(filePathPattern);
return [...acc, ...matchedFiles];
}, []);
logger.info(`Found ${filePaths.length} matching files to analyze`);
for (const filePath of filePaths) {
const absolutePath = path.join(process.cwd(), filePath);
const id = absolutePath.replace(/\\/g, '/'); // 絶対パスに変換
const code = options.transformedCodeCache[id]; // options 経由でキャッシュ参照
if (!code) { // キャッシュミスの場合
logger.error(`Error: No cached code found for: ${id}.`); // エラーログ
throw new Error(`No cached code found for: ${id}.`); // エラーを投げる
}
try { try {
const { descriptor, errors } = vueSfcParse(options.transformedCodeCache[id], { const { descriptor, errors } = vueSfcParse(code, {
filename: filePath, filename: id,
}); });
if (errors.length > 0) { if (errors.length > 0) {
logger.error(`Compile Error: ${filePath}, ${errors}`); logger.error(`Compile Error: ${id}, ${errors}`);
continue; // エラーが発生したファイルはスキップ continue; // エラーが発生したファイルはスキップ
} }
@ -1176,83 +1114,76 @@ export async function analyzeVueProps(options: Options & {
if (fileMarkers && fileMarkers.length > 0) { if (fileMarkers && fileMarkers.length > 0) {
allMarkers.push(...fileMarkers); // すべてのマーカーを収集 allMarkers.push(...fileMarkers); // すべてのマーカーを収集
logger.info(`Successfully extracted ${fileMarkers.length} markers from ${filePath}`); logger.info(`Successfully extracted ${fileMarkers.length} markers from ${id}`);
} else { } else {
logger.info(`No markers found in ${filePath}`); logger.info(`No markers found in ${id}`);
} }
} catch (error) { } catch (error) {
logger.error(`Error analyzing file ${filePath}:`, error); logger.error(`Error analyzing file ${id}:`, error);
} }
} }
// 収集したすべてのマーカー情報を使用 // 収集したすべてのマーカー情報を使用
const analysisResult: AnalysisResult[] = [ return {
{
filePath: "combined-markers", // すべてのファイルのマーカーを1つのエントリとして扱う filePath: "combined-markers", // すべてのファイルのマーカーを1つのエントリとして扱う
usage: allMarkers, usage: allMarkers,
};
}
type TransformedCode = {
code: string,
map: SourceMap,
};
export class MarkerIdAssigner {
// key: file id
private cache: Map<string, TransformedCode>;
constructor() {
this.cache = new Map();
} }
];
outputAnalysisResultAsTS(options.exportFilePath, analysisResult); // すべてのマーカー情報を渡す public onInvalidate(id: string) {
} this.cache.delete(id);
}
interface MarkerRelation { public processFile(id: string, code: string): TransformedCode {
parentId?: string; // try cache first
markerId: string; if (this.cache.has(id)) {
node: VueAstNode; return this.cache.get(id)!;
} }
const transformed = this.#processImpl(id, code);
async function processVueFile( this.cache.set(id, transformed);
code: string, return transformed;
id: string, }
options: Options,
transformedCodeCache: Record<string, string>
): Promise<{
code: string,
map: any,
transformedCodeCache: Record<string, string>
}> {
const normalizedId = id.replace(/\\/g, '/'); // ファイルパスを正規化
// 開発モード時はコード内容に変更があれば常に再処理する
// コード内容が同じ場合のみキャッシュを使用
const isDevMode = process.env.NODE_ENV === 'development';
#processImpl(id: string, code: string): TransformedCode {
const s = new MagicString(code); // magic-string のインスタンスを作成 const s = new MagicString(code); // magic-string のインスタンスを作成
if (!isDevMode && transformedCodeCache[normalizedId] && transformedCodeCache[normalizedId].includes('markerId=')) {
logger.info(`Using cached version for ${id}`);
return {
code: transformedCodeCache[normalizedId],
map: s.generateMap({ source: id, includeContent: true }),
transformedCodeCache
};
}
// すでに処理済みのファイルでコードに変更がない場合はキャッシュを返す
if (transformedCodeCache[normalizedId] === code) {
logger.info(`Code unchanged for ${id}, using cached version`);
return {
code: transformedCodeCache[normalizedId],
map: s.generateMap({ source: id, includeContent: true }),
transformedCodeCache
};
}
const parsed = vueSfcParse(code, { filename: id }); const parsed = vueSfcParse(code, { filename: id });
if (!parsed.descriptor.template) { if (!parsed.descriptor.template) {
return { return {
code, code,
map: s.generateMap({ source: id, includeContent: true }), map: s.generateMap({ source: id, includeContent: true }),
transformedCodeCache
}; };
} }
const ast = parsed.descriptor.template.ast; // テンプレート AST を取得 const ast = parsed.descriptor.template.ast; // テンプレート AST を取得
const markerRelations: MarkerRelation[] = []; // MarkerRelation 配列を初期化 const markerRelations: MarkerRelation[] = []; // MarkerRelation 配列を初期化
if (ast) { if (!ast) {
function traverse(node: any, currentParent?: any) { return {
if (node.type === 1 && node.tag === 'SearchMarker') { code: s.toString(), // 変更後のコードを返す
map: s.generateMap({ source: id, includeContent: true }), // ソースマップも生成 (sourceMap: true が必要)
};
}
type SearchMarkerElementNode = ElementNode & {
__markerId?: string,
__children?: string[],
};
function traverse(node: RootNode | TemplateChildNode | SimpleExpressionNode | CompoundExpressionNode, currentParent?: SearchMarkerElementNode) {
if (node.type === NodeTypes.ELEMENT && node.tag === 'SearchMarker') {
// 行番号はコード先頭からの改行数で取得 // 行番号はコード先頭からの改行数で取得
const lineNumber = code.slice(0, node.loc.start.offset).split('\n').length; const lineNumber = code.slice(0, node.loc.start.offset).split('\n').length;
// ファイルパスと行番号からハッシュ値を生成 // ファイルパスと行番号からハッシュ値を生成
@ -1261,14 +1192,14 @@ async function processVueFile(
const generatedMarkerId = toBase62(hash(`${idKey}:${lineNumber}`)); const generatedMarkerId = toBase62(hash(`${idKey}:${lineNumber}`));
const props = node.props || []; const props = node.props || [];
const hasMarkerIdProp = props.some((prop: any) => prop.type === 6 && prop.name === 'markerId'); const hasMarkerIdProp = props.some((prop) => prop.type === NodeTypes.ATTRIBUTE && prop.name === 'markerId');
const nodeMarkerId = hasMarkerIdProp const nodeMarkerId = hasMarkerIdProp
? props.find((prop: any) => prop.type === 6 && prop.name === 'markerId')?.value?.content as string ? props.find((prop): prop is AttributeNode => prop.type === NodeTypes.ATTRIBUTE && prop.name === 'markerId')?.value?.content as string
: generatedMarkerId; : generatedMarkerId;
node.__markerId = nodeMarkerId; (node as SearchMarkerElementNode).__markerId = nodeMarkerId;
// 子マーカーの場合、親ノードに __children を設定しておく // 子マーカーの場合、親ノードに __children を設定しておく
if (currentParent && currentParent.type === 1 && currentParent.tag === 'SearchMarker') { if (currentParent) {
currentParent.__children = currentParent.__children || []; currentParent.__children = currentParent.__children || [];
currentParent.__children.push(nodeMarkerId); currentParent.__children.push(nodeMarkerId);
} }
@ -1313,9 +1244,13 @@ async function processVueFile(
} }
} }
const newParent = node.type === 1 && node.tag === 'SearchMarker' ? node : currentParent; const newParent: SearchMarkerElementNode | undefined = node.type === NodeTypes.ELEMENT && node.tag === 'SearchMarker' ? node : currentParent;
if (node.children && Array.isArray(node.children)) { if ('children' in node) {
node.children.forEach(child => traverse(child, newParent)); for (const child of node.children) {
if (typeof child == 'object') {
traverse(child, newParent);
}
}
} }
} }
@ -1341,7 +1276,11 @@ async function processVueFile(
if (!parentRelation || !parentRelation.node) continue; if (!parentRelation || !parentRelation.node) continue;
const parentNode = parentRelation.node; const parentNode = parentRelation.node;
const childrenProp = parentNode.props?.find((prop: any) => prop.type === 7 && prop.name === 'bind' && prop.arg?.content === 'children'); const childrenProp = parentNode.props?.find((prop): prop is DirectiveNode =>
prop.type === NodeTypes.DIRECTIVE &&
prop.name === 'bind' &&
prop.arg?.type === NodeTypes.SIMPLE_EXPRESSION &&
prop.arg.content === 'children');
// 親ノードの開始位置を特定 // 親ノードの開始位置を特定
const parentNodeStart = parentNode.loc!.start.offset; const parentNodeStart = parentNode.loc!.start.offset;
@ -1416,53 +1355,64 @@ async function processVueFile(
} }
} }
} }
}
const transformedCode = s.toString(); // 変換後のコードを取得
transformedCodeCache[normalizedId] = transformedCode; // 変換後のコードをキャッシュに保存
return { return {
code: transformedCode, // 変更後のコードを返す code: s.toString(), // 変更後のコードを返す
map: s.generateMap({ source: id, includeContent: true }), // ソースマップも生成 (sourceMap: true が必要) map: s.generateMap({ source: id, includeContent: true }), // ソースマップも生成 (sourceMap: true が必要)
transformedCodeCache // キャッシュも返す
}; };
}
export async function generateSearchIndex(options: Options, transformedCodeCache: Record<string, string> = {}) {
const filePaths = options.targetFilePaths.reduce<string[]>((acc, filePathPattern) => {
const matchedFiles = glob.sync(filePathPattern);
return [...acc, ...matchedFiles];
}, []);
for (const filePath of filePaths) {
const id = path.resolve(filePath); // 絶対パスに変換
const code = fs.readFileSync(filePath, 'utf-8'); // ファイル内容を読み込む
const { transformedCodeCache: newCache } = await processVueFile(code, id, options, transformedCodeCache); // processVueFile 関数を呼び出す
transformedCodeCache = newCache; // キャッシュを更新
} }
await analyzeVueProps({ ...options, transformedCodeCache }); // 開発サーバー起動時にも analyzeVueProps を実行 async getOrLoad(id: string) {
// if there already exists a cache, return it
// note cahce will be invalidated on file change so the cache must be up to date
let code = this.getCached(id)?.code;
if (code != null) {
return code;
}
return transformedCodeCache; // キャッシュを返す // if no cache found, read and parse the file
const originalCode = await fs.promises.readFile(id, 'utf-8');
// Other code may already parsed the file while we were waiting for the file to be read so re-check the cache
code = this.getCached(id)?.code;
if (code != null) {
return code;
}
// parse the file
code = this.processFile(id, originalCode)?.code;
return code;
}
getCached(id: string) {
return this.cache.get(id);
}
} }
// Rollup プラグインとして export // Rollup プラグインとして export
export default function pluginCreateSearchIndex(options: Options): Plugin { export default function pluginCreateSearchIndex(options: Options): PluginOption {
let transformedCodeCache: Record<string, string> = {}; // キャッシュオブジェクトをプラグインスコープで定義 const assigner = new MarkerIdAssigner();
const isDevServer = process.env.NODE_ENV === 'development'; // 開発サーバーかどうか return [
createSearchIndex(options, assigner),
pluginCreateSearchIndexVirtualModule(options, assigner),
]
}
function createSearchIndex(options: Options, assigner: MarkerIdAssigner): Plugin {
initLogger(options); // ロガーを初期化 initLogger(options); // ロガーを初期化
const root = normalizePath(process.cwd());
return { function isTargetFile(id: string): boolean {
name: 'createSearchIndex', const relativePath = path.posix.relative(root, id);
enforce: 'pre', return options.targetFilePaths.some(pat => minimatch(relativePath, pat))
async buildStart() {
if (!isDevServer) {
return;
} }
transformedCodeCache = await generateSearchIndex(options, transformedCodeCache); return {
name: 'autoAssignMarkerId',
enforce: 'pre',
watchChange(id) {
assigner.onInvalidate(id);
}, },
async transform(code, id) { async transform(code, id) {
@ -1470,43 +1420,88 @@ export default function pluginCreateSearchIndex(options: Options): Plugin {
return; return;
} }
// targetFilePaths にマッチするファイルのみ処理を行う if (!isTargetFile(id)) {
// glob パターンでマッチング
let isMatch = false; // isMatch の初期値を false に設定
for (const pattern of options.targetFilePaths) { // パターンごとにマッチング確認
const globbedFiles = glob.sync(pattern);
for (const globbedFile of globbedFiles) {
const normalizedGlobbedFile = path.resolve(globbedFile); // glob 結果を絶対パスに
const normalizedId = path.resolve(id); // id を絶対パスに
if (normalizedGlobbedFile === normalizedId) { // 絶対パス同士で比較
isMatch = true;
break; // マッチしたらループを抜ける
}
}
if (isMatch) break; // いずれかのパターンでマッチしたら、outer loop も抜ける
}
if (!isMatch) {
return; return;
} }
// ファイルの内容が変更された場合は再処理を行う return assigner.processFile(id, code);
const normalizedId = id.replace(/\\/g, '/'); },
const hasContentChanged = !transformedCodeCache[normalizedId] || transformedCodeCache[normalizedId] !== code; };
}
const transformed = await processVueFile(code, id, options, transformedCodeCache); export function pluginCreateSearchIndexVirtualModule(options: Options, asigner: MarkerIdAssigner): Plugin {
transformedCodeCache = transformed.transformedCodeCache; // キャッシュを更新 const searchIndexPrefix = options.fileVirtualModulePrefix ?? 'search-index-individual:';
const searchIndexSuffix = options.fileVirtualModuleSuffix ?? '.ts';
const allSearchIndexFile = options.mainVirtualModule;
const root = normalizePath(process.cwd());
if (isDevServer && hasContentChanged) { function isTargetFile(id: string): boolean {
await analyzeVueProps({ ...options, transformedCodeCache }); // ファイルが変更されたときのみ分析を実行 const relativePath = path.posix.relative(root, id);
return options.targetFilePaths.some(pat => minimatch(relativePath, pat))
} }
return transformed; function parseSearchIndexFileId(id: string): string | null {
const noQuery = id.split('?')[0];
if (noQuery.startsWith(searchIndexPrefix) && noQuery.endsWith(searchIndexSuffix)) {
const filePath = id.slice(searchIndexPrefix.length).slice(0, -searchIndexSuffix.length);
if (isTargetFile(filePath)) {
return filePath;
}
}
return null;
}
return {
name: 'generateSearchIndexVirtualModule',
// hotUpdate hook を vite:vue よりもあとに実行したいため enforce: post
enforce: 'post',
async resolveId(id) {
if (id == allSearchIndexFile) {
return '\0' + allSearchIndexFile;
}
const searchIndexFilePath = parseSearchIndexFileId(id);
if (searchIndexFilePath != null) {
return id;
}
return undefined;
}, },
async writeBundle() { async load(id) {
await analyzeVueProps({ ...options, transformedCodeCache }); // ビルド時にも analyzeVueProps を実行 if (id == '\0' + allSearchIndexFile) {
const files = await Promise.all(options.targetFilePaths.map(async (filePathPattern) => await glob(filePathPattern))).then(paths => paths.flat());
let generatedFile = '';
let arrayElements = '';
for (let file of files) {
const normalizedRelative = normalizePath(file);
const absoluteId = normalizePath(path.join(process.cwd(), normalizedRelative)) + searchIndexSuffix;
const variableName = normalizedRelative.replace(/[\/.-]/g, '_');
generatedFile += `import { searchIndexes as ${variableName} } from '${searchIndexPrefix}${absoluteId}';\n`;
arrayElements += ` ...${variableName},\n`;
}
generatedFile += `export let searchIndexes = [\n${arrayElements}];\n`;
return generatedFile;
}
const searchIndexFilePath = parseSearchIndexFileId(id);
if (searchIndexFilePath != null) {
// call load to update the index file when the file is changed
this.addWatchFile(searchIndexFilePath);
const code = await asigner.getOrLoad(searchIndexFilePath);
return generateJavaScriptCode(collectSearchItemIndexes([collectFileMarkers([[id, code]])]));
}
return null;
}, },
hotUpdate(this: { environment: { moduleGraph: EnvironmentModuleGraph } }, { file, modules }) {
if (isTargetFile(file)) {
const updateMods = options.modulesToHmrOnUpdate.map(id => this.environment.moduleGraph.getModuleById(path.posix.join(root, id))).filter(x => x != null);
return [...modules, ...updateMods];
}
return modules;
}
}; };
} }

View file

@ -5,7 +5,6 @@
"scripts": { "scripts": {
"watch": "vite", "watch": "vite",
"build": "vite build", "build": "vite build",
"build-search-index": "vite-node --config \"./vite-node.config.ts\" \"./scripts/generate-search-index.ts\"",
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"", "storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js", "build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
"build-storybook": "pnpm build-storybook-pre && storybook build --webpack-stats-json storybook-static", "build-storybook": "pnpm build-storybook-pre && storybook build --webpack-stats-json storybook-static",
@ -120,6 +119,7 @@
"@typescript-eslint/eslint-plugin": "8.27.0", "@typescript-eslint/eslint-plugin": "8.27.0",
"@typescript-eslint/parser": "8.27.0", "@typescript-eslint/parser": "8.27.0",
"@vitest/coverage-v8": "3.0.9", "@vitest/coverage-v8": "3.0.9",
"@vue/compiler-core": "3.5.13",
"@vue/runtime-core": "3.5.13", "@vue/runtime-core": "3.5.13",
"acorn": "8.14.1", "acorn": "8.14.1",
"cross-env": "7.0.3", "cross-env": "7.0.3",
@ -129,6 +129,7 @@
"happy-dom": "17.4.4", "happy-dom": "17.4.4",
"intersection-observer": "0.12.2", "intersection-observer": "0.12.2",
"micromatch": "4.0.8", "micromatch": "4.0.8",
"minimatch": "10.0.1",
"msw": "2.7.3", "msw": "2.7.3",
"msw-storybook-addon": "2.0.4", "msw-storybook-addon": "2.0.4",
"nodemon": "3.1.9", "nodemon": "3.1.9",

View file

@ -1,15 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { searchIndexes } from '../vite.config.js';
import { generateSearchIndex } from '../lib/vite-plugin-create-search-index.js';
async function main() {
for (const searchIndex of searchIndexes) {
await generateSearchIndex(searchIndex);
}
}
main();

View file

@ -140,7 +140,7 @@ watch(v, newValue => {
.caption { .caption {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
&:empty { &:empty {
display: none; display: none;

View file

@ -60,7 +60,7 @@ const onInput = () => {
.caption { .caption {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
&:empty { &:empty {
display: none; display: none;

View file

@ -181,12 +181,17 @@ onUnmounted(() => {
left: 0; left: 0;
color: var(--MI_THEME-panelHeaderFg); color: var(--MI_THEME-panelHeaderFg);
background: var(--MI_THEME-panelHeaderBg); background: var(--MI_THEME-panelHeaderBg);
border-bottom: solid 0.5px var(--MI_THEME-panelHeaderDivider);
z-index: 2; z-index: 2;
line-height: 1.4em; line-height: 1.4em;
background: color-mix(in srgb, var(--MI_THEME-panelHeaderBg) 35%, transparent); background: color-mix(in srgb, var(--MI_THEME-panelHeaderBg) 35%, transparent);
} }
@container style(--MI_THEME-panelHeaderBg: var(--MI_THEME-panel)) {
.header {
box-shadow: 0 0.5px 0 0 light-dark(#0002, #fff2);
}
}
.title { .title {
margin: 0; margin: 0;
padding: 12px 16px; padding: 12px 16px;

View file

@ -175,7 +175,7 @@ onMounted(() => {
} }
.headerLower { .headerLower {
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
font-size: .85em; font-size: .85em;
padding-left: 4px; padding-left: 4px;
} }
@ -209,13 +209,13 @@ onMounted(() => {
} }
.headerTextSub { .headerTextSub {
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
font-size: .85em; font-size: .85em;
} }
.headerRight { .headerRight {
margin-left: auto; margin-left: auto;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
white-space: nowrap; white-space: nowrap;
} }

View file

@ -201,7 +201,7 @@ defineExpose({
.caption { .caption {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
&:empty { &:empty {
display: none; display: none;

View file

@ -78,7 +78,7 @@ export default defineComponent({
> .caption { > .caption {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
&:empty { &:empty {
display: none; display: none;

View file

@ -213,7 +213,7 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
> .caption { > .caption {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
&:empty { &:empty {
display: none; display: none;

View file

@ -268,7 +268,7 @@ function show() {
.caption { .caption {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
&:empty { &:empty {
display: none; display: none;

View file

@ -94,7 +94,7 @@ export type SuperMenuDef = {
<script lang="ts" setup> <script lang="ts" setup>
import { useTemplateRef, ref, watch, nextTick } from 'vue'; import { useTemplateRef, ref, watch, nextTick } from 'vue';
import type { SearchIndexItem } from '@/utility/autogen/settings-search-index.js'; import type { SearchIndexItem } from '@/utility/settings-search-index.js';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { getScrollContainer } from '@@/js/scroll.js'; import { getScrollContainer } from '@@/js/scroll.js';

View file

@ -100,7 +100,7 @@ const toggle = () => {
.caption { .caption {
margin: 8px 0 0 0; margin: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
font-size: 0.85em; font-size: 0.85em;
&:empty { &:empty {

View file

@ -307,6 +307,6 @@ onMounted(async () => {
.description { .description {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
} }
</style> </style>

View file

@ -160,7 +160,7 @@ onUnmounted(() => {
.caption { .caption {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
&:empty { &:empty {
display: none; display: none;

View file

@ -298,7 +298,7 @@ onMounted(() => {
.statusItemLabel { .statusItemLabel {
font-size: 0.7em; font-size: 0.7em;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
} }
.menu { .menu {

View file

@ -193,7 +193,7 @@ function showMenu(ev: MouseEvent) {
} }
.statsItemLabel { .statsItemLabel {
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
font-size: 0.9em; font-size: 0.9em;
} }

View file

@ -79,7 +79,7 @@ const props = defineProps<{
margin-right: 0.75em; margin-right: 0.75em;
flex-shrink: 0; flex-shrink: 0;
text-align: center; text-align: center;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
&:empty { &:empty {
display: none; display: none;

View file

@ -49,7 +49,7 @@ defineProps<{
.description { .description {
font-size: 0.85em; font-size: 0.85em;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
margin: 0 0 8px 0; margin: 0 0 8px 0;
} }
</style> </style>

View file

@ -35,7 +35,7 @@ function focus() {
.caption { .caption {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
&:empty { &:empty {
display: none; display: none;

View file

@ -81,7 +81,7 @@ const emit = defineEmits<{
const displayBackButton = props.displayBackButton && window.history.state.key !== 'index' && window.history.length > 1 && inject('shouldBackButton', true); const displayBackButton = props.displayBackButton && window.history.state.key !== 'index' && window.history.length > 1 && inject('shouldBackButton', true);
//const viewId = inject(DI.viewId); //const viewId = inject(DI.viewId);
const injectedPageMetadata = inject(DI.pageMetadata); const injectedPageMetadata = inject(DI.pageMetadata, ref(null));
const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value); const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value);
const hideTitle = computed(() => inject('shouldOmitHeaderTitle', false) || props.hideTitle); const hideTitle = computed(() => inject('shouldOmitHeaderTitle', false) || props.hideTitle);

View file

@ -10,7 +10,7 @@ export const DI = {
routerCurrentDepth: Symbol() as InjectionKey<number>, routerCurrentDepth: Symbol() as InjectionKey<number>,
router: Symbol() as InjectionKey<Router>, router: Symbol() as InjectionKey<Router>,
mock: Symbol() as InjectionKey<boolean>, mock: Symbol() as InjectionKey<boolean>,
pageMetadata: Symbol() as InjectionKey<Ref<Record<string, any>>>, pageMetadata: Symbol() as InjectionKey<Ref<Record<string, any> | null>>,
viewId: Symbol() as InjectionKey<string>, viewId: Symbol() as InjectionKey<string>,
currentStickyTop: Symbol() as InjectionKey<Ref<number>>, currentStickyTop: Symbol() as InjectionKey<Ref<number>>,
currentStickyBottom: Symbol() as InjectionKey<Ref<number>>, currentStickyBottom: Symbol() as InjectionKey<Ref<number>>,

View file

@ -724,7 +724,7 @@ definePage(() => ({
.roleItemSub { .roleItemSub {
padding: 6px 12px; padding: 6px 12px;
font-size: 85%; font-size: 85%;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
} }
.roleUnassign { .roleUnassign {

View file

@ -66,7 +66,7 @@ const emit = defineEmits<{
(ev: 'update:tab', key: string); (ev: 'update:tab', key: string);
}>(); }>();
const pageMetadata = inject(DI.pageMetadata); const pageMetadata = inject(DI.pageMetadata, ref(null));
const el = useTemplateRef('el'); const el = useTemplateRef('el');
const tabHighlightEl = useTemplateRef('tabHighlightEl'); const tabHighlightEl = useTemplateRef('tabHighlightEl');

View file

@ -184,7 +184,7 @@ definePage(() => ({
.userItemSub { .userItemSub {
padding: 6px 12px; padding: 6px 12px;
font-size: 85%; font-size: 85%;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
} }
.userItemMainBody { .userItemMainBody {

View file

@ -459,6 +459,6 @@ definePage(() => ({
<style lang="scss" module> <style lang="scss" module>
.subCaption { .subCaption {
font-size: 0.85em; font-size: 0.85em;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
} }
</style> </style>

View file

@ -75,6 +75,6 @@ function onDeleteClick() {
margin-right: 0.75em; margin-right: 0.75em;
flex-shrink: 0; flex-shrink: 0;
text-align: center; text-align: center;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
} }
</style> </style>

View file

@ -247,7 +247,7 @@ definePage(() => ({
} }
.uiInspectorUnShown { .uiInspectorUnShown {
color: var(--MI_THEME-fgTransparent); color: color(from var(--MI_THEME-fg) srgb r g b / 0.5);
} }
.uiInspectorType { .uiInspectorType {

View file

@ -351,7 +351,7 @@ async function search() {
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 12px; padding: 12px;
border: 2px dashed var(--MI_THEME-fgTransparent); border: 2px dashed color(from var(--MI_THEME-fg) srgb r g b / 0.5);
} }
.userSelectButtonInner { .userSelectButtonInner {

View file

@ -161,6 +161,6 @@ function del(ev: MouseEvent) {
.editorCaption { .editorCaption {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
} }
</style> </style>

View file

@ -285,6 +285,6 @@ definePage(() => ({
.editorCaption { .editorCaption {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
} }
</style> </style>

View file

@ -42,7 +42,7 @@ import { instance } from '@/instance.js';
import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js'; import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { useRouter } from '@/router.js'; import { useRouter } from '@/router.js';
import { searchIndexes } from '@/utility/autogen/settings-search-index.js'; import { searchIndexes } from '@/utility/settings-search-index.js';
import { enableAutoBackup, getPreferencesProfileMenu } from '@/preferences/utility.js'; import { enableAutoBackup, getPreferencesProfileMenu } from '@/preferences/utility.js';
import { store } from '@/store.js'; import { store } from '@/store.js';
import { signout } from '@/signout.js'; import { signout } from '@/signout.js';

View file

@ -308,7 +308,7 @@ definePage(() => ({
.userItemSub { .userItemSub {
padding: 6px 12px; padding: 6px 12px;
font-size: 85%; font-size: 85%;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
} }
.userItemMainBody { .userItemMainBody {

View file

@ -184,6 +184,6 @@ definePage(() => ({
.description { .description {
font-size: 0.85em; font-size: 0.85em;
padding: 8px 0 0 0; padding: 8px 0 0 0;
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
} }
</style> </style>

View file

@ -653,7 +653,7 @@ onUnmounted(() => {
> .heading { > .heading {
text-align: left; text-align: left;
color: var(--MI_THEME-fgTransparent); color: color(from var(--MI_THEME-fg) srgb r g b / 0.5);
line-height: 1.5; line-height: 1.5;
font-size: 85%; font-size: 85%;
} }

View file

@ -67,7 +67,7 @@ export function migrateOldSettings() {
prefer.commit('collapseRenotes', store.s.collapseRenotes); prefer.commit('collapseRenotes', store.s.collapseRenotes);
prefer.commit('rememberNoteVisibility', store.s.rememberNoteVisibility); prefer.commit('rememberNoteVisibility', store.s.rememberNoteVisibility);
prefer.commit('uploadFolder', store.s.uploadFolder); prefer.commit('uploadFolder', store.s.uploadFolder);
prefer.commit('menu', store.s.menu); prefer.commit('menu', [...store.s.menu, 'chat']);
prefer.commit('statusbars', store.s.statusbars); prefer.commit('statusbars', store.s.statusbars);
prefer.commit('pinnedUserLists', store.s.pinnedUserLists); prefer.commit('pinnedUserLists', store.s.pinnedUserLists);
prefer.commit('serverDisconnectedBehavior', store.s.serverDisconnectedBehavior); prefer.commit('serverDisconnectedBehavior', store.s.serverDisconnectedBehavior);

View file

@ -413,11 +413,16 @@ function onDrop(ev) {
font-size: 0.9em; font-size: 0.9em;
color: var(--MI_THEME-panelHeaderFg); color: var(--MI_THEME-panelHeaderFg);
background: var(--MI_THEME-panelHeaderBg); background: var(--MI_THEME-panelHeaderBg);
box-shadow: 0 0.5px 0 0 var(--MI_THEME-panelHeaderDivider);
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
} }
@container style(--MI_THEME-panelHeaderBg: var(--MI_THEME-panel)) {
.header {
box-shadow: 0 0.5px 0 0 light-dark(#0002, #fff2);
}
}
.color { .color {
position: absolute; position: absolute;
top: 12px; top: 12px;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { searchIndexes as generated } from 'search-index:settings';
import type { GeneratedSearchIndexItem } from 'search-index:settings';
export type SearchIndexItem = {
id: string;
path?: string;
label: string;
keywords: string[];
icon?: string;
children?: SearchIndexItem[];
};
const rootMods = new Map(generated.map(item => [item.id, item]));
function walk(item: GeneratedSearchIndexItem) {
if (item.inlining) {
for (const id of item.inlining) {
const inline = rootMods.get(id);
if (inline) {
(item.children ??= []).push(inline);
rootMods.delete(id);
} else {
console.log('[Settings Search Index] Failed to inline', id);
}
}
}
for (const child of item.children ?? []) {
walk(child);
}
}
for (const item of generated) {
walk(item);
}
export const searchIndexes: SearchIndexItem[] = generated;

View file

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
declare module 'search-index:settings' {
export type GeneratedSearchIndexItem = {
id: string;
path?: string;
label: string;
keywords: string[];
icon?: string;
inlining?: string[];
children?: GeneratedSearchIndexItem[];
};
export const searchIndexes: GeneratedSearchIndexItem[];
}

View file

@ -73,6 +73,6 @@ defineExpose<WidgetComponentExpose>({
} }
.text { .text {
color: var(--MI_THEME-fgTransparentWeak); color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
} }
</style> </style>

View file

@ -23,7 +23,8 @@ const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.s
*/ */
export const searchIndexes = [{ export const searchIndexes = [{
targetFilePaths: ['src/pages/settings/*.vue'], targetFilePaths: ['src/pages/settings/*.vue'],
exportFilePath: './src/utility/autogen/settings-search-index.ts', mainVirtualModule: 'search-index:settings',
modulesToHmrOnUpdate: ['src/pages/settings/index.vue'],
verbose: process.env.FRONTEND_SEARCH_INDEX_VERBOSE === 'true', verbose: process.env.FRONTEND_SEARCH_INDEX_VERBOSE === 'true',
}] satisfies SearchIndexOptions[]; }] satisfies SearchIndexOptions[];

View file

@ -1,7 +1,7 @@
{ {
"type": "module", "type": "module",
"name": "misskey-js", "name": "misskey-js",
"version": "2025.4.0-rc.0", "version": "2025.4.0-rc.1",
"description": "Misskey SDK for JavaScript", "description": "Misskey SDK for JavaScript",
"license": "MIT", "license": "MIT",
"main": "./built/index.js", "main": "./built/index.js",

166
pnpm-lock.yaml generated
View file

@ -1005,6 +1005,9 @@ importers:
'@vitest/coverage-v8': '@vitest/coverage-v8':
specifier: 3.0.9 specifier: 3.0.9
version: 3.0.9(vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)) version: 3.0.9(vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))
'@vue/compiler-core':
specifier: 3.5.13
version: 3.5.13
'@vue/runtime-core': '@vue/runtime-core':
specifier: 3.5.13 specifier: 3.5.13
version: 3.5.13 version: 3.5.13
@ -1032,6 +1035,9 @@ importers:
micromatch: micromatch:
specifier: 4.0.8 specifier: 4.0.8
version: 4.0.8 version: 4.0.8
minimatch:
specifier: 10.0.1
version: 10.0.1
msw: msw:
specifier: 2.7.3 specifier: 2.7.3
version: 2.7.3(@types/node@22.13.11)(typescript@5.8.2) version: 2.7.3(@types/node@22.13.11)(typescript@5.8.2)
@ -1358,7 +1364,7 @@ importers:
version: 9.1.0(eslint@9.22.0) version: 9.1.0(eslint@9.22.0)
jest: jest:
specifier: ^29.7.0 specifier: ^29.7.0
version: 29.7.0(@types/node@22.13.10) version: 29.7.0(@types/node@22.13.15)
jest-worker: jest-worker:
specifier: ^29.7.0 specifier: ^29.7.0
version: 29.7.0 version: 29.7.0
@ -1370,7 +1376,7 @@ importers:
version: 3.3.3 version: 3.3.3
ts-jest: ts-jest:
specifier: ^29.1.1 specifier: ^29.1.1
version: 29.1.2(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.25.0)(jest@29.7.0(@types/node@22.13.10))(typescript@5.1.6) version: 29.1.2(@babel/core@7.23.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.5))(esbuild@0.25.0)(jest@29.7.0(@types/node@22.13.15))(typescript@5.1.6)
typedoc: typedoc:
specifier: ^0.25.3 specifier: ^0.25.3
version: 0.25.13(typescript@5.1.6) version: 0.25.13(typescript@5.1.6)
@ -8312,10 +8318,6 @@ packages:
minimatch@3.1.2: minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
minimatch@5.1.2:
resolution: {integrity: sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==}
engines: {node: '>=10'}
minimatch@5.1.6: minimatch@5.1.6:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -8324,10 +8326,6 @@ packages:
resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
minimatch@9.0.4:
resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
engines: {node: '>=16 || 14 >=14.17'}
minimatch@9.0.5: minimatch@9.0.5:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
@ -11927,56 +11925,26 @@ snapshots:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.5)': '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.5)': '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.5)': '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.5)': '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5)': '@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
@ -11987,78 +11955,36 @@ snapshots:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.5)': '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.5)': '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.5)': '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.5)': '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.5)': '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.5)': '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
'@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)':
dependencies:
'@babel/core': 7.24.7
'@babel/helper-plugin-utils': 7.22.5
optional: true
'@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5)': '@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5)':
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
@ -14624,7 +14550,7 @@ snapshots:
'@storybook/manager-api': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/manager-api': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))
'@storybook/preview-api': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/preview-api': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))
'@storybook/theming': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) '@storybook/theming': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))
'@vue/compiler-core': 3.5.12 '@vue/compiler-core': 3.5.13
storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)
ts-dedent: 2.2.0 ts-dedent: 2.2.0
type-fest: 2.19.0 type-fest: 2.19.0
@ -14650,7 +14576,7 @@ snapshots:
'@xhmikosr/bin-wrapper': 13.0.5 '@xhmikosr/bin-wrapper': 13.0.5
commander: 8.3.0 commander: 8.3.0
fast-glob: 3.3.3 fast-glob: 3.3.3
minimatch: 9.0.4 minimatch: 9.0.5
piscina: 4.4.0 piscina: 4.4.0
semver: 7.6.3 semver: 7.6.3
slash: 3.0.0 slash: 3.0.0
@ -15670,7 +15596,7 @@ snapshots:
'@vue/compiler-dom': 3.5.12 '@vue/compiler-dom': 3.5.12
'@vue/shared': 3.5.12 '@vue/shared': 3.5.12
computeds: 0.0.1 computeds: 0.0.1
minimatch: 9.0.4 minimatch: 9.0.5
path-browserify: 1.0.1 path-browserify: 1.0.1
vue-template-compiler: 2.7.14 vue-template-compiler: 2.7.14
optionalDependencies: optionalDependencies:
@ -15683,7 +15609,7 @@ snapshots:
'@vue/compiler-vue2': 2.7.16 '@vue/compiler-vue2': 2.7.16
'@vue/shared': 3.5.12 '@vue/shared': 3.5.12
alien-signals: 1.0.7 alien-signals: 1.0.7
minimatch: 9.0.4 minimatch: 9.0.5
muggle-string: 0.4.1 muggle-string: 0.4.1
path-browserify: 1.0.1 path-browserify: 1.0.1
optionalDependencies: optionalDependencies:
@ -16134,20 +16060,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
babel-jest@29.7.0(@babel/core@7.24.7):
dependencies:
'@babel/core': 7.24.7
'@jest/transform': 29.7.0
'@types/babel__core': 7.20.0
babel-plugin-istanbul: 6.1.1
babel-preset-jest: 29.6.3(@babel/core@7.24.7)
chalk: 4.1.2
graceful-fs: 4.2.11
slash: 3.0.0
transitivePeerDependencies:
- supports-color
optional: true
babel-plugin-istanbul@6.1.1: babel-plugin-istanbul@6.1.1:
dependencies: dependencies:
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
@ -16181,36 +16093,12 @@ snapshots:
'@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5) '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5)
'@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.5) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.5)
babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.7):
dependencies:
'@babel/core': 7.24.7
'@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7)
'@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.7)
'@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7)
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7)
'@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7)
'@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7)
'@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7)
'@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7)
'@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7)
'@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7)
'@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7)
'@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7)
optional: true
babel-preset-jest@29.6.3(@babel/core@7.23.5): babel-preset-jest@29.6.3(@babel/core@7.23.5):
dependencies: dependencies:
'@babel/core': 7.23.5 '@babel/core': 7.23.5
babel-plugin-jest-hoist: 29.6.3 babel-plugin-jest-hoist: 29.6.3
babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.5) babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.5)
babel-preset-jest@29.6.3(@babel/core@7.24.7):
dependencies:
'@babel/core': 7.24.7
babel-plugin-jest-hoist: 29.6.3
babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7)
optional: true
babel-walk@3.0.0-canary-5: babel-walk@3.0.0-canary-5:
dependencies: dependencies:
'@babel/types': 7.24.7 '@babel/types': 7.24.7
@ -18333,7 +18221,7 @@ snapshots:
dependencies: dependencies:
foreground-child: 3.1.1 foreground-child: 3.1.1
jackspeak: 2.3.6 jackspeak: 2.3.6
minimatch: 9.0.4 minimatch: 9.0.5
minipass: 7.1.2 minipass: 7.1.2
path-scurry: 1.10.1 path-scurry: 1.10.1
@ -18341,7 +18229,7 @@ snapshots:
dependencies: dependencies:
foreground-child: 3.1.1 foreground-child: 3.1.1
jackspeak: 3.4.3 jackspeak: 3.4.3
minimatch: 9.0.4 minimatch: 9.0.5
minipass: 7.1.2 minipass: 7.1.2
package-json-from-dist: 1.0.0 package-json-from-dist: 1.0.0
path-scurry: 1.11.1 path-scurry: 1.11.1
@ -18651,7 +18539,7 @@ snapshots:
ignore-walk@7.0.0: ignore-walk@7.0.0:
dependencies: dependencies:
minimatch: 9.0.4 minimatch: 9.0.5
ignore@5.3.1: {} ignore@5.3.1: {}
@ -20162,10 +20050,6 @@ snapshots:
dependencies: dependencies:
brace-expansion: 1.1.11 brace-expansion: 1.1.11
minimatch@5.1.2:
dependencies:
brace-expansion: 2.0.1
minimatch@5.1.6: minimatch@5.1.6:
dependencies: dependencies:
brace-expansion: 2.0.1 brace-expansion: 2.0.1
@ -20174,10 +20058,6 @@ snapshots:
dependencies: dependencies:
brace-expansion: 2.0.1 brace-expansion: 2.0.1
minimatch@9.0.4:
dependencies:
brace-expansion: 2.0.1
minimatch@9.0.5: minimatch@9.0.5:
dependencies: dependencies:
brace-expansion: 2.0.1 brace-expansion: 2.0.1
@ -21408,7 +21288,7 @@ snapshots:
readdir-glob@1.1.2: readdir-glob@1.1.2:
dependencies: dependencies:
minimatch: 5.1.2 minimatch: 5.1.6
readdirp@4.1.2: {} readdirp@4.1.2: {}
@ -22314,7 +22194,7 @@ snapshots:
dependencies: dependencies:
'@istanbuljs/schema': 0.1.3 '@istanbuljs/schema': 0.1.3
glob: 10.4.5 glob: 10.4.5
minimatch: 9.0.4 minimatch: 9.0.5
text-decoding@1.0.0: {} text-decoding@1.0.0: {}
@ -22428,11 +22308,11 @@ snapshots:
ts-dedent@2.2.0: {} ts-dedent@2.2.0: {}
ts-jest@29.1.2(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.25.0)(jest@29.7.0(@types/node@22.13.10))(typescript@5.1.6): ts-jest@29.1.2(@babel/core@7.23.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.5))(esbuild@0.25.0)(jest@29.7.0(@types/node@22.13.15))(typescript@5.1.6):
dependencies: dependencies:
bs-logger: 0.2.6 bs-logger: 0.2.6
fast-json-stable-stringify: 2.1.0 fast-json-stable-stringify: 2.1.0
jest: 29.7.0(@types/node@22.13.10) jest: 29.7.0(@types/node@22.13.15)
jest-util: 29.7.0 jest-util: 29.7.0
json5: 2.2.3 json5: 2.2.3
lodash.memoize: 4.1.2 lodash.memoize: 4.1.2
@ -22441,9 +22321,9 @@ snapshots:
typescript: 5.1.6 typescript: 5.1.6
yargs-parser: 21.1.1 yargs-parser: 21.1.1
optionalDependencies: optionalDependencies:
'@babel/core': 7.24.7 '@babel/core': 7.23.5
'@jest/types': 29.6.3 '@jest/types': 29.6.3
babel-jest: 29.7.0(@babel/core@7.24.7) babel-jest: 29.7.0(@babel/core@7.23.5)
esbuild: 0.25.0 esbuild: 0.25.0
ts-map@1.0.3: {} ts-map@1.0.3: {}
@ -22560,7 +22440,7 @@ snapshots:
dependencies: dependencies:
lunr: 2.3.9 lunr: 2.3.9
marked: 4.3.0 marked: 4.3.0
minimatch: 9.0.4 minimatch: 9.0.5
shiki: 0.14.7 shiki: 0.14.7
typescript: 5.1.6 typescript: 5.1.6
@ -22901,7 +22781,7 @@ snapshots:
vscode-languageclient@9.0.1: vscode-languageclient@9.0.1:
dependencies: dependencies:
minimatch: 5.1.2 minimatch: 5.1.6
semver: 7.6.3 semver: 7.6.3
vscode-languageserver-protocol: 3.17.5 vscode-languageserver-protocol: 3.17.5

View file

@ -28,3 +28,4 @@ onlyBuiltDependencies:
- utf-8-validate - utf-8-validate
- v-code-diff - v-code-diff
- vue-demi - vue-demi
ignorePatchFailures: false

View file

@ -0,0 +1,31 @@
diff --git a/dist/node/chunks/dep-DrOo5SEf.js b/dist/node/chunks/dep-DrOo5SEf.js
index 329e68bd27e55a56d815fa6b4de2d615a8c2b343..9d9f58e90ae836f80063b698e307fec436e53e07 100644
--- a/dist/node/chunks/dep-DrOo5SEf.js
+++ b/dist/node/chunks/dep-DrOo5SEf.js
@@ -45971,7 +45971,7 @@ function importAnalysisPlugin(config) {
let isPartiallySelfAccepting = false;
const importedBindings = enablePartialAccept ? /* @__PURE__ */ new Map() : null;
const toAbsoluteUrl = (url) => path$d.posix.resolve(path$d.posix.dirname(importerModule.url), url);
- const normalizeUrl = async (url, pos, forceSkipImportAnalysis = false) => {
+ const normalizeUrl = async (url, pos, forceSkipImportAnalysis = false, stripBase2 = false) => {
url = stripBase(url, base);
let importerFile = importer;
if (depsOptimizer && moduleListContains(depsOptimizer.options.exclude, url)) {
@@ -46031,7 +46031,7 @@ function importAnalysisPlugin(config) {
e.pos = pos;
throw e;
}
- if (!ssr) url = joinUrlSegments(base, url);
+ if (!ssr && !stripBase2) url = joinUrlSegments(base, url);
return [url, resolved.id];
};
const orderedImportedUrls = new Array(imports.length);
@@ -46288,7 +46288,7 @@ See ${colors$1.blue(
const pluginImports = this._addedImports;
if (pluginImports) {
(await Promise.all(
- [...pluginImports].map((id) => normalizeUrl(id, 0, true))
+ [...pluginImports].map((id) => normalizeUrl(id, 0, true, true))
)).forEach(([url]) => importedUrls.add(url));
}
if (ssr && importerModule.isSelfAccepting) {