diff --git a/config.tana.json b/config.tana.json new file mode 100644 index 00000000..e4acd349 --- /dev/null +++ b/config.tana.json @@ -0,0 +1,48 @@ +{ + "enexSources": [ + "./test-template.enex" + ], + "templateFile": "./sampleTemplate.tmpl", + "outputDir": "out", + "keepOriginalHtml": false, + "isMetadataNeeded": false, + "isNotebookNameNeeded": false, + "isZettelkastenNeeded": false, + "useZettelIdAsFilename": false, + "plainTextNotesOnly": false, + "skipLocation": false, + "skipCreationTime": false, + "skipUpdateTime": false, + "skipSourceUrl": false, + "skipWebClips": false, + "skipTags": false, + "useHashTags": false, + "outputFormat": "ObsidianMD", + "taskOutputFormat": "ObsidianMD", + "keepImageSize": "ObsidianMD", + "skipEnexFileNameFromOutputPath": false, + "keepOriginalAmountOfNewlines": false, + "urlEncodeFileNamesAndLinks": false, + "pathSeparator": "/", + "sanitizeResourceNameSpaces": false, + "haveEnexLevelResources": false, + "haveGlobalResources": false, + "useUniqueUnknownFileNames": false, + "replacementChar": "_", + "generateNakedUrls": true, + "logseqSettings": { + "journalNotes": false + }, + "obsidianSettings": { + "omitLinkDisplayName": false + }, + "nestedTags": { + "separatorInEN": "_", + "replaceSeparatorWith": "---", + "replaceSpaceWith": "-" + }, + "resourcesDir": "", + "keepMDCharactersOfENNotes": false, + "monospaceIsCodeBlock": false, + "dateFormat": "YYYY-MM-DD" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ece4f9ed..03a3afb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1818,7 +1818,6 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", - "dev": true, "optional": true, "dependencies": { "@types/node": "*" @@ -2601,7 +2600,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "dev": true, "optional": true }, "node_modules/bottleneck": { @@ -4350,7 +4348,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, + "devOptional": true, "dependencies": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -4439,7 +4437,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, "optional": true }, "node_modules/diff": { @@ -5296,7 +5293,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true + "devOptional": true }, "node_modules/escalade": { "version": "3.1.1", @@ -6483,7 +6480,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dev": true, + "devOptional": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -6596,7 +6593,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "dev": true, "optional": true, "dependencies": { "boolean": "^3.0.1", @@ -6665,7 +6661,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, + "devOptional": true, "dependencies": { "define-properties": "^1.1.3" }, @@ -6852,7 +6848,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, + "devOptional": true, "dependencies": { "get-intrinsic": "^1.1.1" }, @@ -6876,7 +6872,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, + "devOptional": true, "engines": { "node": ">= 0.4" }, @@ -8027,7 +8023,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true + "devOptional": true }, "node_modules/json5": { "version": "2.2.3", @@ -8495,7 +8491,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, "optional": true, "dependencies": { "escape-string-regexp": "^4.0.0" @@ -8508,7 +8503,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "optional": true, "engines": { "node": ">=10" @@ -12833,7 +12827,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, + "devOptional": true, "engines": { "node": ">= 0.4" } @@ -14069,7 +14063,6 @@ "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, "optional": true, "dependencies": { "boolean": "^3.0.1", @@ -14407,7 +14400,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "dev": true, "optional": true }, "node_modules/semver-diff": { @@ -14447,7 +14439,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, "optional": true, "dependencies": { "type-fest": "^0.13.1" @@ -14463,7 +14454,6 @@ "version": "0.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, "optional": true, "engines": { "node": ">=10" @@ -14731,6 +14721,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -14825,7 +14816,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true, "optional": true }, "node_modules/ssri": { @@ -17995,7 +17985,6 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", - "dev": true, "optional": true, "requires": { "@types/node": "*" @@ -18546,7 +18535,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "dev": true, "optional": true }, "bottleneck": { @@ -19864,7 +19852,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, + "devOptional": true, "requires": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -19928,7 +19916,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, "optional": true }, "diff": { @@ -20615,7 +20602,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true + "devOptional": true }, "escalade": { "version": "3.1.1", @@ -21522,7 +21509,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dev": true, + "devOptional": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -21607,7 +21594,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "dev": true, "optional": true, "requires": { "boolean": "^3.0.1", @@ -21656,7 +21642,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, + "devOptional": true, "requires": { "define-properties": "^1.1.3" } @@ -21792,7 +21778,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, + "devOptional": true, "requires": { "get-intrinsic": "^1.1.1" } @@ -21807,7 +21793,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true + "devOptional": true }, "has-tostringtag": { "version": "1.0.0", @@ -22642,7 +22628,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true + "devOptional": true }, "json5": { "version": "2.2.3", @@ -23012,7 +22998,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, "optional": true, "requires": { "escape-string-regexp": "^4.0.0" @@ -23022,7 +23007,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "optional": true } } @@ -26152,7 +26136,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "devOptional": true }, "object.assign": { "version": "4.1.4", @@ -27076,7 +27060,6 @@ "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, "optional": true, "requires": { "boolean": "^3.0.1", @@ -27323,7 +27306,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "dev": true, "optional": true }, "semver-diff": { @@ -27353,7 +27335,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, "optional": true, "requires": { "type-fest": "^0.13.1" @@ -27363,7 +27344,6 @@ "version": "0.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, "optional": true } } @@ -27571,7 +27551,8 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "devOptional": true }, "source-map-support": { "version": "0.5.21", @@ -27657,7 +27638,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true, "optional": true }, "ssri": { diff --git a/sampleTemplate_tana.tmpl b/sampleTemplate_tana.tmpl new file mode 100644 index 00000000..69be7bc5 --- /dev/null +++ b/sampleTemplate_tana.tmpl @@ -0,0 +1,4 @@ +{created-at-block}Creation date: {created-at}{end-created-at-block} +{updated-at-block}Last modification date: {updated-at}{end-updated-at-block} +{content-block}{content}{end-content-block} + diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 00000000..98e7b6da --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,4 @@ + +export const tanaCodeBlock =''; +export const checkboxTodo = '- [ ]' +export const checkboxDone = '- [x]' \ No newline at end of file diff --git a/src/dropTheRopeRunner.ts b/src/dropTheRopeRunner.ts index 77e4dc40..dc1bd83f 100644 --- a/src/dropTheRopeRunner.ts +++ b/src/dropTheRopeRunner.ts @@ -9,6 +9,8 @@ import { YarleOptions } from './YarleOptions'; import { loggerInfo } from './utils/loggerInfo'; import { clearLogFile } from './utils/clearLogFile'; import { applyLinks } from './utils/apply-links'; +import { createTanaOutput } from './utils/tana/create-tana-output'; +import { isTanaOutput } from './utils/tana/is-tana-output'; export const run = async (opts?: YarleOptions) => { clearLogFile(); @@ -34,6 +36,13 @@ export const run = async (opts?: YarleOptions) => { } const outputNotebookFolders = await yarle.dropTheRope(options); + // POSTPROCESSES // apply internal links applyLinks(options, outputNotebookFolders); + + // create tana output if it's required + if (isTanaOutput()){ + createTanaOutput(options, outputNotebookFolders) + } + }; diff --git a/src/models/EvernoteTask.ts b/src/models/EvernoteTask.ts index 32e6c3b5..fb02e9b9 100644 --- a/src/models/EvernoteTask.ts +++ b/src/models/EvernoteTask.ts @@ -32,6 +32,7 @@ export const mapEvernoteTask = (pureTask: any): EvernoteTask => { duedate: getDateFromProperty(pureTask.duedate), taskflag: pureTask.taskflag === 'true', reminderdate: pureTask.reminder ? getDateFromProperty(pureTask.reminder.reminderdate) : undefined, + sortweight: pureTask.sortweight }; }; diff --git a/src/output-format.ts b/src/output-format.ts index e8a5ea36..84ccd5e1 100644 --- a/src/output-format.ts +++ b/src/output-format.ts @@ -2,6 +2,7 @@ export enum OutputFormat { ObsidianMD= 'ObsidianMD', StandardMD= 'StandardMD', LogSeqMD = 'LogSeqMD', + Tana = 'Tana Internal Format', } module.exports={OutputFormat} \ No newline at end of file diff --git a/src/outputLanguages/outputLanguages.ts b/src/outputLanguages/outputLanguages.ts new file mode 100644 index 00000000..bb8a9a3d --- /dev/null +++ b/src/outputLanguages/outputLanguages.ts @@ -0,0 +1,33 @@ +import { OutputFormat } from "./../output-format" +export interface LanguageItems { + + bold?: string; + italic?: string; + highlight?: string; + strikethrough?: string; +} +const languageItems: any = { +} +languageItems[OutputFormat.ObsidianMD] = { + bold: '**', + italic: '_', + highlight: '==', + strikethrough: '~~', +} +languageItems[OutputFormat.Tana] = { + bold: '**', + italic: '__', + highlight: '^^', + strikethrough: '~~', +} + +languageItems[OutputFormat.StandardMD] = { + bold: '**', + italic: '_', + highlight: '`', + strikethrough: '~~', +} + +export const getLanguageItems = (language: OutputFormat): any => { + return languageItems[language] || languageItems[OutputFormat.StandardMD] +} \ No newline at end of file diff --git a/src/process-node.ts b/src/process-node.ts index 6253e008..3ebdf280 100644 --- a/src/process-node.ts +++ b/src/process-node.ts @@ -16,6 +16,11 @@ import { NoteData } from './models/NoteData'; import { loggerInfo } from './utils/loggerInfo'; import { isTOC } from './utils/is-toc'; import { RuntimePropertiesSingleton } from './runtime-properties'; +import { OutputFormat } from './output-format'; +import { convert2TanaNode } from './utils/tana/convert-to-tana-node'; +import { saveTanaFile } from './utils/save-tana-file'; +import { isTanaOutput } from './utils/tana/is-tana-output'; + export const processNode = (note: any, notebookName: string): void => { const dateStarted: Date = new Date(); @@ -52,7 +57,11 @@ export const processNode = (note: any, notebookName: string): void => { // tslint:disable-next-line:no-console // loggerInfo(`data =>\n ${JSON.stringify(data)} \n***`); - saveMdFile(data, note); + if (isTanaOutput()){ + const tanaJson = convert2TanaNode({...noteData, content: data}, yarleOptions) + saveTanaFile(tanaJson, note) + } + else saveMdFile(data, note); if (yarleOptions.keepOriginalHtml) { convert2Html(noteData); diff --git a/src/process-tasks.ts b/src/process-tasks.ts index 0634902c..9c1b33e8 100644 --- a/src/process-tasks.ts +++ b/src/process-tasks.ts @@ -3,7 +3,8 @@ import moment from 'moment'; import { yarleOptions } from './yarle'; import { TaskOutputFormat } from './task-output-format'; -import { EvernoteTask, EvernoteTaskStatus } from './models/EvernoteTask'; +import { EvernoteTask } from './models/EvernoteTask'; +import { getTaskStatusMd } from './utils/get-task-status-md'; const MEDIUM_PRIORITY_ICON = '🔼'; const LOW_PRIORITY_ICON = '🔽'; @@ -20,18 +21,14 @@ export const processTaskFactory = (outputFormat: TaskOutputFormat): Function => }; const convertTasktoPlainMdTask = (task: EvernoteTask, notebookName: string): string => { - const taskStatusMd = (task.taskstatus === EvernoteTaskStatus.Open) - ? '- [ ]' - : '- [x]'; + const taskStatusMd = getTaskStatusMd(task) const title = task.title ? ` ${task.title}` : ''; return `${taskStatusMd}${title}`; }; export const convertTasktoMd = (task: EvernoteTask, notebookName: string): string => { - const taskStatusMd = (task.taskstatus === EvernoteTaskStatus.Open) - ? '- [ ]' - : '- [x]'; + const taskStatusMd = getTaskStatusMd(task) const title = task.title ? ` ${task.title}` : ''; const tag = yarleOptions.obsidianTaskTag !== '' ? ` ${yarleOptions.obsidianTaskTag}` : ''; const duedate = task.duedate && !isNaN(task.duedate.getTime()) diff --git a/src/ui/index.html b/src/ui/index.html index 69442e9e..8d7a604d 100644 --- a/src/ui/index.html +++ b/src/ui/index.html @@ -167,6 +167,7 @@
Target Dialect
diff --git a/src/ui/main.js b/src/ui/main.js index f6a4df50..3276a5db 100644 --- a/src/ui/main.js +++ b/src/ui/main.js @@ -7,6 +7,8 @@ const { mapSettingsToYarleOptions } = require('./settingsMapper') const yarle = require('../yarle') const {OutputFormat} = require('./../output-format') const { applyLinks } = require('./../utils/apply-links'); +const { isTanaOutput } = require('./../utils/tana/is-tana-output'); +const {createTanaOutput } = require('./../utils/tana/create-tana-output'); initialize(); // tslint:disable-next-line:no-require-imports variable-name @@ -71,6 +73,9 @@ const createWindow = () => { // apply internal links applyLinks(settings, outputNotebookFolders); + if (isTanaOutput()){ + createTanaOutput(settings, outputNotebookFolders) + } }); diff --git a/src/ui/preload.js b/src/ui/preload.js index 83a88738..817d550c 100644 --- a/src/ui/preload.js +++ b/src/ui/preload.js @@ -1,7 +1,7 @@ const { contextBridge, ipcRenderer } = require('electron') const path = require('path'); const fs = require('fs'); -const { OutputFormat } = require("../output-format") +const { OutputFormat } = require("../output-format"); contextBridge.exposeInMainWorld('versions', { node: () => process.versions.node, @@ -24,19 +24,35 @@ contextBridge.exposeInMainWorld('electronAPI', { }, readFileSync: fs.readFileSync, getConfigByType: (type) => { - return fs.readFileSync( - (type === OutputFormat.LogSeqMD) - ? `${__dirname}/../../config.logseq.json` - : `${__dirname}/../../config.json` - , 'utf-8'); + let configFile; + switch(type){ + case OutputFormat.LogSeqMD: + configFile = `${__dirname}/../../config.logseq.json` + break; + case OutputFormat.Tana: + configFile = `${__dirname}/../../config.tana.json` + break; + default: + configFile = `${__dirname}/../../config.json`; + break; + } + return fs.readFileSync(configFile, 'utf-8'); }, getTemplateByType: (type) => { - return fs.readFileSync( - (type === OutputFormat.LogSeqMD) - ? `${__dirname}/../../sampleTemplate_logseq.tmpl` - : `${__dirname}/../../sampleTemplate.tmpl` - , 'utf-8'); + let templateFile; + switch(type){ + case OutputFormat.LogSeqMD: + templateFile = `${__dirname}/../../sampleTemplate_logseq.tmpl` + break; + case OutputFormat.Tana: + templateFile = `${__dirname}/../../sampleTemplate_tana.tmpl` + break; + default: + templateFile = `${__dirname}/../../sampleTemplate.tmpl`; + break; + } + return fs.readFileSync(templateFile, 'utf-8'); }, outputFormat: OutputFormat, @@ -78,6 +94,11 @@ contextBridge.exposeInMainWorld('electronAPI', { ipcRenderer.on('logSeqModeSelected', callback) }, onLogSeqModeDeSelected: (callback) => ipcRenderer.on('logSeqModeDeSelected', callback), + onTanaModeSelected: (callback) => { + console.log('onTanaModeSelected triggered') + ipcRenderer.on('tanaModeSelected', callback) + }, + onTanaModeDeSelected: (callback) => ipcRenderer.on('tanaModeDeSelected', callback), startConversion: () => { ipcRenderer.send('startConversion') diff --git a/src/ui/renderer.js b/src/ui/renderer.js index eaa3788a..a54a01fe 100644 --- a/src/ui/renderer.js +++ b/src/ui/renderer.js @@ -1,5 +1,4 @@ - const selectEnexFilesDialogBtn = document.getElementById('selectEnexFilesDialogBtn') const filePathElement = document.getElementById('filePath') @@ -49,36 +48,68 @@ const handleOutputFormatChange = ((event, initValue) => { // save changed value to store const value = initValue || event.target.value - if (value === window.electronAPI.outputFormat.LogSeqMD) { - console.log('window.electronAPI.onLogSeqModeSelected') - - const logSeqConfig = window.electronAPI.getConfigByType('LogSeqMD') - const logSeqTemplate = window.electronAPI.getTemplateByType('LogSeqMD') - const flatConfig = flatten(JSON.parse(logSeqConfig)); - document.getElementById('currentTemplate').value = logSeqTemplate; - - updateDomByFlatConfig(flatConfig, true); - document.getElementById('currentTemplate').disabled = true; - - document.getElementById('logseqSettings.journalNotes').removeAttribute('disabled'); + switch (value){ + case window.electronAPI.outputFormat.LogSeqMD: + console.log('window.electronAPI.onLogSeqModeSelected') + const logSeqConfig = window.electronAPI.getConfigByType('LogSeqMD') + const logSeqTemplate = window.electronAPI.getTemplateByType('LogSeqMD') + const flatLogseqConfig = flatten(JSON.parse(logSeqConfig)); + document.getElementById('currentTemplate').value = logSeqTemplate; - document.getElementById('logseqSettings.journalNotes.container').style.display = 'block'; - } else { - console.log('window.electronAPI.onLogSeqModeDeSelected') - //const logSeqConfig = window.electronAPI.readFileSync(`${window.electronAPI.currentDir}/../../config.logseq.json`, 'utf-8'); - //const logSeqTemplate = window.electronAPI.readFileSync(`${window.electronAPI.currentDir}/../../sampleTemplate_logseq.tmpl`, 'utf-8'); - const flatConfig = flatten(JSON.parse(defaultConfig)); - flatConfig.currentTemplate = defaultTemplate; + updateDomByFlatConfig(flatLogseqConfig, true); + document.getElementById('currentTemplate').disabled = true; + // because of Tana + document.getElementById('useHashTags').disabled = false; + document.getElementById('generateNakedUrls').disabled = false; + // end of Tana + document.getElementById('logseqSettings.journalNotes').removeAttribute('disabled'); + document.getElementById('logseqSettings.journalNotes.container').style.display = 'block'; + break; + case window.electronAPI.outputFormat.Tana: + console.log('window.electronAPI.onTanaModeSelected') + //const logSeqConfig = window.electronAPI.readFileSync(`${window.electronAPI.currentDir}/../../config.logseq.json`, 'utf-8'); + //const logSeqTemplate = window.electronAPI.readFileSync(`${window.electronAPI.currentDir}/../../sampleTemplate_logseq.tmpl`, 'utf-8'); + const flatTanaConfig = flatten(JSON.parse(defaultConfig)); + const tanaTemplate = window.electronAPI.getTemplateByType('Tana Internal Format') + + flatTanaConfig.currentTemplate = tanaTemplate; + + updateDomByFlatConfig(flatTanaConfig, false); + document.getElementById('currentTemplate').removeAttribute('disabled'); - updateDomByFlatConfig(flatConfig, false); - document.getElementById('currentTemplate').removeAttribute('disabled'); - - document.getElementById('resourcesDir').value = '_resources'; - document.getElementById('logseqSettings.journalNotes.container').style.display = 'none'; + document.getElementById('resourcesDir').value = '_resources'; + document.getElementById('logseqSettings.journalNotes.container').style.display = 'none'; + + window.electronAPI.store.set('resourcesDir', '_resources'); + // because of Tana + document.getElementById('useHashTags').value = false; + window.electronAPI.store.set('useHashTags', false); + document.getElementById('useHashTags').disabled = true; + document.getElementById('generateNakedUrls').value = true; + window.electronAPI.store.set('generateNakedUrls', true); + + document.getElementById('generateNakedUrls').disabled = true; + // end tana + window.electronAPI.store.set('currentTemplate', flatTanaConfig.currentTemplate); + break; + default: + console.log('window.electronAPI.onLogSeqModeDeSelected') + //const logSeqConfig = window.electronAPI.readFileSync(`${window.electronAPI.currentDir}/../../config.logseq.json`, 'utf-8'); + //const logSeqTemplate = window.electronAPI.readFileSync(`${window.electronAPI.currentDir}/../../sampleTemplate_logseq.tmpl`, 'utf-8'); + const flatConfig = flatten(JSON.parse(defaultConfig)); + flatConfig.currentTemplate = defaultTemplate; + + updateDomByFlatConfig(flatConfig, false); + document.getElementById('currentTemplate').removeAttribute('disabled'); - window.electronAPI.store.set('resourcesDir', '_resources'); - window.electronAPI.store.set('currentTemplate', flatConfig.currentTemplate); + document.getElementById('resourcesDir').value = '_resources'; + document.getElementById('logseqSettings.journalNotes.container').style.display = 'none'; + document.getElementById('useHashTags').disabled = false; + document.getElementById('generateNakedUrls').disabled = false; + window.electronAPI.store.set('resourcesDir', '_resources'); + window.electronAPI.store.set('currentTemplate', flatConfig.currentTemplate); } + }) const startConversionBtn = document.getElementById('startConversion') diff --git a/src/utils/apply-links.ts b/src/utils/apply-links.ts index b9f22a74..3d6b6c40 100644 --- a/src/utils/apply-links.ts +++ b/src/utils/apply-links.ts @@ -7,13 +7,15 @@ import { YarleOptions } from './../YarleOptions'; import { RuntimePropertiesSingleton } from './../runtime-properties'; import { truncatFileName } from './folder-utils'; import { escapeStringRegexp } from './escape-string-regexp'; +import { getAllOutputFilesWithExtension } from './get-all-output-files'; +import { isTanaOutput } from './tana/is-tana-output'; export const applyLinks = (options: YarleOptions, outputNotebookFolders: Array): void => { const linkNameMap = RuntimePropertiesSingleton.getInstance(); const allLinks = linkNameMap.getAllNoteIdNameMap(); const allconvertedFiles: Array = []; for (const outputFolder of outputNotebookFolders){ - getAllFiles(outputFolder, allconvertedFiles); + getAllOutputFilesWithExtension(outputFolder, allconvertedFiles, undefined); } for (const [linkName, linkProps] of Object.entries(allLinks)) { const uniqueId = linkProps.uniqueEnd; @@ -37,15 +39,28 @@ export const applyLinks = (options: YarleOptions, outputNotebookFolders: Array { return path.extname(file).toLowerCase() === extension; }); for (const targetFile of targetFiles) { const fileContent = fs.readFileSync(`${notebookFolder}${path.sep}${targetFile}`, 'UTF-8'); + let updatedContent = fileContent; + if (isTanaOutput()){ + const tanaNote = JSON.parse(updatedContent) + const linkedNode = tanaNote.nodes?.find( (note:any) => note.name === realFileNameInContent) + if (linkedNode){ + const linkItem = linkNameMap.getNoteIdNameMapByNoteTitle(realFileNameInContent) + linkedNode.uid = linkItem[0].uniqueEnd + updatedContent = JSON.stringify(tanaNote) + } + + } const regexp = new RegExp(escapeStringRegexp(linkName), 'g'); - const updatedContent = fileContent.replace(regexp, realFileNameInContent); + updatedContent = updatedContent.replace(regexp, realFileNameInContent); + + if (fileContent !== updatedContent) { console.log(`replaced output written to: ${notebookFolder}${path.sep}${targetFile}`); fs.writeFileSync(`${notebookFolder}${path.sep}${targetFile}`, updatedContent); @@ -55,17 +70,3 @@ export const applyLinks = (options: YarleOptions, outputNotebookFolders: Array): Array => { - const files = fs.readdirSync(dirPath); - - arrayOfFiles = arrayOfFiles || []; - files.forEach(file => { - if (fs.statSync(`${dirPath}${path.sep}${file}`).isDirectory()) { - arrayOfFiles = getAllFiles(`${dirPath}${path.sep}${file}`, arrayOfFiles); - } else { - arrayOfFiles.push(path.join(__dirname, dirPath, '/', file)); - } - }); - - return arrayOfFiles; - }; diff --git a/src/utils/create-overall-tana-output.ts b/src/utils/create-overall-tana-output.ts new file mode 100644 index 00000000..3ca74eb0 --- /dev/null +++ b/src/utils/create-overall-tana-output.ts @@ -0,0 +1 @@ +export const createOverallTanaOutput = ()=> {} \ No newline at end of file diff --git a/src/utils/filename-utils.ts b/src/utils/filename-utils.ts index 199a1e90..d7676e45 100644 --- a/src/utils/filename-utils.ts +++ b/src/utils/filename-utils.ts @@ -68,8 +68,8 @@ export const getFilePrefix = (note: any): string => { return normalizeTitle(note['title'] ? `${note['title'].toString()}` : 'Untitled'); }; -export const getNoteFileName = (dstPath: string, note: any): string => { - return `${getNoteName(dstPath, note)}.md`; +export const getNoteFileName = (dstPath: string, note: any, extension: string = 'md'): string => { + return `${getNoteName(dstPath, note)}.${extension}`; }; export const getExtensionFromResourceFileName = (resource: any): string => { if (!(resource['resource-attributes'] && diff --git a/src/utils/folder-utils.ts b/src/utils/folder-utils.ts index 2bdf6370..4648ffae 100644 --- a/src/utils/folder-utils.ts +++ b/src/utils/folder-utils.ts @@ -42,19 +42,22 @@ const truncateFilePath = (note: any, fileName: string, fullFilePath: string): st // -11 is the nanoid 5 char +_+ the max possible extension of the note (.md vs .html) }; -const getFilePath = (dstPath: string, note: any): string => { - const fileName = getNoteFileName(dstPath, note); +const getFilePath = (dstPath: string, note: any, extension: string): string => { + const fileName = getNoteFileName(dstPath, note, extension); const fullFilePath = `${dstPath}${path.sep}${normalizeTitle(fileName)}`; return fullFilePath.length <  MAX_PATH ? fullFilePath : truncateFilePath(note, fileName, fullFilePath); }; export const getMdFilePath = (note: any): string => { - return getFilePath(paths.mdPath, note); + return getFilePath(paths.mdPath, note, 'md'); }; +export const getJsonFilePath = (note: any): string => { + return getFilePath(paths.mdPath, note, 'json'); +}; export const getHtmlFilePath = (note: any): string => { - return getFilePath(paths.resourcePath, note).replace(/\.md$/, '.html'); + return getFilePath(paths.resourcePath, note, 'html'); }; export const getHtmlFileLink = (note: any): string => { diff --git a/src/utils/get-all-output-files.ts b/src/utils/get-all-output-files.ts new file mode 100644 index 00000000..4b271012 --- /dev/null +++ b/src/utils/get-all-output-files.ts @@ -0,0 +1,19 @@ + +import * as fs from 'fs'; +import * as path from 'path'; + +export const getAllOutputFilesWithExtension = (dirPath: string, arrayOfFiles: Array, extension: string): Array => { + const files = fs.readdirSync(dirPath); + + arrayOfFiles = arrayOfFiles || []; + files.forEach(file => { + if (fs.statSync(`${dirPath}${path.sep}${file}`).isDirectory()) { + arrayOfFiles = getAllOutputFilesWithExtension(`${dirPath}${path.sep}${file}`, arrayOfFiles, extension); + } else { + if ((extension && path.extname(file) == `.${extension}`) || !extension) + arrayOfFiles.push(path.join(dirPath, '/', file)); + } + }); + + return arrayOfFiles; + }; \ No newline at end of file diff --git a/src/utils/get-task-status-md.ts b/src/utils/get-task-status-md.ts new file mode 100644 index 00000000..b4400e97 --- /dev/null +++ b/src/utils/get-task-status-md.ts @@ -0,0 +1,8 @@ +import { EvernoteTaskStatus } from './../models/EvernoteTask'; +import { checkboxDone, checkboxTodo } from './../constants'; + +export const getTaskStatusMd = (task: any): string => { + return (task.taskstatus === EvernoteTaskStatus.Open) + ? checkboxTodo + : checkboxDone; + } \ No newline at end of file diff --git a/src/utils/save-tana-file.ts b/src/utils/save-tana-file.ts new file mode 100644 index 00000000..dc16e4bb --- /dev/null +++ b/src/utils/save-tana-file.ts @@ -0,0 +1,14 @@ +import { TanaIntermediateFile } from "./tana/types"; +import { RuntimePropertiesSingleton } from './../runtime-properties'; +import { writeFile } from './file-utils'; +import { getJsonFilePath } from './folder-utils'; +import { loggerInfo } from './loggerInfo'; + +export const saveTanaFile = (tanaNote: TanaIntermediateFile, note: any) => { + + const absJsonFilePath = getJsonFilePath(note); + const runtimeProps = RuntimePropertiesSingleton.getInstance(); + runtimeProps.setCurrentNotePath(absJsonFilePath); + writeFile(absJsonFilePath, JSON.stringify(tanaNote), note); + loggerInfo(`Note saved to ${absJsonFilePath}`); +}; diff --git a/src/utils/tana/convert-to-tana-node.ts b/src/utils/tana/convert-to-tana-node.ts new file mode 100644 index 00000000..1fbfb649 --- /dev/null +++ b/src/utils/tana/convert-to-tana-node.ts @@ -0,0 +1,124 @@ +import { NoteData } from "../../models" +import { NodeType, TanaIntermediateFile, TanaIntermediateNode } from "./types" +import { RuntimePropertiesSingleton } from '../../runtime-properties'; +import { checkboxDone, checkboxTodo, tanaCodeBlock } from "../../constants"; +import { createNewTanaFile } from "./create-new-tana-file"; + +export const cleanTanaContent = (content: string, valueToClean: string):string => { + return content.replace(valueToClean, '') +} +const convertString2TanaNode = (content: string, data: NoteData, addTags: boolean = false): TanaIntermediateNode => { + const linkNameMap = RuntimePropertiesSingleton.getInstance(); + const link = linkNameMap.getNoteIdNameMapByNoteTitle(content); + const uid = link && link[0] ? link[0].uniqueEnd : 'uuid' + Math.random() + + const tanaNode: TanaIntermediateNode = { + uid, + createdAt: new Date(data.createdAt).getTime(), + editedAt: new Date(data.updatedAt).getTime(), + type: 'node' as NodeType, + name: content, + refs: [], + children: [] + } + if (addTags){ + const tags = JSON.parse(data.tags) + tanaNode.supertags = tags + } + return tanaNode +} +const convertString2TanaCheckbox = (content: string, todoState: string, data: NoteData): TanaIntermediateNode => { + return { + uid: 'uuid' + Math.random(), + createdAt: new Date(data.createdAt).getTime(), + editedAt: new Date(data.updatedAt).getTime(), + type: 'node' as NodeType, + name: cleanTanaContent(content, todoState === 'todo' ? checkboxTodo: checkboxDone), + todoState: todoState as "todo"|"done", + refs:[], + children: [] + } +} +const convertString2TanaCodeblock = (content: string, data: NoteData): TanaIntermediateNode => { + return { + uid: 'uuid' + Math.random(), + createdAt: new Date(data.createdAt).getTime(), + editedAt: new Date(data.updatedAt).getTime(), + type: 'codeblock' as NodeType, + name: cleanTanaContent(content, tanaCodeBlock), + refs:[], + children: [] + } +} +export const convertChild = (data: NoteData, child: string) => { + // if it is a link + const regex = /\evernote:\/\/[^)]*\)/g; + const found = child.match(regex); + const linkNameMap = RuntimePropertiesSingleton.getInstance(); + + let convertedChild = child.includes(tanaCodeBlock) + ? convertString2TanaCodeblock(child, data) + : convertString2TanaNode(child, data) + if (child.startsWith(checkboxTodo)) + convertedChild = convertString2TanaCheckbox(child, 'todo', data) + if (child.startsWith(checkboxDone)) + convertedChild = convertString2TanaCheckbox(child, 'done', data) + if (found && found.length > 0){ + for (const link of found){ + const pureLink = link.slice(0, -1) + const linkItem = linkNameMap.getNoteIdNameMap()[pureLink]; + convertedChild.refs.push(linkItem.uniqueEnd); + convertedChild.name = convertedChild.name.replace(pureLink,`[[${linkItem.uniqueEnd}]]`) + + } + + } + return convertedChild +} + +export const convert2TanaNode = (data: NoteData, node: any):TanaIntermediateFile => { + const firstLevelChildren = data.content.replace(/(\n|\r\n)+/g, '\n').split('\n') + const rootContent = data.title + + const childrenNodes: Array = []; + const parentCandidates: Array = [] + const levelRecognizerRegexp = new RegExp("^\t+") + + for (const child of firstLevelChildren){ + const matchedLevel = child.match(levelRecognizerRegexp) + const convertedChild = convertChild(data, child) + + const deepLevel = matchedLevel?.[0] ? matchedLevel?.[0].split('').length : 0; + if (deepLevel > 0) { + convertedChild.name = convertedChild.name.replace(levelRecognizerRegexp, '') + parentCandidates[deepLevel-1].children.push(convertedChild) + } + else childrenNodes.push(convertedChild) + parentCandidates[deepLevel] = convertedChild + + + } + const tif: TanaIntermediateFile = { + ...createNewTanaFile(), + nodes: [{ + ...convertString2TanaNode(rootContent, data, data.tags? true: false), + children: childrenNodes, + + }], + summary: { + leafNodes: childrenNodes.filter(child => child.children?.length === 0).length, + topLevelNodes: 0, + totalNodes: childrenNodes.length+1, + calendarNodes: 0, + fields: 0, + brokenRefs: 0, + } + } + + if (data.tags){ + const tags = JSON.parse(data.tags) + const superTags = tags.map((tag: any) => { return {uid: tag, name: tag}}) + tif.supertags = superTags + } + return tif; +} \ No newline at end of file diff --git a/src/utils/tana/create-new-tana-file.ts b/src/utils/tana/create-new-tana-file.ts new file mode 100644 index 00000000..266358c3 --- /dev/null +++ b/src/utils/tana/create-new-tana-file.ts @@ -0,0 +1,17 @@ +import { TanaIntermediateFile } from "./types"; + +export const createNewTanaFile =(): TanaIntermediateFile => { + return { + version: 'TanaIntermediateFile V0.1', + nodes: [], + supertags: [], + summary: { + leafNodes:0, + topLevelNodes: 0, + totalNodes: 0, + calendarNodes: 0, + fields: 0, + brokenRefs: 0, + } + } +} \ No newline at end of file diff --git a/src/utils/tana/create-tana-output.ts b/src/utils/tana/create-tana-output.ts new file mode 100644 index 00000000..00deb9e3 --- /dev/null +++ b/src/utils/tana/create-tana-output.ts @@ -0,0 +1,51 @@ +import * as fs from 'fs'; + +import { YarleOptions } from "../../YarleOptions"; +import { getAllOutputFilesWithExtension } from "../get-all-output-files"; +import { TanaIntermediateFile } from "./types"; +import { createNewTanaFile } from './create-new-tana-file'; +const tanaNoteFileName = 'notes-converted-in-tana-intermediate-format.json'; + +export const createTanaOutput = (options: YarleOptions, outputNotebookFolders: Array): void => { + const allconvertedFiles: Array = []; + for (const outputFolder of outputNotebookFolders){ + getAllOutputFilesWithExtension(outputFolder, allconvertedFiles, 'json'); + } + for (const convertedFile of allconvertedFiles){ + // load and parse mergedTanaNotes, or create if + const mergedNotes = getMergedTanaNotes(options) + const convertedTanaNote = JSON.parse(fs.readFileSync(convertedFile, 'UTF-8')) + + updateMergedNotes(mergedNotes, convertedTanaNote) + saveMergedTanaNotes(options, mergedNotes) + } +} + +const updateMergedNotes = (mergedNotes: TanaIntermediateFile, convertedTanaNote: TanaIntermediateFile): void => { + + mergedNotes.nodes.push(convertedTanaNote.nodes[0]) + if(convertedTanaNote.supertags){ + for (const supertag of convertedTanaNote.supertags){ + if (!mergedNotes.supertags.find(mergedSupertag => mergedSupertag.uid === supertag.uid)) + mergedNotes.supertags.push(supertag) + } + } + mergedNotes.summary.leafNodes += convertedTanaNote.summary.leafNodes + mergedNotes.summary.totalNodes += convertedTanaNote.summary.totalNodes + mergedNotes.summary.topLevelNodes += 1 +} + +const getMergedTanaNotes = (options: YarleOptions): TanaIntermediateFile => { + + let mergedTanaNote + try { + mergedTanaNote = JSON.parse(fs.readFileSync(`${options.outputDir}/${tanaNoteFileName}`, 'UTF-8')) + }catch(error){ + mergedTanaNote = createNewTanaFile() + } + return mergedTanaNote + +} +const saveMergedTanaNotes = (options: YarleOptions, mergedNotes: TanaIntermediateFile): void => { + fs.writeFileSync(`${options.outputDir}/${tanaNoteFileName}`, JSON.stringify(mergedNotes)) +} diff --git a/src/utils/tana/is-tana-output.ts b/src/utils/tana/is-tana-output.ts new file mode 100644 index 00000000..6180d528 --- /dev/null +++ b/src/utils/tana/is-tana-output.ts @@ -0,0 +1,6 @@ +import { OutputFormat } from "../../output-format" +import { yarleOptions } from "../../yarle" + +export const isTanaOutput = (): boolean => { + return yarleOptions.outputFormat === OutputFormat.Tana +} \ No newline at end of file diff --git a/src/utils/tana/types.d.ts b/src/utils/tana/types.d.ts new file mode 100644 index 00000000..f7873c60 --- /dev/null +++ b/src/utils/tana/types.d.ts @@ -0,0 +1,52 @@ +export declare type TanaIntermediateFile = { + version: 'TanaIntermediateFile V0.1'; + summary: TanaIntermediateSummary; + nodes: TanaIntermediateNode[]; + attributes?: TanaIntermediateAttribute[]; + supertags?: TanaIntermediateSupertag[]; +}; +export declare type TanaIntermediateSummary = { + leafNodes: number; + topLevelNodes: number; + totalNodes: number; + calendarNodes: number; + fields: number; + brokenRefs: number; +}; +export declare type TanaIntermediateAttribute = { + name: string; + values: string[]; + count: number; + dataType?: 'any' | 'url' | 'email' | 'number' | 'date' | 'checkbox'; +}; +export declare type TanaIntermediateSupertag = { + uid: string; + name: string; +}; +export declare type NodeType = 'field' | 'image' | 'codeblock' | 'node' | 'date'; +export declare type TanaIntermediateNode = { + uid: string; + /** + * Contents of the node. + * + * For type=date this must contain the date : "MM-DD-YYYY" + * + * Supported text formatting: **bold** __italic__ ~~striked~~ ^^highlighted^^ + * + * Link formats: + * - external content: [See Tana](https://wwww.tana.inc) + * - internal: [[uid]] + * - internal with alias: [test page]([[uid]]) + */ + name: string; + description?: string; + children?: TanaIntermediateNode[]; + refs?: string[]; + createdAt: number; + editedAt: number; + type: NodeType; + mediaUrl?: string; + codeLanguage?: string; + supertags?: string[]; + todoState?: 'todo' | 'done'; +}; diff --git a/src/utils/turndown-rules/div-rule.ts b/src/utils/turndown-rules/div-rule.ts index e92dd20c..09334fd2 100644 --- a/src/utils/turndown-rules/div-rule.ts +++ b/src/utils/turndown-rules/div-rule.ts @@ -4,8 +4,6 @@ import { filterByNodeName } from './filter-by-nodename'; import { getAttributeProxy } from './get-attribute-proxy'; import { replaceMonospaceCodeBlock } from './replace-monospace-code-block'; -const markdownBlock = '\n```\n'; - const isTaskBlock = (node: any) => { const nodeProxy = getAttributeProxy(node); const taskFlag = '--en-task-group:true'; diff --git a/src/utils/turndown-rules/index.ts b/src/utils/turndown-rules/index.ts index a5bdafc0..e79d0b2c 100644 --- a/src/utils/turndown-rules/index.ts +++ b/src/utils/turndown-rules/index.ts @@ -5,5 +5,6 @@ export * from './internal-links-rule'; export * from './span-rule'; export * from './strikethrough-rule'; export * from './task-items-rule'; -export * from './newline-rule'; +export * from './newline-rule'; export * from './div-rule'; +export * from './italic-rule'; \ No newline at end of file diff --git a/src/utils/turndown-rules/italic-rule.ts b/src/utils/turndown-rules/italic-rule.ts new file mode 100644 index 00000000..f0b5500f --- /dev/null +++ b/src/utils/turndown-rules/italic-rule.ts @@ -0,0 +1,13 @@ +import { yarleOptions } from '../../yarle'; +import { getLanguageItems } from './../../outputLanguages/outputLanguages'; + +// Note: this rule must appear *after* use(gfm) so it can override +// turndown-plugin-gfm rule for strikethrough (which always uses single '~') +export const italicRule = { + filter: ['i'], + replacement: (content: any) => { + const languageItems = getLanguageItems(yarleOptions.outputFormat); + + return `${languageItems.italic}${content}${languageItems.italic}`; + }, +}; diff --git a/src/utils/turndown-rules/newline-rule.ts b/src/utils/turndown-rules/newline-rule.ts index 0a88b777..e4ba2407 100644 --- a/src/utils/turndown-rules/newline-rule.ts +++ b/src/utils/turndown-rules/newline-rule.ts @@ -1,5 +1,3 @@ -import { yarleOptions } from '../../yarle'; - import { filterByNodeName } from './filter-by-nodename'; import { getAttributeProxy } from './get-attribute-proxy'; diff --git a/src/utils/turndown-rules/replace-code-block.ts b/src/utils/turndown-rules/replace-code-block.ts index 4efa4353..fd23a85c 100644 --- a/src/utils/turndown-rules/replace-code-block.ts +++ b/src/utils/turndown-rules/replace-code-block.ts @@ -1,7 +1,10 @@ import { filterByNodeName } from './filter-by-nodename'; import { getAttributeProxy } from './get-attribute-proxy'; +import { OutputFormat } from './../../output-format'; +import { isTanaOutput } from '../tana/is-tana-output'; const markdownBlock = '\n```\n'; +const tanaCodeBlock =''; const isCodeBlock = (node: any) => { const nodeProxy = getAttributeProxy(node); @@ -38,8 +41,8 @@ export const replaceCodeBlock = (content: string, node: any): any => { // turndown has already escaped markdown chars (and all '\') in content; // reverse that to avoid extraneous backslashes in code block. content = unescapeMarkdown(content); - - return `${markdownBlock}${content}${markdownBlock}`; + const codeBlock = isTanaOutput() ? tanaCodeBlock: markdownBlock + return `${codeBlock}${content}${codeBlock}`; } if (node.parentElement && isCodeBlock(node.parentElement) && node.parentElement.firstElementChild === node) { diff --git a/src/utils/turndown-rules/span-rule.ts b/src/utils/turndown-rules/span-rule.ts index 53d9641a..2c32d89b 100644 --- a/src/utils/turndown-rules/span-rule.ts +++ b/src/utils/turndown-rules/span-rule.ts @@ -4,15 +4,18 @@ import { OutputFormat } from '../../output-format'; import { filterByNodeName } from './filter-by-nodename'; import { getAttributeProxy } from './get-attribute-proxy'; +import { getLanguageItems } from './../../outputLanguages/outputLanguages'; const EVERNOTE_HIGHLIGHT = '-evernote-highlight:true;'; const EVERNOTE_COLORHIGHLIGHT = '--en-highlight'; const BOLD = 'bold'; const ITALIC = 'italic'; + export const spanRule = { filter: filterByNodeName('SPAN'), replacement: (content: any, node: any) => { - const HIGHLIGHT_SEPARATOR = yarleOptions.outputFormat === OutputFormat.ObsidianMD ? '==' : '`' ; + const languageItems = getLanguageItems(yarleOptions.outputFormat); + //const HIGHLIGHT_SEPARATOR = yarleOptions.outputFormat === OutputFormat.ObsidianMD ? '==' : '`' ; const nodeProxy = getAttributeProxy(node); if (nodeProxy.style) { const nodeValue: string = nodeProxy.style.value; @@ -21,13 +24,13 @@ export const spanRule = { if (content !== '') { const hasBold = nodeValue.includes(BOLD); const hasItalic = nodeValue.includes(ITALIC); - if (hasBold && !hasItalic) { return `**${content}**`; } - if (!hasBold && hasItalic) { return `_${content}_`; } - if (hasBold && hasItalic) { return `_**${content}**_`; } + if (hasBold && !hasItalic) { return `${languageItems.bold}${content}${languageItems.bold}`; } + if (!hasBold && hasItalic) { return `${languageItems.italic}${content}${languageItems.italic}`; } + if (hasBold && hasItalic) { return `${languageItems.italic}${languageItems.bold}${content}${languageItems.bold}${languageItems.italic}`; } } return nodeValue.includes(EVERNOTE_HIGHLIGHT) || nodeValue.includes(EVERNOTE_COLORHIGHLIGHT) ? - `${HIGHLIGHT_SEPARATOR}${content}${HIGHLIGHT_SEPARATOR}` : + `${languageItems.highlight}${content}${languageItems.highlight}` : content; } diff --git a/src/utils/turndown-rules/strikethrough-rule.ts b/src/utils/turndown-rules/strikethrough-rule.ts index ce3c9445..8a64ea95 100644 --- a/src/utils/turndown-rules/strikethrough-rule.ts +++ b/src/utils/turndown-rules/strikethrough-rule.ts @@ -1,8 +1,13 @@ +import { yarleOptions } from '../../yarle'; +import { getLanguageItems } from './../../outputLanguages/outputLanguages'; + // Note: this rule must appear *after* use(gfm) so it can override // turndown-plugin-gfm rule for strikethrough (which always uses single '~') export const strikethroughRule = { filter: ['del', 's', 'strike'], replacement: (content: any) => { - return `~~${content}~~`; + const languageItems = getLanguageItems(yarleOptions.outputFormat); + + return `${languageItems.strikethrough}${content}${languageItems.strikethrough}`; }, }; diff --git a/src/utils/turndown-rules/task-list-rule.ts b/src/utils/turndown-rules/task-list-rule.ts index 3dc6337f..d9ec7975 100644 --- a/src/utils/turndown-rules/task-list-rule.ts +++ b/src/utils/turndown-rules/task-list-rule.ts @@ -1,8 +1,9 @@ +import { isTanaOutput } from '../tana/is-tana-output'; import { OutputFormat } from '../../output-format'; import { yarleOptions } from '../../yarle'; -import { filterByNodeName } from './filter-by-nodename'; import { getAttributeProxy } from './get-attribute-proxy'; +import { checkboxDone, checkboxTodo } from './../../constants'; const indentCharacter = ' '; export const taskListRule = { filter: 'li', @@ -36,10 +37,10 @@ export const taskListRule = { let prefix = indentCount > 0 ? indentChars : (isTodoDoneBlock(node) - ? '- [x] ' + ? `${checkboxDone} ` : (isTodoBlock(node) - ? '- [ ] ' - :'* ')) + ? `${checkboxTodo} ` + : getListItem())) ; const parent = node.parentNode; if (parent.nodeName === 'OL') { @@ -54,3 +55,9 @@ export const taskListRule = { return ret; }}; + +const getListItem = (): string => { + return isTanaOutput() + ? '' + : '* ' +} \ No newline at end of file diff --git a/src/utils/turndown-service.ts b/src/utils/turndown-service.ts index 4cdcf3f8..d359b8c5 100644 --- a/src/utils/turndown-service.ts +++ b/src/utils/turndown-service.ts @@ -5,6 +5,7 @@ import { YarleOptions } from './../YarleOptions'; import { divRule, imagesRule, + italicRule, newLineRule, spanRule, strikethroughRule, @@ -35,6 +36,7 @@ export const getTurndownService = (yarleOptions: YarleOptions) => { turndownService.addRule('wikistyle links', wikiStyleLinksRule); turndownService.addRule('images', imagesRule); turndownService.addRule('list', taskListRule); + turndownService.addRule('italic', italicRule); if (yarleOptions.outputFormat === OutputFormat.LogSeqMD) { turndownService.addRule('logseq_hr', { diff --git a/src/yarle.ts b/src/yarle.ts index 963eb87b..47091302 100644 --- a/src/yarle.ts +++ b/src/yarle.ts @@ -23,6 +23,10 @@ import { RuntimePropertiesSingleton } from './runtime-properties'; import { processTaskFactory } from './process-tasks'; import { mapEvernoteTask } from './models/EvernoteTask'; import { TaskOutputFormat } from './task-output-format'; +import { isTanaOutput } from './utils/tana/is-tana-output'; +import { NodeType } from "./utils/tana/types"; +import { checkboxDone, checkboxTodo } from './constants'; +import { cleanTanaContent } from './utils/tana/convert-to-tana-node'; export const defaultYarleOptions: YarleOptions = { enexSources: ['notebook.enex'], @@ -66,7 +70,7 @@ const setOptions = (options: YarleOptions): void => { yarleOptions.skipCreationTime = !hasCreationTimeInTemplate(template); yarleOptions.skipLocation = !hasLocationInTemplate(template); yarleOptions.skipSourceUrl = !hasSourceURLInTemplate(template); - yarleOptions.skipTags = !hasAnyTagsInTemplate(template); + yarleOptions.skipTags = !hasAnyTagsInTemplate(template) && !isTanaOutput(); yarleOptions.skipUpdateTime = !hasUpdateTimeInTemplate(template); yarleOptions.isNotebookNameNeeded = hasNotebookInTemplate(template); yarleOptions.keepOriginalHtml = hasLinkToOriginalInTemplate(template); @@ -77,6 +81,9 @@ const setOptions = (options: YarleOptions): void => { loggerInfo(`Path separator:${path.sep}`); /*}*/ }; +interface TaskGroups { + [key: string]: Map; +} export const parseStream = async (options: YarleOptions, enexSource: string): Promise => { loggerInfo(`Getting stream from ${enexSource}`); @@ -85,7 +92,7 @@ export const parseStream = async (options: YarleOptions, enexSource: string): Pr let noteNumber = 0; let failed = 0; let skipped = 0; - const tasks: any = {}; // key: taskId value: generated md text + const tasks: TaskGroups = {}; // key: taskId value: generated md text const notebookName = utils.getNotebookName(enexSource); const processTaskFn = processTaskFactory(yarleOptions.taskOutputFormat); @@ -129,9 +136,40 @@ export const parseStream = async (options: YarleOptions, enexSource: string): Pr if (currentNotePath) { for (const task of Object.keys(tasks)) { + const taskPlaceholder = `${task}` const fileContent = fs.readFileSync(currentNotePath, 'UTF-8'); - const updatedContent = fileContent.replace(`${task}`, tasks[task].join('\n')); + const sortedTasks = new Map([...tasks[task]].sort()); + + let updatedContent = fileContent.replace(taskPlaceholder, [...sortedTasks.values()].join('\n')); + + if (isTanaOutput()){ + const tanaNote = JSON.parse(fileContent); + const rootTaskChild = tanaNote.nodes?.[0].children?.find((child:any) => child.name === taskPlaceholder) + if (rootTaskChild){ + for (const taskItem of sortedTasks.values()){ + // split by tasks + const todoState = taskItem.startsWith(checkboxTodo)? 'todo':'done' + tanaNote.nodes?.[0].children?.push({ + + uid: 'uuid' + Math.random(), + createdAt: rootTaskChild.createdAt, + editedAt: rootTaskChild.editedAt, + type: 'node' as NodeType, + + name: cleanTanaContent(taskItem, todoState === 'todo' ? checkboxTodo: checkboxDone), + todoState: todoState as "todo"|"done", + refs:[], + } + + ) + } + tanaNote.nodes?.[0].children.splice(tanaNote.nodes?.[0].children.indexOf(rootTaskChild), 1) + updatedContent = JSON.stringify(tanaNote) + } + + } fs.writeFileSync(currentNotePath, updatedContent); + } } }); @@ -139,10 +177,10 @@ export const parseStream = async (options: YarleOptions, enexSource: string): Pr xml.on('tag:task', (pureTask: any) => { const task = mapEvernoteTask(pureTask); if (!tasks[task.taskgroupnotelevelid]) { - tasks[task.taskgroupnotelevelid] = []; + tasks[task.taskgroupnotelevelid] = new Map(); } - tasks[task.taskgroupnotelevelid].push(processTaskFn(task, notebookName)); + tasks[task.taskgroupnotelevelid].set(task.sortweight, processTaskFn(task, notebookName)); }); diff --git a/test/data/notes-converted-in-tana-intermediate-format.json b/test/data/notes-converted-in-tana-intermediate-format.json new file mode 100644 index 00000000..5abee66e --- /dev/null +++ b/test/data/notes-converted-in-tana-intermediate-format.json @@ -0,0 +1 @@ +{"version":"TanaIntermediateFile V0.1","nodes":[{"uid":"uuid0.672118737732891","createdAt":1683819252000,"editedAt":1683819332000,"type":"node","name":"Table","refs":[],"children":[{"uid":"uuid0.10734740306521551","createdAt":1683819252000,"editedAt":1683819332000,"type":"node","name":"| | |","refs":[]},{"uid":"uuid0.28925806951549315","createdAt":1683819252000,"editedAt":1683819332000,"type":"node","name":"| --- | --- |","refs":[]},{"uid":"uuid0.2963330869890446","createdAt":1683819252000,"editedAt":1683819332000,"type":"node","name":"| Cell1.1 | Cell1.2 |","refs":[]},{"uid":"uuid0.4730246629488386","createdAt":1683819252000,"editedAt":1683819332000,"type":"node","name":"| Cell2.1 | Cell2.2 |","refs":[]}]},{"uid":"xq3g6","createdAt":1680026895000,"editedAt":1683756087000,"type":"node","name":"TanaNote1","refs":[],"supertags":["tanaTag1","tanaTag2"],"children":[{"uid":"uuid0.9883972644044354","createdAt":1680026895000,"editedAt":1683756087000,"type":"node","name":"Hello  bulletpoint1","refs":[]},{"uid":"uuid0.8708447301456232","createdAt":1680026895000,"editedAt":1683756087000,"type":"node","name":"hello bulletpoint2","refs":[]},{"uid":"uuid0.37385827133591554","createdAt":1680026895000,"editedAt":1683756087000,"type":"node","name":"Hello bulletpoint3","refs":[]},{"uid":"uuid0.7827529056098466","createdAt":1680026895000,"editedAt":1683756087000,"type":"codeblock","name":"This is a codeblock","refs":[]},{"uid":"uuid0.039448692373003746","createdAt":1680026895000,"editedAt":1683756087000,"type":"codeblock","name":"This is a next line within the codeblock","refs":[]}]},{"uid":"uuid0.6065091026572809","createdAt":1683701463000,"editedAt":1683701602000,"type":"node","name":"TanaNote2","refs":[],"supertags":["tanaTag1","tanaTag2"],"children":[{"uid":"uuid0.0014911392050451777","createdAt":1683701463000,"editedAt":1683701602000,"type":"node","name":"Hello  bulletpoint1","refs":[]},{"uid":"uuid0.4071901995550742","createdAt":1683701463000,"editedAt":1683701602000,"type":"node","name":"hello bulletpoint2","refs":[]},{"uid":"uuid0.1267667152857852","createdAt":1683701463000,"editedAt":1683701602000,"type":"node","name":"line3","refs":[]},{"uid":"uuid0.5270248421682651","createdAt":1683701463000,"editedAt":1683701602000,"type":"node","name":"lorem ipsum","refs":[]},{"uid":"uuid0.7897151746214437","createdAt":1683701463000,"editedAt":1683701602000,"type":"node","name":" checkpoint1","todoState":"todo","refs":[]},{"uid":"uuid0.30132724081829276","createdAt":1683701463000,"editedAt":1683701602000,"type":"node","name":" checkpoint2","todoState":"todo","refs":[]},{"uid":"uuid0.7898931625647523","createdAt":1683701463000,"editedAt":1683701602000,"type":"node","name":"link to [TanaNote1]([[xq3g6]])","refs":["xq3g6"]}]},{"uid":"uuid0.12980320569404502","createdAt":1683819201000,"editedAt":1683819279000,"type":"node","name":"Tasks","refs":[],"children":[{"uid":"uuid0.028418138110846147","createdAt":1683819201000,"editedAt":1683819279000,"type":"node","name":" Task2 🔽","todoState":"todo","refs":[]},{"uid":"uuid0.984523040455328","createdAt":1683819201000,"editedAt":1683819279000,"type":"node","name":" Done Task3 🔽","todoState":"done","refs":[]},{"uid":"uuid0.19542095497442702","createdAt":1683819201000,"editedAt":1683819279000,"type":"node","name":" Task1 🔽","todoState":"todo","refs":[]}]}],"supertags":[{"uid":"tanaTag1","name":"tanaTag1"},{"uid":"tanaTag2","name":"tanaTag2"}],"summary":{"leafNodes":17,"topLevelNodes":5,"totalNodes":21,"calendarNodes":0,"fields":0,"brokenRefs":0}} \ No newline at end of file diff --git a/test/data/sampleTemplate_tana.tmpl b/test/data/sampleTemplate_tana.tmpl new file mode 100644 index 00000000..38418c4f --- /dev/null +++ b/test/data/sampleTemplate_tana.tmpl @@ -0,0 +1,4 @@ +{created-at-block}Created at: {created-at}{end-created-at-block} +{updated-at-block}Last updated at: {updated-at}{end-updated-at-block} +{content-block}{content}{end-content-block} + diff --git a/test/data/test-justTextButCustomDate.md b/test/data/test-justTextButCustomDate.md index 7ad39154..be777f1e 100644 --- a/test/data/test-justTextButCustomDate.md +++ b/test/data/test-justTextButCustomDate.md @@ -2,6 +2,6 @@ This is the content - Created at: 2018 October 06 - Updated at: 2018 October 06 + Created at: 2018-10-06 + Updated at: 2018-10-06 diff --git a/test/data/test-tana-notes.enex b/test/data/test-tana-notes.enex new file mode 100644 index 00000000..9d42f1c8 --- /dev/null +++ b/test/data/test-tana-notes.enex @@ -0,0 +1,164 @@ + + + + + Nested tasks + 20230513T123501Z + 20230513T123604Z + + akos + + + +
  • RootTask1
    • childTask1.1
      • task1.1.1
      • task1.1.2
    • childTask1.2
  • RootTask2
    • childTask2.1
      • task2.1.1
      • task2.1.2
    • childTask2.2
      • task2.2.1
]]> +
+
+ + Tasks + 20230511T153321Z + 20230513T080549Z + + akos + + + +
Content not supported
This block is a placeholder for Tasks, which has been officially released on the newest version of Evernote and is no longer supported on this version. Deleting or moving this block may cause unexpected behavior in newer versions of Evernote.


]]> +
+ + Task2 + 20230511T153439Z + 20230511T153439Z + open + false + J + 0cf6ea1a-2aca-446d-9a09-dea60df73226 + 9589497d-5dba-4cd9-9098-97f2d302e2d4 + 2757600913T000000Z + 20230511T153439Z + 16093948 + 16093948 + + + Done Task3 + 20230511T153439Z + 20230511T153439Z + completed + false + R + 78684eaa-6a14-4e90-8dee-68f59a8c0679 + 9589497d-5dba-4cd9-9098-97f2d302e2d4 + 2757600913T000000Z + 20230511T153439Z + 16093948 + 16093948 + + + Task1 + 20230511T153439Z + 20230511T153439Z + open + false + B + 34a53b4a-d9f0-4e61-b8aa-6a224ec628e7 + 9589497d-5dba-4cd9-9098-97f2d302e2d4 + 2757600913T000000Z + 20230511T153439Z + 16093948 + 16093948 + + + Task4 + 20230513T080528Z + 20230513T080539Z + open + false + V + b40889ef-49d1-4f31-9ee3-a7da062ed6ae + 9589497d-5dba-4cd9-9098-97f2d302e2d4 + 2757600913T000000Z + 20230513T080528Z + 16093948 + 16093948 + + + CheckTheLastInOrderTask6 + 20230513T080539Z + 20230513T080549Z + completed + false + Y + 0a4d8820-71bb-4191-8da4-0d9eeb6471f4 + 9589497d-5dba-4cd9-9098-97f2d302e2d4 + 2757600913T000000Z + 20230513T080549Z + 16093948 + 16093948 + + + Task5 + 20230513T080539Z + 20230513T080539Z + open + false + X + 82a38d96-a74e-48e2-a8fb-e16742b5b5a7 + 9589497d-5dba-4cd9-9098-97f2d302e2d4 + 2757600913T000000Z + 20230513T080539Z + 16093948 + 16093948 + +
+ + Links + 20230513T074548Z + 20230513T075641Z + + akos + + + + ]]> + + + + TanaNote1 + 20230328T180815Z + 20230513T065059Z + tanaTag1 + tanaTag2 + + akos + + + +
Hello  bulletpoint1
hello bulletpoint2
Hello bulletpoint3

