refactor: remove outdated method signature

This commit is contained in:
linonetwo 2025-12-06 02:10:54 +08:00
parent a1f99632a6
commit a90ccdf44e
8 changed files with 188 additions and 145 deletions

View file

@ -482,7 +482,7 @@ export class Wiki implements IWikiService {
this.logProgress(i18n.t('AddWorkspace.WikiTemplateCopyCompleted') + newWikiPath);
}
public async createSubWiki(parentFolderLocation: string, folderName: string, _subWikiFolderName: string, _mainWikiPath: string, _tagName = '', onlyLink = false): Promise<void> {
public async createSubWiki(parentFolderLocation: string, folderName: string, onlyLink = false): Promise<void> {
this.logProgress(i18n.t('AddWorkspace.StartCreatingSubWiki'));
const newWikiPath = path.join(parentFolderLocation, folderName);
if (!(await pathExists(parentFolderLocation))) {
@ -625,14 +625,7 @@ export class Wiki implements IWikiService {
await gitService.clone(gitRepoUrl, path.join(parentFolderLocation, wikiFolderName), gitUserInfo);
}
public async cloneSubWiki(
parentFolderLocation: string,
wikiFolderName: string,
_mainWikiPath: string,
gitRepoUrl: string,
gitUserInfo: IGitUserInfos,
_tagName = '',
): Promise<void> {
public async cloneSubWiki(parentFolderLocation: string, wikiFolderName: string, gitRepoUrl: string, gitUserInfo: IGitUserInfos): Promise<void> {
this.logProgress(i18n.t('AddWorkspace.StartCloningSubWiki'));
const newWikiPath = path.join(parentFolderLocation, wikiFolderName);
if (!(await pathExists(parentFolderLocation))) {

View file

@ -26,14 +26,7 @@ export interface IWikiService {
/** return true if wiki does existed and folder is a valid tiddlywiki folder, return error message (a string) if there is an error checking wiki existence */
checkWikiExist(workspace: IWorkspace, options?: { shouldBeMainWiki?: boolean; showDialog?: boolean }): Promise<string | true>;
checkWikiStartLock(wikiFolderLocation: string): boolean;
cloneSubWiki(
parentFolderLocation: string,
wikiFolderName: string,
mainWikiPath: string,
gitRepoUrl: string,
gitUserInfo: IGitUserInfos,
tagName?: string,
): Promise<void>;
cloneSubWiki(parentFolderLocation: string, wikiFolderName: string, gitRepoUrl: string, gitUserInfo: IGitUserInfos): Promise<void>;
cloneWiki(parentFolderLocation: string, wikiFolderName: string, gitRepoUrl: string, gitUserInfo: IGitUserInfos): Promise<void>;
copyWikiTemplate(newFolderPath: string, folderName: string): Promise<void>;
/**
@ -43,7 +36,7 @@ export interface IWikiService {
* @param mainWikiToLink
* @param onlyLink not creating new subwiki folder, just link existed subwiki folder to main wiki folder
*/
createSubWiki(parentFolderLocation: string, folderName: string, subWikiFolderName: string, mainWikiPath: string, tagName?: string, onlyLink?: boolean): Promise<void>;
createSubWiki(parentFolderLocation: string, folderName: string, onlyLink?: boolean): Promise<void>;
ensureWikiExist(wikiPath: string, shouldBeMainWiki: boolean): Promise<void>;
extractWikiHTML(htmlWikiPath: string, saveWikiFolderPath: string): Promise<string | undefined>;
/**

View file

@ -7,6 +7,7 @@ import fs from 'fs';
import path from 'path';
import type { IFileInfo } from 'tiddlywiki';
import type { Tiddler, Wiki } from 'tiddlywiki';
import { isWikiWorkspaceWithRouting, matchTiddlerToWorkspace } from './routingUtilities';
import { isFileLockError } from './utilities';
/**
@ -83,32 +84,10 @@ export class FileSystemAdaptor {
const allWorkspaces = await workspace.getWorkspacesAsList();
// Include both main workspace and sub-wikis for tag-based routing or filter-based routing
const isWikiWorkspaceWithRouting = (workspaceItem: IWorkspace): workspaceItem is IWikiWorkspace => {
if (!('wikiFolderLocation' in workspaceItem) || !workspaceItem.wikiFolderLocation) {
return false;
}
// Check if workspace has routing config (either tagNames or fileSystemPathFilter)
const hasRoutingConfig = ('tagNames' in workspaceItem && workspaceItem.tagNames.length > 0) ||
('fileSystemPathFilterEnable' in workspaceItem && workspaceItem.fileSystemPathFilterEnable && 'fileSystemPathFilter' in workspaceItem &&
workspaceItem.fileSystemPathFilter);
if (!hasRoutingConfig) {
return false;
}
// Include if it's the main workspace
const isMain = workspaceItem.id === currentWorkspace.id;
// Include if it's a sub-wiki of the current main workspace
const isSubWiki = 'isSubWiki' in workspaceItem &&
workspaceItem.isSubWiki &&
workspaceItem.mainWikiID === currentWorkspace.id;
return isMain || isSubWiki;
};
const workspacesWithRouting = allWorkspaces.filter(isWikiWorkspaceWithRouting).sort(workspaceSorter);
// Filter to wiki workspaces with routing config (main or sub-wikis)
const workspacesWithRouting = allWorkspaces
.filter((w: IWorkspace): w is IWikiWorkspace => isWikiWorkspaceWithRouting(w, currentWorkspace.id))
.sort(workspaceSorter);
this.wikisWithRouting = workspacesWithRouting;
} catch (error) {
@ -163,7 +142,7 @@ export class FileSystemAdaptor {
}
// Find matching workspace using the routing logic
const matchingWiki = this.matchTitleToWiki(title, tags);
const matchingWiki = matchTiddlerToWorkspace(title, tags, this.wikisWithRouting, $tw.wiki, $tw.rootWidget);
// Determine the target directory based on routing
// Sub-wikis store tiddlers directly in their root folder (not in /tiddlers subfolder)
@ -209,56 +188,6 @@ export class FileSystemAdaptor {
}
}
/**
* Match a tiddler to a workspace based on routing rules.
* Checks workspaces in order (priority) and returns the first match.
*
* For each workspace, checks in order (any match wins):
* 1. Direct tag match (including if tiddler's title IS one of the tagNames - it's a "tag tiddler")
* 2. If includeTagTree is enabled, use in-tagtree-of filter for recursive tag matching
* 3. If fileSystemPathFilterEnable is enabled, use custom filter expressions (one per line, any match wins)
*/
protected matchTitleToWiki(title: string, tags: string[]): IWikiWorkspace | undefined {
for (const wiki of this.wikisWithRouting) {
// Direct tag match - check if any of the tiddler's tags match any of the wiki's tagNames
// Also check if the tiddler's title IS one of the tagNames (it's a "tag tiddler" that defines that tag)
if (wiki.tagNames.length > 0) {
const hasMatchingTag = wiki.tagNames.some(tagName => tags.includes(tagName));
const isTitleATagName = wiki.tagNames.includes(title);
if (hasMatchingTag || isTitleATagName) {
return wiki;
}
}
// Tag tree match if enabled - check all tagNames
if (wiki.includeTagTree && wiki.tagNames.length > 0) {
for (const tagName of wiki.tagNames) {
const result = $tw.wiki.filterTiddlers(
`[in-tagtree-of:inclusive<tagName>]`,
$tw.rootWidget.makeFakeWidgetWithVariables({ tagName }),
$tw.wiki.makeTiddlerIterator([title]),
);
if (result.length > 0) {
return wiki;
}
}
}
// Custom filter match if enabled
if (wiki.fileSystemPathFilterEnable && wiki.fileSystemPathFilter) {
// Split by newlines and try each filter
const filters = wiki.fileSystemPathFilter.split('\n').map(f => f.trim()).filter(f => f.length > 0);
for (const filter of filters) {
const result = $tw.wiki.filterTiddlers(filter, undefined, $tw.wiki.makeTiddlerIterator([title]));
if (result.length > 0) {
return wiki;
}
}
}
}
return undefined;
}
/**
* Generate file info for sub-wiki directory
* Handles symlinks correctly across platforms (Windows junctions and Linux symlinks)

View file

@ -12,20 +12,19 @@ Version: TiddlyWiki v5.3.x (as of 2025-10-24)
!!! Key Modifications
!!!! 1. Dynamic Workspace Information via IPC
!!!! 1. Dynamic Workspace Information via Worker Services
* ''Original'': Uses static `$:/config/FileSystemPaths` tiddler for routing
* ''Modified'': Queries workspace information from main process via worker threads IPC
* ''Modified'': Queries workspace information from main process via worker thread services
* ''Reason'': Eliminates need for complex string manipulation of `FileSystemPaths` configuration
```typescript
// Added: Worker service caller integration
import { callMainProcessService } from '@services/wiki/wikiWorker/workerServiceCaller';
import type { IWorkspace } from '@services/workspaces/interface';
// Added: Worker service integration
import { workspace } from '@services/wiki/wikiWorker/services';
// Added: Methods to query workspace dynamically
private async getCurrentWorkspace(): Promise<IWorkspace | undefined>
private async getSubWikis(currentWorkspace: IWorkspace): Promise<IWorkspace[]>
// Queries workspace data dynamically
const currentWorkspace = await workspace.get(this.workspaceID);
const allWorkspaces = await workspace.getWorkspacesAsList();
```
!!!! 2. Tag-Based and Filter-Based Sub-Wiki Routing
@ -40,52 +39,42 @@ private async getSubWikis(currentWorkspace: IWorkspace): Promise<IWorkspace[]>
* ''Important'': Always recalculates path on save to handle tag changes - old `fileInfo` only used for cleanup
* ''Implementation'':
** Checks tiddler tags/filters against sub-workspace routing rules (in priority order)
** Routes matching tiddlers to sub-wiki's `tiddlers` folder
** Routes matching tiddlers directly to sub-wiki's root folder (not `/tiddlers` subfolder)
** Falls back to TiddlyWiki's `$:/config/FileSystemPaths` logic for non-matching tiddlers
** Loads sub-wikis cache on initialization
** Currently loads sub-wikis once, future enhancements can watch for workspace changes
```typescript
// Modified: getTiddlerFileInfo is now async (safe since callers only use callback)
async getTiddlerFileInfo(tiddler: Tiddler, callback: IFileSystemAdaptorCallback): Promise<void> {
// Direct async/await instead of nested void IIFE
const currentWorkspace = await this.getCurrentWorkspace();
const subWikis = this.getSubWikis(); // Uses cache instead of IPC
const matchingSubWiki = subWikis.find(...);
// Uses pure functions from routingUtils.ts for matching logic
import { matchTiddlerToWorkspace, isWikiWorkspaceWithRouting } from './routingUtils';
if (matchingSubWiki) {
this.routeToSubWorkspace(...);
} else {
this.useDefaultFileSystemLogic(...);
}
}
// Match tiddler to workspace
const matchingWiki = matchTiddlerToWorkspace(title, tags, this.wikisWithRouting, $tw.wiki, $tw.rootWidget);
// Added: Caching mechanism
private subWikis: IWorkspace[] = [];
private async initializeSubWikisCache(): Promise<void> {
await this.updateSubWikisCache();
}
private async updateSubWikisCache(): Promise<void> {
// Load sub-wikis once and cache them
const allWorkspaces = await callMainProcessService(...);
this.subWikis = allWorkspaces.filter(...);
// Generate file info based on routing result
if (matchingWiki) {
return this.generateSubWikiFileInfo(tiddler, matchingWiki);
} else {
return this.generateDefaultFileInfo(tiddler);
}
```
!!!! 3. Separated Routing Logic
!!!! 3. Separated Routing Logic into Pure Functions
* ''Added'': `routeToSubWorkspace()` method for sub-wiki routing
* ''Added'': `useDefaultFileSystemLogic()` method for standard routing
* ''Reason'': Better code organization and maintainability
* ''Added'': `routingUtils.ts` with pure functions for routing logic:
** `isWikiWorkspaceWithRouting()` - checks if workspace has routing config
** `matchTiddlerToWorkspace()` - matches tiddler to workspace based on routing rules
** `matchesDirectTag()` - checks direct tag match
** `matchesTagTree()` - checks tag tree match using in-tagtree-of filter
** `matchesCustomFilter()` - checks custom filter match
* ''Reason'': Better code organization, testability, and maintainability
!!! Future Compatibility Notes
When updating from upstream TiddlyWiki filesystem adaptor:
# Review changes to core methods: `saveTiddler`, `deleteTiddler`, `getTiddlerInfo`
# Preserve our IPC-based workspace querying logic
# Preserve our worker-service-based workspace querying logic
# Preserve tag-based routing in `getTiddlerFileInfo`
# Update type definitions if TiddlyWiki's FileInfo interface changes
# Test sub-wiki routing functionality after merge
@ -96,7 +85,7 @@ When validating this adaptor:
* [ ] Tiddlers with matching tags route to correct sub-wiki
* [ ] Tiddlers without matching tags use default FileSystemPaths
* [ ] IPC communication works correctly in worker thread
* [ ] Worker service communication works correctly
* [ ] Error handling falls back gracefully
* [ ] File operations (save/delete) work in both main and sub-wikis
* [ ] Workspace ID caching reduces IPC overhead
* [ ] Workspace caching reduces service call overhead

View file

@ -0,0 +1,148 @@
import type { IWikiWorkspace, IWorkspace } from '@services/workspaces/interface';
/**
* Check if a workspace has routing configuration (tagNames or fileSystemPathFilter).
*/
export function hasRoutingConfig(workspaceItem: IWorkspace): boolean {
const hasTagNames = 'tagNames' in workspaceItem && Array.isArray(workspaceItem.tagNames) && workspaceItem.tagNames.length > 0;
const hasFilter = 'fileSystemPathFilterEnable' in workspaceItem &&
workspaceItem.fileSystemPathFilterEnable &&
'fileSystemPathFilter' in workspaceItem &&
Boolean(workspaceItem.fileSystemPathFilter);
return hasTagNames || hasFilter;
}
/**
* Check if a workspace is a wiki workspace with routing configuration.
* This filters to wiki workspaces that are either the main workspace or sub-wikis of it.
*/
export function isWikiWorkspaceWithRouting(
workspaceItem: IWorkspace,
mainWorkspaceId: string,
): workspaceItem is IWikiWorkspace {
// Must have wiki folder location
if (!('wikiFolderLocation' in workspaceItem) || !workspaceItem.wikiFolderLocation) {
return false;
}
// Must have routing config
if (!hasRoutingConfig(workspaceItem)) {
return false;
}
// Include if it's the main workspace
const isMain = workspaceItem.id === mainWorkspaceId;
// Include if it's a sub-wiki of the current main workspace
const isSubWiki = 'isSubWiki' in workspaceItem &&
workspaceItem.isSubWiki &&
'mainWikiID' in workspaceItem &&
workspaceItem.mainWikiID === mainWorkspaceId;
return isMain || isSubWiki;
}
/**
* Check if a tiddler matches a workspace's direct tag routing.
* Returns true if:
* - Any of the tiddler's tags match any of the workspace's tagNames
* - The tiddler's title IS one of the tagNames (it's a "tag tiddler")
*/
export function matchesDirectTag(
tiddlerTitle: string,
tiddlerTags: string[],
workspaceTagNames: string[],
): boolean {
if (workspaceTagNames.length === 0) {
return false;
}
const hasMatchingTag = workspaceTagNames.some(tagName => tiddlerTags.includes(tagName));
const isTitleATagName = workspaceTagNames.includes(tiddlerTitle);
return hasMatchingTag || isTitleATagName;
}
/**
* Check if a tiddler matches a workspace's tag tree routing.
* Uses TiddlyWiki's in-tagtree-of filter for recursive tag hierarchy matching.
*/
export function matchesTagTree(
tiddlerTitle: string,
workspaceTagNames: string[],
wiki: typeof $tw.wiki,
rootWidget: typeof $tw.rootWidget,
): boolean {
for (const tagName of workspaceTagNames) {
const result = wiki.filterTiddlers(
`[in-tagtree-of:inclusive<tagName>]`,
rootWidget.makeFakeWidgetWithVariables({ tagName }),
wiki.makeTiddlerIterator([tiddlerTitle]),
);
if (result.length > 0) {
return true;
}
}
return false;
}
/**
* Check if a tiddler matches a workspace's custom filter routing.
* Filters are separated by newlines; any match wins.
*/
export function matchesCustomFilter(
tiddlerTitle: string,
filterExpression: string,
wiki: typeof $tw.wiki,
): boolean {
const filters = filterExpression.split('\n').map(f => f.trim()).filter(f => f.length > 0);
for (const filter of filters) {
const result = wiki.filterTiddlers(filter, undefined, wiki.makeTiddlerIterator([tiddlerTitle]));
if (result.length > 0) {
return true;
}
}
return false;
}
/**
* Match a tiddler to a workspace based on routing rules.
* Checks workspaces in order (priority) and returns the first match.
*
* For each workspace, checks in order (any match wins):
* 1. Direct tag match (including if tiddler's title IS one of the tagNames)
* 2. If includeTagTree is enabled, use in-tagtree-of filter for recursive tag matching
* 3. If fileSystemPathFilterEnable is enabled, use custom filter expressions
*/
export function matchTiddlerToWorkspace(
tiddlerTitle: string,
tiddlerTags: string[],
workspacesWithRouting: IWikiWorkspace[],
wiki: typeof $tw.wiki,
rootWidget: typeof $tw.rootWidget,
): IWikiWorkspace | undefined {
for (const workspace of workspacesWithRouting) {
// 1. Direct tag match
if (matchesDirectTag(tiddlerTitle, tiddlerTags, workspace.tagNames)) {
return workspace;
}
// 2. Tag tree match (if enabled)
if (workspace.includeTagTree && workspace.tagNames.length > 0) {
if (matchesTagTree(tiddlerTitle, workspace.tagNames, wiki, rootWidget)) {
return workspace;
}
}
// 3. Custom filter match (if enabled)
if (workspace.fileSystemPathFilterEnable && workspace.fileSystemPathFilter) {
if (matchesCustomFilter(tiddlerTitle, workspace.fileSystemPathFilter, wiki)) {
return workspace;
}
}
}
return undefined;
}

View file

@ -73,10 +73,8 @@ export function useCloneWiki(
await window.service.wiki.cloneSubWiki(
form.parentFolderLocation,
form.wikiFolderName,
form.mainWikiToLink.wikiFolderLocation,
form.gitRepoUrl,
form.gitUserInfo!,
form.tagNames[0] ?? '',
);
}
await callWikiInitialization(newWorkspaceConfig, wikiCreationMessageSetter, t, form.gitUserInfo, { from: WikiCreationMethod.Clone });

View file

@ -81,14 +81,7 @@ export function useExistedWiki(
);
}
await window.service.wiki.ensureWikiExist(form.wikiFolderLocation, false);
await window.service.wiki.createSubWiki(
parentFolderLocationForExistedFolder,
wikiFolderNameForExistedFolder,
'subwiki',
form.mainWikiToLink.wikiFolderLocation,
form.tagNames[0] ?? '',
true,
);
await window.service.wiki.createSubWiki(parentFolderLocationForExistedFolder, wikiFolderNameForExistedFolder, true);
}
await callWikiInitialization(newWorkspaceConfig, wikiCreationMessageSetter, t, form.gitUserInfo, { from: WikiCreationMethod.LoadExisting });
} catch (error) {

View file

@ -80,7 +80,7 @@ export function useNewWiki(
await window.service.wiki.copyWikiTemplate(form.parentFolderLocation, form.wikiFolderName);
}
} else {
await window.service.wiki.createSubWiki(form.parentFolderLocation, form.wikiFolderName, 'subwiki', form.mainWikiToLink.wikiFolderLocation, form.tagNames[0] ?? '');
await window.service.wiki.createSubWiki(form.parentFolderLocation, form.wikiFolderName);
}
await callWikiInitialization(newWorkspaceConfig, wikiCreationMessageSetter, t, form.gitUserInfo, { notClose: options?.notClose, from: WikiCreationMethod.Create });
} catch (error) {