hashをパスと行数から決定

This commit is contained in:
tai-cha 2025-02-21 22:29:42 +09:00
parent dc9472771b
commit 1e80ee5bd0
No known key found for this signature in database
GPG Key ID: 1D5EE39F870DC283
7 changed files with 95 additions and 17 deletions

1
.gitignore vendored
View File

@ -64,7 +64,6 @@ temp
/packages/frontend/src/**/*.stories.ts /packages/frontend/src/**/*.stories.ts
tsdoc-metadata.json tsdoc-metadata.json
misskey-assets misskey-assets
/packages/frontend/src/scripts/autogen/settings-search-index.ts
# Vite temporary files # Vite temporary files
vite.config.js.timestamp-* vite.config.js.timestamp-*

View File

@ -11,6 +11,7 @@ import JSON5 from 'json5';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
import MagicString from 'magic-string'; import MagicString from 'magic-string';
import path from 'node:path' import path from 'node:path'
import { hash, toBase62 } from '../vite.config';
export interface AnalysisResult { export interface AnalysisResult {
filePath: string; filePath: string;
@ -201,20 +202,26 @@ async function processVueFile(
transformedCodeCache: Record<string, string> transformedCodeCache: Record<string, string>
) { ) {
const s = new MagicString(code); // magic-string のインスタンスを作成 const s = new MagicString(code); // magic-string のインスタンスを作成
const ast = vueSfcParse(code, { filename: id }).descriptor.template?.ast; // テンプレート AST を取得 const parsed = vueSfcParse(code, { filename: id });
if (!parsed.descriptor.template) {
return;
}
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) { function traverse(node: any, currentParent?: any) {
// ノードが MkSearchMarker なら、markerId を生成しノードにセット(すでに存在する場合はその値を使用)
let nodeMarkerId: string | undefined;
if (node.type === 1 && node.tag === 'MkSearchMarker') { if (node.type === 1 && node.tag === 'MkSearchMarker') {
const markerId = String(randomUUID()); // 行番号はコード先頭からの改行数で取得
const lineNumber = code.slice(0, node.loc.start.offset).split('\n').length;
// ファイルパスと行番号からハッシュ値を生成
const generatedMarkerId = toBase62(hash(`${id}:${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: any) => prop.type === 6 && prop.name === 'markerId');
nodeMarkerId = hasMarkerIdProp const nodeMarkerId = hasMarkerIdProp
? props.find((prop: any) => prop.type === 6 && prop.name === 'markerId')?.value?.content as string ? props.find((prop: any) => prop.type === 6 && prop.name === 'markerId')?.value?.content as string
: markerId; : generatedMarkerId;
node.__markerId = nodeMarkerId; node.__markerId = nodeMarkerId;
// 子マーカーの場合、親ノードに __children を設定しておく // 子マーカーの場合、親ノードに __children を設定しておく
@ -223,7 +230,6 @@ async function processVueFile(
currentParent.__children.push(nodeMarkerId); currentParent.__children.push(nodeMarkerId);
} }
// markerRelations の処理はそのまま
const parentMarkerId = currentParent && currentParent.__markerId; const parentMarkerId = currentParent && currentParent.__markerId;
markerRelations.push({ markerRelations.push({
parentId: parentMarkerId, parentId: parentMarkerId,
@ -234,7 +240,7 @@ async function processVueFile(
if (!hasMarkerIdProp) { if (!hasMarkerIdProp) {
const startTagEnd = code.indexOf('>', node.loc.start.offset); const startTagEnd = code.indexOf('>', node.loc.start.offset);
if (startTagEnd !== -1) { if (startTagEnd !== -1) {
s.appendRight(startTagEnd, ` markerId="${markerId}"`); s.appendRight(startTagEnd, ` markerId="${generatedMarkerId}"`);
} }
} }
} }

View File

@ -6,11 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div class="_gaps_m"> <div class="_gaps_m">
<MkSearchMarker <MkSearchMarker
markerId="test"
:locationLabel="[i18n.ts.muteAndBlock]" :locationLabel="[i18n.ts.muteAndBlock]"
icon="ti ti-ban" icon="ti ti-ban"
:keywords="['mute', i18n.ts.wordMute]" :keywords="['mute', i18n.ts.wordMute]"
:children="['test2']"
> >
<MkFolder> <MkFolder>
<template #icon><i class="ti ti-message-off"></i></template> <template #icon><i class="ti ti-message-off"></i></template>
@ -20,7 +18,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInfo>{{ i18n.ts.wordMuteDescription }}</MkInfo> <MkInfo>{{ i18n.ts.wordMuteDescription }}</MkInfo>
<MkSearchMarker <MkSearchMarker
markerId="test2"
:locationLabel="[i18n.ts.muteAndBlock, i18n.ts.wordMute]" :locationLabel="[i18n.ts.muteAndBlock, i18n.ts.wordMute]"
icon="ti ti-ban" icon="ti ti-ban"
:keywords="['showMutedWord', i18n.ts.showMutedWord]" :keywords="['showMutedWord', i18n.ts.showMutedWord]"

View File

@ -18,13 +18,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</div> </div>
<MkSearchMarker markerId="727cc9e8-ad67-474a-9241-b5a9a6475e47"> <MkSearchMarker>
<MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']"> <MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']">
<template #label>{{ i18n.ts._profile.name }}</template> <template #label>{{ i18n.ts._profile.name }}</template>
</MkInput> </MkInput>
</MkSearchMarker> </MkSearchMarker>
<MkSearchMarker markerId="1a06c7f9-e85e-46cb-bf5f-b3efa8e71b93"> <MkSearchMarker>
<MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true"> <MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true">
<template #label>{{ i18n.ts._profile.description }}</template> <template #label>{{ i18n.ts._profile.description }}</template>
<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>

View File

@ -0,0 +1,76 @@
/*
* 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 const searchIndexes = [
{
filePath: 'src/pages/settings/profile.vue',
usage: [
{
staticProps: {
markerId: '4mWPWOqkN',
},
bindProps: {},
},
{
staticProps: {
markerId: 'gw1zundQ3',
},
bindProps: {},
},
],
},
{
filePath: 'src/pages/settings/privacy.vue',
usage: [
{
staticProps: {
icon: 'ti ti-lock-open',
markerId: 'ssTCH2oBL',
},
bindProps: {
locationLabel: [i18n.ts.privacy, i18n.ts.makeFollowManuallyApprove],
keywords: ['follow', 'lock', i18n.ts.lockedAccountInfo],
},
},
],
},
{
filePath: 'src/pages/settings/mute-block.vue',
usage: [
{
staticProps: {
icon: 'ti ti-ban',
markerId: 'CVhDOYgMv',
},
bindProps: {
locationLabel: [i18n.ts.muteAndBlock],
keywords: ['mute', i18n.ts.wordMute],
children: [
'kcHPXo5ZV',
],
},
},
{
staticProps: {
icon: 'ti ti-ban',
markerId: 'kcHPXo5ZV',
},
bindProps: {
locationLabel: [i18n.ts.muteAndBlock, i18n.ts.wordMute],
keywords: ['showMutedWord', i18n.ts.showMutedWord],
},
},
],
},
] as const;
export type AnalysisResults = typeof searchIndexes;
export type ComponentUsageInfo = AnalysisResults[number]['usage'][number];

View File

@ -35,7 +35,7 @@ const externalPackages = [
}, },
]; ];
const hash = (str: string, seed = 0): number => { export const hash = (str: string, seed = 0): number => {
let h1 = 0xdeadbeef ^ seed, let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed; h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) { for (let i = 0, ch; i < str.length; i++) {
@ -50,9 +50,9 @@ const hash = (str: string, seed = 0): number => {
return 4294967296 * (2097151 & h2) + (h1 >>> 0); return 4294967296 * (2097151 & h2) + (h1 >>> 0);
}; };
const BASE62_DIGITS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; export const BASE62_DIGITS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
function toBase62(n: number): string { export function toBase62(n: number): string {
if (n === 0) { if (n === 0) {
return '0'; return '0';
} }