v2.2.320: /naver-shopping "CATEGORY_TREE is not iterable" 크래시 수정 + 카테고리 목록 조회
- import * as (namespace import) 로 배열 JSON 을 불러오면 esbuild 번들에서
{ default: [...] } 로 감싸져 배열이 아니게 되는 문제 — import x = require(...)
로 전환해 esbuild/tsc/ts-jest 세 경로 모두 동일하게 동작하도록 수정
- 실제 esbuild 번들 산출물을 실행하는 회귀 테스트 추가 (ts-jest만으론 이 버그를 못 잡음)
- /naver-shopping categories [검색어] — 대분류 목록/세부 카테고리 검색 명령 추가
This commit is contained in:
@@ -13,7 +13,15 @@
|
||||
* isnow890/naver-search-mcp, MIT) 에서 fuzzy 매칭으로 찾는다.
|
||||
*/
|
||||
|
||||
import * as categoryTreeRaw from './data/naverShoppingCategories.json';
|
||||
// ES 네임스페이스 import(`import * as x`)는 esbuild 의 JSON 로더에서 배열 최상위
|
||||
// JSON 을 `{ default: [...] }` 로 감싸버려 `x` 자체는 배열이 아니게 된다(ts-jest 는
|
||||
// require() 그대로라 이 문제가 안 보여 테스트를 통과하고도 실제 번들에서만
|
||||
// "CATEGORY_TREE is not iterable" 로 터졌다). ES default import 는 esModuleInterop
|
||||
// 이 필요한데, 이 프로젝트는 그 옵션을 켜면 `jest.spyOn(fs, 'statSync')` 같은 기존
|
||||
// 테스트의 CJS 네임스페이스 모킹이 깨진다(Cannot redefine property). 그래서 여기선
|
||||
// TS 의 `import x = require(...)` — 순수 CJS require 그대로 — 를 써서 esbuild·tsc·
|
||||
// ts-jest 세 경로 모두에서 동일하게 배열 그대로를 받는다.
|
||||
import categoryTreeRaw = require('./data/naverShoppingCategories.json');
|
||||
|
||||
export interface CategoryRow {
|
||||
code: string;
|
||||
|
||||
@@ -18,7 +18,7 @@ import * as vscode from 'vscode';
|
||||
import { registerSlashCommand, chunk } from '../datacollect/slashRouter';
|
||||
import { AIService } from '../../core/services';
|
||||
import { parseShoppingIntent, parseSearchTrendIntent } from './intentParser';
|
||||
import { findCategoryCode, listTopLevelCategoryNames, type CategoryMatch } from './categoryCodes';
|
||||
import { findCategoryCode, listTopLevelCategoryNames, ROOT_CATEGORY_CODES, type CategoryMatch } from './categoryCodes';
|
||||
import { rankCategories, rankKeywords, type RankedResult } from './rankEngine';
|
||||
import type { CommonFilters, TimeUnit } from './naverApi';
|
||||
|
||||
@@ -73,17 +73,47 @@ function cmdShoppingHelp(view: Webview): void {
|
||||
'',
|
||||
'- `/naver-shopping 패션의류 vs 화장품/미용 최근 일주일` — 명시 분야 비교',
|
||||
'- `/naver-shopping 요즘 뜨는 뷰티 카테고리 알려줘` — 탐색(후보 제안 + 검증)',
|
||||
'- `/naver-shopping categories` — 대분류(11종) 목록 보기',
|
||||
'- `/naver-shopping categories 원피스` — 카테고리 검색(이름 · 경로 · cat_id)',
|
||||
'',
|
||||
'- API 키 설정: Astra 설정 → Naver Trend 탭에서 Client ID/Secret 입력',
|
||||
'',
|
||||
].join('\n'));
|
||||
}
|
||||
|
||||
/** `/naver-shopping categories [검색어]` — 검색어 없으면 대분류 11종, 있으면 트리 fuzzy 검색 결과. */
|
||||
function cmdListCategories(view: Webview, query: string): void {
|
||||
if (!query) {
|
||||
const lines = [
|
||||
'',
|
||||
'**네이버쇼핑 대분류 (11종)**',
|
||||
'',
|
||||
...listTopLevelCategoryNames().map(n => `- ${n}${ROOT_CATEGORY_CODES[n] ? ` (cat_id: ${ROOT_CATEGORY_CODES[n]})` : ''}`),
|
||||
'',
|
||||
'세부 카테고리 검색: `/naver-shopping categories <키워드>` (예: `/naver-shopping categories 원피스`)',
|
||||
'',
|
||||
];
|
||||
chunk(view, lines.join('\n'));
|
||||
return;
|
||||
}
|
||||
const matches = findCategoryCode(query, 15);
|
||||
if (matches.length === 0) {
|
||||
chunk(view, `\n❓ "${query}" 에 매칭되는 카테고리를 찾지 못했습니다.\n`);
|
||||
return;
|
||||
}
|
||||
const lines = ['', `**"${query}" 카테고리 검색 결과**`, '', '| 이름 | 경로 | cat_id |', '|---|---|---|'];
|
||||
for (const m of matches) lines.push(`| ${m.name} | ${m.path} | ${m.code} |`);
|
||||
chunk(view, lines.join('\n') + '\n');
|
||||
}
|
||||
|
||||
async function runShopping(arg: string, view: Webview, context?: vscode.ExtensionContext): Promise<boolean> {
|
||||
if (!context) { chunk(view, '\n⚠️ ExtensionContext 없음.\n'); return true; }
|
||||
const trimmed = arg.trim();
|
||||
if (!trimmed) { cmdShoppingHelp(view); return true; }
|
||||
|
||||
const listMatch = trimmed.match(/^(?:categories|category|카테고리|list)\b\s*(.*)$/i);
|
||||
if (listMatch) { cmdListCategories(view, listMatch[1].trim()); return true; }
|
||||
|
||||
try {
|
||||
chunk(view, '\n🔍 질문 분석 중…\n');
|
||||
const ai = new AIService();
|
||||
@@ -98,7 +128,7 @@ async function runShopping(arg: string, view: Webview, context?: vscode.Extensio
|
||||
}
|
||||
|
||||
if (resolved.length === 0) {
|
||||
chunk(view, `\n❓ 분야를 찾지 못했습니다: ${unresolved.join(', ')}\n\n지원 대분류 예시: ${listTopLevelCategoryNames().join(', ')}\n(세부 카테고리명으로도 검색 가능 — 예: "원피스", "무선이어폰")\n`);
|
||||
chunk(view, `\n❓ 분야를 찾지 못했습니다: ${unresolved.join(', ')}\n\n지원 대분류 예시: ${listTopLevelCategoryNames().join(', ')}\n(세부 카테고리명으로도 검색 가능 — 예: "원피스", "무선이어폰")\n전체 목록/검색: \`/naver-shopping categories [검색어]\`\n`);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user