Skip to content

Commit 4d30501

Browse files
committed
make sure the import has filePath now
1 parent 7b52dbe commit 4d30501

1 file changed

Lines changed: 49 additions & 8 deletions

File tree

apps/obsidian/src/utils/importNodes.ts

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ const updateMarkdownAssetLinks = ({
647647
}
648648
}
649649

650-
// Only resolve to files under import/{spaceName}/ so we don't point at the wrong vault's files; leave other links unchanged so they resolve from this folder when the target is created
650+
// Only resolve to files under import/{spaceName}/ so we don't point at the wrong vault's files
651651
const resolvedFile = app.metadataCache.getFirstLinkpathDest(
652652
linkPath,
653653
targetFile.path,
@@ -665,7 +665,21 @@ const updateMarkdownAssetLinks = ({
665665
return `[[${linkText}]]`;
666666
}
667667

668-
// No resolved file in import folder: keep link as-is. Using ./ prefix breaks Obsidian (getParentPrefix null, "Folder already exists"); where new notes are created is controlled by app settings.
668+
// Unresolved (dead) link from another vault: rewrite so that when the user creates the file from this link, it is created under import/{vaultName}/ in the same relative position as in the source vault
669+
if (importFolder && originalNodePath) {
670+
// Vault-relative link (e.g. "Discourse Nodes/EVD - no relation testing") -> use as-is. Path-from-current-file (e.g. "EVD - no relation testing") -> resolve relative to source note dir
671+
const canonicalSourcePath =
672+
linkPath.includes("/") && !linkPath.startsWith(".") && !linkPath.startsWith("/")
673+
? normalizePathForLookup(linkPath)
674+
: (getCanonicalFromOriginalNote(linkPath) ??
675+
normalizePathForLookup(linkPath));
676+
const linkUnderImport = `${importFolder}/${canonicalSourcePath}`;
677+
if (alias) {
678+
return `[[${linkUnderImport}|${alias}]]`;
679+
}
680+
return `[[${linkUnderImport}]]`;
681+
}
682+
669683
return match;
670684
},
671685
);
@@ -939,6 +953,15 @@ const sanitizeFileName = (fileName: string): string => {
939953
.trim();
940954
};
941955

956+
/** Sanitize each path segment for use under import folder (preserves source vault folder structure). */
957+
const sanitizePathForImport = (path: string): string => {
958+
return path
959+
.split("/")
960+
.map((segment) => sanitizeFileName(segment))
961+
.filter(Boolean)
962+
.join("/");
963+
};
964+
942965
type ParsedFrontmatter = {
943966
nodeTypeId?: string;
944967
nodeInstanceId?: string;
@@ -1209,11 +1232,13 @@ export const importSelectedNodes = async ({
12091232
content,
12101233
createdAt: contentCreatedAt,
12111234
modifiedAt: contentModifiedAt,
1212-
filePath,
1235+
filePath: contentFilePath,
12131236
} = nodeContent;
12141237
const createdAt = node.createdAt ?? contentCreatedAt;
12151238
const modifiedAt = node.modifiedAt ?? contentModifiedAt;
1216-
const originalNodePath: string | undefined = node.filePath;
1239+
// Use source vault path from Content direct variant metadata for wikilink rewriting and asset placement
1240+
const originalNodePath: string | undefined =
1241+
contentFilePath ?? node.filePath;
12171242

12181243
// Sanitize file name
12191244
const sanitizedFileName = sanitizeFileName(fileName);
@@ -1223,13 +1248,29 @@ export const importSelectedNodes = async ({
12231248
// Update existing file - use its current path
12241249
finalFilePath = existingFile.path;
12251250
} else {
1226-
// Create new file in the import folder
1227-
finalFilePath = `${importFolderPath}/${sanitizedFileName}.md`;
1251+
// Preserve source vault folder structure under import/{vaultName} when we have filePath from Content
1252+
const pathUnderImport =
1253+
contentFilePath && contentFilePath.includes("/")
1254+
? sanitizePathForImport(contentFilePath)
1255+
: `${sanitizedFileName}.md`;
1256+
finalFilePath = `${importFolderPath}/${pathUnderImport}`;
1257+
1258+
// Ensure parent folder exists (e.g. import/VaultName/Discourse Nodes)
1259+
const parentDir = finalFilePath.replace(/\/[^/]*$/, "");
1260+
if (parentDir !== importFolderPath) {
1261+
const folderExists =
1262+
await plugin.app.vault.adapter.exists(parentDir);
1263+
if (!folderExists) {
1264+
await plugin.app.vault.createFolder(parentDir);
1265+
}
1266+
}
12281267

12291268
// Check if file path already exists (edge case: same title but different nodeInstanceId)
12301269
let counter = 1;
12311270
while (await plugin.app.vault.adapter.exists(finalFilePath)) {
1232-
finalFilePath = `${importFolderPath}/${sanitizedFileName} (${counter}).md`;
1271+
const baseWithoutExt = finalFilePath.replace(/\.md$/i, "");
1272+
const base = baseWithoutExt.replace(/\s*\(\d+\)$/, "");
1273+
finalFilePath = `${base} (${counter}).md`;
12331274
counter++;
12341275
}
12351276
}
@@ -1242,7 +1283,7 @@ export const importSelectedNodes = async ({
12421283
sourceSpaceId: spaceId,
12431284
sourceSpaceUri: spaceUri,
12441285
rawContent: content,
1245-
originalFilePath: filePath,
1286+
originalFilePath: contentFilePath,
12461287
filePath: finalFilePath,
12471288
importedCreatedAt: createdAt,
12481289
importedModifiedAt: modifiedAt,

0 commit comments

Comments
 (0)