Compare commits

...

7 commits

Author SHA1 Message Date
lin onetwo
d242d9a63c v0.13.0-prerelease10 2025-11-24 17:37:22 +08:00
lin onetwo
23aa09eaac fix: nsfw can't watch parent folder, just ignore .git in symlink sub wiki folder before any side-effect 2025-11-24 17:37:01 +08:00
lin onetwo
5310365a8a v0.13.0-prerelease9 2025-11-24 16:54:30 +08:00
lin onetwo
f3454b71e5 fix: shouldn't watch .git folder for file system plugin 2025-11-24 16:54:18 +08:00
lin onetwo
e2634e9aa5 v0.13.0-prerelease8 2025-11-24 16:05:56 +08:00
lin onetwo
e00addeffc fix: unstable test 2025-11-24 16:05:38 +08:00
lin onetwo
c0a48ce727 Update release.yml 2025-11-24 16:04:11 +08:00
4 changed files with 57 additions and 20 deletions

View file

@ -134,7 +134,9 @@ jobs:
draft: true
generate_release_notes: true
files: |
${{ matrix.platform == 'win' && 'out/make/**/*.{exe,msix,appx}' || 'out/make/**/*' }}
${{ matrix.platform == 'win' && 'out/make/**/*.exe
out/make/**/*.msix
out/make/**/*.appx' || 'out/make/**/*' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -83,6 +83,7 @@ Feature: Filesystem Plugin
# Modify the tiddler file externally - need to preserve .tid format with metadata
When I modify file "{tmpDir}/SubWiki/TestTiddlerTitle.tid" to contain "Content modified in SubWiki symlink"
# Wait for watch-fs to detect the change
And I wait for 1 seconds for "watch-fs to detect file change"
Then I wait for tiddler "TestTiddlerTitle" to be updated by watch-fs
And I wait for 2 seconds
# Verify the modified content appears in the wiki

View file

@ -2,7 +2,7 @@
"name": "tidgi",
"productName": "TidGi",
"description": "Customizable personal knowledge-base with Github as unlimited storage and blogging platform.",
"version": "0.13.0-prerelease7",
"version": "0.13.0-prerelease10",
"license": "MPL 2.0",
"packageManager": "pnpm@10.18.2",
"scripts": {

View file

@ -63,8 +63,10 @@ export class WatchFileSystemAdaptor extends FileSystemAdaptor {
private inverseFilesIndex: InverseFilesIndex = new InverseFilesIndex();
/** NSFW watcher instance for main wiki */
private watcher: nsfw.NSFW | undefined;
/** Base excluded paths (permanent) */
/** Base excluded paths (permanent) - absolute paths for main wiki */
private baseExcludedPaths: string[] = [];
/** Excluded path patterns that apply to all wikis (main and sub-wikis) */
private readonly excludedPathPatterns: string[] = ['.git', 'node_modules', '.DS_Store'];
/**
* Track pending file deletions to handle git revert/checkout scenarios.
* Maps absolute file path to deletion timer.
@ -242,12 +244,11 @@ export class WatchFileSystemAdaptor extends FileSystemAdaptor {
// Initialize inverse index from boot.files
this.initializeInverseFilesIndex();
// Setup base excluded paths (permanent exclusions)
// Setup base excluded paths (permanent exclusions) for main wiki
// Only include paths that are subdirectories of watchPathBase
this.baseExcludedPaths = [
path.join(this.watchPathBase, 'subwiki'),
path.join(this.watchPathBase, '.git'),
path.join(this.watchPathBase, '$__StoryList'),
path.join(this.watchPathBase, '.DS_Store'),
];
// Setup nsfw watcher
@ -262,7 +263,7 @@ export class WatchFileSystemAdaptor extends FileSystemAdaptor {
errorCallback: (error) => {
this.logger.alert('WatchFileSystemAdaptor NSFW error:', error);
},
// Start with base excluded paths
// Start with base excluded paths - nsfw will filter these at the native level
// @ts-expect-error - nsfw types are incorrect, it accepts string[] not just [string]
excludedPaths: [...this.baseExcludedPaths],
},
@ -320,6 +321,9 @@ export class WatchFileSystemAdaptor extends FileSystemAdaptor {
errorCallback: (error) => {
this.logger.alert(`WatchFileSystemAdaptor NSFW error for sub-wiki ${subWiki.name}:`, error);
},
// Exclude common patterns for sub-wikis (e.g., .git, node_modules)
// @ts-expect-error - nsfw types are incorrect, it accepts string[] not just [string]
excludedPaths: this.excludedPathPatterns.map(pattern => path.join(subWikiPath, pattern)),
},
);
@ -416,6 +420,23 @@ export class WatchFileSystemAdaptor extends FileSystemAdaptor {
this.pendingDeletions.set(fileAbsolutePath, timer);
}
/**
* Check if a path contains any excluded pattern (like .git, node_modules)
* This checks all parts of the path, so it will catch:
* - Direct .git directories: wiki/.git/config
* - Sub-wiki .git directories: wiki/tiddlers/subwiki/.git/index.lock
* - Symlinked .git directories: wiki/tiddlers/link-to-subwiki/.git/config
* @param filePath File or directory path to check
* @returns true if path should be excluded
*/
private shouldExcludeByPattern(filePath: string): boolean {
// Check if any part of the path contains excluded patterns
return this.excludedPathPatterns.some(pattern => {
const pathParts = filePath.split(path.sep);
return pathParts.includes(pattern);
});
}
/**
* Handle NSFW file system change events
*/
@ -436,7 +457,28 @@ export class WatchFileSystemAdaptor extends FileSystemAdaptor {
// Compute absolute path
const fileAbsolutePath = path.join(directory, fileName);
// Check if this file is in our exclusion list - if so, skip processing
// Early check: skip files in excluded patterns (e.g., .git, node_modules)
if (this.shouldExcludeByPattern(fileAbsolutePath) || this.shouldExcludeByPattern(directory)) {
this.logger.log(`WatchFileSystemAdaptor Skipping file in excluded pattern directory: ${fileAbsolutePath}`);
continue;
}
// Early check: skip if it's a directory (nsfw sometimes reports directory changes)
// Must check before processing to avoid creating tiddlers for .git files
if (action === nsfw.actions.CREATED || action === nsfw.actions.MODIFIED) {
try {
const stats = fs.statSync(fileAbsolutePath);
if (stats.isDirectory()) {
this.logger.log(`WatchFileSystemAdaptor Skipping directory: ${fileAbsolutePath}`);
continue;
}
} catch {
// File might have been deleted already, skip
continue;
}
}
// Check if this file is in our dynamic exclusion list (for files being saved/deleted by the app)
const subWikiForExclusion = this.inverseFilesIndex.getSubWikiForFile(fileAbsolutePath);
const isExcluded = subWikiForExclusion
? this.inverseFilesIndex.isSubWikiFileExcluded(subWikiForExclusion.id, fileAbsolutePath)
@ -462,18 +504,6 @@ export class WatchFileSystemAdaptor extends FileSystemAdaptor {
// Handle different event types
if (action === nsfw.actions.CREATED || action === nsfw.actions.MODIFIED) {
// Skip if it's a directory (nsfw sometimes reports directory changes)
try {
const stats = fs.statSync(fileAbsolutePath);
if (stats.isDirectory()) {
this.logger.log(`WatchFileSystemAdaptor Skipping directory: ${fileAbsolutePath}`);
continue;
}
} catch {
// File might have been deleted already, skip
continue;
}
// Cancel any pending deletion for this file (e.g., git revert scenario)
this.cancelPendingDeletion(fileAbsolutePath);
@ -595,10 +625,14 @@ export class WatchFileSystemAdaptor extends FileSystemAdaptor {
const isCreatingNewNonTiddlerFile = changeType === 'add' && !fs.existsSync(metaFileAbsolutePath) && !ignoredExtension.includes(fileExtension.slice(1));
if (isCreatingNewNonTiddlerFile) {
const createdTime = $tw.utils.formatDateString(new Date(), '[UTC]YYYY0MM0DD0hh0mm0ss0XXX');
// Exclude the .meta file before creating it to avoid triggering another watch event
this.excludeFile(metaFileAbsolutePath);
fs.writeFileSync(
metaFileAbsolutePath,
`caption: ${fileNameBase}\ncreated: ${createdTime}\nmodified: ${createdTime}\ntitle: ${fileName}\ntype: ${fileMimeType}\n`,
);
// Schedule re-inclusion after delay
this.scheduleFileInclusion(metaFileAbsolutePath);
// After creating .meta, continue to process the file normally
// TiddlyWiki will detect the .meta file on next event
}