mirror of
https://github.com/uppinote20/obsidian-auto-note-importer.git
synced 2026-07-22 05:48:42 +00:00
refactor: route exhaustiveness guards through assertNever
- sync mode/scope, conflict mode, setup-requirement kind, credential type 5개 throw 가드 → assertNever(x, label) (에러 메시지 보존) - migration.ts: 가짜 `type as never` → 진짜 `credType satisfies never` 가드로 업그레이드 (컴파일타임 exhaustiveness 확보, return undefined 동작 유지) - settings-tab filterMode는 non-throw fallback이라 satisfies never 유지
This commit is contained in:
parent
2181997e27
commit
9c4c7b4f13
4 changed files with 20 additions and 26 deletions
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import { Notice } from "obsidian";
|
||||
import { areValuesEqual } from '../utils';
|
||||
import { areValuesEqual, assertNever } from '../utils';
|
||||
import type { LegacySettings, ConflictInfo, SyncResult, DatabaseProvider } from '../types';
|
||||
|
||||
/**
|
||||
|
|
@ -80,10 +80,8 @@ export class ConflictResolver {
|
|||
case 'manual':
|
||||
return this.resolveManual(conflicts, recordId);
|
||||
|
||||
default: {
|
||||
const _exhaustive: never = this.settings.conflictResolution;
|
||||
throw new Error(`Unknown conflict resolution mode: ${_exhaustive}`);
|
||||
}
|
||||
default:
|
||||
return assertNever(this.settings.conflictResolution, 'Unknown conflict resolution mode');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import { FieldCache } from '../services';
|
|||
import { ConflictResolver } from './conflict-resolver';
|
||||
import { FrontmatterParser, FileWatcher } from '../file-operations';
|
||||
import { parseTemplate, buildMarkdownContent, generateBasesContent, resolveBasesFilePath, collectFieldNames } from '../builders';
|
||||
import { sanitizeFileName, sanitizeSubfolderValue, validateAndSanitizeFilename } from '../utils';
|
||||
import { sanitizeFileName, sanitizeSubfolderValue, validateAndSanitizeFilename, assertNever } from '../utils';
|
||||
|
||||
export interface StatusBarHandle {
|
||||
setText(text: string): void;
|
||||
|
|
@ -126,10 +126,8 @@ export class SyncOrchestrator {
|
|||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
const _exhaustive: never = mode;
|
||||
throw new Error(`Unknown sync mode: ${_exhaustive}`);
|
||||
}
|
||||
default:
|
||||
assertNever(mode, 'Unknown sync mode');
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Unknown error';
|
||||
|
|
@ -166,10 +164,8 @@ export class SyncOrchestrator {
|
|||
return this.collectMarkdownFiles(folder);
|
||||
}
|
||||
|
||||
default: {
|
||||
const _exhaustive: never = scope;
|
||||
throw new Error(`Unknown sync scope: ${_exhaustive}`);
|
||||
}
|
||||
default:
|
||||
return assertNever(scope, 'Unknown sync scope');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import { FolderSuggest, FileSuggest } from './suggest';
|
|||
import { generateId } from '../utils/object-utils';
|
||||
import { validateFolderPath } from '../utils/validation';
|
||||
import { debounce } from '../utils/debounce';
|
||||
import { assertNever } from '../utils/assert';
|
||||
|
||||
/**
|
||||
* Interface for the plugin that the settings tab needs.
|
||||
|
|
@ -1410,10 +1411,8 @@ export class AutoNoteImporterSettingTab extends PluginSettingTab {
|
|||
}
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
const _exhaustive: never = needsSetup.kind;
|
||||
throw new Error(`Unhandled setup requirement kind: ${String(_exhaustive)}`);
|
||||
}
|
||||
default:
|
||||
return assertNever(needsSetup.kind, 'Unhandled setup requirement kind');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1531,10 +1530,8 @@ export class AutoNoteImporterSettingTab extends PluginSettingTab {
|
|||
return [credential.type, credential.integrationToken];
|
||||
case 'custom-api':
|
||||
return [credential.type, credential.baseUrl, credential.authHeader, credential.authValue];
|
||||
default: {
|
||||
const _exhaustive: never = credential;
|
||||
throw new Error(`Unknown credential type: ${String(_exhaustive)}`);
|
||||
}
|
||||
default:
|
||||
return assertNever(credential, 'Unknown credential type');
|
||||
}
|
||||
})();
|
||||
return AutoNoteImporterSettingTab.hashCredentialFingerprint(parts.map(p => p.trim()).join('\0'));
|
||||
|
|
|
|||
|
|
@ -155,7 +155,8 @@ function buildCredentialFromRecord(raw: Record<string, unknown>): Credential | u
|
|||
}
|
||||
|
||||
const base = { id, name: readString(raw, 'name', '') };
|
||||
switch (type as CredentialType) {
|
||||
const credType = type as CredentialType;
|
||||
switch (credType) {
|
||||
case 'airtable':
|
||||
return { ...base, type: 'airtable', apiKey: readString(raw, 'apiKey', '') };
|
||||
case 'seatable':
|
||||
|
|
@ -180,9 +181,11 @@ function buildCredentialFromRecord(raw: Record<string, unknown>): Credential | u
|
|||
authValue: readString(raw, 'authValue', ''),
|
||||
};
|
||||
default: {
|
||||
// Compile-time exhaustiveness guard — unreachable at runtime (CREDENTIAL_TYPES.includes guards above).
|
||||
const _exhaustive: never = type as never;
|
||||
void _exhaustive;
|
||||
// Real compile-time exhaustiveness guard: credType narrows to never once
|
||||
// every CredentialType has a case. Returns undefined (not throw) so a
|
||||
// malformed legacy setting is skipped rather than fatal — the
|
||||
// CREDENTIAL_TYPES.includes() check above already gates the runtime path.
|
||||
credType satisfies never;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue