refactor: add MarkerIdAssigner instead of processVueFile and remove transformedCodeCache object
This commit is contained in:
parent
1a3866c4f6
commit
c42c6ae6e2
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { parse as vueSfcParse } from 'vue/compiler-sfc';
|
import { parse as vueSfcParse } from 'vue/compiler-sfc';
|
||||||
import type { LogOptions, Plugin } from 'vite';
|
import { LogOptions, normalizePath, Plugin } 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';
|
||||||
|
@ -1138,13 +1138,9 @@ function parseArrayExpression(expr: string): any[] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function analyzeVueProps(options: Options & {
|
export async function analyzeVueProps(options: Options, assigner: MarkerIdAssigner): Promise<void> {
|
||||||
transformedCodeCache: Record<string, string>,
|
|
||||||
}): Promise<void> {
|
|
||||||
initLogger(options);
|
initLogger(options);
|
||||||
|
|
||||||
const allMarkers: SearchIndexItem[] = [];
|
|
||||||
|
|
||||||
// 対象ファイルパスを glob で展開
|
// 対象ファイルパスを glob で展開
|
||||||
const filePaths = options.targetFilePaths.reduce<string[]>((acc, filePathPattern) => {
|
const filePaths = options.targetFilePaths.reduce<string[]>((acc, filePathPattern) => {
|
||||||
const matchedFiles = glob.sync(filePathPattern);
|
const matchedFiles = glob.sync(filePathPattern);
|
||||||
|
@ -1152,23 +1148,34 @@ export async function analyzeVueProps(options: Options & {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
logger.info(`Found ${filePaths.length} matching files to analyze`);
|
logger.info(`Found ${filePaths.length} matching files to analyze`);
|
||||||
|
const files: [string, string][] = filePaths.map(filePath => {
|
||||||
for (const filePath of filePaths) {
|
|
||||||
const absolutePath = path.join(process.cwd(), filePath);
|
const absolutePath = path.join(process.cwd(), filePath);
|
||||||
const id = absolutePath.replace(/\\/g, '/'); // 絶対パスに変換
|
const id = absolutePath.replace(/\\/g, '/'); // 絶対パスに変換
|
||||||
const code = options.transformedCodeCache[id]; // options 経由でキャッシュ参照
|
const code = assigner.getCached(id); // options 経由でキャッシュ参照
|
||||||
if (!code) { // キャッシュミスの場合
|
if (!code) { // キャッシュミスの場合
|
||||||
logger.error(`Error: No cached code found for: ${id}.`); // エラーログ
|
logger.error(`Error: No cached code found for: ${id}.`); // エラーログ
|
||||||
throw new Error(`No cached code found for: ${id}.`); // エラーを投げる
|
throw new Error(`No cached code found for: ${id}.`); // エラーを投げる
|
||||||
}
|
}
|
||||||
|
return [id, code.code];
|
||||||
|
});
|
||||||
|
await analyzeVuePropsByFiles({
|
||||||
|
exportFilePath: options.exportFilePath,
|
||||||
|
files,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function analyzeVuePropsByFiles(options: Pick<Options, 'exportFilePath'> & {
|
||||||
|
files: [id: string, code: string][]
|
||||||
|
}): Promise<void> {
|
||||||
|
const allMarkers: SearchIndexItem[] = [];
|
||||||
|
for (const [id, code] of options.files) {
|
||||||
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,12 +1183,12 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1202,55 +1209,53 @@ interface MarkerRelation {
|
||||||
node: VueAstNode;
|
node: VueAstNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processVueFile(
|
type TransformedCode = {
|
||||||
code: string,
|
|
||||||
id: string,
|
|
||||||
options: Options,
|
|
||||||
transformedCodeCache: Record<string, string>
|
|
||||||
): Promise<{
|
|
||||||
code: string,
|
code: string,
|
||||||
map: any,
|
map: any,
|
||||||
transformedCodeCache: Record<string, string>
|
};
|
||||||
}> {
|
|
||||||
const normalizedId = id.replace(/\\/g, '/'); // ファイルパスを正規化
|
|
||||||
|
|
||||||
// 開発モード時はコード内容に変更があれば常に再処理する
|
export class MarkerIdAssigner {
|
||||||
// コード内容が同じ場合のみキャッシュを使用
|
// key: file id
|
||||||
const isDevMode = process.env.NODE_ENV === 'development';
|
private cache: Map<string, TransformedCode>;
|
||||||
|
|
||||||
const s = new MagicString(code); // magic-string のインスタンスを作成
|
constructor() {
|
||||||
|
this.cache = new Map();
|
||||||
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
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// すでに処理済みのファイルでコードに変更がない場合はキャッシュを返す
|
public onInvalidate(id: string) {
|
||||||
if (transformedCodeCache[normalizedId] === code) {
|
this.cache.delete(id);
|
||||||
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 });
|
public processFile(id: string, code: string): TransformedCode {
|
||||||
if (!parsed.descriptor.template) {
|
// try cache first
|
||||||
return {
|
if (this.cache.has(id)) {
|
||||||
code,
|
return this.cache.get(id);
|
||||||
map: s.generateMap({ source: id, includeContent: true }),
|
}
|
||||||
transformedCodeCache
|
const transformed = this.#processImpl(id, code);
|
||||||
};
|
this.cache.set(id, transformed);
|
||||||
|
return transformed;
|
||||||
}
|
}
|
||||||
const ast = parsed.descriptor.template.ast; // テンプレート AST を取得
|
|
||||||
const markerRelations: MarkerRelation[] = []; // MarkerRelation 配列を初期化
|
|
||||||
|
|
||||||
if (ast) {
|
#processImpl(id: string, code: string): TransformedCode {
|
||||||
|
const s = new MagicString(code); // magic-string のインスタンスを作成
|
||||||
|
|
||||||
|
const parsed = vueSfcParse(code, { filename: id });
|
||||||
|
if (!parsed.descriptor.template) {
|
||||||
|
return {
|
||||||
|
code,
|
||||||
|
map: s.generateMap({ source: id, includeContent: true }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const ast = parsed.descriptor.template.ast; // テンプレート AST を取得
|
||||||
|
const markerRelations: MarkerRelation[] = []; // MarkerRelation 配列を初期化
|
||||||
|
|
||||||
|
if (!ast) {
|
||||||
|
return {
|
||||||
|
code: s.toString(), // 変更後のコードを返す
|
||||||
|
map: s.generateMap({ source: id, includeContent: true }), // ソースマップも生成 (sourceMap: true が必要)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function traverse(node: any, currentParent?: any) {
|
function traverse(node: any, currentParent?: any) {
|
||||||
if (node.type === 1 && node.tag === 'SearchMarker') {
|
if (node.type === 1 && node.tag === 'SearchMarker') {
|
||||||
// 行番号はコード先頭からの改行数で取得
|
// 行番号はコード先頭からの改行数で取得
|
||||||
|
@ -1416,39 +1421,42 @@ async function processVueFile(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: s.toString(), // 変更後のコードを返す
|
||||||
|
map: s.generateMap({ source: id, includeContent: true }), // ソースマップも生成 (sourceMap: true が必要)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const transformedCode = s.toString(); // 変換後のコードを取得
|
getCached(id: string) {
|
||||||
transformedCodeCache[normalizedId] = transformedCode; // 変換後のコードをキャッシュに保存
|
return this.cache.get(id);
|
||||||
|
}
|
||||||
return {
|
|
||||||
code: transformedCode, // 変更後のコードを返す
|
|
||||||
map: s.generateMap({ source: id, includeContent: true }), // ソースマップも生成 (sourceMap: true が必要)
|
|
||||||
transformedCodeCache // キャッシュも返す
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateSearchIndex(options: Options, transformedCodeCache: Record<string, string> = {}) {
|
export async function generateSearchIndex(options: Options, assigner: MarkerIdAssigner) {
|
||||||
const filePaths = options.targetFilePaths.reduce<string[]>((acc, filePathPattern) => {
|
const filePaths = options.targetFilePaths.reduce<string[]>((acc, filePathPattern) => {
|
||||||
const matchedFiles = glob.sync(filePathPattern);
|
const matchedFiles = glob.sync(filePathPattern);
|
||||||
return [...acc, ...matchedFiles];
|
return [...acc, ...matchedFiles];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const files: [string, string][] = [];
|
||||||
|
|
||||||
for (const filePath of filePaths) {
|
for (const filePath of filePaths) {
|
||||||
const id = path.resolve(filePath); // 絶対パスに変換
|
const id = path.resolve(filePath); // 絶対パスに変換
|
||||||
const code = fs.readFileSync(filePath, 'utf-8'); // ファイル内容を読み込む
|
const code = fs.readFileSync(filePath, 'utf-8'); // ファイル内容を読み込む
|
||||||
const { transformedCodeCache: newCache } = await processVueFile(code, id, options, transformedCodeCache); // processVueFile 関数を呼び出す
|
const transformed = assigner.processFile(normalizePath(id), code);
|
||||||
transformedCodeCache = newCache; // キャッシュを更新
|
files.push([id, transformed.code]);
|
||||||
}
|
}
|
||||||
|
|
||||||
await analyzeVueProps({ ...options, transformedCodeCache }); // 開発サーバー起動時にも analyzeVueProps を実行
|
await analyzeVuePropsByFiles({
|
||||||
|
exportFilePath: options.exportFilePath,
|
||||||
return transformedCodeCache; // キャッシュを返す
|
files,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rollup プラグインとして export
|
// Rollup プラグインとして export
|
||||||
export default function pluginCreateSearchIndex(options: Options): Plugin {
|
export default function pluginCreateSearchIndex(options: Options): Plugin {
|
||||||
let transformedCodeCache: Record<string, string> = {}; // キャッシュオブジェクトをプラグインスコープで定義
|
const assigner = new MarkerIdAssigner();
|
||||||
const isDevServer = process.env.NODE_ENV === 'development'; // 開発サーバーかどうか
|
const isDevServer = process.env.NODE_ENV === 'development'; // 開発サーバーかどうか
|
||||||
|
|
||||||
initLogger(options); // ロガーを初期化
|
initLogger(options); // ロガーを初期化
|
||||||
|
@ -1462,7 +1470,11 @@ export default function pluginCreateSearchIndex(options: Options): Plugin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
transformedCodeCache = await generateSearchIndex(options, transformedCodeCache);
|
await generateSearchIndex(options, assigner);
|
||||||
|
},
|
||||||
|
|
||||||
|
watchChange(id) {
|
||||||
|
assigner.onInvalidate(id);
|
||||||
},
|
},
|
||||||
|
|
||||||
async transform(code, id) {
|
async transform(code, id) {
|
||||||
|
@ -1491,21 +1503,19 @@ export default function pluginCreateSearchIndex(options: Options): Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ファイルの内容が変更された場合は再処理を行う
|
// ファイルの内容が変更された場合は再処理を行う
|
||||||
const normalizedId = id.replace(/\\/g, '/');
|
const hasContentChanged = true; // TODO
|
||||||
const hasContentChanged = !transformedCodeCache[normalizedId] || transformedCodeCache[normalizedId] !== code;
|
|
||||||
|
|
||||||
const transformed = await processVueFile(code, id, options, transformedCodeCache);
|
const transformed = assigner.processFile(id, code);
|
||||||
transformedCodeCache = transformed.transformedCodeCache; // キャッシュを更新
|
|
||||||
|
|
||||||
if (isDevServer && hasContentChanged) {
|
if (isDevServer && hasContentChanged) {
|
||||||
await analyzeVueProps({ ...options, transformedCodeCache }); // ファイルが変更されたときのみ分析を実行
|
await analyzeVueProps(options, assigner); // ファイルが変更されたときのみ分析を実行
|
||||||
}
|
}
|
||||||
|
|
||||||
return transformed;
|
return transformed;
|
||||||
},
|
},
|
||||||
|
|
||||||
async writeBundle() {
|
async writeBundle() {
|
||||||
await analyzeVueProps({ ...options, transformedCodeCache }); // ビルド時にも analyzeVueProps を実行
|
await analyzeVueProps(options, assigner); // ビルド時にも analyzeVueProps を実行
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { searchIndexes } from '../vite.config.js';
|
import { searchIndexes } from '../vite.config.js';
|
||||||
import { generateSearchIndex } from '../lib/vite-plugin-create-search-index.js';
|
import { generateSearchIndex, MarkerIdAssigner } from '../lib/vite-plugin-create-search-index.js';
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
const assigner = new MarkerIdAssigner();
|
||||||
for (const searchIndex of searchIndexes) {
|
for (const searchIndex of searchIndexes) {
|
||||||
await generateSearchIndex(searchIndex);
|
await generateSearchIndex(searchIndex, assigner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue