refactor: piece系設定解決をresolveConfigValueへ統一

This commit is contained in:
nrslib 2026-02-19 10:57:07 +09:00
parent cbde7ac654
commit 6b425d64fc
9 changed files with 79 additions and 51 deletions

View File

@ -20,15 +20,22 @@ vi.mock('../infra/config/global/globalConfig.js', () => ({
loadGlobalConfig: vi.fn().mockReturnValue({}),
}));
vi.mock('../infra/config/loadConfig.js', () => ({
loadConfig: vi.fn(() => ({
global: {
language: languageState.value,
disabledBuiltins: [],
enableBuiltinPieces: true,
},
project: {},
})),
vi.mock('../infra/config/resolveConfigValue.js', () => ({
resolveConfigValue: vi.fn((_cwd: string, key: string) => {
if (key === 'language') return languageState.value;
if (key === 'enableBuiltinPieces') return true;
if (key === 'disabledBuiltins') return [];
return undefined;
}),
resolveConfigValues: vi.fn((_cwd: string, keys: readonly string[]) => {
const result: Record<string, unknown> = {};
for (const key of keys) {
if (key === 'language') result[key] = languageState.value;
if (key === 'enableBuiltinPieces') result[key] = true;
if (key === 'disabledBuiltins') result[key] = [];
}
return result;
}),
}));
// --- Imports (after mocks) ---

View File

@ -57,14 +57,21 @@ vi.mock('../infra/config/project/projectConfig.js', () => ({
loadProjectConfig: vi.fn().mockReturnValue({}),
}));
vi.mock('../infra/config/loadConfig.js', () => ({
loadConfig: vi.fn().mockReturnValue({
global: {
language: 'en',
enableBuiltinPieces: true,
disabledBuiltins: [],
},
project: {},
vi.mock('../infra/config/resolveConfigValue.js', () => ({
resolveConfigValue: vi.fn((_cwd: string, key: string) => {
if (key === 'language') return 'en';
if (key === 'enableBuiltinPieces') return true;
if (key === 'disabledBuiltins') return [];
return undefined;
}),
resolveConfigValues: vi.fn((_cwd: string, keys: readonly string[]) => {
const result: Record<string, unknown> = {};
for (const key of keys) {
if (key === 'language') result[key] = 'en';
if (key === 'enableBuiltinPieces') result[key] = true;
if (key === 'disabledBuiltins') result[key] = [];
}
return result;
}),
}));

View File

@ -17,15 +17,22 @@ vi.mock('../infra/config/global/globalConfig.js', async (importOriginal) => {
};
});
vi.mock('../infra/config/loadConfig.js', () => ({
loadConfig: () => ({
global: {
language: 'en',
enableBuiltinPieces: false,
disabledBuiltins: [],
vi.mock('../infra/config/resolveConfigValue.js', () => ({
resolveConfigValue: (_cwd: string, key: string) => {
if (key === 'language') return 'en';
if (key === 'enableBuiltinPieces') return false;
if (key === 'disabledBuiltins') return [];
return undefined;
},
resolveConfigValues: (_cwd: string, keys: readonly string[]) => {
const result: Record<string, unknown> = {};
for (const key of keys) {
if (key === 'language') result[key] = 'en';
if (key === 'enableBuiltinPieces') result[key] = false;
if (key === 'disabledBuiltins') result[key] = [];
}
return result;
},
project: {},
}),
}));
const { listPieces } = await import('../infra/config/loaders/pieceLoader.js');

View File

@ -26,15 +26,22 @@ vi.mock('../infra/config/global/globalConfig.js', async (importOriginal) => {
};
});
vi.mock('../infra/config/loadConfig.js', () => ({
loadConfig: () => ({
global: {
language: languageState.value,
enableBuiltinPieces: true,
disabledBuiltins: [],
vi.mock('../infra/config/resolveConfigValue.js', () => ({
resolveConfigValue: (_cwd: string, key: string) => {
if (key === 'language') return languageState.value;
if (key === 'enableBuiltinPieces') return true;
if (key === 'disabledBuiltins') return [];
return undefined;
},
resolveConfigValues: (_cwd: string, keys: readonly string[]) => {
const result: Record<string, unknown> = {};
for (const key of keys) {
if (key === 'language') result[key] = languageState.value;
if (key === 'enableBuiltinPieces') result[key] = true;
if (key === 'disabledBuiltins') result[key] = [];
}
return result;
},
project: {},
}),
}));
vi.mock('../infra/resources/index.js', async (importOriginal) => {

View File

@ -7,7 +7,7 @@
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { getGlobalConfigDir } from '../paths.js';
import { loadConfig } from '../loadConfig.js';
import { resolveConfigValue } from '../resolveConfigValue.js';
const INITIAL_USER_CATEGORIES_CONTENT = 'piece_categories: {}\n';
@ -17,9 +17,9 @@ function getDefaultPieceCategoriesPath(): string {
/** Get the path to the user's piece categories file. */
export function getPieceCategoriesPath(cwd: string): string {
const config = loadConfig(cwd);
if (config.pieceCategoriesFile) {
return config.pieceCategoriesFile;
const pieceCategoriesFile = resolveConfigValue(cwd, 'pieceCategoriesFile');
if (pieceCategoriesFile) {
return pieceCategoriesFile;
}
return getDefaultPieceCategoriesPath();
}

View File

@ -16,11 +16,11 @@ import {
getBuiltinPiecesDir,
isPathSafe,
} from '../paths.js';
import { loadConfig } from '../loadConfig.js';
import { resolveConfigValue } from '../resolveConfigValue.js';
/** Get all allowed base directories for persona prompt files */
function getAllowedPromptBases(cwd: string): string[] {
const lang = loadConfig(cwd).language;
const lang = resolveConfigValue(cwd, 'language');
return [
getGlobalPersonasDir(),
getGlobalPiecesDir(),

View File

@ -13,7 +13,7 @@ import { z } from 'zod/v4';
import { getPieceCategoriesPath } from '../global/pieceCategories.js';
import { getLanguageResourcesDir } from '../../resources/index.js';
import { listBuiltinPieceNames } from './pieceResolver.js';
import { loadConfig } from '../loadConfig.js';
import { resolveConfigValues } from '../resolveConfigValue.js';
import type { PieceWithSource } from './pieceResolver.js';
const CategoryConfigSchema = z.object({
@ -233,7 +233,7 @@ function resolveOthersCategoryName(defaultConfig: ParsedCategoryConfig, userConf
* Returns null if file doesn't exist or has no piece_categories.
*/
export function loadDefaultCategories(cwd: string): CategoryConfig | null {
const lang = loadConfig(cwd).language;
const { language: lang } = resolveConfigValues(cwd, ['language']);
const filePath = join(getLanguageResourcesDir(lang), 'piece-categories.yaml');
const parsed = loadCategoryConfigFromPath(filePath, filePath);
@ -256,7 +256,7 @@ export function loadDefaultCategories(cwd: string): CategoryConfig | null {
/** Get the path to the builtin default categories file. */
export function getDefaultCategoriesPath(cwd: string): string {
const lang = loadConfig(cwd).language;
const { language: lang } = resolveConfigValues(cwd, ['language']);
return join(getLanguageResourcesDir(lang), 'piece-categories.yaml');
}
@ -378,7 +378,7 @@ export function buildCategorizedPieces(
config: CategoryConfig,
cwd: string,
): CategorizedPieces {
const globalConfig = loadConfig(cwd);
const globalConfig = resolveConfigValues(cwd, ['enableBuiltinPieces', 'disabledBuiltins']);
const ignoreMissing = new Set<string>();
if (globalConfig.enableBuiltinPieces === false) {
for (const name of listBuiltinPieceNames(cwd, { includeDisabled: true })) {

View File

@ -11,7 +11,7 @@ import { parse as parseYaml } from 'yaml';
import type { z } from 'zod';
import { PieceConfigRawSchema, PieceMovementRawSchema } from '../../../core/models/index.js';
import type { PieceConfig, PieceMovement, PieceRule, OutputContractEntry, OutputContractItem, LoopMonitorConfig, LoopMonitorJudge, ArpeggioMovementConfig, ArpeggioMergeMovementConfig, TeamLeaderConfig } from '../../../core/models/index.js';
import { loadConfig } from '../loadConfig.js';
import { resolveConfigValue } from '../resolveConfigValue.js';
import {
type PieceSections,
type FacetResolutionContext,
@ -439,7 +439,7 @@ export function loadPieceFromFile(filePath: string, projectDir: string): PieceCo
const pieceDir = dirname(filePath);
const context: FacetResolutionContext = {
lang: loadConfig(projectDir).language,
lang: resolveConfigValue(projectDir, 'language'),
projectDir,
};

View File

@ -10,7 +10,7 @@ import { join, resolve, isAbsolute } from 'node:path';
import { homedir } from 'node:os';
import type { PieceConfig, PieceMovement, InteractiveMode } from '../../../core/models/index.js';
import { getGlobalPiecesDir, getBuiltinPiecesDir, getProjectConfigDir } from '../paths.js';
import { loadConfig } from '../loadConfig.js';
import { resolveConfigValues } from '../resolveConfigValue.js';
import { createLogger, getErrorMessage } from '../../../shared/utils/index.js';
import { loadPieceFromFile } from './pieceParser.js';
@ -24,7 +24,7 @@ export interface PieceWithSource {
}
export function listBuiltinPieceNames(cwd: string, options?: { includeDisabled?: boolean }): string[] {
const config = loadConfig(cwd);
const config = resolveConfigValues(cwd, ['language', 'disabledBuiltins']);
const lang = config.language;
const dir = getBuiltinPiecesDir(lang);
const disabled = options?.includeDisabled ? undefined : (config.disabledBuiltins ?? []);
@ -37,7 +37,7 @@ export function listBuiltinPieceNames(cwd: string, options?: { includeDisabled?:
/** Get builtin piece by name */
export function getBuiltinPiece(name: string, projectCwd: string): PieceConfig | null {
const config = loadConfig(projectCwd);
const config = resolveConfigValues(projectCwd, ['enableBuiltinPieces', 'language', 'disabledBuiltins']);
if (config.enableBuiltinPieces === false) return null;
const lang = config.language;
const disabled = config.disabledBuiltins ?? [];
@ -373,7 +373,7 @@ function* iteratePieceDir(
/** Get the 3-layer directory list (builtin → user → project-local) */
function getPieceDirs(cwd: string): { dir: string; source: PieceSource; disabled?: string[] }[] {
const config = loadConfig(cwd);
const config = resolveConfigValues(cwd, ['enableBuiltinPieces', 'language', 'disabledBuiltins']);
const disabled = config.disabledBuiltins ?? [];
const lang = config.language;
const dirs: { dir: string; source: PieceSource; disabled?: string[] }[] = [];