This is a codeblock
This is a next line within the codeblock

]]> +
+
+ + Plain text styles + 20230513T063526Z + 20230513T064855Z + + akos + + + +
bold
italic
striked
highlighted
]]> +
+
+ + Tana note2 + 20230510T065103Z + 20230510T065322Z + tanaTag1 + tanaTag2 + + akos + + + +
  • Hello  bulletpoint1
  • hello bulletpoint2
line3

lorem ipsum
  • checkpoint1
  • checkpoint2

link to TanaNote1
]]> +
+
+
diff --git a/test/data/test-things-to-do-global-filter.md b/test/data/test-things-to-do-global-filter.md index 34ce8147..6227140a 100644 --- a/test/data/test-things-to-do-global-filter.md +++ b/test/data/test-things-to-do-global-filter.md @@ -3,10 +3,10 @@ - [ ] #globalTaskTag Simple task 🔽 +- [ ] #globalTaskTag Task with a reminder ⏳ 2022-05-22 🔽 - [x] #globalTaskTag Done 🔽 - [ ] #globalTaskTag Task with due date 📅 2022-05-28 🔽 - [ ] #globalTaskTag Task with a flag 🔼 -- [ ] #globalTaskTag Task with a reminder ⏳ 2022-05-22 🔽 Created at: 2021-07-22T09:59:28+01:00 Updated at: 2022-05-20T23:20:12+01:00 diff --git a/test/data/test-things-to-do-standard.md b/test/data/test-things-to-do-standard.md index 92ada43a..6299852d 100644 --- a/test/data/test-things-to-do-standard.md +++ b/test/data/test-things-to-do-standard.md @@ -3,10 +3,10 @@ - [ ] Simple task +- [ ] Task with a reminder - [x] Done - [ ] Task with due date - [ ] Task with a flag -- [ ] Task with a reminder Created at: 2021-07-22T09:59:28+01:00 Updated at: 2022-05-20T23:20:12+01:00 diff --git a/test/data/test-things-to-do.md b/test/data/test-things-to-do.md index 078eeac9..7ab6cbc6 100644 --- a/test/data/test-things-to-do.md +++ b/test/data/test-things-to-do.md @@ -3,10 +3,10 @@ - [ ] Simple task 🔽 +- [ ] Task with a reminder ⏳ 2022-05-22 🔽 - [x] Done 🔽 - [ ] Task with due date 📅 2022-05-28 🔽 - [ ] Task with a flag 🔼 -- [ ] Task with a reminder ⏳ 2022-05-22 🔽 Created at: 2021-07-22T09:59:28+01:00 Updated at: 2022-05-20T23:20:12+01:00 diff --git a/test/yarle-special-cases.spec.ts b/test/yarle-special-cases.spec.ts index e9b073b7..1d1b4c85 100644 --- a/test/yarle-special-cases.spec.ts +++ b/test/yarle-special-cases.spec.ts @@ -952,7 +952,7 @@ dateFormat: undefined, }); it('tasks from Evernote v10+ - no global filter', async () => { const options: YarleOptions = { -dateFormat: undefined, + dateFormat: undefined, enexSources: [ `${testDataFolder}test-things-to-do.enex` ], outputDir: 'out', templateFile: `${testDataFolder}full_template.templ`, @@ -1135,8 +1135,6 @@ dateFormat: undefined, ); }); - - it('dots in enex file name', async () => { const options: YarleOptions = { dateFormat: undefined, @@ -1198,4 +1196,33 @@ describe('Yarle error cases', async () => { } }); + it('Tana recognization', async () => { + const options: YarleOptions = { + dateFormat: "YYYY-MM-DD", + enexSources: [ `${testDataFolder}test-tana-notes.enex` ], + outputDir: 'out', + templateFile: `${testDataFolder}sampleTemplate_tana.tmpl`, + isMetadataNeeded: true, + outputFormat: OutputFormat.Tana, + skipEnexFileNameFromOutputPath: false, + skipTags: false, + useHashTags: false, // IMPORTANT, IT HAS TO BE FALSE + generateNakedUrls: true // IMPORTANT, IT HAS TO BE TRUE + + }; + await dropTheRopeRunner.run(options); + /*assert.equal( + fs.existsSync( + `${__dirname}/../out/notes-converted-in-tana-intermediate-format.json`, + ), + true, + ); + assert.equal( + eol.auto(fs.readFileSync( + `${__dirname}/../out/notes-converted-in-tana-intermediate-format.json`, + 'utf8', + )), + fs.readFileSync(`${__dirname}/data/notes-converted-in-tana-intermediate-format.json`, 'utf8'), + );*/ + }); }); diff --git a/test/yarle-tests.ts b/test/yarle-tests.ts index a7aff784..920b43f2 100644 --- a/test/yarle-tests.ts +++ b/test/yarle-tests.ts @@ -767,7 +767,7 @@ export const yarleTests: Array = [ isMetadataNeeded: true, skipLocation: true, keepMDCharactersOfENNotes: true, - dateFormat: 'YYYY MMMM DD', + dateFormat: 'YYYY-MM-DD', }, testOutputPath: `notes${path.sep}test-justTextButCustomDate${path.sep}test -note with text only.md`, expectedOutputPath: `${dataFolder}test-justTextButCustomDate.md`,