rollupでビルド時・devモード時に毎回uuidを生成するように
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -64,6 +64,7 @@ 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/search-index.ts
|
||||||
|
|
||||||
# Vite temporary files
|
# Vite temporary files
|
||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
|
@@ -1,253 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { parse as vueSfcParse } from 'vue/compiler-sfc';
|
|
||||||
import type { Plugin } from 'rollup';
|
|
||||||
import fs from 'node:fs';
|
|
||||||
import { glob } from 'glob';
|
|
||||||
import JSON5 from 'json5';
|
|
||||||
import { randomUUID } from 'crypto';
|
|
||||||
import MagicString from 'magic-string';
|
|
||||||
import path from 'node:path'
|
|
||||||
|
|
||||||
export interface AnalysisResult {
|
|
||||||
filePath: string;
|
|
||||||
usage: ComponentUsageInfo[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ComponentUsageInfo {
|
|
||||||
staticProps: Record<string, string>;
|
|
||||||
bindProps: Record<string, string>;
|
|
||||||
componentName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function outputAnalysisResultAsTS(outputPath: string, analysisResults: AnalysisResult[]): void {
|
|
||||||
// (outputAnalysisResultAsTS 関数の実装は前回と同様)
|
|
||||||
const varName = 'searchIndexes'; // 変数名
|
|
||||||
|
|
||||||
const jsonString = JSON5.stringify(analysisResults, { space: "\t", quote: "'" }); // JSON.stringify で JSON 文字列を生成
|
|
||||||
|
|
||||||
// bindProps の値を文字列置換で修正する関数
|
|
||||||
function modifyBindPropsInString(jsonString: string): string {
|
|
||||||
const modifiedString = jsonString.replace(
|
|
||||||
/bindProps:\s*\{([^}]*)\}/g, // bindProps: { ... } にマッチ (g フラグで複数箇所を置換)
|
|
||||||
(match, bindPropsBlock) => {
|
|
||||||
// bindPropsBlock ( { ... } 内) の各プロパティをさらに置換
|
|
||||||
const modifiedBlock = bindPropsBlock.replace(
|
|
||||||
/(.*):\s*\'(.*)\'/g, // propName: 'propValue' にマッチ
|
|
||||||
(propMatch, propName, propValue) => {
|
|
||||||
return `${propName}: ${propValue}`; // propValue のクォートを除去
|
|
||||||
}
|
|
||||||
).replaceAll("\\'", "'");
|
|
||||||
return `bindProps: {${modifiedBlock}}`; // 置換後の block で bindProps: { ... } を再構成
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return modifiedString;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const tsOutput = `
|
|
||||||
/*
|
|
||||||
* 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 ${varName} = ${modifyBindPropsInString(jsonString)} as const;
|
|
||||||
|
|
||||||
export type AnalysisResults = typeof ${varName};
|
|
||||||
export type ComponentUsageInfo = AnalysisResults[number]['usage'][number];
|
|
||||||
`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
fs.writeFileSync(outputPath, tsOutput, 'utf-8');
|
|
||||||
console.log(`[create-search-index]: output done. ${outputPath}`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[create-search-index]: error: ', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractUsageInfoFromTemplateAst(
|
|
||||||
templateAst: any,
|
|
||||||
targetComponents: string[]
|
|
||||||
): ComponentUsageInfo[] {
|
|
||||||
const usageInfoList: ComponentUsageInfo[] = [];
|
|
||||||
|
|
||||||
if (!templateAst) {
|
|
||||||
return usageInfoList;
|
|
||||||
}
|
|
||||||
|
|
||||||
function traverse(node: any) {
|
|
||||||
if (node.type === 1 /* ELEMENT */ && node.tag && targetComponents.includes(node.tag)) {
|
|
||||||
const componentTag = node.tag;
|
|
||||||
|
|
||||||
const staticProps: Record<string, string> = {};
|
|
||||||
const bindProps: Record<string, string> = {}; // bindProps の型を string に戻す
|
|
||||||
|
|
||||||
if (node.props && Array.isArray(node.props)) {
|
|
||||||
node.props.forEach((prop: any) => {
|
|
||||||
if (prop.type === 6 /* ATTRIBUTE */) { // type 6 は StaticAttribute
|
|
||||||
staticProps[prop.name] = prop.value?.content || ''; // 属性値を文字列として取得
|
|
||||||
} else if (prop.type === 7 /* DIRECTIVE */ && prop.name === 'bind' && prop.arg?.content) { // type 7 は DirectiveNode, v-bind:propName の場合
|
|
||||||
if (prop.exp?.content && prop.arg.content !== 'class') {
|
|
||||||
bindProps[prop.arg.content] = prop.exp.content; // prop.exp.content (文字列) を格納
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
usageInfoList.push({
|
|
||||||
staticProps,
|
|
||||||
bindProps,
|
|
||||||
componentName: componentTag,
|
|
||||||
});
|
|
||||||
|
|
||||||
} else if (node.children && Array.isArray(node.children)) {
|
|
||||||
node.children.forEach(child => traverse(child));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
traverse(templateAst);
|
|
||||||
return usageInfoList;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function analyzeVueProps(options: {
|
|
||||||
targetComponents: string[],
|
|
||||||
targetFilePaths: string[],
|
|
||||||
exportFilePath: string,
|
|
||||||
transformedCodeCache: Record<string, string>
|
|
||||||
}): Promise<void> {
|
|
||||||
|
|
||||||
const targetComponents = options.targetComponents || [];
|
|
||||||
const analysisResults: AnalysisResult[] = [];
|
|
||||||
|
|
||||||
// 対象ファイルパスを glob で展開
|
|
||||||
const filePaths = options.targetFilePaths.reduce<string[]>((acc, filePathPattern) => {
|
|
||||||
const matchedFiles = glob.sync(filePathPattern);
|
|
||||||
return [...acc, ...matchedFiles];
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
for (const filePath of filePaths) {
|
|
||||||
// ★ キャッシュから変換済みコードを取得 (修正): キャッシュに存在しない場合はエラーにする (キャッシュ必須)
|
|
||||||
const code = options.transformedCodeCache[path.resolve(filePath)]; // キャッシュからコードを取得 (キャッシュミス時は undefined)
|
|
||||||
if (!code) { // キャッシュミスの場合
|
|
||||||
console.error(`[create-search-index] Error: No cached code found for: ${filePath}.`); // エラーログ
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
console.log(`[create-search-index] analyzeVueProps: Processing file: ${filePath}, using cached code: true`); // ★ ログ: キャッシュ使用
|
|
||||||
const { descriptor, errors } = vueSfcParse(code, {
|
|
||||||
filename: filePath,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (errors.length) {
|
|
||||||
console.error(`[create-search-index] Compile Error: ${filePath}`, errors);
|
|
||||||
continue; // エラーが発生したファイルはスキップ
|
|
||||||
}
|
|
||||||
|
|
||||||
// テンプレートASTを走査してコンポーネント使用箇所とpropsの値を取得
|
|
||||||
const usageInfo = extractUsageInfoFromTemplateAst(descriptor.template?.ast, targetComponents);
|
|
||||||
if (!usageInfo) continue;
|
|
||||||
|
|
||||||
if (usageInfo.length > 0) {
|
|
||||||
analysisResults.push({
|
|
||||||
filePath: filePath,
|
|
||||||
usage: usageInfo,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outputAnalysisResultAsTS(options.exportFilePath, analysisResults); // outputAnalysisResultAsTS を呼び出す
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rollup プラグインとして export
|
|
||||||
export default function pluginCreateSearchIndex(options: {
|
|
||||||
targetComponents: string[],
|
|
||||||
targetFilePaths: string[],
|
|
||||||
exportFilePath: string
|
|
||||||
}): Plugin {
|
|
||||||
const transformedCodeCache: Record<string, string> = {}; // キャッシュオブジェクトを定義
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'createSearchIndex',
|
|
||||||
|
|
||||||
async transform(code, id) {
|
|
||||||
if (!id.endsWith('.vue')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// targetFilePaths にマッチするファイルのみ処理を行う
|
|
||||||
// glob パターンでマッチング
|
|
||||||
let fullFileName = '';
|
|
||||||
|
|
||||||
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;
|
|
||||||
fullFileName = normalizedId;
|
|
||||||
break; // マッチしたらループを抜ける
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isMatch) break; // いずれかのパターンでマッチしたら、outer loop も抜ける
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!isMatch) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
console.log(`[create-search-index] Processing file: ${id}`); // ログ: マッチしたファイルを処理中
|
|
||||||
|
|
||||||
const s = new MagicString(code); // magic-string のインスタンスを作成
|
|
||||||
const ast = vueSfcParse(code, { filename: id }).descriptor.template?.ast; // テンプレート AST を取得
|
|
||||||
|
|
||||||
if (ast) {
|
|
||||||
function traverse(node: any) {
|
|
||||||
if (node.type === 1 /* ELEMENT */ && node.tag === 'MkSearchMarker') { // MkSearchMarker コンポーネントを検出
|
|
||||||
const markerId = randomUUID(); // UUID を生成
|
|
||||||
const props = node.props || [];
|
|
||||||
const hasMarkerIdProp = props.some((prop: any) => prop.type === 6 && prop.name === 'markerId'); // markerId 属性が既に存在するか確認
|
|
||||||
|
|
||||||
if (!hasMarkerIdProp) {
|
|
||||||
// magic-string を使って markerId 属性を <MkSearchMarker> に追加
|
|
||||||
const startTagEnd = code.indexOf('>', node.loc.start.offset); // 開始タグの閉じ > の位置を検索
|
|
||||||
if (startTagEnd !== -1) {
|
|
||||||
s.appendRight(startTagEnd, ` markerId="${markerId}"`); // markerId 属性を追記
|
|
||||||
console.log(`[create-search-index] 付与 markerId="${markerId}" to MkSearchMarker in ${id}`); // 付与ログ
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.children && Array.isArray(node.children)) {
|
|
||||||
node.children.forEach(child => traverse(child)); // 子ノードを再帰的に traverse
|
|
||||||
}
|
|
||||||
}
|
|
||||||
traverse(ast); // AST を traverse
|
|
||||||
|
|
||||||
const transformedCode = s.toString(); // ★ 変換後のコードを取得
|
|
||||||
transformedCodeCache[id] = transformedCode; // ★ 変換後のコードをキャッシュに保存
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: transformedCode, // 変更後のコードを返す
|
|
||||||
map: s.generateMap({ source: id, includeContent: true }), // ソースマップも生成 (sourceMap: true が必要)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null; // テンプレート AST がない場合は null を返す
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
async writeBundle() {
|
|
||||||
await analyzeVueProps({ ...options, transformedCodeCache }); // writeBundle フックで analyzeVueProps 関数を呼び出す (変更なし)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
276
packages/frontend/lib/vite-plugin-create-search-index.ts
Normal file
276
packages/frontend/lib/vite-plugin-create-search-index.ts
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { parse as vueSfcParse } from 'vue/compiler-sfc';
|
||||||
|
import type { Plugin } from 'vite';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import { glob } from 'glob';
|
||||||
|
import JSON5 from 'json5';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
import MagicString from 'magic-string';
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
|
export interface AnalysisResult {
|
||||||
|
filePath: string;
|
||||||
|
usage: ComponentUsageInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComponentUsageInfo {
|
||||||
|
staticProps: Record<string, string>;
|
||||||
|
bindProps: Record<string, string>;
|
||||||
|
componentName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function outputAnalysisResultAsTS(outputPath: string, analysisResults: AnalysisResult[]): void {
|
||||||
|
// (outputAnalysisResultAsTS 関数の実装は前回と同様)
|
||||||
|
const varName = 'searchIndexes'; // 変数名
|
||||||
|
|
||||||
|
const jsonString = JSON5.stringify(analysisResults, { space: "\t", quote: "'" }); // JSON.stringify で JSON 文字列を生成
|
||||||
|
|
||||||
|
// bindProps の値を文字列置換で修正する関数
|
||||||
|
function modifyBindPropsInString(jsonString: string): string {
|
||||||
|
const modifiedString = jsonString.replace(
|
||||||
|
/bindProps:\s*\{([^}]*)\}/g, // bindProps: { ... } にマッチ (g フラグで複数箇所を置換)
|
||||||
|
(match, bindPropsBlock) => {
|
||||||
|
// bindPropsBlock ( { ... } 内) の各プロパティをさらに置換
|
||||||
|
const modifiedBlock = bindPropsBlock.replace(
|
||||||
|
/(.*):\s*\'(.*)\'/g, // propName: 'propValue' にマッチ
|
||||||
|
(propMatch, propName, propValue) => {
|
||||||
|
return `${propName}: ${propValue}`; // propValue のクォートを除去
|
||||||
|
}
|
||||||
|
).replaceAll("\\'", "'");
|
||||||
|
return `bindProps: {${modifiedBlock}}`; // 置換後の block で bindProps: { ... } を再構成
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return modifiedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const tsOutput = `
|
||||||
|
/*
|
||||||
|
* 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 ${varName} = ${modifyBindPropsInString(jsonString)} as const;
|
||||||
|
|
||||||
|
export type AnalysisResults = typeof ${varName};
|
||||||
|
export type ComponentUsageInfo = AnalysisResults[number]['usage'][number];
|
||||||
|
`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(outputPath, tsOutput, 'utf-8');
|
||||||
|
console.log(`[create-search-index]: output done. ${outputPath}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[create-search-index]: error: ', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractUsageInfoFromTemplateAst(
|
||||||
|
templateAst: any,
|
||||||
|
targetComponents: string[]
|
||||||
|
): ComponentUsageInfo[] {
|
||||||
|
const usageInfoList: ComponentUsageInfo[] = [];
|
||||||
|
|
||||||
|
if (!templateAst) {
|
||||||
|
return usageInfoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function traverse(node: any) {
|
||||||
|
if (node.type === 1 /* ELEMENT */ && node.tag && targetComponents.includes(node.tag)) {
|
||||||
|
const componentTag = node.tag;
|
||||||
|
|
||||||
|
const staticProps: Record<string, string> = {};
|
||||||
|
const bindProps: Record<string, string> = {}; // bindProps の型を string に戻す
|
||||||
|
|
||||||
|
if (node.props && Array.isArray(node.props)) {
|
||||||
|
node.props.forEach((prop: any) => {
|
||||||
|
if (prop.type === 6 /* ATTRIBUTE */) { // type 6 は StaticAttribute
|
||||||
|
staticProps[prop.name] = prop.value?.content || ''; // 属性値を文字列として取得
|
||||||
|
} else if (prop.type === 7 /* DIRECTIVE */ && prop.name === 'bind' && prop.arg?.content) { // type 7 は DirectiveNode, v-bind:propName の場合
|
||||||
|
if (prop.exp?.content && prop.arg.content !== 'class') {
|
||||||
|
bindProps[prop.arg.content] = prop.exp.content; // prop.exp.content (文字列) を格納
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
usageInfoList.push({
|
||||||
|
staticProps,
|
||||||
|
bindProps,
|
||||||
|
componentName: componentTag,
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (node.children && Array.isArray(node.children)) {
|
||||||
|
node.children.forEach(child => traverse(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(templateAst);
|
||||||
|
return usageInfoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function analyzeVueProps(options: {
|
||||||
|
targetComponents: string[],
|
||||||
|
targetFilePaths: string[],
|
||||||
|
exportFilePath: string,
|
||||||
|
transformedCodeCache: Record<string, string> // ★ transformedCodeCache を options から受け取る
|
||||||
|
}): Promise<void> {
|
||||||
|
|
||||||
|
const targetComponents = options.targetComponents || [];
|
||||||
|
const analysisResults: AnalysisResult[] = [];
|
||||||
|
|
||||||
|
// 対象ファイルパスを glob で展開
|
||||||
|
const filePaths = options.targetFilePaths.reduce<string[]>((acc, filePathPattern) => {
|
||||||
|
const matchedFiles = glob.sync(filePathPattern);
|
||||||
|
return [...acc, ...matchedFiles];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
for (const filePath of filePaths) {
|
||||||
|
// ★ キャッシュから変換済みコードを取得 (修正): キャッシュに存在しない場合はエラーにする (キャッシュ必須)
|
||||||
|
const code = options.transformedCodeCache[path.resolve(filePath)]; // options 経由でキャッシュ参照
|
||||||
|
if (!code) { // キャッシュミスの場合
|
||||||
|
console.error(`[create-search-index] Error: No cached code found for: ${filePath}.`); // エラーログ
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const { descriptor, errors } = vueSfcParse(code, {
|
||||||
|
filename: filePath,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errors.length) {
|
||||||
|
console.error(`[create-search-index] Compile Error: ${filePath}`, errors);
|
||||||
|
continue; // エラーが発生したファイルはスキップ
|
||||||
|
}
|
||||||
|
|
||||||
|
// テンプレートASTを走査してコンポーネント使用箇所とpropsの値を取得
|
||||||
|
const usageInfo = extractUsageInfoFromTemplateAst(descriptor.template?.ast, targetComponents);
|
||||||
|
if (!usageInfo) continue;
|
||||||
|
|
||||||
|
if (usageInfo.length > 0) {
|
||||||
|
analysisResults.push({
|
||||||
|
filePath: filePath,
|
||||||
|
usage: usageInfo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outputAnalysisResultAsTS(options.exportFilePath, analysisResults); // outputAnalysisResultAsTS を呼び出す
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processVueFile(
|
||||||
|
code: string,
|
||||||
|
id: string,
|
||||||
|
options: { targetComponents: string[], targetFilePaths: string[], exportFilePath: string },
|
||||||
|
transformedCodeCache: Record<string, string>
|
||||||
|
) {
|
||||||
|
const s = new MagicString(code); // magic-string のインスタンスを作成
|
||||||
|
const ast = vueSfcParse(code, { filename: id }).descriptor.template?.ast; // テンプレート AST を取得
|
||||||
|
|
||||||
|
if (ast) {
|
||||||
|
function traverse(node: any) {
|
||||||
|
if (node.type === 1 /* ELEMENT */ && node.tag === 'MkSearchMarker') { // MkSearchMarker コンポーネントを検出
|
||||||
|
const markerId = randomUUID(); // UUID を生成
|
||||||
|
const props = node.props || [];
|
||||||
|
const hasMarkerIdProp = props.some((prop: any) => prop.type === 6 && prop.name === 'markerId'); // markerId 属性が既に存在するか確認
|
||||||
|
|
||||||
|
if (!hasMarkerIdProp) {
|
||||||
|
// magic-string を使って markerId 属性を <MkSearchMarker> に追加
|
||||||
|
const startTagEnd = code.indexOf('>', node.loc.start.offset); // 開始タグの閉じ > の位置を検索
|
||||||
|
if (startTagEnd !== -1) {
|
||||||
|
s.appendRight(startTagEnd, ` markerId="${markerId}"`); // markerId 属性を追記
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.children && Array.isArray(node.children)) {
|
||||||
|
node.children.forEach(child => traverse(child)); // 子ノードを再帰的に traverse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traverse(ast); // AST を traverse
|
||||||
|
}
|
||||||
|
|
||||||
|
const transformedCode = s.toString(); // ★ 変換後のコードを取得
|
||||||
|
transformedCodeCache[id] = transformedCode; // ★ 変換後のコードをキャッシュに保存
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: transformedCode, // 変更後のコードを返す
|
||||||
|
map: s.generateMap({ source: id, includeContent: true }), // ソースマップも生成 (sourceMap: true が必要)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Rollup プラグインとして export
|
||||||
|
export default function pluginCreateSearchIndex(options: {
|
||||||
|
targetComponents: string[],
|
||||||
|
targetFilePaths: string[],
|
||||||
|
exportFilePath: string
|
||||||
|
}): Plugin {
|
||||||
|
let transformedCodeCache: Record<string, string> = {}; // ★ キャッシュオブジェクトをプラグインスコープで定義
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'createSearchIndex',
|
||||||
|
enforce: 'pre',
|
||||||
|
|
||||||
|
async buildStart() {
|
||||||
|
transformedCodeCache = {};
|
||||||
|
|
||||||
|
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'); // ファイル内容を読み込む
|
||||||
|
await processVueFile(code, id, options, transformedCodeCache); // processVueFile 関数を呼び出す
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await analyzeVueProps({ ...options, transformedCodeCache }); // 開発サーバー起動時にも analyzeVueProps を実行
|
||||||
|
},
|
||||||
|
|
||||||
|
async transform(code, id) {
|
||||||
|
if (!id.endsWith('.vue')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// targetFilePaths にマッチするファイルのみ処理を行う
|
||||||
|
// 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 null;
|
||||||
|
}
|
||||||
|
console.log(`[create-search-index] transform: Processing file: ${id}`); // ログ: transform で処理中のファイル
|
||||||
|
|
||||||
|
const transformed = await processVueFile(code, id, options, transformedCodeCache);
|
||||||
|
await analyzeVueProps({ ...options, transformedCodeCache }); // analyzeVueProps を呼び出す
|
||||||
|
return transformed;
|
||||||
|
},
|
||||||
|
|
||||||
|
async writeBundle() {
|
||||||
|
await analyzeVueProps({ ...options, transformedCodeCache }); // ビルド時にも analyzeVueProps を実行
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
0
packages/frontend/src/scripts/autogen/.gitkeep
Normal file
0
packages/frontend/src/scripts/autogen/.gitkeep
Normal file
@@ -1,68 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
* 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: '727cc9e8-ad67-474a-9241-b5a9a6475e47',
|
|
||||||
},
|
|
||||||
bindProps: {},
|
|
||||||
componentName: 'MkSearchMarker',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
staticProps: {
|
|
||||||
markerId: '1a06c7f9-e85e-46cb-bf5f-b3efa8e71b93',
|
|
||||||
},
|
|
||||||
bindProps: {},
|
|
||||||
componentName: 'MkSearchMarker',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filePath: 'src/pages/settings/privacy.vue',
|
|
||||||
usage: [
|
|
||||||
{
|
|
||||||
staticProps: {
|
|
||||||
icon: 'ti ti-lock-open',
|
|
||||||
markerId: 'db7de893-e299-40af-a515-8954da435f4b',
|
|
||||||
},
|
|
||||||
bindProps: {
|
|
||||||
locationLabel: [i18n.ts.privacy, i18n.ts.makeFollowManuallyApprove],
|
|
||||||
keywords: ['follow', 'lock', i18n.ts.lockedAccountInfo],
|
|
||||||
},
|
|
||||||
componentName: 'MkSearchMarker',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filePath: 'src/pages/settings/mute-block.vue',
|
|
||||||
usage: [
|
|
||||||
{
|
|
||||||
staticProps: {
|
|
||||||
markerId: 'test',
|
|
||||||
icon: 'ti ti-ban',
|
|
||||||
},
|
|
||||||
bindProps: {
|
|
||||||
locationLabel: [i18n.ts.muteAndBlock],
|
|
||||||
keywords: ['mute', i18n.ts.wordMute],
|
|
||||||
children: ['test2'],
|
|
||||||
},
|
|
||||||
componentName: 'MkSearchMarker',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export type AnalysisResults = typeof searchIndexes;
|
|
||||||
export type ComponentUsageInfo = AnalysisResults[number]['usage'][number];
|
|
@@ -10,7 +10,7 @@ import meta from '../../package.json';
|
|||||||
import packageInfo from './package.json' with { type: 'json' };
|
import packageInfo from './package.json' with { type: 'json' };
|
||||||
import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js';
|
import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js';
|
||||||
import pluginJson5 from './vite.json5.js';
|
import pluginJson5 from './vite.json5.js';
|
||||||
import pluginCreateSearchIndex from './lib/rollup-plugin-create-search-index.js';
|
import pluginCreateSearchIndex from './lib/vite-plugin-create-search-index.js';
|
||||||
|
|
||||||
const url = process.env.NODE_ENV === 'development' ? yaml.load(await fsp.readFile('../../.config/default.yml', 'utf-8')).url : null;
|
const url = process.env.NODE_ENV === 'development' ? yaml.load(await fsp.readFile('../../.config/default.yml', 'utf-8')).url : null;
|
||||||
const host = url ? (new URL(url)).hostname : undefined;
|
const host = url ? (new URL(url)).hostname : undefined;
|
||||||
|
Reference in New Issue
Block a user