From 62a4e14c7c8db53dfe2b407344d8d079c9084686 Mon Sep 17 00:00:00 2001 From: Mara Date: Wed, 22 May 2024 10:50:38 +0200 Subject: [PATCH] chore: switch to biome.js formatter instead of eslint the new configuration of eslint (v9) is a mess, I hate it. biome include prettier AND linter, with some good rules, including a simple configuration. For the moment, we will use it. --- .eslintignore | 2 - .eslintrc.js | 14 - biome.json | 115 + package.json | 19 +- pnpm-lock.yaml | 2117 +----------- src/@types/i18next.d.ts | 16 +- src/GitHub/branch.ts | 237 +- src/GitHub/delete.ts | 181 +- src/GitHub/files.ts | 238 +- src/GitHub/upload.ts | 236 +- src/commands/create_link.ts | 29 +- src/commands/file_menu/file.ts | 146 +- src/commands/file_menu/folder.ts | 107 +- src/commands/file_menu/index.ts | 4 +- src/commands/index.ts | 24 +- src/commands/purge.ts | 73 +- src/commands/repository_validity.ts | 29 +- src/commands/set.ts | 18 +- src/commands/share/all_notes.ts | 104 +- src/commands/share/edited_notes.ts | 131 +- src/commands/share/new_notes.ts | 76 +- src/commands/share/unique_note.ts | 115 +- .../suggest_other_repo_commands_modal.ts | 414 +-- src/conversion/compiler/dataview.ts | 113 +- src/conversion/compiler/embeds.ts | 114 +- src/conversion/compiler/excalidraw.ts | 8 +- src/conversion/file_path.ts | 1068 ++++--- src/conversion/find_and_replace_text.ts | 207 +- src/conversion/index.ts | 68 +- src/conversion/links.ts | 823 ++--- src/i18n/i18next.ts | 132 +- src/interfaces/constant.ts | 11 +- src/interfaces/enum.ts | 3 +- src/interfaces/icons.ts | 21 +- src/interfaces/index.ts | 53 +- src/interfaces/list_edited_files.ts | 8 +- src/interfaces/main.ts | 37 +- src/interfaces/properties.ts | 7 +- src/interfaces/settings.ts | 206 +- src/main.ts | 665 ++-- src/settings.ts | 2843 +++++++++-------- src/settings/help.ts | 982 +++--- src/settings/migrate.ts | 752 +++-- src/settings/modals/import_export.ts | 842 ++--- src/settings/modals/list_changed.ts | 175 +- src/settings/modals/manage_repo.ts | 1265 ++++---- src/settings/modals/popup.ts | 44 +- src/settings/modals/regex_edition.ts | 1077 ++++--- src/settings/modals/token_path.ts | 225 +- src/settings/style.ts | 336 +- src/utils/data_validation_test.ts | 240 +- src/utils/index.ts | 950 +++--- src/utils/parse_frontmatter.ts | 262 +- src/utils/status_bar.ts | 264 +- tsconfig.json | 4 +- 55 files changed, 8657 insertions(+), 9593 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.js create mode 100644 biome.json diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 32909b2e..00000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -npm node_modules -build \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index fa96c6f8..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - env: { - browser: true, - es2021: true, - node: true, - }, - root: true, - extends: [ - "@lisandra-dev/eslint-config", - ], - rules: { - "@typescript-eslint/ban-ts-comment": "off", - } -}; diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..e7b4311e --- /dev/null +++ b/biome.json @@ -0,0 +1,115 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.7.3/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": false, + "complexity": { + "noExtraBooleanCast": "error", + "noMultipleSpacesInRegularExpressionLiterals": "error", + "noUselessCatch": "error", + "noWith": "error" + }, + "correctness": { + "noConstAssign": "error", + "noConstantCondition": "error", + "noEmptyCharacterClassInRegex": "error", + "noEmptyPattern": "error", + "noGlobalObjectCalls": "error", + "noInnerDeclarations": "error", + "noInvalidConstructorSuper": "error", + "noNewSymbol": "error", + "noNonoctalDecimalEscape": "error", + "noPrecisionLoss": "error", + "noSelfAssign": "error", + "noSetterReturn": "error", + "noSwitchDeclarations": "error", + "noUndeclaredVariables": "error", + "noUnreachable": "error", + "noUnreachableSuper": "error", + "noUnsafeFinally": "error", + "noUnsafeOptionalChaining": "error", + "noUnusedLabels": "error", + "noUnusedVariables": "warn", + "noUnusedImports": "warn", + "useIsNan": "error", + "useValidForDirection": "error", + "useYield": "error" + }, + "suspicious": { + "noAssignInExpressions": "error", + "noAsyncPromiseExecutor": "error", + "noCatchAssign": "error", + "noClassAssign": "error", + "noCompareNegZero": "error", + "noControlCharactersInRegex": "error", + "noDebugger": "error", + "noDuplicateCase": "error", + "noDuplicateClassMembers": "error", + "noDuplicateObjectKeys": "error", + "noDuplicateParameters": "error", + "noEmptyBlockStatements": "error", + "noFallthroughSwitchClause": "error", + "noFunctionAssign": "error", + "noGlobalAssign": "error", + "noImportAssign": "error", + "noMisleadingCharacterClass": "error", + "noPrototypeBuiltins": "error", + "noRedeclare": "error", + "noShadowRestrictedNames": "error", + "noUnsafeNegation": "error", + "useGetterReturn": "error", + "useValidTypeof": "error" + }, + "nursery": { + "noDuplicateJsonKeys": "error" + } + }, + "ignore": [ + "**/npm node_modules", + "**/build" + ] + }, + "overrides": [ + { + "ignore": [ + "**/*.js" + ], + "include": [ + "**/*.ts", + "**/*.tsx" + ] + }, + { + "include": [ + "**/*.js" + ] + }, + { + "include": [ + "*.json" + ] + } + ], + "formatter": { + "enabled": true, + "indentStyle": "tab", + "indentWidth": 2, + "lineWidth": 90 + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "semicolons": "always", + "trailingComma": "es5" + } + }, + "json": { + "formatter": { + "trailingCommas": "none" + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index efe480be..57c84a45 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Github Publisher is a plugin that help you to send file in a configured Github Repository, based on a frontmatter entry state.", "main": "main.js", "scripts": { - "lint:fix": "eslint --ext .ts,.js,.json src/ --fix", + "lint:fix": "biome --ext .ts,.js,.json src/ --fix", "prebuild": "tsc --noEmit --skipLibCheck", "build": "node esbuild.config.mjs --production", "dev:prod": "node esbuild.config.mjs --vault", @@ -22,16 +22,14 @@ "author": "Mara", "license": "AGPL-3.0", "devDependencies": { + "@biomejs/biome": "1.7.3", "@codemirror/state": "^6.4.1", "@codemirror/view": "^6.26.3", - "@lisandra-dev/eslint-config": "^1.1.4", "@obsidian_publisher/obsidian-dataview": "^0.5.66", "@octokit/core": "^4.2.4", "@types/luxon": "^3.4.2", "@types/node": "^20.11.19", "@types/obsidian-typings": "npm:obsidian-typings@^1.1.3", - "@typescript-eslint/eslint-plugin": "^7.0.1", - "@typescript-eslint/parser": "^7.0.1", "ansi-colors": "^4.1.3", "builtin-modules": "^3.3.0", "commander": "^12.0.0", @@ -41,15 +39,6 @@ "detect-newline": "^4.0.1", "dotenv": "^16.4.4", "esbuild": "^0.21.3", - "eslint": "^8.56.0", - "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-json": "^3.1.0", - "eslint-plugin-jsonc": "^2.13.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-simple-import-sort": "^12.0.0", - "eslint-plugin-unused-imports": "^3.1.0", "i18next": "^23.9.0", "js-base64": "^3.7.6", "obsidian": "1.5.7-1", @@ -62,7 +51,7 @@ "dependencies": { "@types/electron": "npm:@ophidian/electron-types@^24.3.1", "dedent": "^1.5.1", - "ts-deepmerge": "^6.2.0" + "ts-deepmerge": "^6.2.1" }, "pnpm": { "overrides": { @@ -70,4 +59,4 @@ "got@<11.8.5": ">=11.8.5" } } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 472895e2..acf9cf9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,18 +19,18 @@ importers: specifier: ^1.5.1 version: 1.5.3 ts-deepmerge: - specifier: ^6.2.0 + specifier: ^6.2.1 version: 6.2.1 devDependencies: + '@biomejs/biome': + specifier: 1.7.3 + version: 1.7.3 '@codemirror/state': specifier: ^6.4.1 version: 6.4.1 '@codemirror/view': specifier: ^6.26.3 version: 6.26.3 - '@lisandra-dev/eslint-config': - specifier: ^1.1.4 - version: 1.1.6 '@obsidian_publisher/obsidian-dataview': specifier: ^0.5.66 version: 0.5.66 @@ -46,12 +46,6 @@ importers: '@types/obsidian-typings': specifier: npm:obsidian-typings@^1.1.3 version: obsidian-typings@1.1.3(@ophidian/electron-types@24.3.1)(@types/node@20.12.12)(obsidian@1.5.7-1(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)) - '@typescript-eslint/eslint-plugin': - specifier: ^7.0.1 - version: 7.10.0(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/parser': - specifier: ^7.0.1 - version: 7.10.0(eslint@8.57.0)(typescript@5.4.5) ansi-colors: specifier: ^4.1.3 version: 4.1.3 @@ -79,33 +73,6 @@ importers: esbuild: specifier: ^0.21.3 version: 0.21.3 - eslint: - specifier: ^8.56.0 - version: 8.57.0 - eslint-config-standard: - specifier: ^17.1.0 - version: 17.1.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint-plugin-n@16.6.2(eslint@8.57.0))(eslint-plugin-promise@6.1.1(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: - specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) - eslint-plugin-json: - specifier: ^3.1.0 - version: 3.1.0 - eslint-plugin-jsonc: - specifier: ^2.13.0 - version: 2.15.1(eslint@8.57.0) - eslint-plugin-node: - specifier: ^11.1.0 - version: 11.1.0(eslint@8.57.0) - eslint-plugin-promise: - specifier: ^6.1.1 - version: 6.1.1(eslint@8.57.0) - eslint-plugin-simple-import-sort: - specifier: ^12.0.0 - version: 12.1.0(eslint@8.57.0) - eslint-plugin-unused-imports: - specifier: ^3.1.0 - version: 3.2.0(@typescript-eslint/eslint-plugin@7.10.0(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) i18next: specifier: ^23.9.0 version: 23.11.5 @@ -156,6 +123,59 @@ packages: resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} engines: {node: '>=6.9.0'} + '@biomejs/biome@1.7.3': + resolution: {integrity: sha512-ogFQI+fpXftr+tiahA6bIXwZ7CSikygASdqMtH07J2cUzrpjyTMVc9Y97v23c7/tL1xCZhM+W9k4hYIBm7Q6cQ==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@1.7.3': + resolution: {integrity: sha512-eDvLQWmGRqrPIRY7AIrkPHkQ3visEItJKkPYSHCscSDdGvKzYjmBJwG1Gu8+QC5ed6R7eiU63LEC0APFBobmfQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@1.7.3': + resolution: {integrity: sha512-JXCaIseKRER7dIURsVlAJacnm8SG5I0RpxZ4ya3dudASYUc68WGl4+FEN03ABY3KMIq7hcK1tzsJiWlmXyosZg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@1.7.3': + resolution: {integrity: sha512-c8AlO45PNFZ1BYcwaKzdt46kYbuP6xPGuGQ6h4j3XiEDpyseRRUy/h+6gxj07XovmyxKnSX9GSZ6nVbZvcVUAw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@1.7.3': + resolution: {integrity: sha512-phNTBpo7joDFastnmZsFjYcDYobLTx4qR4oPvc9tJ486Bd1SfEVPHEvJdNJrMwUQK56T+TRClOQd/8X1nnjA9w==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@1.7.3': + resolution: {integrity: sha512-UdEHKtYGWEX3eDmVWvQeT+z05T9/Sdt2+F/7zmMOFQ7boANeX8pcO6EkJPK3wxMudrApsNEKT26rzqK6sZRTRA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@1.7.3': + resolution: {integrity: sha512-vnedYcd5p4keT3iD48oSKjOIRPYcjSNNbd8MO1bKo9ajg3GwQXZLAH+0Cvlr+eMsO67/HddWmscSQwTFrC/uPA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@1.7.3': + resolution: {integrity: sha512-unNCDqUKjujYkkSxs7gFIfdasttbDC4+z0kYmcqzRk6yWVoQBL4dNLcCbdnJS+qvVDNdI9rHp2NwpQ0WAdla4Q==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@1.7.3': + resolution: {integrity: sha512-ZmByhbrnmz/UUFYB622CECwhKIPjJLLPr5zr3edhu04LzbfcOrz16VYeNq5dpO1ADG70FORhAJkaIGdaVBG00w==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + '@codemirror/language@https://codeload.github.com/lishid/cm-language/tar.gz/2644bfc27afda707a7e1f3aedaf3ca7120f63cd9': resolution: {tarball: https://codeload.github.com/lishid/cm-language/tar.gz/2644bfc27afda707a7e1f3aedaf3ca7120f63cd9} version: 6.10.1 @@ -304,35 +324,6 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.4.0': - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.10.0': - resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@eslint/js@8.57.0': - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@humanwhocodes/config-array@0.11.14': - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - '@hutson/parse-repository-url@3.0.2': resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} engines: {node: '>=6.9.0'} @@ -364,21 +355,6 @@ packages: '@lezer/lr@1.4.0': resolution: {integrity: sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==} - '@lisandra-dev/eslint-config@1.1.6': - resolution: {integrity: sha512-2ldnrlZuCoDktWtHbB+brUjpf3ZJ9i4p4xGVedUDhpL1o6AiI29Y7UlQ4YRNU6DMZXVhV6ep4ZHCcJvevREwfw==} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - '@obsidian_publisher/obsidian-dataview@0.5.66': resolution: {integrity: sha512-+X2DqXvQpgSdVlzMxPnaXVoGTtb+rIMpchw8etTvs/qAFqlPQcKMbhZQVrHxYRWuKL3M8LyqcuyG2o2qWwEaHg==} @@ -421,9 +397,6 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - '@types/json5@0.0.29': - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/luxon@3.4.2': resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==} @@ -439,76 +412,10 @@ packages: '@types/tern@0.23.9': resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==} - '@typescript-eslint/eslint-plugin@7.10.0': - resolution: {integrity: sha512-PzCr+a/KAef5ZawX7nbyNwBDtM1HdLIT53aSA2DDlxmxMngZ43O8SIePOeX8H5S+FHXeI6t97mTt/dDdzY4Fyw==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/parser@7.10.0': - resolution: {integrity: sha512-2EjZMA0LUW5V5tGQiaa2Gys+nKdfrn2xiTIBLR4fxmPmVSvgPcKNW+AE/ln9k0A4zDUti0J/GZXMDupQoI+e1w==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/scope-manager@7.10.0': - resolution: {integrity: sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/type-utils@7.10.0': - resolution: {integrity: sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/types@7.10.0': - resolution: {integrity: sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/typescript-estree@7.10.0': - resolution: {integrity: sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/utils@7.10.0': - resolution: {integrity: sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - - '@typescript-eslint/visitor-keys@7.10.0': - resolution: {integrity: sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@ungap/structured-clone@1.2.0': - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} @@ -521,9 +428,6 @@ packages: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -540,43 +444,12 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} - array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} - array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} - array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} - engines: {node: '>= 0.4'} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} - engines: {node: '>= 0.4'} - - array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} - arrify@1.0.1: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} @@ -584,10 +457,6 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - axobject-query@4.0.0: resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} @@ -603,13 +472,6 @@ packages: brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -617,17 +479,6 @@ packages: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} - builtins@5.1.0: - resolution: {integrity: sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==} - - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - camelcase-keys@6.2.2: resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} engines: {node: '>=8'} @@ -640,10 +491,6 @@ packages: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -805,29 +652,9 @@ packages: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} - data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} - dateformat@3.0.3: resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} - debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -856,17 +683,6 @@ packages: babel-plugin-macros: optional: true - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -894,31 +710,6 @@ packages: resolution: {integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - - dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} - - domutils@3.1.0: - resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} - dot-prop@5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} @@ -944,33 +735,6 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - - es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - esbuild@0.21.3: resolution: {integrity: sha512-Kgq0/ZsAPzKrbOjCQcjoSmPoWhlcVnGAUo7jvaLHoxW1Drto0KGkR1xBNg2Cp43b9ImvxmPEJZ9xkfcnqPsfBw==} engines: {node: '>=12'} @@ -984,194 +748,13 @@ packages: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - eslint-compat-utils@0.5.0: - resolution: {integrity: sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==} - engines: {node: '>=12'} - peerDependencies: - eslint: '>=6.0.0' - - eslint-config-standard@17.1.0: - resolution: {integrity: sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==} - engines: {node: '>=12.0.0'} - peerDependencies: - eslint: ^8.0.1 - eslint-plugin-import: ^2.25.2 - eslint-plugin-n: '^15.0.0 || ^16.0.0 ' - eslint-plugin-promise: ^6.0.0 - - eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - - eslint-module-utils@2.8.1: - resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - - eslint-plugin-es-x@7.6.0: - resolution: {integrity: sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '>=8' - - eslint-plugin-es@3.0.1: - resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} - engines: {node: '>=8.10.0'} - peerDependencies: - eslint: '>=4.19.1' - - eslint-plugin-html@8.1.1: - resolution: {integrity: sha512-6qmlJsc40D2m3Dn9oEH+0PAOkJhxVu0f5sVItqpCE0YWgYnyP4xCjBc3UWTHaJcY9ARkWOLIIuXLq0ndRnQOHw==} - engines: {node: '>=16.0.0'} - - eslint-plugin-import@2.29.1: - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - - eslint-plugin-json@3.1.0: - resolution: {integrity: sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g==} - engines: {node: '>=12.0'} - - eslint-plugin-jsonc@2.15.1: - resolution: {integrity: sha512-PVFrqIJa8BbM/e828RSn0SwB/Z5ye+2LDuy2XqG6AymNgPsfApRRcznsbxP7VrjdLEU4Nb+g9n/d6opyp0jp9A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - - eslint-plugin-n@16.6.2: - resolution: {integrity: sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - eslint: '>=7.0.0' - - eslint-plugin-node@11.1.0: - resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==} - engines: {node: '>=8.10.0'} - peerDependencies: - eslint: '>=5.16.0' - - eslint-plugin-promise@6.1.1: - resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - - eslint-plugin-simple-import-sort@12.1.0: - resolution: {integrity: sha512-Y2fqAfC11TcG/WP3TrI1Gi3p3nc8XJyEOJYHyEPEGI/UAgNx6akxxlX74p7SbAQdLcgASKhj8M0GKvH3vq/+ig==} - peerDependencies: - eslint: '>=5.0.0' - - eslint-plugin-unused-imports@3.2.0: - resolution: {integrity: sha512-6uXyn6xdINEpxE1MtDjxQsyXB37lfyO2yKGVVgtD7WEWQGORSOZjgrD6hBhvGv4/SO+TOlS+UnC6JppRqbuwGQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/eslint-plugin': 6 - 7 - eslint: '8' - peerDependenciesMeta: - '@typescript-eslint/eslint-plugin': - optional: true - - eslint-rule-composer@0.3.0: - resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==} - engines: {node: '>=4.0.0'} - - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-utils@2.1.0: - resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} - engines: {node: '>=6'} - - eslint-visitor-keys@1.3.0: - resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} - engines: {node: '>=4'} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} - figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - find-up@2.1.0: resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} engines: {node: '>=4'} @@ -1188,53 +771,22 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - - flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - form-data@4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - get-pkg-repo@4.2.1: resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==} engines: {node: '>=6.9.0'} hasBin: true - get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} - - get-tsconfig@4.7.5: - resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==} - git-raw-commits@2.0.11: resolution: {integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==} engines: {node: '>=10'} @@ -1262,38 +814,9 @@ packages: gitconfiglocal@1.0.0: resolution: {integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -1303,32 +826,10 @@ packages: resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} engines: {node: '>=6'} - has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -1344,9 +845,6 @@ packages: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} - htmlparser2@9.1.0: - resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} - http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -1362,103 +860,33 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} - - is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} - is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - - is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - - is-builtin-module@3.2.1: - resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} - engines: {node: '>=6'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} - - is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} - - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} + is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - - is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - is-obj@2.0.0: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} engines: {node: '>=8'} - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - is-plain-obj@1.1.0: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} @@ -1473,39 +901,13 @@ packages: is-reference@3.0.2: resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} - is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} - - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - - is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - is-text-path@1.0.1: resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} engines: {node: '>=0.10.0'} - is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} - - is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1515,10 +917,6 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - jsdom@23.2.0: resolution: {integrity: sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==} engines: {node: '>=18'} @@ -1528,50 +926,23 @@ packages: canvas: optional: true - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-better-errors@1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - - jsonc-eslint-parser@2.4.0: - resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - jsonc-parser@3.2.1: - resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} - jsonparse@1.3.1: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - lie@3.1.1: resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==} @@ -1607,9 +978,6 @@ packages: lodash.ismatch@4.4.0: resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -1639,14 +1007,6 @@ packages: resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} engines: {node: '>=10'} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.6: - resolution: {integrity: sha512-Y4Ypn3oujJYxJcMacVgcs92wofTHxp9FzfDpQON4msDefoC0lb3ETvQLOdLcbhSwU1bz8HrL/1sygfBIHudrkQ==} - engines: {node: '>=8.6'} - mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -1662,10 +1022,6 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} - engines: {node: '>=16 || 14 >=14.17'} - minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} @@ -1683,12 +1039,6 @@ packages: ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -1708,29 +1058,6 @@ packages: resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} engines: {node: '>=10'} - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} - - object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} - - object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} - engines: {node: '>= 0.4'} - obsidian-calendar-ui@0.3.12: resolution: {integrity: sha512-hdoRqCPnukfRgCARgArXaqMQZ+Iai0eY7f0ZsFHHfywpv4gKg3Tx5p47UsLvRO5DD+4knlbrL7Gel57MkfcLTw==} @@ -1761,10 +1088,6 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - p-limit@1.3.0: resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} engines: {node: '>=4'} @@ -1804,10 +1127,6 @@ packages: papaparse@5.4.1: resolution: {integrity: sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==} - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - parse-json@4.0.0: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} engines: {node: '>=4'} @@ -1830,10 +1149,6 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -1845,20 +1160,12 @@ packages: resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} engines: {node: '>=4'} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} - engines: {node: '>=12'} - pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -1867,17 +1174,9 @@ packages: resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} engines: {node: '>=4'} - possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - preact@10.22.0: resolution: {integrity: sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==} - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - prettier@3.2.5: resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} engines: {node: '>=14'} @@ -1900,9 +1199,6 @@ packages: querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - quick-lru@4.0.1: resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} engines: {node: '>=8'} @@ -1937,14 +1233,6 @@ packages: regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} - - regexpp@3.2.0: - resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} - engines: {node: '>=8'} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -1956,45 +1244,19 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true - rrweb-cssom@0.6.0: resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} - safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -2015,14 +1277,6 @@ packages: engines: {node: '>=10'} hasBin: true - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2031,14 +1285,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - slugify@1.6.6: resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} engines: {node: '>=8.0.0'} @@ -2073,17 +1319,6 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} @@ -2102,10 +1337,6 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - style-mod@4.1.2: resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} @@ -2113,10 +1344,6 @@ packages: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -2128,17 +1355,10 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - synckit@0.6.2: - resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==} - engines: {node: '>=12.20'} - text-extensions@1.9.0: resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} engines: {node: '>=0.10'} - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} @@ -2148,10 +1368,6 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - tough-cookie@4.1.4: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} @@ -2167,12 +1383,6 @@ packages: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' - ts-dedent@2.2.0: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'} @@ -2181,27 +1391,16 @@ packages: resolution: {integrity: sha512-8CYSLazCyj0DJDpPIxOFzJG46r93uh6EynYjuey+bxcLltBeqZL7DMfaE5ZPzZNFlav7wx+2TDa/mBl8gkTYzw==} engines: {node: '>=14.13.1'} - tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tslib@2.1.0: resolution: {integrity: sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==} tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - type-fest@0.18.1: resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} engines: {node: '>=10'} - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} @@ -2210,22 +1409,6 @@ packages: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} - typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} - engines: {node: '>= 0.4'} - typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} @@ -2239,9 +1422,6 @@ packages: engines: {node: '>=0.8.0'} hasBin: true - unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -2252,9 +1432,6 @@ packages: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} @@ -2264,21 +1441,6 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - vscode-json-languageservice@4.2.1: - resolution: {integrity: sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA==} - - vscode-languageserver-textdocument@1.0.11: - resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==} - - vscode-languageserver-types@3.17.5: - resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} - - vscode-nls@5.2.0: - resolution: {integrity: sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==} - - vscode-uri@3.0.8: - resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} - w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} @@ -2308,22 +1470,11 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - - which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} @@ -2420,6 +1571,41 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 + '@biomejs/biome@1.7.3': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 1.7.3 + '@biomejs/cli-darwin-x64': 1.7.3 + '@biomejs/cli-linux-arm64': 1.7.3 + '@biomejs/cli-linux-arm64-musl': 1.7.3 + '@biomejs/cli-linux-x64': 1.7.3 + '@biomejs/cli-linux-x64-musl': 1.7.3 + '@biomejs/cli-win32-arm64': 1.7.3 + '@biomejs/cli-win32-x64': 1.7.3 + + '@biomejs/cli-darwin-arm64@1.7.3': + optional: true + + '@biomejs/cli-darwin-x64@1.7.3': + optional: true + + '@biomejs/cli-linux-arm64-musl@1.7.3': + optional: true + + '@biomejs/cli-linux-arm64@1.7.3': + optional: true + + '@biomejs/cli-linux-x64-musl@1.7.3': + optional: true + + '@biomejs/cli-linux-x64@1.7.3': + optional: true + + '@biomejs/cli-win32-arm64@1.7.3': + optional: true + + '@biomejs/cli-win32-x64@1.7.3': + optional: true + '@codemirror/language@https://codeload.github.com/lishid/cm-language/tar.gz/2644bfc27afda707a7e1f3aedaf3ca7120f63cd9': dependencies: '@codemirror/state': 6.4.1 @@ -2506,41 +1692,6 @@ snapshots: '@esbuild/win32-x64@0.21.3': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': - dependencies: - eslint: 8.57.0 - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.10.0': {} - - '@eslint/eslintrc@2.1.4': - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.1 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@8.57.0': {} - - '@humanwhocodes/config-array@0.11.14': - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/object-schema@2.0.3': {} - '@hutson/parse-repository-url@3.0.2': {} '@jridgewell/gen-mapping@0.3.5': @@ -2570,34 +1721,6 @@ snapshots: dependencies: '@lezer/common': 1.2.1 - '@lisandra-dev/eslint-config@1.1.6': - dependencies: - '@typescript-eslint/eslint-plugin': 7.10.0(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/parser': 7.10.0(eslint@8.57.0)(typescript@5.4.5) - eslint: 8.57.0 - eslint-plugin-html: 8.1.1 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) - eslint-plugin-jsonc: 2.15.1(eslint@8.57.0) - eslint-plugin-simple-import-sort: 12.1.0(eslint@8.57.0) - eslint-plugin-unused-imports: 3.2.0(@typescript-eslint/eslint-plugin@7.10.0(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) - typescript: 5.4.5 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 - '@obsidian_publisher/obsidian-dataview@0.5.66': dependencies: '@codemirror/language': https://codeload.github.com/lishid/cm-language/tar.gz/2644bfc27afda707a7e1f3aedaf3ca7120f63cd9 @@ -2670,8 +1793,6 @@ snapshots: '@types/estree@1.0.5': {} - '@types/json5@0.0.29': {} - '@types/luxon@3.4.2': {} '@types/minimist@1.2.5': {} @@ -2686,98 +1807,11 @@ snapshots: dependencies: '@types/estree': 1.0.5 - '@typescript-eslint/eslint-plugin@7.10.0(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)': - dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.10.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/scope-manager': 7.10.0 - '@typescript-eslint/type-utils': 7.10.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/utils': 7.10.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.10.0 - eslint: 8.57.0 - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.4.5) - optionalDependencies: - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5)': - dependencies: - '@typescript-eslint/scope-manager': 7.10.0 - '@typescript-eslint/types': 7.10.0 - '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.10.0 - debug: 4.3.4 - eslint: 8.57.0 - optionalDependencies: - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@7.10.0': - dependencies: - '@typescript-eslint/types': 7.10.0 - '@typescript-eslint/visitor-keys': 7.10.0 - - '@typescript-eslint/type-utils@7.10.0(eslint@8.57.0)(typescript@5.4.5)': - dependencies: - '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5) - '@typescript-eslint/utils': 7.10.0(eslint@8.57.0)(typescript@5.4.5) - debug: 4.3.4 - eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.4.5) - optionalDependencies: - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@7.10.0': {} - - '@typescript-eslint/typescript-estree@7.10.0(typescript@5.4.5)': - dependencies: - '@typescript-eslint/types': 7.10.0 - '@typescript-eslint/visitor-keys': 7.10.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.4 - semver: 7.6.2 - ts-api-utils: 1.3.0(typescript@5.4.5) - optionalDependencies: - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@7.10.0(eslint@8.57.0)(typescript@5.4.5)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 7.10.0 - '@typescript-eslint/types': 7.10.0 - '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/visitor-keys@7.10.0': - dependencies: - '@typescript-eslint/types': 7.10.0 - eslint-visitor-keys: 3.4.3 - - '@ungap/structured-clone@1.2.0': {} - JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 through: 2.3.8 - acorn-jsx@5.3.2(acorn@8.11.3): - dependencies: - acorn: 8.11.3 - acorn@8.11.3: {} add-stream@1.0.0: {} @@ -2788,13 +1822,6 @@ snapshots: transitivePeerDependencies: - supports-color - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - ansi-colors@4.1.3: {} ansi-regex@5.0.1: {} @@ -2807,72 +1834,16 @@ snapshots: dependencies: color-convert: 2.0.1 - argparse@2.0.1: {} - aria-query@5.3.0: dependencies: dequal: 2.0.3 - array-buffer-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 - array-ify@1.0.0: {} - array-includes@3.1.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 - is-string: 1.0.7 - - array-union@2.1.0: {} - - array.prototype.findlastindex@1.2.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 - - array.prototype.flat@1.3.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - - array.prototype.flatmap@1.3.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - - arraybuffer.prototype.slice@1.0.3: - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 - arrify@1.0.1: {} asynckit@0.4.0: {} - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.0.0 - axobject-query@4.0.0: dependencies: dequal: 2.0.3 @@ -2890,32 +1861,10 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.1: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - buffer-from@1.1.2: {} builtin-modules@3.3.0: {} - builtins@5.1.0: - dependencies: - semver: 7.6.2 - - call-bind@1.0.7: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - - callsites@3.1.0: {} - camelcase-keys@6.2.2: dependencies: camelcase: 5.3.1 @@ -2930,11 +1879,6 @@ snapshots: escape-string-regexp: 1.0.5 supports-color: 5.5.0 - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - cliui@7.0.4: dependencies: string-width: 4.2.3 @@ -3168,35 +2112,13 @@ snapshots: whatwg-mimetype: 4.0.0 whatwg-url: 14.0.0 - data-view-buffer@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 + dateformat@3.0.3: {} - data-view-byte-length@1.0.1: + debug@4.3.4: dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 + ms: 2.1.2 - data-view-byte-offset@1.0.0: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - dateformat@3.0.3: {} - - debug@3.2.7: - dependencies: - ms: 2.1.3 - - debug@4.3.4: - dependencies: - ms: 2.1.2 - - decamelize-keys@1.1.1: + decamelize-keys@1.1.1: dependencies: decamelize: 1.2.0 map-obj: 1.0.1 @@ -3207,20 +2129,6 @@ snapshots: dedent@1.5.3: {} - deep-is@0.1.4: {} - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - gopd: 1.0.1 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - delayed-stream@1.0.0: {} deprecation@2.3.1: {} @@ -3235,36 +2143,6 @@ snapshots: detect-newline@4.0.1: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 - - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - - dom-serializer@2.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 - - domelementtype@2.3.0: {} - - domhandler@5.0.3: - dependencies: - domelementtype: 2.3.0 - - domutils@3.1.0: - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - dot-prop@5.3.0: dependencies: is-obj: 2.0.0 @@ -3286,81 +2164,6 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-abstract@1.23.3: - dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - data-view-buffer: 1.0.1 - data-view-byte-length: 1.0.1 - data-view-byte-offset: 1.0.0 - es-define-property: 1.0.0 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 - globalthis: 1.0.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 - is-callable: 1.2.7 - is-data-view: 1.0.1 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.1 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.9 - string.prototype.trimend: 1.0.8 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.6 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 - - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 - - es-errors@1.3.0: {} - - es-object-atoms@1.0.0: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.0.3: - dependencies: - get-intrinsic: 1.2.4 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.0.2: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.2.1: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - esbuild@0.21.3: optionalDependencies: '@esbuild/aix-ppc64': 0.21.3 @@ -3391,248 +2194,14 @@ snapshots: escape-string-regexp@1.0.5: {} - escape-string-regexp@4.0.0: {} - - eslint-compat-utils@0.5.0(eslint@8.57.0): - dependencies: - eslint: 8.57.0 - semver: 7.6.2 - - eslint-config-standard@17.1.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint-plugin-n@16.6.2(eslint@8.57.0))(eslint-plugin-promise@6.1.1(eslint@8.57.0))(eslint@8.57.0): - dependencies: - eslint: 8.57.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) - eslint-plugin-n: 16.6.2(eslint@8.57.0) - eslint-plugin-promise: 6.1.1(eslint@8.57.0) - - eslint-import-resolver-node@0.3.9: - dependencies: - debug: 3.2.7 - is-core-module: 2.13.1 - resolve: 1.22.8 - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 7.10.0(eslint@8.57.0)(typescript@5.4.5) - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - - eslint-plugin-es-x@7.6.0(eslint@8.57.0): - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.10.0 - eslint: 8.57.0 - eslint-compat-utils: 0.5.0(eslint@8.57.0) - - eslint-plugin-es@3.0.1(eslint@8.57.0): - dependencies: - eslint: 8.57.0 - eslint-utils: 2.1.0 - regexpp: 3.2.0 - - eslint-plugin-html@8.1.1: - dependencies: - htmlparser2: 9.1.0 - - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0): - dependencies: - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) - hasown: 2.0.2 - is-core-module: 2.13.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 7.10.0(eslint@8.57.0)(typescript@5.4.5) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-json@3.1.0: - dependencies: - lodash: 4.17.21 - vscode-json-languageservice: 4.2.1 - - eslint-plugin-jsonc@2.15.1(eslint@8.57.0): - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - eslint: 8.57.0 - eslint-compat-utils: 0.5.0(eslint@8.57.0) - espree: 9.6.1 - graphemer: 1.4.0 - jsonc-eslint-parser: 2.4.0 - natural-compare: 1.4.0 - synckit: 0.6.2 - - eslint-plugin-n@16.6.2(eslint@8.57.0): - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - builtins: 5.1.0 - eslint: 8.57.0 - eslint-plugin-es-x: 7.6.0(eslint@8.57.0) - get-tsconfig: 4.7.5 - globals: 13.24.0 - ignore: 5.3.1 - is-builtin-module: 3.2.1 - is-core-module: 2.13.1 - minimatch: 3.1.2 - resolve: 1.22.8 - semver: 7.6.2 - - eslint-plugin-node@11.1.0(eslint@8.57.0): - dependencies: - eslint: 8.57.0 - eslint-plugin-es: 3.0.1(eslint@8.57.0) - eslint-utils: 2.1.0 - ignore: 5.3.1 - minimatch: 3.1.2 - resolve: 1.22.8 - semver: 6.3.1 - - eslint-plugin-promise@6.1.1(eslint@8.57.0): - dependencies: - eslint: 8.57.0 - - eslint-plugin-simple-import-sort@12.1.0(eslint@8.57.0): - dependencies: - eslint: 8.57.0 - - eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@7.10.0(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0): - dependencies: - eslint: 8.57.0 - eslint-rule-composer: 0.3.0 - optionalDependencies: - '@typescript-eslint/eslint-plugin': 7.10.0(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5) - - eslint-rule-composer@0.3.0: {} - - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-utils@2.1.0: - dependencies: - eslint-visitor-keys: 1.3.0 - - eslint-visitor-keys@1.3.0: {} - - eslint-visitor-keys@3.4.3: {} - - eslint@8.57.0: - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.1 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - - espree@9.6.1: - dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) - eslint-visitor-keys: 3.4.3 - - esquery@1.5.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - estree-walker@3.0.3: dependencies: '@types/estree': 1.0.5 - esutils@2.0.3: {} - - fast-deep-equal@3.1.3: {} - - fast-glob@3.3.2: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.6 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fastq@1.17.1: - dependencies: - reusify: 1.0.4 - figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 - file-entry-cache@6.0.1: - dependencies: - flat-cache: 3.2.0 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - find-up@2.1.0: dependencies: locate-path: 2.0.0 @@ -3651,47 +2220,16 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - flat-cache@3.2.0: - dependencies: - flatted: 3.3.1 - keyv: 4.5.4 - rimraf: 3.0.2 - - flatted@3.3.1: {} - - for-each@0.3.3: - dependencies: - is-callable: 1.2.7 - form-data@4.0.0: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - fs.realpath@1.0.0: {} - function-bind@1.1.2: {} - function.prototype.name@1.1.6: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - functions-have-names: 1.2.3 - - functions-have-names@1.2.3: {} - get-caller-file@2.0.5: {} - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - get-pkg-repo@4.2.1: dependencies: '@hutson/parse-repository-url': 3.0.2 @@ -3699,16 +2237,6 @@ snapshots: through2: 2.0.5 yargs: 16.2.0 - get-symbol-description@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - - get-tsconfig@4.7.5: - dependencies: - resolve-pkg-maps: 1.0.0 - git-raw-commits@2.0.11: dependencies: dargs: 7.0.0 @@ -3742,49 +2270,8 @@ snapshots: dependencies: ini: 1.3.8 - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.0.1 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.1 - merge2: 1.4.1 - slash: 3.0.0 - - gopd@1.0.1: - dependencies: - get-intrinsic: 1.2.4 - graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -3796,24 +2283,8 @@ snapshots: hard-rejection@2.1.0: {} - has-bigints@1.0.2: {} - has-flag@3.0.0: {} - has-flag@4.0.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.0 - - has-proto@1.0.3: {} - - has-symbols@1.0.3: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.0.3 - hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -3828,13 +2299,6 @@ snapshots: dependencies: whatwg-encoding: 3.1.1 - htmlparser2@9.1.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.1.0 - entities: 4.5.0 - http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 @@ -3857,88 +2321,24 @@ snapshots: dependencies: safer-buffer: 2.1.2 - ignore@5.3.1: {} - immediate@3.0.6: {} - import-fresh@3.3.0: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - imurmurhash@0.1.4: {} - indent-string@4.0.0: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - inherits@2.0.4: {} ini@1.3.8: {} - internal-slot@1.0.7: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.0.6 - - is-array-buffer@3.0.4: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - is-arrayish@0.2.1: {} - is-bigint@1.0.4: - dependencies: - has-bigints: 1.0.2 - - is-boolean-object@1.1.2: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-builtin-module@3.2.1: - dependencies: - builtin-modules: 3.3.0 - - is-callable@1.2.7: {} - is-core-module@2.13.1: dependencies: hasown: 2.0.2 - is-data-view@1.0.1: - dependencies: - is-typed-array: 1.1.13 - - is-date-object@1.0.5: - dependencies: - has-tostringtag: 1.0.2 - - is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: {} - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-negative-zero@2.0.3: {} - - is-number-object@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - - is-number@7.0.0: {} - is-obj@2.0.0: {} - is-path-inside@3.0.3: {} - is-plain-obj@1.1.0: {} is-plain-object@5.0.0: {} @@ -3949,49 +2349,18 @@ snapshots: dependencies: '@types/estree': 1.0.5 - is-regex@1.1.4: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-shared-array-buffer@1.0.3: - dependencies: - call-bind: 1.0.7 - - is-string@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - - is-symbol@1.0.4: - dependencies: - has-symbols: 1.0.3 - is-text-path@1.0.1: dependencies: text-extensions: 1.9.0 - is-typed-array@1.1.13: - dependencies: - which-typed-array: 1.1.15 - - is-weakref@1.0.2: - dependencies: - call-bind: 1.0.7 - isarray@1.0.0: {} - isarray@2.0.5: {} - isexe@2.0.0: {} js-base64@3.7.7: {} js-tokens@4.0.0: {} - js-yaml@4.1.0: - dependencies: - argparse: 2.0.1 - jsdom@23.2.0: dependencies: '@asamuzakjp/dom-selector': 2.0.2 @@ -4020,44 +2389,16 @@ snapshots: - supports-color - utf-8-validate - json-buffer@3.0.1: {} - json-parse-better-errors@1.0.2: {} json-parse-even-better-errors@2.3.1: {} - json-schema-traverse@0.4.1: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - json-stringify-safe@5.0.1: {} - json5@1.0.2: - dependencies: - minimist: 1.2.8 - - jsonc-eslint-parser@2.4.0: - dependencies: - acorn: 8.11.3 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - semver: 7.6.2 - - jsonc-parser@3.2.1: {} - jsonparse@1.3.1: {} - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - kind-of@6.0.3: {} - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - lie@3.1.1: dependencies: immediate: 3.0.6 @@ -4097,8 +2438,6 @@ snapshots: lodash.ismatch@4.4.0: {} - lodash.merge@4.6.2: {} - lodash@4.17.21: {} lru-cache@6.0.0: @@ -4131,13 +2470,6 @@ snapshots: type-fest: 0.18.1 yargs-parser: 20.2.9 - merge2@1.4.1: {} - - micromatch@4.0.6: - dependencies: - braces: 3.0.3 - picomatch: 4.0.2 - mime-db@1.52.0: {} mime-types@2.1.35: @@ -4150,10 +2482,6 @@ snapshots: dependencies: brace-expansion: 1.1.11 - minimatch@9.0.4: - dependencies: - brace-expansion: 2.0.1 - minimist-options@4.1.0: dependencies: arrify: 1.0.1 @@ -4168,10 +2496,6 @@ snapshots: ms@2.1.2: {} - ms@2.1.3: {} - - natural-compare@1.4.0: {} - neo-async@2.6.2: {} node-fetch@2.7.0: @@ -4192,36 +2516,6 @@ snapshots: semver: 7.6.2 validate-npm-package-license: 3.0.4 - object-inspect@1.13.1: {} - - object-keys@1.1.1: {} - - object.assign@4.1.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - - object.fromentries@2.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - - object.groupby@1.0.3: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - - object.values@1.2.0: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - obsidian-calendar-ui@0.3.12(@codemirror/state@6.4.1)(@codemirror/view@6.26.3): dependencies: obsidian-daily-notes-interface: 0.8.4(@codemirror/state@6.4.1)(@codemirror/view@6.26.3) @@ -4263,15 +2557,6 @@ snapshots: dependencies: wrappy: 1.0.2 - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - p-limit@1.3.0: dependencies: p-try: 1.0.0 @@ -4306,10 +2591,6 @@ snapshots: papaparse@5.4.1: {} - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - parse-json@4.0.0: dependencies: error-ex: 1.3.2 @@ -4332,8 +2613,6 @@ snapshots: path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} path-parse@1.0.7: {} @@ -4342,8 +2621,6 @@ snapshots: dependencies: pify: 3.0.0 - path-type@4.0.0: {} - periscopic@3.1.0: dependencies: '@types/estree': 1.0.5 @@ -4352,18 +2629,12 @@ snapshots: picocolors@1.0.1: {} - picomatch@4.0.2: {} - pify@2.3.0: {} pify@3.0.0: {} - possible-typed-array-names@1.0.0: {} - preact@10.22.0: {} - prelude-ls@1.2.1: {} - prettier@3.2.5: {} process-nextick-args@2.0.1: {} @@ -4376,8 +2647,6 @@ snapshots: querystringify@2.2.0: {} - queue-microtask@1.2.3: {} - quick-lru@4.0.1: {} read-pkg-up@3.0.0: @@ -4427,60 +2696,24 @@ snapshots: regenerator-runtime@0.14.1: {} - regexp.prototype.flags@1.5.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-errors: 1.3.0 - set-function-name: 2.0.2 - - regexpp@3.2.0: {} - require-directory@2.1.1: {} require-from-string@2.0.2: {} requires-port@1.0.0: {} - resolve-from@4.0.0: {} - - resolve-pkg-maps@1.0.0: {} - resolve@1.22.8: dependencies: is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - reusify@1.0.4: {} - - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - rrweb-cssom@0.6.0: {} - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safe-array-concat@1.1.2: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - isarray: 2.0.5 - safe-buffer@5.1.2: {} safe-buffer@5.2.1: {} - safe-regex-test@1.0.3: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-regex: 1.1.4 - safer-buffer@2.1.2: {} saxes@6.0.0: @@ -4493,37 +2726,12 @@ snapshots: semver@7.6.2: {} - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} - side-channel@1.0.6: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 - - slash@3.0.0: {} - slugify@1.6.6: {} source-map-js@1.2.0: {} @@ -4558,25 +2766,6 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string.prototype.trim@1.2.9: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - - string.prototype.trimend@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 @@ -4595,18 +2784,12 @@ snapshots: dependencies: min-indent: 1.0.1 - strip-json-comments@3.1.1: {} - style-mod@4.1.2: {} supports-color@5.5.0: dependencies: has-flag: 3.0.0 - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} svelte@4.2.17: @@ -4628,14 +2811,8 @@ snapshots: symbol-tree@3.2.4: {} - synckit@0.6.2: - dependencies: - tslib: 2.6.2 - text-extensions@1.9.0: {} - text-table@0.2.0: {} - through2@2.0.5: dependencies: readable-stream: 2.3.8 @@ -4647,10 +2824,6 @@ snapshots: through@2.3.8: {} - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - tough-cookie@4.1.4: dependencies: psl: 1.9.0 @@ -4666,69 +2839,20 @@ snapshots: trim-newlines@3.0.1: {} - ts-api-utils@1.3.0(typescript@5.4.5): - dependencies: - typescript: 5.4.5 - ts-dedent@2.2.0: {} ts-deepmerge@6.2.1: {} - tsconfig-paths@3.15.0: - dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 - tslib@2.1.0: {} tslib@2.6.2: {} - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - type-fest@0.18.1: {} - type-fest@0.20.2: {} - type-fest@0.6.0: {} type-fest@0.8.1: {} - typed-array-buffer@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-typed-array: 1.1.13 - - typed-array-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - - typed-array-byte-offset@1.0.2: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - - typed-array-length@1.0.6: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - possible-typed-array-names: 1.0.0 - typedarray@0.0.6: {} typescript@5.4.5: {} @@ -4736,23 +2860,12 @@ snapshots: uglify-js@3.17.4: optional: true - unbox-primitive@1.0.2: - dependencies: - call-bind: 1.0.7 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 - undici-types@5.26.5: {} universal-user-agent@6.0.1: {} universalify@0.2.0: {} - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - url-parse@1.5.10: dependencies: querystringify: 2.2.0 @@ -4765,22 +2878,6 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vscode-json-languageservice@4.2.1: - dependencies: - jsonc-parser: 3.2.1 - vscode-languageserver-textdocument: 1.0.11 - vscode-languageserver-types: 3.17.5 - vscode-nls: 5.2.0 - vscode-uri: 3.0.8 - - vscode-languageserver-textdocument@1.0.11: {} - - vscode-languageserver-types@3.17.5: {} - - vscode-nls@5.2.0: {} - - vscode-uri@3.0.8: {} - w3c-keyname@2.2.8: {} w3c-xmlserializer@5.0.0: @@ -4807,28 +2904,10 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - which-boxed-primitive@1.0.2: - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - - which-typed-array@1.1.15: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - which@2.0.2: dependencies: isexe: 2.0.0 - word-wrap@1.2.5: {} - wordwrap@1.0.0: {} wrap-ansi@7.0.0: diff --git a/src/@types/i18next.d.ts b/src/@types/i18next.d.ts index b7b6cc6f..1969a058 100644 --- a/src/@types/i18next.d.ts +++ b/src/@types/i18next.d.ts @@ -1,8 +1,8 @@ -import { resources } from "src/i18n/i18next"; - -declare module "i18next" { - interface CustomTypeOptions { - readonly resources: typeof resources["en"]; - readonly returnNull: false - } -} +import { resources } from "src/i18n/i18next"; + +declare module "i18next" { + interface CustomTypeOptions { + readonly resources: (typeof resources)["en"]; + readonly returnNull: false; + } +} diff --git a/src/GitHub/branch.ts b/src/GitHub/branch.ts index 5dcb0e58..bbf3c8aa 100644 --- a/src/GitHub/branch.ts +++ b/src/GitHub/branch.ts @@ -1,20 +1,13 @@ -import { - Properties, -} from "@interfaces/main"; +import { Properties } from "@interfaces/main"; import { Octokit } from "@octokit/core"; import i18next from "i18next"; -import {Notice } from "obsidian"; +import { Notice } from "obsidian"; import { FilesManagement } from "src/GitHub/files"; import GithubPublisher from "src/main"; import { logs, notif } from "src/utils"; - export class GithubBranch extends FilesManagement { - - constructor( - octokit: Octokit, - plugin: GithubPublisher, - ) { + constructor(octokit: Octokit, plugin: GithubPublisher) { super(octokit, plugin); } @@ -23,12 +16,8 @@ export class GithubBranch extends FilesManagement { * @param {Properties[] | Properties} prop The repo to use */ - async newBranch( - prop: Properties[] | Properties - ) { - prop = Array.isArray(prop) - ? prop - : [prop]; + async newBranch(prop: Properties[] | Properties) { + prop = Array.isArray(prop) ? prop : [prop]; for (const repo of prop) { await this.newBranchOnRepo(repo); } @@ -42,34 +31,26 @@ export class GithubBranch extends FilesManagement { * @return {Promise} True if the branch is created */ - async newBranchOnRepo( - prop: Properties - ): Promise { - const allBranch = await this.octokit.request( - "GET /repos/{owner}/{repo}/branches", - { - owner: prop.owner, - repo: prop.repo, - } - ); + async newBranchOnRepo(prop: Properties): Promise { + const allBranch = await this.octokit.request("GET /repos/{owner}/{repo}/branches", { + owner: prop.owner, + repo: prop.repo, + }); const mainBranch = allBranch.data.find( (branch: { name: string }) => branch.name === prop.branch ); if (!mainBranch) return false; try { const shaMainBranch = mainBranch!.commit.sha; - const branch = await this.octokit.request( - "POST /repos/{owner}/{repo}/git/refs", - { - owner: prop.owner, - repo: prop.repo, - ref: `refs/heads/${this.branchName}`, - sha: shaMainBranch, - } - ); + const branch = await this.octokit.request("POST /repos/{owner}/{repo}/git/refs", { + owner: prop.owner, + repo: prop.repo, + ref: `refs/heads/${this.branchName}`, + sha: shaMainBranch, + }); notif( - {settings: this.settings}, - i18next.t("publish.branch.success", {branchStatus: branch.status, repo: prop}) + { settings: this.settings }, + i18next.t("publish.branch.success", { branchStatus: branch.status, repo: prop }) ); return branch.status === 201; } catch (e) { @@ -85,10 +66,16 @@ export class GithubBranch extends FilesManagement { const mainBranch = allBranch.data.find( (branch: { name: string }) => branch.name === this.branchName ); - notif({settings: this.settings}, i18next.t("publish.branch.alreadyExists", {branchName:this.branchName, repo: prop})); + notif( + { settings: this.settings }, + i18next.t("publish.branch.alreadyExists", { + branchName: this.branchName, + repo: prop, + }) + ); return !!mainBranch; } catch (e) { - notif({settings: this.settings, e: true}, e); + notif({ settings: this.settings, e: true }, e); return false; } } @@ -101,40 +88,32 @@ export class GithubBranch extends FilesManagement { * @return {Promise} False in case of error, the pull request number otherwise */ - async pullRequestOnRepo( - prop: Properties, - ): Promise { + async pullRequestOnRepo(prop: Properties): Promise { try { - const PR = await this.octokit.request( - "POST /repos/{owner}/{repo}/pulls", - { - owner: prop.owner, - repo: prop.repo, - title: i18next.t("publish.branch.prMessage", {branchName:this.branchName}), - body: "", - head: this.branchName, - base: prop.branch, - } - ); + const PR = await this.octokit.request("POST /repos/{owner}/{repo}/pulls", { + owner: prop.owner, + repo: prop.repo, + title: i18next.t("publish.branch.prMessage", { branchName: this.branchName }), + body: "", + head: this.branchName, + base: prop.branch, + }); return PR.data.number; } catch (e) { - logs({settings: this.settings, e: true}, e); + logs({ settings: this.settings, e: true }, e); //trying to get the last open PR number try { - const PR = await this.octokit.request( - "GET /repos/{owner}/{repo}/pulls", - { - owner: prop.owner, - repo: prop.repo, - state: "open", - } - ); + const PR = await this.octokit.request("GET /repos/{owner}/{repo}/pulls", { + owner: prop.owner, + repo: prop.repo, + state: "open", + }); return PR.data[0]?.number || 0; } catch (e) { // there is no open PR and impossible to create a new one notif( - {settings: this.settings, e: true}, - i18next.t("publish.branch.error", {error: e, repo: prop}) + { settings: this.settings, e: true }, + i18next.t("publish.branch.error", { error: e, repo: prop }) ); return 0; } @@ -148,14 +127,10 @@ export class GithubBranch extends FilesManagement { * @return {Promise} true if the branch is deleted */ - async deleteBranchOnRepo( - prop: Properties - ): Promise { + async deleteBranchOnRepo(prop: Properties): Promise { try { const branch = await this.octokit.request( - "DELETE" + - " /repos/{owner}/{repo}/git/refs/heads/" + - this.branchName, + "DELETE" + " /repos/{owner}/{repo}/git/refs/heads/" + this.branchName, { owner: prop.owner, repo: prop.repo, @@ -163,7 +138,7 @@ export class GithubBranch extends FilesManagement { ); return branch.status === 200; } catch (e) { - logs({settings: this.settings, e: true}, e); + logs({ settings: this.settings, e: true }, e); return false; } } @@ -175,11 +150,11 @@ export class GithubBranch extends FilesManagement { * @param {Properties} prop The repo to use */ - async mergePullRequestOnRepo( - pullRequestNumber: number, - prop: Properties - ) { - const commitMsg = prop.commitMsg || prop.commitMsg.trim().length > 0 ? `${prop.commitMsg} #${pullRequestNumber}` : `[PUBLISHER] Merge #${pullRequestNumber}`; + async mergePullRequestOnRepo(pullRequestNumber: number, prop: Properties) { + const commitMsg = + prop.commitMsg || prop.commitMsg.trim().length > 0 + ? `${prop.commitMsg} #${pullRequestNumber}` + : `[PUBLISHER] Merge #${pullRequestNumber}`; try { const branch = await this.octokit.request( "PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge", @@ -193,7 +168,7 @@ export class GithubBranch extends FilesManagement { ); return branch.status === 200; } catch (e) { - notif({settings: this.settings, e: true}, e); + notif({ settings: this.settings, e: true }, e); new Notice(i18next.t("error.mergeconflic")); return false; } @@ -208,9 +183,7 @@ export class GithubBranch extends FilesManagement { dryRun = false ): Promise { if (dryRun) return true; - prop = Array.isArray(prop) - ? prop - : [prop]; + prop = Array.isArray(prop) ? prop : [prop]; const success: boolean[] = []; for (const repo of prop) { success.push(await this.updateRepositoryOnOne(repo)); @@ -225,19 +198,12 @@ export class GithubBranch extends FilesManagement { * @returns {Promise} true if the update is successful */ - async updateRepositoryOnOne( - prop: Properties - ): Promise { + async updateRepositoryOnOne(prop: Properties): Promise { if (this.settings.github.dryRun.enable) return true; try { - const pullRequest = await this.pullRequestOnRepo( - prop - ); + const pullRequest = await this.pullRequestOnRepo(prop); if (prop.automaticallyMergePR && pullRequest !== 0) { - const PRSuccess = await this.mergePullRequestOnRepo( - pullRequest, - prop - ); + const PRSuccess = await this.mergePullRequestOnRepo(pullRequest, prop); if (PRSuccess) { await this.deleteBranchOnRepo(prop); return true; @@ -246,15 +212,12 @@ export class GithubBranch extends FilesManagement { } return true; } catch (e) { - logs({settings: this.settings, e: true}, e); - new Notice(i18next.t("error.errorConfig", {repo: prop}) - ); + logs({ settings: this.settings, e: true }, e); + new Notice(i18next.t("error.errorConfig", { repo: prop })); return false; } } - - /** * Use octokit to check if: * - the repo exists @@ -265,66 +228,58 @@ export class GithubBranch extends FilesManagement { * @param silent Send a notice if the repo is valid * @return {Promise} */ - async checkRepository( - prop: Properties | Properties[], - silent= true): Promise - { - prop = Array.isArray(prop) - ? prop - : [prop]; + async checkRepository(prop: Properties | Properties[], silent = true): Promise { + prop = Array.isArray(prop) ? prop : [prop]; for (const repo of prop) { try { - const repoExist = await this.octokit.request("GET /repos/{owner}/{repo}", { - owner: repo.owner, - repo: repo.repo, - }).catch((e) => { - //check the error code - if (e.status === 404) { - new Notice( - (i18next.t("commands.checkValidity.inRepo.error404", {repo})) - ); - } else if (e.status === 403) { - new Notice( - (i18next.t("commands.checkValidity.inRepo.error403", {repo})) - ); - } else if (e.status === 301) { - new Notice( - (i18next.t("commands.checkValidity.inRepo.error301", {repo})) - ); - } - }); - //@ts-ignore - if (repoExist.status === 200) { - notif({settings: this.settings}, i18next.t("commands.checkValidity.repoExistsTestBranch", {repo})); - - const branchExist = await this.octokit.request("GET /repos/{owner}/{repo}/branches/{branch}", { + const repoExist = await this.octokit + .request("GET /repos/{owner}/{repo}", { owner: repo.owner, repo: repo.repo, - branch: repo.branch, - }).catch((e) => { + }) + .catch((e) => { //check the error code if (e.status === 404) { - new Notice( - (i18next.t("commands.checkValidity.inBranch.error404", { repo})) - ); + new Notice(i18next.t("commands.checkValidity.inRepo.error404", { repo })); } else if (e.status === 403) { - new Notice( - (i18next.t("commands.checkValidity.inBranch.error403", {repo})) - ); + new Notice(i18next.t("commands.checkValidity.inRepo.error403", { repo })); + } else if (e.status === 301) { + new Notice(i18next.t("commands.checkValidity.inRepo.error301", { repo })); } }); + //@ts-ignore + if (repoExist.status === 200) { + notif( + { settings: this.settings }, + i18next.t("commands.checkValidity.repoExistsTestBranch", { repo }) + ); + + const branchExist = await this.octokit + .request("GET /repos/{owner}/{repo}/branches/{branch}", { + owner: repo.owner, + repo: repo.repo, + branch: repo.branch, + }) + .catch((e) => { + //check the error code + if (e.status === 404) { + new Notice( + i18next.t("commands.checkValidity.inBranch.error404", { repo }) + ); + } else if (e.status === 403) { + new Notice( + i18next.t("commands.checkValidity.inBranch.error403", { repo }) + ); + } + }); //@ts-ignore if (branchExist.status === 200 && !silent) { - new Notice( - (i18next.t("commands.checkValidity.success", {repo})) - ); + new Notice(i18next.t("commands.checkValidity.success", { repo })); } } } catch (e) { - logs({settings: this.settings, e: true}, e); - new Notice( - (i18next.t("commands.checkValidity.error", {repo})) - ); + logs({ settings: this.settings, e: true }, e); + new Notice(i18next.t("commands.checkValidity.error", { repo })); break; } } diff --git a/src/GitHub/delete.ts b/src/GitHub/delete.ts index 5ffd4b6d..fc9bcef9 100644 --- a/src/GitHub/delete.ts +++ b/src/GitHub/delete.ts @@ -10,10 +10,19 @@ import { import { Octokit } from "@octokit/core"; import i18next from "i18next"; import { Base64 } from "js-base64"; -import { MetadataCache, normalizePath, Notice, parseYaml, TAbstractFile, TFile, TFolder, Vault } from "obsidian"; +import { + MetadataCache, + normalizePath, + Notice, + parseYaml, + TAbstractFile, + TFile, + TFolder, + Vault, +} from "obsidian"; import { FilesManagement } from "src/GitHub/files"; -import { logs, notif, trimObject} from "src/utils"; -import {isAttachment, verifyRateLimitAPI} from "src/utils/data_validation_test"; +import { logs, notif, trimObject } from "src/utils"; +import { isAttachment, verifyRateLimitAPI } from "src/utils/data_validation_test"; import { frontmatterSettingsRepository } from "src/utils/parse_frontmatter"; /** @@ -28,9 +37,8 @@ export async function deleteFromGithub( silent: boolean = false, branchName: string, filesManagement: FilesManagement, - repoProperties: MonoRepoProperties, + repoProperties: MonoRepoProperties ): Promise { - const prop = Array.isArray(repoProperties.frontmatter) ? repoProperties.frontmatter : [repoProperties.frontmatter]; @@ -41,12 +49,9 @@ export async function deleteFromGithub( repository: repoProperties.repository, convert: frontmatterSettingsRepository(filesManagement.plugin, repo), }; - deleted.push(await deleteFromGithubOneRepo( - silent, - branchName, - filesManagement, - monoProperties - )); + deleted.push( + await deleteFromGithubOneRepo(silent, branchName, filesManagement, monoProperties) + ); } return deleted[0]; //needed only for main repo (not for repo in frontmatter) } @@ -60,33 +65,34 @@ export async function deleteFromGithub( */ async function deleteFromGithubOneRepo( - silent:boolean = false, + silent: boolean = false, branchName: string, filesManagement: FilesManagement, - repoProperties: MonoRepoProperties, + repoProperties: MonoRepoProperties ): Promise { const repo = repoProperties.frontmatter; if (repo.dryRun.autoclean) return cleanDryRun(silent, filesManagement, repoProperties); - if (!repo.autoclean) return {success: false, deleted: [], undeleted: []}; - const getAllFile = await filesManagement.getAllFileFromRepo( - branchName, - repo - ); + if (!repo.autoclean) return { success: false, deleted: [], undeleted: [] }; + const getAllFile = await filesManagement.getAllFileFromRepo(branchName, repo); const settings = filesManagement.settings; const octokit = filesManagement.octokit; const filesInRepo = await filterGithubFile(getAllFile, settings, repo); if ( - (settings.github.rateLimit === 0 || filesInRepo.length > settings.github.rateLimit) - && await verifyRateLimitAPI(octokit, settings, false, filesInRepo.length) === 0 + (settings.github.rateLimit === 0 || filesInRepo.length > settings.github.rateLimit) && + (await verifyRateLimitAPI(octokit, settings, false, filesInRepo.length)) === 0 ) { console.warn("Rate limited exceeded, please try again later"); - return {success: false, deleted: [], undeleted: []}; + return { success: false, deleted: [], undeleted: [] }; } if (filesInRepo.length === 0) { - logs({settings}, `No file to delete in ${repo.owner}/${repo.repo}`); - return {success: false, deleted: [], undeleted: []}; + logs({ settings }, `No file to delete in ${repo.owner}/${repo.repo}`); + return { success: false, deleted: [], undeleted: [] }; } - const allSharedFiles = filesManagement.getAllFileWithPath(repoProperties.repository, repoProperties.convert, true); + const allSharedFiles = filesManagement.getAllFileWithPath( + repoProperties.repository, + repoProperties.convert, + true + ); const allSharedConverted = allSharedFiles.map((file) => { return { converted: file.converted, repo: file.prop, otherPath: file.otherPaths }; }); @@ -102,17 +108,15 @@ async function deleteFromGithubOneRepo( (f) => f.converted === file.file || f.otherPath?.includes(file.file) ); const isMarkdownForAnotherRepo = file.file.trim().endsWith(".md") - ? !allSharedConverted.some( - (f) => { + ? !allSharedConverted.some((f) => { let prop = f.repo; if (Array.isArray(prop)) { prop = prop.find((r) => JSON.stringify(r.repo) === JSON.stringify(repo.repo)); - } return (f.converted === file.file || f.otherPath?.includes(file.file)) && prop; + } + return (f.converted === file.file || f.otherPath?.includes(file.file)) && prop; }) : false; - const isNeedToBeDeleted = isInObsidian - ? isMarkdownForAnotherRepo - : true; + const isNeedToBeDeleted = isInObsidian ? isMarkdownForAnotherRepo : true; if (isNeedToBeDeleted) { const checkingIndex = file.file.contains(settings.upload.folderNote.rename) ? await checkIndexFiles(octokit, settings, file.file, repo) @@ -120,7 +124,7 @@ async function deleteFromGithubOneRepo( try { if (!checkingIndex) { notif( - {settings}, + { settings }, `trying to delete file : ${file.file} from ${repo.owner}/${repo.repo}` ); const reponse = await octokit.request( @@ -143,17 +147,17 @@ async function deleteFromGithubOneRepo( } } } catch (e) { - if (!(e instanceof DOMException)) logs({settings, e: true}, e); + if (!(e instanceof DOMException)) logs({ settings, e: true }, e); } } } - let successMsg = i18next.t("deletion.noFile") ; + let successMsg = i18next.t("deletion.noFile"); let failedMsg = ""; if (deletedSuccess > 0) { - successMsg = (i18next.t("deletion.success", {nb: deletedSuccess.toString()})); + successMsg = i18next.t("deletion.success", { nb: deletedSuccess.toString() }); } if (deletedFailed > 0) { - failedMsg = (i18next.t("deletion.failed", {nb: deletedFailed.toString()})); + failedMsg = i18next.t("deletion.failed", { nb: deletedFailed.toString() }); } if (!silent) { new Notice(successMsg + failedMsg); @@ -179,10 +183,7 @@ function excludedFileFromDelete( const regex = isRegex ? new RegExp(isRegex[1], isRegex[2]) : null; if (regex?.test(file)) { return true; - } else if ( - file.trim().includes(excludedFile.trim()) && - excludedFile.length > 0 - ) { + } else if (file.trim().includes(excludedFile.trim()) && excludedFile.length > 0) { return true; } } @@ -190,7 +191,6 @@ function excludedFileFromDelete( return false; } - /** * Scan all file in repo, and excluding some from the list. Also check for some parameters. * Only file supported by GitHub are checked. @@ -213,13 +213,13 @@ export async function filterGithubFile( const root = prop.path?.rootFolder ?? settings.upload.rootFolder; const defaultName = prop.path?.defaultName ?? settings.upload.defaultName; const attachmentFolder = prop.path?.attachment?.folder ?? settings.embed.folder; - const enabledAttachments = settings.upload.autoclean.includeAttachments && isAttachment(file.file, settings.embed.unHandledObsidianExt); + const enabledAttachments = + settings.upload.autoclean.includeAttachments && + isAttachment(file.file, settings.embed.unHandledObsidianExt); if ( (file.file.includes(defaultName) || - (behavior === FolderSettings.yaml && - file.file.includes(root)) || - (attachmentFolder.length > 0 && - file.file.includes(attachmentFolder))) && + (behavior === FolderSettings.yaml && file.file.includes(root)) || + (attachmentFolder.length > 0 && file.file.includes(attachmentFolder))) && !excludedFileFromDelete(file.file, settings) && (enabledAttachments || file.file.match("md$")) ) { @@ -287,7 +287,7 @@ async function checkIndexFiles( } } catch (e) { if (!(e instanceof DOMException)) { - notif({settings, e: true}, e); + notif({ settings, e: true }, e); return false; } } @@ -296,27 +296,41 @@ async function checkIndexFiles( function cleanDryRun( silent: boolean = false, - filesManagement: FilesManagement, repoProperties: MonoRepoProperties): Deleted { - const {vault, settings} = filesManagement; + filesManagement: FilesManagement, + repoProperties: MonoRepoProperties +): Deleted { + const { vault, settings } = filesManagement; const app = filesManagement.plugin.app; const repo = repoProperties.frontmatter; - const dryRunFolderPath = normalizePath(repo.dryRun.folderName - .replace("{{owner}}", repo.owner) - .replace("{{repo}}", repo.repo) - .replace("{{branch}}", repo.branch)); + const dryRunFolderPath = normalizePath( + repo.dryRun.folderName + .replace("{{owner}}", repo.owner) + .replace("{{repo}}", repo.repo) + .replace("{{branch}}", repo.branch) + ); const dryRunFolder = vault.getAbstractFileByPath(dryRunFolderPath); - if (!dryRunFolder || dryRunFolder instanceof TFile) return {success: false, deleted: [], undeleted: []}; - const dryRunFiles:TFile[] = []; + if (!dryRunFolder || dryRunFolder instanceof TFile) + return { success: false, deleted: [], undeleted: [] }; + const dryRunFiles: TFile[] = []; Vault.recurseChildren(dryRunFolder as TFolder, (file: TAbstractFile) => { - const enabledAttachments = settings.upload.autoclean.includeAttachments && isAttachment(file.path, settings.embed.unHandledObsidianExt); + const enabledAttachments = + settings.upload.autoclean.includeAttachments && + isAttachment(file.path, settings.embed.unHandledObsidianExt); if ( - !excludedFileFromDelete(normalizePath(file.path.replace(dryRunFolderPath, "")), settings) - && (enabledAttachments || file.path.match("md$")) - && file instanceof TFile) dryRunFiles.push(file); - }); - const allSharedFiles = filesManagement.getAllFileWithPath(repoProperties.repository, repoProperties.convert, true).map((file) => { - return { converted: file.converted, repo: file.prop, otherPath: file.otherPaths }; + !excludedFileFromDelete( + normalizePath(file.path.replace(dryRunFolderPath, "")), + settings + ) && + (enabledAttachments || file.path.match("md$")) && + file instanceof TFile + ) + dryRunFiles.push(file); }); + const allSharedFiles = filesManagement + .getAllFileWithPath(repoProperties.repository, repoProperties.convert, true) + .map((file) => { + return { converted: file.converted, repo: file.prop, otherPath: file.otherPaths }; + }); let deletedSuccess = 0; const result: Deleted = { deleted: [], @@ -329,20 +343,28 @@ function cleanDryRun( const isInObsidian = allSharedFiles.some( (f) => f.converted === convertedPath || f.otherPath?.includes(convertedPath) ); - const isMarkdownForAnotherRepo = file.path.trim().endsWith(".md") ? - !allSharedFiles.some( - (f) => { + const isMarkdownForAnotherRepo = file.path.trim().endsWith(".md") + ? !allSharedFiles.some((f) => { let prop = f.repo; if (Array.isArray(prop)) { prop = prop.find((r) => JSON.stringify(r.repo) === JSON.stringify(repo.repo)); - } return (f.converted === convertedPath || f.otherPath?.includes(convertedPath)) && prop; + } + return ( + (f.converted === convertedPath || f.otherPath?.includes(convertedPath)) && + prop + ); }) : false; const isNeedToBeDeleted = isInObsidian ? isMarkdownForAnotherRepo : true; if (isNeedToBeDeleted) { - const indexFile = (convertedPath.contains(settings.upload.folderNote.rename)) ? indexFileDryRun(file as TFile, app.metadataCache) : false; + const indexFile = convertedPath.contains(settings.upload.folderNote.rename) + ? indexFileDryRun(file as TFile, app.metadataCache) + : false; if (!indexFile) { - notif({settings}, `[DRYRUN] trying to delete file : ${file.path} from ${dryRunFolderPath}`); + notif( + { settings }, + `[DRYRUN] trying to delete file : ${file.path} from ${dryRunFolderPath}` + ); vault.trash(file, false); deletedSuccess++; deletedFolder.push(file); @@ -352,12 +374,15 @@ function cleanDryRun( //recursive delete empty folder in dryRunFolder //empty folder are folder with children.length === 0 - const dryRunFolders:TFolder[] = []; - Vault.recurseChildren(vault.getAbstractFileByPath(dryRunFolderPath) as TFolder, (file: TAbstractFile) => { - if (file instanceof TFolder) { - dryRunFolders.push(file); + const dryRunFolders: TFolder[] = []; + Vault.recurseChildren( + vault.getAbstractFileByPath(dryRunFolderPath) as TFolder, + (file: TAbstractFile) => { + if (file instanceof TFolder) { + dryRunFolders.push(file); + } } - }); + ); for (const folder of dryRunFolders.reverse()) { const children = folder.children.filter((child) => !deletedFolder.includes(child)); if (children.length === 0) { @@ -367,14 +392,16 @@ function cleanDryRun( } } - const successMsg = deletedSuccess > 0 ? (i18next.t("deletion.success", {nb: deletedSuccess.toString()})) : i18next.t("deletion.noFile"); - if (!silent) - new Notice(successMsg); + const successMsg = + deletedSuccess > 0 + ? i18next.t("deletion.success", { nb: deletedSuccess.toString() }) + : i18next.t("deletion.noFile"); + if (!silent) new Notice(successMsg); result.success = deletedSuccess === 0; return result; } -function indexFileDryRun(file: TFile, metadataCache: MetadataCache):boolean { +function indexFileDryRun(file: TFile, metadataCache: MetadataCache): boolean { const frontmatter = metadataCache.getFileCache(file)?.frontmatter; if (frontmatter) { const index = frontmatter.index; @@ -385,4 +412,4 @@ function indexFileDryRun(file: TFile, metadataCache: MetadataCache):boolean { } } return false; -} \ No newline at end of file +} diff --git a/src/GitHub/files.ts b/src/GitHub/files.ts index 1e3bde0f..1c951415 100644 --- a/src/GitHub/files.ts +++ b/src/GitHub/files.ts @@ -7,29 +7,26 @@ import { Repository, } from "@interfaces/main"; import { Octokit } from "@octokit/core"; -import { EmbedCache, LinkCache, TFile, TFolder} from "obsidian"; +import { EmbedCache, LinkCache, TFile, TFolder } from "obsidian"; import { getAPI, Link } from "obsidian-dataview"; -import { - getImagePath, - getReceiptFolder, -} from "src/conversion/file_path"; +import { getImagePath, getReceiptFolder } from "src/conversion/file_path"; import Publisher from "src/GitHub/upload"; import GithubPublisher from "src/main"; -import {logs} from "src/utils"; +import { logs } from "src/utils"; import { isAttachment, isShared } from "src/utils/data_validation_test"; -import { frontmatterFromFile, getFrontmatterSettings, getProperties } from "src/utils/parse_frontmatter"; +import { + frontmatterFromFile, + getFrontmatterSettings, + getProperties, +} from "src/utils/parse_frontmatter"; export class FilesManagement extends Publisher { - /** * @param {Octokit} octokit The octokit instance * @param {GitHubPublisherSettings} plugin The plugin */ - constructor( - octokit: Octokit, - plugin: GithubPublisher, - ) { + constructor(octokit: Octokit, plugin: GithubPublisher) { super(octokit, plugin); } @@ -43,14 +40,12 @@ export class FilesManagement extends Publisher { const shared_File: TFile[] = []; for (const file of files) { try { - const frontMatter = this.metadataCache.getCache( - file.path - )?.frontmatter; + const frontMatter = this.metadataCache.getCache(file.path)?.frontmatter; if (isShared(frontMatter, this.settings, file, repo)) { shared_File.push(file); } - } catch(e) { - logs({settings: this.settings, e: true}, e); + } catch (e) { + logs({ settings: this.settings, e: true }, e); } } return shared_File; @@ -62,19 +57,23 @@ export class FilesManagement extends Publisher { * @param {Repository | null} repo The repository * @return {TFile[]} The shared files */ - getSharedFileOfFolder(folder: TFolder, repo: Repository | null, addFolderNote?: boolean): TFile[] { + getSharedFileOfFolder( + folder: TFolder, + repo: Repository | null, + addFolderNote?: boolean + ): TFile[] { const files: TFile[] = []; for (const file of folder.children) { if (file instanceof TFolder) { - files.push(... this.getSharedFileOfFolder(file, repo)); + files.push(...this.getSharedFileOfFolder(file, repo)); } else { try { const frontMatter = this.metadataCache.getCache(file.path)?.frontmatter; if (isShared(frontMatter, this.settings, file as TFile, repo)) { files.push(file as TFile); } - } catch(e) { - logs({settings: this.settings, e: true}, e); + } catch (e) { + logs({ settings: this.settings, e: true }, e); } } } @@ -83,10 +82,13 @@ export class FilesManagement extends Publisher { const folderNote = this.vault.getAbstractFileByPath(`${folder.path}.md`); if (folderNote && folderNote instanceof TFile) { const frontMatter = this.metadataCache.getCache(folderNote.path)?.frontmatter; - if (isShared(frontMatter, this.settings, folderNote, repo) && !files.includes(folderNote)) { + if ( + isShared(frontMatter, this.settings, folderNote, repo) && + !files.includes(folderNote) + ) { files.push(folderNote); } - } + } } return [...new Set(files)]; //prevent duplicate; } @@ -99,8 +101,14 @@ export class FilesManagement extends Publisher { * @return {ConvertedLink[]} The shared files */ - getAllFileWithPath(repo: Repository | null, convert: PropertiesConversion, withBackLinks?: boolean): ConvertedLink[] { - const files = this.vault.getFiles().filter((x) => !x.path.startsWith(this.settings.github.dryRun.folderName)); + getAllFileWithPath( + repo: Repository | null, + convert: PropertiesConversion, + withBackLinks?: boolean + ): ConvertedLink[] { + const files = this.vault + .getFiles() + .filter((x) => !x.path.startsWith(this.settings.github.dryRun.folderName)); const allFileWithPath: ConvertedLink[] = []; const sourceFrontmatter = getProperties(this.plugin, repo, null, true); for (const file of files) { @@ -109,16 +117,12 @@ export class FilesManagement extends Publisher { allFileWithPath.push({ converted: filepath, real: file, - otherPaths: this.getBackLinksOfImage(file, repo, withBackLinks) + otherPaths: this.getBackLinksOfImage(file, repo, withBackLinks), }); } else if (file.extension == "md") { const frontMatter = frontmatterFromFile(file, this.plugin, repo); if (isShared(frontMatter, this.settings, file, repo)) { - const prop = getProperties( - this.plugin, - repo, - frontMatter - ); + const prop = getProperties(this.plugin, repo, frontMatter); const filepath = getReceiptFolder(file, repo, this.plugin, prop); allFileWithPath.push({ converted: filepath, @@ -131,7 +135,11 @@ export class FilesManagement extends Publisher { return allFileWithPath; } - getBackLinksOfImage(file: TFile, repository: Repository | null, withBackLinks?: boolean): string[] | undefined { + getBackLinksOfImage( + file: TFile, + repository: Repository | null, + withBackLinks?: boolean + ): string[] | undefined { if (!withBackLinks) return undefined; const backlinks = this.metadataCache.getBacklinksForFile(file); if (backlinks.count() === 0) return undefined; @@ -142,7 +150,11 @@ export class FilesManagement extends Publisher { if (tfile && tfile instanceof TFile) { const frontmatter = this.metadataCache.getFileCache(tfile)?.frontmatter; if (!frontmatter) continue; - const propertiesConversion = getFrontmatterSettings(frontmatter, this.settings, repository); + const propertiesConversion = getFrontmatterSettings( + frontmatter, + this.settings, + repository + ); const properties = getProperties(this.plugin, repository, frontmatter); const path = getImagePath(file, this.plugin, propertiesConversion, properties); otherPath.push(path); @@ -151,7 +163,6 @@ export class FilesManagement extends Publisher { return otherPath.length > 0 ? otherPath : undefined; } - /** * Create a database with every internal links and embedded image and files * Used for the links conversion (for markdown links and receipt folder) @@ -169,18 +180,24 @@ export class FilesManagement extends Publisher { file.path ); if (imageLink !== null) { - let altText = image.displayText !== imageLink.path.replace(".md", "") ? image.displayText : imageLink.basename; + let altText = + image.displayText !== imageLink.path.replace(".md", "") + ? image.displayText + : imageLink.basename; let frontmatterDestinationFilePath; if (this.settings.upload.frontmatterTitle.enable) { - const frontmatter = this.metadataCache.getCache(imageLink.path)?.frontmatter; + const frontmatter = this.metadataCache.getCache( + imageLink.path + )?.frontmatter; /** * In case there's a frontmatter configuration, pass along * `filename` so we can later use that to convert wikilinks. */ if (frontmatter?.[this.settings.upload.frontmatterTitle.key]) { - frontmatterDestinationFilePath = frontmatter[this.settings.upload.frontmatterTitle.key]; + frontmatterDestinationFilePath = + frontmatter[this.settings.upload.frontmatterTitle.key]; if (altText === imageLink.basename) { altText = frontmatterDestinationFilePath; } @@ -195,7 +212,7 @@ export class FilesManagement extends Publisher { position: { start: image.position.start.offset, end: image.position.end.offset, - } + }, }; if (image.link.includes("#")) { thisLinkedFile.anchor = `#${image.link.split("#")[1]}`; @@ -203,7 +220,7 @@ export class FilesManagement extends Publisher { linkedFiles.push(thisLinkedFile); } } catch (e) { - logs({settings: this.settings}, e); + logs({ settings: this.settings }, e); } } } @@ -227,18 +244,25 @@ export class FilesManagement extends Publisher { file.path ); if (linkedFile) { - let altText = embedCache.original.match(/\[.*\]\(.*\)/) ? embedCache.original!.match(/\[(.*)\]/)![1] : embedCache.displayText !== linkedFile.path.replace(".md", "") ? embedCache.displayText : linkedFile.basename; + let altText = embedCache.original.match(/\[.*\]\(.*\)/) + ? embedCache.original!.match(/\[(.*)\]/)![1] + : embedCache.displayText !== linkedFile.path.replace(".md", "") + ? embedCache.displayText + : linkedFile.basename; let frontmatterDestinationFilePath; if (this.settings.upload.frontmatterTitle.enable) { - const frontmatter = this.metadataCache.getCache(linkedFile.path)?.frontmatter; + const frontmatter = this.metadataCache.getCache( + linkedFile.path + )?.frontmatter; /** * In case there's a frontmatter configuration, pass along * `filename` so we can later use that to convert wikilinks. */ if (frontmatter?.[this.settings.upload.frontmatterTitle.key]) { - frontmatterDestinationFilePath = frontmatter[this.settings.upload.frontmatterTitle.key]; + frontmatterDestinationFilePath = + frontmatter[this.settings.upload.frontmatterTitle.key]; if (altText === linkedFile.basename) { altText = frontmatterDestinationFilePath; } @@ -258,7 +282,7 @@ export class FilesManagement extends Publisher { } } catch (e) { logs( - {settings: this.settings, e: true}, + { settings: this.settings, e: true }, `Error with this links : ${embedCache.link}`, e ); @@ -277,43 +301,54 @@ export class FilesManagement extends Publisher { * @param {PropertiesConversion} frontmatterSourceFile frontmatter of the file * @return {TFile[]} the file embedded & shared in form of an array of TFile */ - getSharedEmbed( - file: TFile, - frontmatterSourceFile: PropertiesConversion, - ): TFile[] { + getSharedEmbed(file: TFile, frontmatterSourceFile: PropertiesConversion): TFile[] { const embedCaches = this.metadataCache.getCache(file.path)?.embeds ?? []; const cacheLinks = this.metadataCache.getCache(file.path)?.links ?? []; const imageList: TFile[] = []; - - for(const embed of embedCaches) { - const embedFile = this.checkIfFileIsShared(embed, file, frontmatterSourceFile, "embed"); + + for (const embed of embedCaches) { + const embedFile = this.checkIfFileIsShared( + embed, + file, + frontmatterSourceFile, + "embed" + ); if (embedFile) imageList.push(embedFile); } - - for(const link of cacheLinks) { - const linkFile = this.checkIfFileIsShared(link, file, frontmatterSourceFile, "link"); + + for (const link of cacheLinks) { + const linkFile = this.checkIfFileIsShared( + link, + file, + frontmatterSourceFile, + "link" + ); if (linkFile) imageList.push(linkFile); } - + return [...new Set(imageList)].filter((x) => x !== null); - } - private checkIfFileIsShared( - embed: EmbedCache | LinkCache, - file: TFile, + embed: EmbedCache | LinkCache, + file: TFile, frontmatterSourceFile: PropertiesConversion, - fromWhat: "link" | "embed"): TFile | undefined{ + fromWhat: "link" | "embed" + ): TFile | undefined { try { const imageLink = this.metadataCache.getFirstLinkpathDest( embed.link.replace(/#(.*)/, ""), file.path ); - if (imageLink) return(this.imageSharedOrNote(imageLink as TFile, frontmatterSourceFile, fromWhat) as TFile); + if (imageLink) + return this.imageSharedOrNote( + imageLink as TFile, + frontmatterSourceFile, + fromWhat + ) as TFile; } catch (e) { logs( - {settings: this.settings, e: true}, + { settings: this.settings, e: true }, `Error with this file : ${embed.displayText}`, e ); @@ -327,19 +362,19 @@ export class FilesManagement extends Publisher { * @return {Promise} */ - async getLastEditedTimeRepo( - githubRepo: GithubRepo, - ): Promise { - const commits = await this.octokit.request( - "GET /repos/{owner}/{repo}/commits", - { - owner: this.settings.github.user, - repo: this.settings.github.repo, - path: githubRepo.file, - } - ); + async getLastEditedTimeRepo(githubRepo: GithubRepo): Promise { + const commits = await this.octokit.request("GET /repos/{owner}/{repo}/commits", { + owner: this.settings.github.user, + repo: this.settings.github.repo, + path: githubRepo.file, + }); const lastCommittedFile = commits.data[0]; - if (!lastCommittedFile || !lastCommittedFile.commit || !lastCommittedFile.commit.committer || !lastCommittedFile.commit.committer.date) { + if ( + !lastCommittedFile || + !lastCommittedFile.commit || + !lastCommittedFile.commit.committer || + !lastCommittedFile.commit.committer.date + ) { return null; } return new Date(lastCommittedFile.commit.committer.date); @@ -369,12 +404,8 @@ export class FilesManagement extends Publisher { const files = repoContents.data.tree; for (const file of files) { if (!file.path || !file.sha) continue; - const basename = (name: string) => - /([^/\\.]*)(\..*)?$/.exec(name)![1]; //don't delete file starting with . - if ( - file.type === "blob" && - basename(file.path).length > 0 - ) { + const basename = (name: string) => /([^/\\.]*)(\..*)?$/.exec(name)![1]; //don't delete file starting with . + if (file.type === "blob" && basename(file.path).length > 0) { filesInRepo.push({ file: file.path, sha: file.sha, @@ -383,7 +414,7 @@ export class FilesManagement extends Publisher { } } } catch (e) { - logs({settings: this.settings, e: true}, e); + logs({ settings: this.settings, e: true }, e); } return filesInRepo; } @@ -398,18 +429,14 @@ export class FilesManagement extends Publisher { getNewFiles( allFileWithPath: ConvertedLink[], - githubSharedFiles: GithubRepo[], + githubSharedFiles: GithubRepo[] ): TFile[] { const newFiles: TFile[] = []; //new file : present in allFileswithPath but not in githubSharedFiles for (const file of allFileWithPath) { - if ( - !githubSharedFiles.some((x) => x.file === file.converted.trim()) - ) { + if (!githubSharedFiles.some((x) => x.file === file.converted.trim())) { //get TFile from file - const fileInVault = this.vault.getAbstractFileByPath( - file.real.path.trim() - ); + const fileInVault = this.vault.getAbstractFileByPath(file.real.path.trim()); if ( fileInVault && fileInVault instanceof TFile && @@ -465,10 +492,15 @@ export class FilesManagement extends Publisher { settingsConversion: PropertiesConversion, fromWhat: "embed" | "link" = "embed" ): undefined | TFile { - const transferImage = fromWhat === "embed" ? settingsConversion.attachment : settingsConversion.includeLinks; - const transferEmbeds = fromWhat === "embed" ? settingsConversion.embed : settingsConversion.includeLinks; + const transferImage = + fromWhat === "embed" + ? settingsConversion.attachment + : settingsConversion.includeLinks; + const transferEmbeds = + fromWhat === "embed" ? settingsConversion.embed : settingsConversion.includeLinks; if ( - (isAttachment(file.name, this.settings.embed.unHandledObsidianExt) && transferImage) || + (isAttachment(file.name, this.settings.embed.unHandledObsidianExt) && + transferImage) || (transferEmbeds && file.extension === "md") ) { return file; @@ -502,10 +534,9 @@ export class FilesManagement extends Publisher { }); } for (const path of imageLinkPath) { - const imageLink = this.metadataCache.getFirstLinkpathDest( - path, - file.path - ) ?? this.vault.getAbstractFileByPath(path); + const imageLink = + this.metadataCache.getFirstLinkpathDest(path, file.path) ?? + this.vault.getAbstractFileByPath(path); if (imageLink instanceof TFile && !embedFiles.includes(imageLink)) { embedFiles.push( this.imageSharedOrNote(imageLink, frontmatterSettings) as TFile @@ -526,11 +557,7 @@ export class FilesManagement extends Publisher { if (fieldValue.constructor.name === "Array") { // @ts-ignore for (const value of fieldValue) { - const path = this.getImageByPath( - file.path, - value, - frontmatterSettings - ); + const path = this.getImageByPath(file.path, value, frontmatterSettings); if (path) embedFiles.push(path); } } else { @@ -539,8 +566,7 @@ export class FilesManagement extends Publisher { fieldValue.toString() as string, frontmatterSettings ); - if (path) - embedFiles.push(path); + if (path) embedFiles.push(path); } } } @@ -564,17 +590,13 @@ export class FilesManagement extends Publisher { newFiles: TFile[] ): Promise { for (const file of allFileWithPath) { - if ( - githubSharedFiles.some((x) => x.file === file.converted.trim()) - ) { + if (githubSharedFiles.some((x) => x.file === file.converted.trim())) { const githubSharedFile = githubSharedFiles.find( (x) => x.file === file.converted.trim() ); if (!githubSharedFile) continue; const repoEditedTime = await this.getLastEditedTimeRepo(githubSharedFile); - const fileInVault = this.vault.getAbstractFileByPath( - file.real.path.trim() - ); + const fileInVault = this.vault.getAbstractFileByPath(file.real.path.trim()); if ( fileInVault && fileInVault instanceof TFile && @@ -583,7 +605,7 @@ export class FilesManagement extends Publisher { const vaultEditedTime = new Date(fileInVault.stat.mtime); if (repoEditedTime && vaultEditedTime > repoEditedTime) { logs( - {settings: this.settings}, + { settings: this.settings }, `edited file : ${fileInVault.path} / ${vaultEditedTime} vs ${repoEditedTime}` ); newFiles.push(fileInVault); diff --git a/src/GitHub/upload.ts b/src/GitHub/upload.ts index e9d1b770..e4c55a57 100644 --- a/src/GitHub/upload.ts +++ b/src/GitHub/upload.ts @@ -1,11 +1,13 @@ import { Deleted, GitHubPublisherSettings, - MetadataExtractor, MonoProperties, + MetadataExtractor, + MonoProperties, MonoRepoProperties, MultiProperties, MultiRepoProperties, - Properties, UploadedFiles, + Properties, + UploadedFiles, } from "@interfaces"; import { LOADING_ICON } from "@interfaces/icons"; import { Octokit } from "@octokit/core"; @@ -23,19 +25,11 @@ import { } from "obsidian"; import { mainConverting } from "src/conversion"; import { convertToHTMLSVG } from "src/conversion/compiler/excalidraw"; -import { - getImagePath, - getReceiptFolder, -} from "src/conversion/file_path"; +import { getImagePath, getReceiptFolder } from "src/conversion/file_path"; import { deleteFromGithub } from "src/GitHub/delete"; import { FilesManagement } from "src/GitHub/files"; import GithubPublisher from "src/main"; -import { - logs, - noticeMobile, - notif, - notifError, -} from "src/utils"; +import { logs, noticeMobile, notif, notifError } from "src/utils"; import { checkEmptyConfiguration, checkIfRepoIsInAnother, @@ -43,7 +37,12 @@ import { isAttachment, isShared, } from "src/utils/data_validation_test"; -import { frontmatterFromFile, frontmatterSettingsRepository, getFrontmatterSettings, getProperties } from "src/utils/parse_frontmatter"; +import { + frontmatterFromFile, + frontmatterSettingsRepository, + getFrontmatterSettings, + getProperties, +} from "src/utils/parse_frontmatter"; import { ShareStatusBar } from "src/utils/status_bar"; import merge from "ts-deepmerge"; @@ -65,10 +64,7 @@ export default class Publisher { * @param {GithubPublisher} plugin GithubPublisher instance */ - constructor( - octokit: Octokit, - plugin: GithubPublisher, - ) { + constructor(octokit: Octokit, plugin: GithubPublisher) { this.vault = plugin.app.vault; this.metadataCache = plugin.app.metadataCache; this.settings = plugin.settings; @@ -88,18 +84,13 @@ export default class Publisher { linkedFiles: TFile[], fileHistory: TFile[], deepScan: boolean, - properties: MonoProperties, - + properties: MonoProperties ) { const uploadedFile: UploadedFiles[] = []; const fileError: string[] = []; if (linkedFiles.length > 0) { const statusBarItems = this.plugin.addStatusBarItem(); - const statusBar = new ShareStatusBar( - statusBarItems, - linkedFiles.length, - true - ); + const statusBar = new ShareStatusBar(statusBarItems, linkedFiles.length, true); const prop = properties.frontmatter.prop; const repoProperties: MonoRepoProperties = { frontmatter: properties.frontmatter.prop, @@ -127,23 +118,16 @@ export default class Publisher { isAttachment(file.name, this.settings.embed.unHandledObsidianExt) && properties.frontmatter.general.attachment ) { - const published = await this.uploadImage( - file, - properties - ); + const published = await this.uploadImage(file, properties); fileHistory.push(file); if (published) { uploadedFile.push(published); } - } } statusBar.increment(); } catch (e) { - new Notice( - (i18next.t("error.unablePublishNote", { file: file.name }) - ) - ); + new Notice(i18next.t("error.unablePublishNote", { file: file.name })); fileError.push(file.name); logs({ settings: this.settings, e: true }, e); } @@ -176,24 +160,20 @@ export default class Publisher { repo: MultiRepoProperties | MonoRepoProperties, fileHistory: TFile[] = [], deepScan: boolean = false, - sourceFrontmatter: FrontMatterCache | null | undefined, + sourceFrontmatter: FrontMatterCache | null | undefined ) { - const shareFiles = new FilesManagement( - this.octokit, - this.plugin, - ); + const shareFiles = new FilesManagement(this.octokit, this.plugin); let frontmatter = frontmatterFromFile(file, this.plugin, null); - if (sourceFrontmatter && frontmatter) frontmatter = merge(sourceFrontmatter, frontmatter); + if (sourceFrontmatter && frontmatter) + frontmatter = merge(sourceFrontmatter, frontmatter); const prop = getProperties(this.plugin, repo.repository, frontmatter); const isNotEmpty = await checkEmptyConfiguration(prop, this.plugin); repo.frontmatter = prop; if ( !isShared(frontmatter, this.settings, file, repo.repository) || fileHistory.includes(file) || - !checkIfRepoIsInAnother( - prop, - repo.frontmatter - ) || !isNotEmpty + !checkIfRepoIsInAnother(prop, repo.frontmatter) || + !isNotEmpty ) { return false; } @@ -205,19 +185,22 @@ export default class Publisher { this.settings, repo.repository ); - const frontmatterRepository = frontmatterSettingsRepository(this.plugin, repo.repository); - const frontmatterSettings = merge(frontmatterRepository, frontmatterSettingsFromFile); - let embedFiles = shareFiles.getSharedEmbed( - file, - frontmatterSettings, + const frontmatterRepository = frontmatterSettingsRepository( + this.plugin, + repo.repository + ); + const frontmatterSettings = merge( + frontmatterRepository, + frontmatterSettingsFromFile ); + let embedFiles = shareFiles.getSharedEmbed(file, frontmatterSettings); embedFiles = await shareFiles.getMetadataLinks( file, embedFiles, frontmatterSettings ); const linkedFiles = shareFiles.getLinkedByEmbedding(file); - + let text = await this.vault.cachedRead(file); const multiProperties: MultiProperties = { plugin: this.plugin, @@ -248,23 +231,22 @@ export default class Publisher { frontmatter: { general: frontmatterSettings, prop: repo, - source: sourceFrontmatter + source: sourceFrontmatter, }, repository: multiProperties.repository, filepath: multiProperties.filepath, }; - const deleted = - await this.uploadOnMultipleRepo( - file, - text, - path, - embedFiles, - fileHistory, - deepScan, - shareFiles, - autoclean, - monoProperties - ); + const deleted = await this.uploadOnMultipleRepo( + file, + text, + path, + embedFiles, + fileHistory, + deepScan, + shareFiles, + autoclean, + monoProperties + ); fileDeleted.push(deleted.deleted); // convert to UploadedFiles[] updated.push(deleted.uploaded); @@ -299,12 +281,16 @@ export default class Publisher { deepScan: boolean, shareFiles: FilesManagement, autoclean: boolean, - properties: MonoProperties, + properties: MonoProperties ) { const load = this.plugin.addStatusBarItem(); //add a little load icon from lucide icons, using SVG - load.createEl("span", { cls: ["obsidian-publisher", "loading", "icons"] }).innerHTML = LOADING_ICON; - load.createEl("span", { text: i18next.t("statusBar.loading"), cls: ["obsidian-publisher", "loading", "icons"] }); + load.createEl("span", { cls: ["obsidian-publisher", "loading", "icons"] }).innerHTML = + LOADING_ICON; + load.createEl("span", { + text: i18next.t("statusBar.loading"), + cls: ["obsidian-publisher", "loading", "icons"], + }); embedFiles = await this.cleanLinkedImageIfAlreadyInRepo(embedFiles, properties); const repo = properties.frontmatter.prop; notif( @@ -319,12 +305,19 @@ export default class Publisher { }; load.remove(); notifMob?.hide(); - const uploaded: UploadedFiles | undefined = await this.uploadText(text, path, file.name, repo); + const uploaded: UploadedFiles | undefined = await this.uploadText( + text, + path, + file.name, + repo + ); if (!uploaded) { return { deleted, uploaded: [], - error: [`Error while uploading ${file.name} to ${repo.owner}/${repo.repo}/${repo.branch}`] + error: [ + `Error while uploading ${file.name} to ${repo.owner}/${repo.repo}/${repo.branch}`, + ], }; } const embeded = await this.statusBarForEmbed( @@ -337,21 +330,16 @@ export default class Publisher { const embeddedUploaded = embeded.uploaded; embeddedUploaded.push(uploaded); if (autoclean || repo.dryRun.autoclean) { - deleted = await deleteFromGithub( - true, - this.branchName, - shareFiles, - { - frontmatter: repo, - repository: properties.repository, - convert: properties.frontmatter.general, - } - ); + deleted = await deleteFromGithub(true, this.branchName, shareFiles, { + frontmatter: repo, + repository: properties.repository, + convert: properties.frontmatter.general, + }); } return { deleted, uploaded: embeddedUploaded, - error: embeded.error + error: embeded.error, }; } @@ -363,12 +351,7 @@ export default class Publisher { * @param {Properties} prop frontmatter settings */ - async upload( - content: string, - path: string, - title: string = "", - prop: Properties - ) { + async upload(content: string, path: string, title: string = "", prop: Properties) { if (!prop.repo) { new Notice( "Config error : You need to define a github repo in the plugin settings" @@ -398,7 +381,7 @@ export default class Publisher { }; const result: UploadedFiles = { isUpdated: false, - file: title + file: title, }; try { const response = await octokit.request( @@ -417,17 +400,11 @@ export default class Publisher { result.isUpdated = true; } } catch { - logs( - { settings: this.settings }, - i18next.t("error.normal") - ); + logs({ settings: this.settings }, i18next.t("error.normal")); } payload.message = msg; - await octokit.request( - "PUT /repos/{owner}/{repo}/contents/{path}", - payload - ); + await octokit.request("PUT /repos/{owner}/{repo}/contents/{path}", payload); return result; } @@ -435,10 +412,7 @@ export default class Publisher { * Convert image in base64 and upload it to GitHub */ - async uploadImage( - imageFile: TFile, - properties: MonoProperties, - ) { + async uploadImage(imageFile: TFile, properties: MonoProperties) { let imageBin = await this.vault.readBinary(imageFile); const prop = properties.frontmatter.prop; let image64 = arrayBufferToBase64(imageBin); @@ -470,9 +444,9 @@ export default class Publisher { } return { isUpdated: needToByUpdated, - file: imageFile.name + file: imageFile.name, }; - } + } const folder = dryRunPath.split("/").slice(0, -1).join("/"); const folderExists = this.vault.getAbstractFileByPath(folder); if (!folderExists || !(folderExists instanceof TFolder)) @@ -480,11 +454,10 @@ export default class Publisher { await this.vault.createBinary(dryRunPath, imageBin); return { isUpdated: true, - file: imageFile.name + file: imageFile.name, }; } return await this.upload(image64, path, "", properties.frontmatter.prop); - } /** @@ -500,7 +473,7 @@ export default class Publisher { text: string, path: string, title: string = "", - prop: Properties, + prop: Properties ): Promise { if (this.settings.github.dryRun.enable) { //create a new file in the vault @@ -516,7 +489,7 @@ export default class Publisher { await this.vault.modify(isAlreadyExist, text); return { isUpdated: true, - file: title + file: title, }; } //create const folder = newPath.split("/").slice(0, -1).join("/"); @@ -526,17 +499,12 @@ export default class Publisher { await this.vault.create(newPath, text); return { isUpdated: false, - file: title + file: title, }; } try { const contentBase64 = Base64.encode(text).toString(); - return await this.upload( - contentBase64, - path, - title, - prop - ); + return await this.upload(contentBase64, path, title, prop); } catch (e) { notif({ settings: this.settings, e: true }, e); return undefined; @@ -560,19 +528,10 @@ export default class Publisher { if (file) { const contents = await this.vault.adapter.read(file); const path = - this.settings.upload.metadataExtractorPath + - "/" + - file.split("/").pop(); - prop = Array.isArray(prop) - ? prop - : [prop]; + this.settings.upload.metadataExtractorPath + "/" + file.split("/").pop(); + prop = Array.isArray(prop) ? prop : [prop]; for (const repo of prop) { - await this.uploadText( - contents, - path, - file.split("/").pop(), - repo - ); + await this.uploadText(contents, path, file.split("/").pop(), repo); } } } @@ -612,9 +571,7 @@ export default class Publisher { ); if (workflowGet.data.workflow_runs.length > 0) { const build = workflowGet.data.workflow_runs.find( - (run) => - run.name === - prop.workflowName.replace(".yml", "").replace(".yaml", "") + (run) => run.name === prop.workflowName.replace(".yml", "").replace(".yaml", "") ); if (build && build.status === "completed") { finished = true; @@ -625,7 +582,6 @@ export default class Publisher { return false; } - /** * Remove all image embed in the note if they are already in the repo (same path) * Skip for files, as they are updated by GitHub directly (basic git behavior) @@ -635,7 +591,7 @@ export default class Publisher { */ async cleanLinkedImageIfAlreadyInRepo( embedFiles: TFile[], - properties: MonoProperties, + properties: MonoProperties ): Promise { const newLinkedFiles: TFile[] = []; for (const file of embedFiles) { @@ -664,31 +620,42 @@ export default class Publisher { repo: prop.prop.repo, path: imagePath, ref: this.branchName, - }); + } + ); if (response.status === 200) { - const reply = await this.octokit.request( + const reply = await this.octokit.request( "GET /repos/{owner}/{repo}/commits", { owner: prop.prop.owner, repo: prop.prop.repo, path: imagePath, sha: this.branchName, - }); + } + ); if (reply.status === 200) { const data = reply.data; const lastEditedInRepo = data[0]?.commit?.committer?.date; - const lastEditedDate = lastEditedInRepo ? new Date(lastEditedInRepo) : undefined; + const lastEditedDate = lastEditedInRepo + ? new Date(lastEditedInRepo) + : undefined; const lastEditedAttachment = new Date(file.stat.mtime); //if the file in the vault is newer than the file in the repo, push it - if (lastEditedDate && lastEditedAttachment > lastEditedDate || !lastEditedDate) { + if ( + (lastEditedDate && lastEditedAttachment > lastEditedDate) || + !lastEditedDate + ) { newLinkedFiles.push(file); - } else logs({ settings: this.settings }, i18next.t("error.alreadyExists", { file: file.name })); + } else + logs( + { settings: this.settings }, + i18next.t("error.alreadyExists", { file: file.name }) + ); } } } catch (e) { newLinkedFiles.push(file); } - //pass non image file as they are updated basically by GitHub with checking the content (basic git behavior) + //pass non image file as they are updated basically by GitHub with checking the content (basic git behavior) } else { newLinkedFiles.push(file); } @@ -696,4 +663,3 @@ export default class Publisher { return newLinkedFiles; } } - diff --git a/src/commands/create_link.ts b/src/commands/create_link.ts index e47a2c41..f7fd1d7e 100644 --- a/src/commands/create_link.ts +++ b/src/commands/create_link.ts @@ -12,7 +12,10 @@ import { frontmatterFromFile, getProperties } from "src/utils/parse_frontmatter" * @param {GithubPublisher} plugin * @return {Promise} */ -export async function createLinkCallback(repo: Repository | null, plugin: GithubPublisher): Promise { +export async function createLinkCallback( + repo: Repository | null, + plugin: GithubPublisher +): Promise { const id = repo ? `copy-link-K${repo.smartKey}` : "copy-link"; const common = i18next.t("common.repository"); let name = i18next.t("commands.copyLink.title"); @@ -30,11 +33,7 @@ export async function createLinkCallback(repo: Repository | null, plugin: Github frontmatter: getProperties(plugin, repo, frontmatter, true), repository: repo, }; - createLink( - file, - multiRepo, - plugin - ); + createLink(file, multiRepo, plugin); new Notice(i18next.t("commands.copyLink.onActivation")); } return true; @@ -44,7 +43,6 @@ export async function createLinkCallback(repo: Repository | null, plugin: Github } as Command; } - /** * Create the command to create a link to the note in the repo if a file is active ; else do nothing * @call createLink @@ -52,22 +50,19 @@ export async function createLinkCallback(repo: Repository | null, plugin: Github * @param {GithubPublisher} plugin - The plugin instance * @return {Promise} */ -export async function createLinkOnActiveFile(repo: Repository | null, plugin: GithubPublisher): Promise { +export async function createLinkOnActiveFile( + repo: Repository | null, + plugin: GithubPublisher +): Promise { const file = plugin.app.workspace.getActiveFile(); const frontmatter = frontmatterFromFile(file, plugin, repo); - if ( - file && frontmatter && isShared(frontmatter, plugin.settings, file, repo) - ) { + if (file && frontmatter && isShared(frontmatter, plugin.settings, file, repo)) { const multiRepo: MultiRepoProperties = { frontmatter: getProperties(plugin, repo, frontmatter), - repository: repo + repository: repo, }; - await createLink( - file, - multiRepo, - plugin - ); + await createLink(file, multiRepo, plugin); new Notice(i18next.t("commands.copyLink.onActivation")); return; } diff --git a/src/commands/file_menu/file.ts b/src/commands/file_menu/file.ts index 12cf41d7..ae26e633 100644 --- a/src/commands/file_menu/file.ts +++ b/src/commands/file_menu/file.ts @@ -1,10 +1,15 @@ import { Repository } from "@interfaces"; import i18next from "i18next"; -import { Menu, MenuItem,Platform, TFile } from "obsidian"; +import { Menu, MenuItem, Platform, TFile } from "obsidian"; import { shareOneNote } from "src/commands"; import { ChooseRepoToRun } from "src/commands/suggest_other_repo_commands_modal"; import GithubPublisher from "src/main"; -import { defaultRepo,getRepoSharedKey, isShared, multipleSharedKey } from "src/utils/data_validation_test"; +import { + defaultRepo, + getRepoSharedKey, + isShared, + multipleSharedKey, +} from "src/utils/data_validation_test"; import { frontmatterFromFile, getProperties } from "src/utils/parse_frontmatter"; /** @@ -14,56 +19,67 @@ import { frontmatterFromFile, getProperties } from "src/utils/parse_frontmatter" * @param {string} branchName - The branch name for the repository * @param {Menu} menu - The menu to add the item to */ -export function addMenuFile(plugin: GithubPublisher, file: TFile, branchName: string, menu: Menu) { +export function addMenuFile( + plugin: GithubPublisher, + file: TFile, + branchName: string, + menu: Menu +) { const frontmatterSharedKey = frontmatterFromFile(file, plugin, null); let getSharedKey = getRepoSharedKey(plugin, frontmatterSharedKey, file); const frontmatter = frontmatterFromFile(file, plugin, getSharedKey); const allKeysFromFile = multipleSharedKey(frontmatter, file, plugin); if ( - !(isShared(frontmatter, plugin.settings, file, getSharedKey) && - plugin.settings.plugin.fileMenu) - ) return; + !( + isShared(frontmatter, plugin.settings, file, getSharedKey) && + plugin.settings.plugin.fileMenu + ) + ) + return; const prop = getProperties(plugin, getSharedKey, frontmatter, true); menu.addItem((item) => { /** - * Create a submenu if multiple repo exists in the settings & platform is desktop - */ + * Create a submenu if multiple repo exists in the settings & platform is desktop + */ if (allKeysFromFile.length > 1 || (prop instanceof Array && prop.length > 1)) { if (Platform.isDesktop) { - item - .setTitle("Github Publisher") - .setIcon("upload-cloud"); + item.setTitle("Github Publisher").setIcon("upload-cloud"); } else { //add the line to separate the commands menu.addSeparator(); item.setIsLabel(true); } - subMenuCommandsFile( - plugin, - item, - file, - branchName, - getSharedKey, - menu - ); + subMenuCommandsFile(plugin, item, file, branchName, getSharedKey, menu); return; } - const fileName = plugin.getTitleFieldForCommand(file, plugin.app.metadataCache.getFileCache(file)?.frontmatter).replace(".md", ""); + const fileName = plugin + .getTitleFieldForCommand( + file, + plugin.app.metadataCache.getFileCache(file)?.frontmatter + ) + .replace(".md", ""); if (!frontmatter || !frontmatter[plugin.settings.plugin.shareKey]) { - const otherRepo = plugin.settings.github.otherRepo.find((repo) => repo.shareAll?.enable); + const otherRepo = plugin.settings.github.otherRepo.find( + (repo) => repo.shareAll?.enable + ); if (otherRepo) getSharedKey = otherRepo; - else if (plugin.settings.plugin.shareAll?.enable) getSharedKey = defaultRepo(plugin.settings); + else if (plugin.settings.plugin.shareAll?.enable) + getSharedKey = defaultRepo(plugin.settings); } else if (frontmatter[plugin.settings.plugin.shareKey]) { getSharedKey = defaultRepo(plugin.settings); } item - .setTitle(i18next.t("commands.shareViewFiles.multiple.on", { - doc: fileName, - smartKey: getSharedKey?.smartKey?.toUpperCase() || i18next.t("common.default").toUpperCase() - })) + .setTitle( + i18next.t("commands.shareViewFiles.multiple.on", { + doc: fileName, + smartKey: + getSharedKey?.smartKey?.toUpperCase() || + i18next.t("common.default").toUpperCase(), + }) + ) .setIcon("file-up") .onClick(async () => { await shareOneNote( @@ -88,23 +104,35 @@ export function addMenuFile(plugin: GithubPublisher, file: TFile, branchName: st * @param {Repository} repo - The data repository found in the file * @return {Menu} - The submenu created */ -function subMenuCommandsFile(plugin: GithubPublisher, item: MenuItem, file: TFile, branchName: string, repo: Repository | null, originalMenu: Menu): Menu { +function subMenuCommandsFile( + plugin: GithubPublisher, + item: MenuItem, + file: TFile, + branchName: string, + repo: Repository | null, + originalMenu: Menu +): Menu { const frontmatter = frontmatterFromFile(file, plugin, repo); const fileName = plugin.getTitleFieldForCommand(file, frontmatter).replace(".md", ""); - const subMenu = Platform.isDesktop ? item.setSubmenu() as Menu : originalMenu; + const subMenu = Platform.isDesktop ? (item.setSubmenu() as Menu) : originalMenu; let prop = getProperties(plugin, repo, frontmatter, true); prop = prop instanceof Array ? prop : [prop]; /** * default repo */ - if ((repo?.shareKey === plugin.settings.plugin.shareKey || (frontmatter?.[plugin.settings.plugin.shareKey])) && (!frontmatter?.repo || !frontmatter?.multipleRepo)) { + if ( + (repo?.shareKey === plugin.settings.plugin.shareKey || + frontmatter?.[plugin.settings.plugin.shareKey]) && + (!frontmatter?.repo || !frontmatter?.multipleRepo) + ) { subMenu.addItem((subItem) => { subItem .setTitle( - (i18next.t("commands.shareViewFiles.multiple.on", { + i18next.t("commands.shareViewFiles.multiple.on", { smartKey: i18next.t("common.default").toUpperCase(), doc: fileName, - }))) + }) + ) .setIcon("file-up") .onClick(async () => { await shareOneNote( @@ -117,17 +145,21 @@ function subMenuCommandsFile(plugin: GithubPublisher, item: MenuItem, file: TFil }); }); } - const activatedRepoCommands = plugin.settings.github.otherRepo.filter((repo) => repo.createShortcuts); + const activatedRepoCommands = plugin.settings.github.otherRepo.filter( + (repo) => repo.createShortcuts + ); if (activatedRepoCommands.length > 0) { activatedRepoCommands.forEach((otherRepo) => { - if (otherRepo.shareKey === repo?.shareKey || (frontmatter?.[otherRepo.shareKey])) { + if (otherRepo.shareKey === repo?.shareKey || frontmatter?.[otherRepo.shareKey]) { subMenu.addItem((item) => { item - .setTitle(i18next.t("commands.shareViewFiles.multiple.on", { - smartKey: otherRepo.smartKey.toUpperCase(), - doc: fileName - })) + .setTitle( + i18next.t("commands.shareViewFiles.multiple.on", { + smartKey: otherRepo.smartKey.toUpperCase(), + doc: fileName, + }) + ) .setIcon("file-up") .onClick(async () => { await shareOneNote( @@ -146,10 +178,12 @@ function subMenuCommandsFile(plugin: GithubPublisher, item: MenuItem, file: TFil prop.forEach((repoFront) => { subMenu.addItem((item) => { item - .setTitle(i18next.t("commands.shareViewFiles.multiple.on", { - smartKey: repoFront.repo.toUpperCase(), - doc: fileName - })) + .setTitle( + i18next.t("commands.shareViewFiles.multiple.on", { + smartKey: repoFront.repo.toUpperCase(), + doc: fileName, + }) + ) .setIcon("file-up") .onClick(async () => { await shareOneNote( @@ -169,17 +203,25 @@ function subMenuCommandsFile(plugin: GithubPublisher, item: MenuItem, file: TFil .setTitle(i18next.t("commands.shareViewFiles.multiple.other")) .setIcon("file-input") .onClick(async () => { - new ChooseRepoToRun(plugin.app, plugin, repo?.shareKey, branchName, "file", file.basename, async (item: Repository) => { - const sourceFrontmatter = frontmatterFromFile(file, plugin, item); - await shareOneNote( - await plugin.reloadOctokit(item.smartKey), - file, - item, - sourceFrontmatter, - fileName - ); - }).open(); + new ChooseRepoToRun( + plugin.app, + plugin, + repo?.shareKey, + branchName, + "file", + file.basename, + async (item: Repository) => { + const sourceFrontmatter = frontmatterFromFile(file, plugin, item); + await shareOneNote( + await plugin.reloadOctokit(item.smartKey), + file, + item, + sourceFrontmatter, + fileName + ); + } + ).open(); }); }); return subMenu; -} \ No newline at end of file +} diff --git a/src/commands/file_menu/folder.ts b/src/commands/file_menu/folder.ts index e5e64101..362cef31 100644 --- a/src/commands/file_menu/folder.ts +++ b/src/commands/file_menu/folder.ts @@ -1,11 +1,19 @@ -import { MonoRepoProperties,Repository } from "@interfaces"; +import { MonoRepoProperties, Repository } from "@interfaces"; import i18next from "i18next"; import { Menu, MenuItem, Platform, TFolder } from "obsidian"; import { shareAllMarkedNotes } from "src/commands"; import { ChooseRepoToRun } from "src/commands/suggest_other_repo_commands_modal"; import GithubPublisher from "src/main"; -import { defaultRepo, getRepoSharedKey, isExcludedPath, isInDryRunFolder } from "src/utils/data_validation_test"; -import { frontmatterSettingsRepository,getProperties } from "src/utils/parse_frontmatter"; +import { + defaultRepo, + getRepoSharedKey, + isExcludedPath, + isInDryRunFolder, +} from "src/utils/data_validation_test"; +import { + frontmatterSettingsRepository, + getProperties, +} from "src/utils/parse_frontmatter"; /** * Share the shared file of a folder to a repository @@ -14,14 +22,19 @@ import { frontmatterSettingsRepository,getProperties } from "src/utils/parse_fro * @param {string} branchName - The branch name for the repository * @param {Repository | null} repo - The data repository found in the file */ -async function shareFolderRepo(plugin: GithubPublisher, folder: TFolder, branchName: string, repo: Repository | null) { +async function shareFolderRepo( + plugin: GithubPublisher, + folder: TFolder, + branchName: string, + repo: Repository | null +) { const publisher = await plugin.reloadOctokit(repo?.smartKey); const statusBarItems = plugin.addStatusBarItem(); const prop = getProperties(plugin, repo, null, true); const monoProperties: MonoRepoProperties = { frontmatter: Array.isArray(prop) ? prop[0] : prop, repository: repo, - convert: frontmatterSettingsRepository(plugin, repo) + convert: frontmatterSettingsRepository(plugin, repo), }; await shareAllMarkedNotes( publisher, @@ -29,7 +42,7 @@ async function shareFolderRepo(plugin: GithubPublisher, folder: TFolder, branchN branchName, monoProperties, publisher.getSharedFileOfFolder(folder, repo, true), - true, + true ); } @@ -43,15 +56,23 @@ async function shareFolderRepo(plugin: GithubPublisher, folder: TFolder, branchN * @param {string} branchName - The branch name for the repository * @return {Menu} - The submenu created */ -function addSubMenuCommandsFolder(plugin: GithubPublisher, item: MenuItem, folder: TFolder, branchName: string, originalMenu: Menu): Menu { - const subMenu = Platform.isDesktop ? item.setSubmenu() as Menu : originalMenu; +function addSubMenuCommandsFolder( + plugin: GithubPublisher, + item: MenuItem, + folder: TFolder, + branchName: string, + originalMenu: Menu +): Menu { + const subMenu = Platform.isDesktop ? (item.setSubmenu() as Menu) : originalMenu; if (!isExcludedPath(plugin.settings, folder, defaultRepo(plugin.settings))) { subMenu.addItem((subItem) => { subItem - .setTitle(i18next.t("commands.shareViewFiles.multiple.on", { - smartKey: i18next.t("common.default").toUpperCase(), - doc: folder.name - })) + .setTitle( + i18next.t("commands.shareViewFiles.multiple.on", { + smartKey: i18next.t("common.default").toUpperCase(), + doc: folder.name, + }) + ) .setIcon("folder-up") .onClick(async () => { const repo = getRepoSharedKey(plugin, undefined); @@ -59,16 +80,20 @@ function addSubMenuCommandsFolder(plugin: GithubPublisher, item: MenuItem, folde }); }); } - const activatedRepoCommands = plugin.settings.github.otherRepo.filter((repo) => repo.createShortcuts); + const activatedRepoCommands = plugin.settings.github.otherRepo.filter( + (repo) => repo.createShortcuts + ); if (activatedRepoCommands.length > 0) { activatedRepoCommands.forEach((otherRepo) => { if (isInDryRunFolder(plugin.settings, otherRepo, folder)) return; subMenu.addItem((item) => { - item.setTitle( - i18next.t("commands.shareViewFiles.multiple.on", { - smartKey: otherRepo.smartKey.toUpperCase(), - doc: folder.name - })) + item + .setTitle( + i18next.t("commands.shareViewFiles.multiple.on", { + smartKey: otherRepo.smartKey.toUpperCase(), + doc: folder.name, + }) + ) .setIcon("folder-up") .onClick(async () => { await shareFolderRepo(plugin, folder, branchName, otherRepo); @@ -81,10 +106,17 @@ function addSubMenuCommandsFolder(plugin: GithubPublisher, item: MenuItem, folde .setTitle(i18next.t("commands.shareViewFiles.multiple.other")) .setIcon("folder-symlink") .onClick(async () => { - new ChooseRepoToRun(plugin.app, plugin, null, branchName, "folder", null, async (item: Repository) => { - await shareFolderRepo(plugin, folder, branchName, item); - }).open(); - + new ChooseRepoToRun( + plugin.app, + plugin, + null, + branchName, + "folder", + null, + async (item: Repository) => { + await shareFolderRepo(plugin, folder, branchName, item); + } + ).open(); }); }); return subMenu; @@ -97,11 +129,16 @@ function addSubMenuCommandsFolder(plugin: GithubPublisher, item: MenuItem, folde * @param branchName {string} - The branch name for the repository * @param plugin {GithubPublisher} - The plugin instance */ -export async function addMenuFolder(menu: Menu, folder: TFolder, branchName: string, plugin: GithubPublisher) { +export async function addMenuFolder( + menu: Menu, + folder: TFolder, + branchName: string, + plugin: GithubPublisher +) { menu.addItem((item) => { /** * Create a submenu if multiple repo exists in the settings - */ + */ const areTheyMultipleRepo = plugin.settings.github?.otherRepo?.length > 0; if (areTheyMultipleRepo) { if (Platform.isDesktop) { @@ -112,25 +149,21 @@ export async function addMenuFolder(menu: Menu, folder: TFolder, branchName: str menu.addSeparator(); item.setIsLabel(true); } - addSubMenuCommandsFolder( - plugin, - item, - folder, - branchName, - menu - ); + addSubMenuCommandsFolder(plugin, item, folder, branchName, menu); return; } item.setSection("action"); - item.setTitle( - i18next.t("commands.shareViewFiles.multiple.on", { - smartKey: i18next.t("common.default").toUpperCase(), - doc: folder.name - })) + item + .setTitle( + i18next.t("commands.shareViewFiles.multiple.on", { + smartKey: i18next.t("common.default").toUpperCase(), + doc: folder.name, + }) + ) .setIcon("folder-up") .onClick(async () => { const repo = getRepoSharedKey(plugin); await shareFolderRepo(plugin, folder, branchName, repo); }); }); -} \ No newline at end of file +} diff --git a/src/commands/file_menu/index.ts b/src/commands/file_menu/index.ts index ae275467..f58696a1 100644 --- a/src/commands/file_menu/index.ts +++ b/src/commands/file_menu/index.ts @@ -1,6 +1,4 @@ import { addMenuFile } from "src/commands/file_menu/file"; import { addMenuFolder } from "src/commands/file_menu/folder"; -export { - addMenuFile, addMenuFolder -}; \ No newline at end of file +export { addMenuFile, addMenuFolder }; diff --git a/src/commands/index.ts b/src/commands/index.ts index a4e56e02..f9b4198f 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,9 +1,21 @@ import { createLinkCallback, createLinkOnActiveFile } from "./create_link"; -import { purgeCallback,purgeForRepo } from "./purge"; -import { checkRepositoryValidityCallback, repositoryValidityActiveFile } from "./repository_validity"; +import { purgeCallback, purgeForRepo } from "./purge"; +import { + checkRepositoryValidityCallback, + repositoryValidityActiveFile, +} from "./repository_validity"; import { refreshAllSets, refreshOpenedSet } from "./set"; -import { shareAllMarkedNotes, uploadAllNotes, uploadAllNotesCallback } from "./share/all_notes"; -import { shareEditedOnly, shareEditedOnlyCallback, uploadAllEditedNotes, uploadAllEditedNotesCallback } from "./share/edited_notes"; +import { + shareAllMarkedNotes, + uploadAllNotes, + uploadAllNotesCallback, +} from "./share/all_notes"; +import { + shareEditedOnly, + shareEditedOnlyCallback, + uploadAllEditedNotes, + uploadAllEditedNotesCallback, +} from "./share/edited_notes"; import { uploadNewNotes, uploadNewNotesCallback } from "./share/new_notes"; import { shareActiveFile, shareOneNote, shareOneNoteCallback } from "./share/unique_note"; @@ -26,4 +38,6 @@ export { uploadAllEditedNotesCallback, uploadAllNotes, uploadAllNotesCallback, - uploadNewNotes,uploadNewNotesCallback}; \ No newline at end of file + uploadNewNotes, + uploadNewNotesCallback, +}; diff --git a/src/commands/purge.ts b/src/commands/purge.ts index 2e4d8bad..730e01d8 100644 --- a/src/commands/purge.ts +++ b/src/commands/purge.ts @@ -1,4 +1,4 @@ -import { MonoRepoProperties,Repository } from "@interfaces"; +import { MonoRepoProperties, Repository } from "@interfaces"; import i18next from "i18next"; import { Command, Notice } from "obsidian"; import { GithubBranch } from "src/GitHub/branch"; @@ -7,7 +7,10 @@ import GithubPublisher from "src/main"; import { ListChangedFiles } from "src/settings/modals/list_changed"; import { notif } from "src/utils"; import { checkRepositoryValidityWithProperties } from "src/utils/data_validation_test"; -import { frontmatterSettingsRepository,getProperties } from "src/utils/parse_frontmatter"; +import { + frontmatterSettingsRepository, + getProperties, +} from "src/utils/parse_frontmatter"; /** * Command to delete file on the repo @@ -17,7 +20,11 @@ import { frontmatterSettingsRepository,getProperties } from "src/utils/parse_fro * @param {string} branchName - The branch name to delete the file * @return {Promise} */ -export async function purgeCallback(plugin: GithubPublisher, repo: Repository | null, branchName: string): Promise { +export async function purgeCallback( + plugin: GithubPublisher, + repo: Repository | null, + branchName: string +): Promise { const id = repo ? `delete-clean-K${repo.smartKey}` : "delete-clean"; let name = i18next.t("commands.publisherDeleteClean"); const common = i18next.t("common.repository"); @@ -31,51 +38,45 @@ export async function purgeCallback(plugin: GithubPublisher, repo: Repository | const monoRepo: MonoRepoProperties = { frontmatter: Array.isArray(frontmatter) ? frontmatter[0] : frontmatter, repository: repo, - convert: frontmatterSettingsRepository(plugin, repo) + convert: frontmatterSettingsRepository(plugin, repo), }; const publisher = await plugin.reloadOctokit(repo?.smartKey); - await purge( - publisher, - branchName, - monoRepo - ); + await purge(publisher, branchName, monoRepo); }, } as Command; } /** - * Delete unshared/deleted in the repo - * @param {GithubBranch} PublisherManager - * @param {string} branchName - The branch name created by the plugin - * @param {MonoRepoProperties} monoRepo - The repo where to delete the files - * @returns {Promise} - */ + * Delete unshared/deleted in the repo + * @param {GithubBranch} PublisherManager + * @param {string} branchName - The branch name created by the plugin + * @param {MonoRepoProperties} monoRepo - The repo where to delete the files + * @returns {Promise} + */ async function purge( PublisherManager: GithubBranch, branchName: string, - monoRepo: MonoRepoProperties, -): Promise { + monoRepo: MonoRepoProperties +): Promise { try { const noticeFragment = document.createDocumentFragment(); - noticeFragment.createSpan({ cls: ["obsidian-publisher", "notification"] }).innerHTML = i18next.t("informations.startingClean", { repo: monoRepo.frontmatter }); - new Notice( - noticeFragment + noticeFragment.createSpan({ cls: ["obsidian-publisher", "notification"] }).innerHTML = + i18next.t("informations.startingClean", { repo: monoRepo.frontmatter }); + new Notice(noticeFragment); + const isValid = await checkRepositoryValidityWithProperties( + PublisherManager, + monoRepo.frontmatter ); - const isValid = await checkRepositoryValidityWithProperties(PublisherManager, monoRepo.frontmatter); if (!isValid) return false; if (!PublisherManager.settings.github.dryRun.enable) await PublisherManager.newBranch(monoRepo.frontmatter); - const deleted = await deleteFromGithub( - false, - branchName, - PublisherManager, - monoRepo - ); + const deleted = await deleteFromGithub(false, branchName, PublisherManager, monoRepo); if (!PublisherManager.settings.github.dryRun.enable) await PublisherManager.updateRepository(monoRepo.frontmatter); - if (PublisherManager.settings.plugin.displayModalRepoEditing) new ListChangedFiles(PublisherManager.plugin.app, deleted).open(); + if (PublisherManager.settings.plugin.displayModalRepoEditing) + new ListChangedFiles(PublisherManager.plugin.app, deleted).open(); } catch (e) { - notif({settings: PublisherManager.settings, e: true}, e); + notif({ settings: PublisherManager.settings, e: true }, e); } } @@ -86,17 +87,17 @@ async function purge( * @param {string} branchName * @return {Promise} */ -export async function purgeForRepo(plugin : GithubPublisher, repo: Repository | null, branchName: string): Promise { +export async function purgeForRepo( + plugin: GithubPublisher, + repo: Repository | null, + branchName: string +): Promise { const prop = getProperties(plugin, repo, null, true); const publisher = await plugin.reloadOctokit(repo?.smartKey); const mono: MonoRepoProperties = { frontmatter: Array.isArray(prop) ? prop[0] : prop, repository: repo, - convert: frontmatterSettingsRepository(plugin, repo) + convert: frontmatterSettingsRepository(plugin, repo), }; - await purge( - publisher, - branchName, - mono - ); + await purge(publisher, branchName, mono); } diff --git a/src/commands/repository_validity.ts b/src/commands/repository_validity.ts index 22fc8fcb..f5d99cdb 100644 --- a/src/commands/repository_validity.ts +++ b/src/commands/repository_validity.ts @@ -11,14 +11,13 @@ import { checkRepositoryValidity } from "src/utils/data_validation_test"; * @param {Repository | null} repo - Other repo if the command is called from the suggest_other_repo_command.ts * @return {Promise} */ -export async function repositoryValidityActiveFile(plugin:GithubPublisher, repo: Repository | null): Promise { +export async function repositoryValidityActiveFile( + plugin: GithubPublisher, + repo: Repository | null +): Promise { const file = plugin.app.workspace.getActiveFile(); if (file) { - await checkRepositoryValidity( - await plugin.reloadOctokit(repo?.smartKey), - repo, - file, - ); + await checkRepositoryValidity(await plugin.reloadOctokit(repo?.smartKey), repo, file); } else { new Notice("No file is active"); } @@ -31,8 +30,13 @@ export async function repositoryValidityActiveFile(plugin:GithubPublisher, repo: * @return {Promise} */ -export async function checkRepositoryValidityCallback(plugin: GithubPublisher, repo: Repository | null): Promise { - const id = repo ? `check-plugin-repo-validy-K${repo.smartKey}` : "check-plugin-repo-validy"; +export async function checkRepositoryValidityCallback( + plugin: GithubPublisher, + repo: Repository | null +): Promise { + const id = repo + ? `check-plugin-repo-validy-K${repo.smartKey}` + : "check-plugin-repo-validy"; let name = i18next.t("commands.checkValidity.title"); const common = i18next.t("common.repository"); name = repo ? `${name} (${common} : ${repo.smartKey})` : name; @@ -41,14 +45,9 @@ export async function checkRepositoryValidityCallback(plugin: GithubPublisher, r id, name, checkCallback: (checking) => { - if (plugin.app.workspace.getActiveFile()) - { + if (plugin.app.workspace.getActiveFile()) { if (!checking) { - checkRepositoryValidity( - octokit, - repo, - plugin.app.workspace.getActiveFile() - ); + checkRepositoryValidity(octokit, repo, plugin.app.workspace.getActiveFile()); } return true; } diff --git a/src/commands/set.ts b/src/commands/set.ts index 1df50d83..6a36fbb7 100644 --- a/src/commands/set.ts +++ b/src/commands/set.ts @@ -1,13 +1,13 @@ import i18next from "i18next"; -import { Command,TFile } from "obsidian"; +import { Command, TFile } from "obsidian"; import GithubPublisher from "src/main"; export function refreshOpenedSet(plugin: GithubPublisher) { - const findRepo= (file: TFile | null) => { + const findRepo = (file: TFile | null) => { if (!file) return []; return plugin.settings.github.otherRepo.filter((repo) => repo.set === file.path); }; - + return { id: "reload-opened-set", name: i18next.t("commands.refreshOpenedSet"), @@ -17,7 +17,8 @@ export function refreshOpenedSet(plugin: GithubPublisher) { if (file && repos.length > 0) { if (!checking) { repos.forEach((repo) => { - plugin.repositoryFrontmatter[repo.smartKey] = plugin.app.metadataCache.getFileCache(file)?.frontmatter; + plugin.repositoryFrontmatter[repo.smartKey] = + plugin.app.metadataCache.getFileCache(file)?.frontmatter; }); } return true; @@ -32,19 +33,22 @@ export function refreshAllSets(plugin: GithubPublisher) { id: "reload-all-sets", name: i18next.t("commands.refreshAllSets"), checkCallback: (checking) => { - const allSets = plugin.settings.github.otherRepo.filter((repo) => repo.set !== "" || repo.set !== null); + const allSets = plugin.settings.github.otherRepo.filter( + (repo) => repo.set !== "" || repo.set !== null + ); if (allSets.length > 0) { if (!checking) { allSets.forEach((repo) => { if (!repo.set) return; const file = plugin.app.vault.getAbstractFileByPath(repo.set); if (!file || !(file instanceof TFile)) return; - plugin.repositoryFrontmatter[repo.smartKey] = plugin.app.metadataCache.getFileCache(file)?.frontmatter; + plugin.repositoryFrontmatter[repo.smartKey] = + plugin.app.metadataCache.getFileCache(file)?.frontmatter; }); } return true; } return false; - } + }, } as Command; } diff --git a/src/commands/share/all_notes.ts b/src/commands/share/all_notes.ts index aa22f357..6a64c919 100644 --- a/src/commands/share/all_notes.ts +++ b/src/commands/share/all_notes.ts @@ -6,9 +6,18 @@ import { GithubBranch } from "src/GitHub/branch"; import { deleteFromGithub } from "src/GitHub/delete"; import GithubPublisher from "src/main"; import { ListChangedFiles } from "src/settings/modals/list_changed"; -import { createListEdited, getSettingsOfMetadataExtractor, logs, notifError,publisherNotification } from "src/utils"; +import { + createListEdited, + getSettingsOfMetadataExtractor, + logs, + notifError, + publisherNotification, +} from "src/utils"; import { checkRepositoryValidityWithProperties } from "src/utils/data_validation_test"; -import { frontmatterSettingsRepository,getProperties } from "src/utils/parse_frontmatter"; +import { + frontmatterSettingsRepository, + getProperties, +} from "src/utils/parse_frontmatter"; import { ShareStatusBar } from "src/utils/status_bar"; /** @@ -19,7 +28,11 @@ import { ShareStatusBar } from "src/utils/status_bar"; * @param {string} branchName - The branch name to upload the file * @return {Promise} */ -export async function uploadAllNotesCallback(plugin: GithubPublisher, repo: Repository|null, branchName: string): Promise { +export async function uploadAllNotesCallback( + plugin: GithubPublisher, + repo: Repository | null, + branchName: string +): Promise { const id = repo ? `publish-all-K${repo.smartKey}` : "publish-all"; let name = i18next.t("commands.uploadAllNotes"); const common = i18next.t("common.repository"); @@ -28,7 +41,7 @@ export async function uploadAllNotesCallback(plugin: GithubPublisher, repo: Repo id, name, callback: async () => { - await uploadAllNotes(plugin,repo, branchName); + await uploadAllNotes(plugin, repo, branchName); }, } as Command; } @@ -41,7 +54,11 @@ export async function uploadAllNotesCallback(plugin: GithubPublisher, repo: Repo * @return {Promise} */ -export async function uploadAllNotes(plugin: GithubPublisher, repo: Repository | null, branchName: string): Promise { +export async function uploadAllNotes( + plugin: GithubPublisher, + repo: Repository | null, + branchName: string +): Promise { const statusBarItems = plugin.addStatusBarItem(); const publisher = await plugin.reloadOctokit(repo?.smartKey); const sharedFiles = publisher.getSharedFiles(repo); @@ -49,10 +66,7 @@ export async function uploadAllNotes(plugin: GithubPublisher, repo: Repository | const mono: MonoRepoProperties = { frontmatter: Array.isArray(prop) ? prop[0] : prop, repository: repo, - convert: frontmatterSettingsRepository( - plugin, - repo - ) + convert: frontmatterSettingsRepository(plugin, repo), }; await shareAllMarkedNotes( publisher, @@ -60,19 +74,19 @@ export async function uploadAllNotes(plugin: GithubPublisher, repo: Repository | branchName, mono, sharedFiles, - true, + true ); } /** - * Share all marked note (share: true) from Obsidian to GitHub - * @param {GithubBranch} PublisherManager - * @param {HTMLElement} statusBarItems - The status bar element - * @param {string} branchName - The branch name created by the plugin - * @param {MonoRepoProperties} monoRepo - The repo where to share the files - * @param {TFile[]} sharedFiles - The files to share - * @param {boolean} createGithubBranch - If true, create the branch before sharing the files - */ + * Share all marked note (share: true) from Obsidian to GitHub + * @param {GithubBranch} PublisherManager + * @param {HTMLElement} statusBarItems - The status bar element + * @param {string} branchName - The branch name created by the plugin + * @param {MonoRepoProperties} monoRepo - The repo where to share the files + * @param {TFile[]} sharedFiles - The files to share + * @param {boolean} createGithubBranch - If true, create the branch before sharing the files + */ export async function shareAllMarkedNotes( PublisherManager: GithubBranch, statusBarItems: HTMLElement, @@ -80,16 +94,20 @@ export async function shareAllMarkedNotes( monoRepo: MonoRepoProperties, sharedFiles: TFile[], createGithubBranch: boolean = true, - sourceFrontmatter: FrontMatterCache | undefined | null = null, + sourceFrontmatter: FrontMatterCache | undefined | null = null ) { const statusBar = new ShareStatusBar(statusBarItems, sharedFiles.length); const prop = monoRepo.frontmatter; try { - const fileError : string[] = []; + const fileError: string[] = []; const listStateUploaded: UploadedFiles[] = []; if (sharedFiles.length > 0) { if (createGithubBranch) { - const isValid = await checkRepositoryValidityWithProperties(PublisherManager, prop, sharedFiles.length); + const isValid = await checkRepositoryValidityWithProperties( + PublisherManager, + prop, + sharedFiles.length + ); if (!isValid) return false; await PublisherManager.newBranch(prop); } @@ -102,16 +120,15 @@ export async function shareAllMarkedNotes( monoRepo, undefined, undefined, - sourceFrontmatter, - ) ; + sourceFrontmatter + ); if (uploaded) { listStateUploaded.push(...uploaded.uploaded); } - } catch(e) { + } catch (e) { fileError.push(sharedFile.name); - new Notice( - (i18next.t("error.unablePublishNote", {file: sharedFile.name}))); - logs({settings: PublisherManager.settings, e: true}, e); + new Notice(i18next.t("error.unablePublishNote", { file: sharedFile.name })); + logs({ settings: PublisherManager.settings, e: true }, e); } } statusBar.finish(8000); @@ -123,31 +140,18 @@ export async function shareAllMarkedNotes( monoRepo ); const settings = PublisherManager.settings; - if ( - settings.upload.metadataExtractorPath.length > 0 && - Platform.isDesktop - ) { + if (settings.upload.metadataExtractorPath.length > 0 && Platform.isDesktop) { const metadataExtractor = await getSettingsOfMetadataExtractor( PublisherManager.plugin.app, settings ); if (metadataExtractor) { - await PublisherManager.uploadMetadataExtractorFiles( - metadataExtractor, - prop - ); + await PublisherManager.uploadMetadataExtractorFiles(metadataExtractor, prop); } } - const update = await PublisherManager.updateRepository( - prop - ); + const update = await PublisherManager.updateRepository(prop); if (update) { - await publisherNotification( - PublisherManager, - noticeValue, - settings, - prop - ); + await publisherNotification(PublisherManager, noticeValue, settings, prop); if (settings.plugin.displayModalRepoEditing) { const listEdited = createListEdited(listStateUploaded, deleted, fileError); new ListChangedFiles(PublisherManager.plugin.app, listEdited).open(); @@ -157,11 +161,15 @@ export async function shareAllMarkedNotes( } } } catch (error) { - logs({settings: PublisherManager.settings, e: true}, error); + logs({ settings: PublisherManager.settings, e: true }, error); const errorFrag = document.createDocumentFragment(); - errorFrag.createSpan({ cls: ["error", "obsidian-publisher", "icons", "notification"] }).innerHTML = ERROR_ICONS; - errorFrag.createSpan({ cls: ["error", "obsidian-publisher", "notification"], text: i18next.t("error.unablePublishMultiNotes") }); + errorFrag.createSpan({ + cls: ["error", "obsidian-publisher", "icons", "notification"], + }).innerHTML = ERROR_ICONS; + errorFrag.createSpan({ + cls: ["error", "obsidian-publisher", "notification"], + text: i18next.t("error.unablePublishMultiNotes"), + }); statusBar.error(prop); } } - diff --git a/src/commands/share/edited_notes.ts b/src/commands/share/edited_notes.ts index 64242076..4b3948a6 100644 --- a/src/commands/share/edited_notes.ts +++ b/src/commands/share/edited_notes.ts @@ -4,7 +4,10 @@ import { Command, Notice, TFile } from "obsidian"; import { GithubBranch } from "src/GitHub/branch"; import GithubPublisher from "src/main"; import { checkRepositoryValidityWithProperties } from "src/utils/data_validation_test"; -import { frontmatterSettingsRepository,getProperties } from "src/utils/parse_frontmatter"; +import { + frontmatterSettingsRepository, + getProperties, +} from "src/utils/parse_frontmatter"; import { shareAllMarkedNotes } from "./all_notes"; @@ -16,7 +19,11 @@ import { shareAllMarkedNotes } from "./all_notes"; * @param {string} branchName * @return {Promise} */ -export async function uploadAllEditedNotesCallback(plugin: GithubPublisher, repo: Repository|null, branchName: string): Promise { +export async function uploadAllEditedNotesCallback( + plugin: GithubPublisher, + repo: Repository | null, + branchName: string +): Promise { const id = repo ? `upload-all-edited-new-K${repo.smartKey}` : "upload-all-edited-new"; let name = i18next.t("commands.uploadAllNewEditedNote"); const common = i18next.t("common.repository"); @@ -37,42 +44,45 @@ export async function uploadAllEditedNotesCallback(plugin: GithubPublisher, repo * @param {Repository | null} repo - Other repo if the command is called from the suggest_other_repo_command.ts * @return {Promise} */ -export async function uploadAllEditedNotes(plugin: GithubPublisher ,branchName: string, repo: Repository|null=null): Promise { +export async function uploadAllEditedNotes( + plugin: GithubPublisher, + branchName: string, + repo: Repository | null = null +): Promise { const publisher = await plugin.reloadOctokit(repo?.smartKey); const prop = getProperties(plugin, repo, null, true); - await shareAllEditedNotes( - publisher, - branchName, - { - frontmatter: Array.isArray(prop) ? prop[0] : prop, - repository: repo, - convert: frontmatterSettingsRepository(plugin, repo) - }, - ); + await shareAllEditedNotes(publisher, branchName, { + frontmatter: Array.isArray(prop) ? prop[0] : prop, + repository: repo, + convert: frontmatterSettingsRepository(plugin, repo), + }); } /** - * Share edited notes : they exist on the repo, BUT the last edited time in Obsidian is after the last upload. Also share new notes. - * @param {GithubBranch} PublisherManager - * @param {string} branchName - The branch name created by the plugin - * @param {MonoRepoProperties} monoRepo - The repo - */ + * Share edited notes : they exist on the repo, BUT the last edited time in Obsidian is after the last upload. Also share new notes. + * @param {GithubBranch} PublisherManager + * @param {string} branchName - The branch name created by the plugin + * @param {MonoRepoProperties} monoRepo - The repo + */ async function shareAllEditedNotes( PublisherManager: GithubBranch, branchName: string, - monoRepo: MonoRepoProperties, + monoRepo: MonoRepoProperties ) { const plugin = PublisherManager.plugin; - new Notice(i18next.t("informations.scanningRepo") ); - const sharedFilesWithPaths = PublisherManager.getAllFileWithPath(monoRepo.repository, monoRepo.convert); + new Notice(i18next.t("informations.scanningRepo")); + const sharedFilesWithPaths = PublisherManager.getAllFileWithPath( + monoRepo.repository, + monoRepo.convert + ); const githubSharedNotes = await PublisherManager.getAllFileFromRepo( monoRepo.frontmatter.branch, monoRepo.frontmatter ); const newSharedFiles = PublisherManager.getNewFiles( sharedFilesWithPaths, - githubSharedNotes, + githubSharedNotes ); const newlySharedNotes = await PublisherManager.getEditedFiles( sharedFilesWithPaths, @@ -81,12 +91,15 @@ async function shareAllEditedNotes( ); if (newlySharedNotes.length > 0) { new Notice( - (i18next.t("informations.foundNoteToSend", {nbNotes: newlySharedNotes.length}) - ) + i18next.t("informations.foundNoteToSend", { nbNotes: newlySharedNotes.length }) ); - + const statusBarElement = plugin.addStatusBarItem(); - const isValid = await checkRepositoryValidityWithProperties(PublisherManager, monoRepo.frontmatter, newlySharedNotes.length); + const isValid = await checkRepositoryValidityWithProperties( + PublisherManager, + monoRepo.frontmatter, + newlySharedNotes.length + ); if (!isValid) return false; await PublisherManager.newBranch(monoRepo.frontmatter); await shareAllMarkedNotes( @@ -95,33 +108,32 @@ async function shareAllEditedNotes( branchName, monoRepo, newlySharedNotes, - false, + false ); return; } - new Notice(i18next.t("informations.noNewNote") ); + new Notice(i18next.t("informations.noNewNote")); } - /** -* share **only** edited notes : they exist on the repo, but the last edited time is after the last upload. -* @param {GithubBranch} PublisherManager -* @param {string} branchName - The branch name created by the plugin -* @param {MonoRepoProperties} monoRepo - The repo -*/ + * share **only** edited notes : they exist on the repo, but the last edited time is after the last upload. + * @param {GithubBranch} PublisherManager + * @param {string} branchName - The branch name created by the plugin + * @param {MonoRepoProperties} monoRepo - The repo + */ async function shareOnlyEdited( PublisherManager: GithubBranch, branchName: string, - monoRepo: MonoRepoProperties, + monoRepo: MonoRepoProperties ) { const shortRepo = monoRepo.repository; const prop = monoRepo.frontmatter; - new Notice(i18next.t("informations.scanningRepo") ); - const sharedFilesWithPaths = PublisherManager.getAllFileWithPath(shortRepo, monoRepo.convert); - const githubSharedNotes = await PublisherManager.getAllFileFromRepo( - prop.branch, - prop + new Notice(i18next.t("informations.scanningRepo")); + const sharedFilesWithPaths = PublisherManager.getAllFileWithPath( + shortRepo, + monoRepo.convert ); + const githubSharedNotes = await PublisherManager.getAllFileFromRepo(prop.branch, prop); const newSharedFiles: TFile[] = []; const newlySharedNotes = await PublisherManager.getEditedFiles( sharedFilesWithPaths, @@ -130,10 +142,14 @@ async function shareOnlyEdited( ); if (newlySharedNotes.length > 0) { new Notice( - (i18next.t("informations.foundNoteToSend", {nbNotes: newlySharedNotes.length})) + i18next.t("informations.foundNoteToSend", { nbNotes: newlySharedNotes.length }) ); const statusBarElement = PublisherManager.plugin.addStatusBarItem(); - const isValid = await checkRepositoryValidityWithProperties(PublisherManager, prop, newlySharedNotes.length); + const isValid = await checkRepositoryValidityWithProperties( + PublisherManager, + prop, + newlySharedNotes.length + ); if (!isValid) return false; await PublisherManager.newBranch(prop); await shareAllMarkedNotes( @@ -142,11 +158,11 @@ async function shareOnlyEdited( branchName, monoRepo, newlySharedNotes, - false, + false ); return; } - new Notice(i18next.t("informations.noNewNote") ); + new Notice(i18next.t("informations.noNewNote")); } /** @@ -157,21 +173,20 @@ async function shareOnlyEdited( * @param {GithubPublisher} plugin * @return {Promise} */ -export async function shareEditedOnly(branchName: string, repo: Repository|null, plugin: GithubPublisher): Promise { +export async function shareEditedOnly( + branchName: string, + repo: Repository | null, + plugin: GithubPublisher +): Promise { const publisher = await plugin.reloadOctokit(repo?.smartKey); const prop = getProperties(plugin, repo, null, true); - await shareOnlyEdited( - publisher, - branchName, - { - frontmatter: Array.isArray(prop) ? prop[0] : prop, - repository: repo, - convert: frontmatterSettingsRepository(plugin, repo) - }, - ); + await shareOnlyEdited(publisher, branchName, { + frontmatter: Array.isArray(prop) ? prop[0] : prop, + repository: repo, + convert: frontmatterSettingsRepository(plugin, repo), + }); } - /** * Share edited note only * @call shareEditedOnly @@ -180,7 +195,11 @@ export async function shareEditedOnly(branchName: string, repo: Repository|null, * @param {GithubPublisher} plugin * @return {Promise} */ -export async function shareEditedOnlyCallback(repo: Repository|null, branchName: string, plugin: GithubPublisher): Promise { +export async function shareEditedOnlyCallback( + repo: Repository | null, + branchName: string, + plugin: GithubPublisher +): Promise { const id = repo ? `upload-edited-K${repo.smartKey}` : "upload-edited"; let name = i18next.t("commands.uploadAllEditedNote"); const common = i18next.t("common.repository"); @@ -192,4 +211,4 @@ export async function shareEditedOnlyCallback(repo: Repository|null, branchName: await shareEditedOnly(branchName, repo, plugin); }, } as Command; -} \ No newline at end of file +} diff --git a/src/commands/share/new_notes.ts b/src/commands/share/new_notes.ts index dfed43c7..e069e533 100644 --- a/src/commands/share/new_notes.ts +++ b/src/commands/share/new_notes.ts @@ -5,7 +5,10 @@ import { shareAllMarkedNotes } from "src/commands"; import { GithubBranch } from "src/GitHub/branch"; import GithubPublisher from "src/main"; import { checkRepositoryValidityWithProperties } from "src/utils/data_validation_test"; -import { frontmatterSettingsRepository,getProperties } from "src/utils/parse_frontmatter"; +import { + frontmatterSettingsRepository, + getProperties, +} from "src/utils/parse_frontmatter"; /** * Upload all new notes only @@ -14,7 +17,11 @@ import { frontmatterSettingsRepository,getProperties } from "src/utils/parse_fro * @param branchName {string} - The branch name to upload the file * @returns {Promise} */ -export async function uploadNewNotesCallback(plugin: GithubPublisher, repo: Repository | null, branchName: string): Promise { +export async function uploadNewNotesCallback( + plugin: GithubPublisher, + repo: Repository | null, + branchName: string +): Promise { const id = repo ? `upload-new-K${repo.smartKey}` : "upload-new"; let name = i18next.t("commands.uploadNewNotes"); const common = i18next.t("common.repository"); @@ -23,7 +30,7 @@ export async function uploadNewNotesCallback(plugin: GithubPublisher, repo: Repo id, name, callback: async () => { - await uploadNewNotes(plugin,branchName, repo); + await uploadNewNotes(plugin, branchName, repo); }, } as Command; } @@ -37,36 +44,38 @@ export async function uploadNewNotesCallback(plugin: GithubPublisher, repo: Repo * @return {Promise} */ -export async function uploadNewNotes(plugin: GithubPublisher, branchName: string, repo: Repository|null): Promise { +export async function uploadNewNotes( + plugin: GithubPublisher, + branchName: string, + repo: Repository | null +): Promise { const publisher = await plugin.reloadOctokit(repo?.smartKey); const prop = getProperties(plugin, repo, null, true); - await shareNewNote( - publisher, - branchName, - { - frontmatter: Array.isArray(prop) ? prop[0] : prop, - repository: repo, - convert: frontmatterSettingsRepository(plugin, repo) - }, - ); + await shareNewNote(publisher, branchName, { + frontmatter: Array.isArray(prop) ? prop[0] : prop, + repository: repo, + convert: frontmatterSettingsRepository(plugin, repo), + }); } - /** - * Deep scan the repository and send only the note that not exist in the repository - * @param {GithubBranch} PublisherManager - * @param {string} branchName - The branch name created by the plugin - * @param {MonoRepoProperties} monoRepo - The repo - * @returns {Promise} - */ + * Deep scan the repository and send only the note that not exist in the repository + * @param {GithubBranch} PublisherManager + * @param {string} branchName - The branch name created by the plugin + * @param {MonoRepoProperties} monoRepo - The repo + * @returns {Promise} + */ export async function shareNewNote( PublisherManager: GithubBranch, branchName: string, - monoRepo: MonoRepoProperties, -): Promise { + monoRepo: MonoRepoProperties +): Promise { const plugin = PublisherManager.plugin; - new Notice(i18next.t("informations.scanningRepo") ); - const sharedFilesWithPaths = PublisherManager.getAllFileWithPath(monoRepo.repository, monoRepo.convert); + new Notice(i18next.t("informations.scanningRepo")); + const sharedFilesWithPaths = PublisherManager.getAllFileWithPath( + monoRepo.repository, + monoRepo.convert + ); // Get all file in the repo before the creation of the branch const githubSharedNotes = await PublisherManager.getAllFileFromRepo( monoRepo.frontmatter.branch, // we need to take the master branch because the branch to create doesn't exist yet @@ -74,16 +83,19 @@ export async function shareNewNote( ); const newlySharedNotes = PublisherManager.getNewFiles( sharedFilesWithPaths, - githubSharedNotes, + githubSharedNotes ); if (newlySharedNotes.length > 0) { new Notice( - (i18next.t("informations.foundNoteToSend", {nbNotes: newlySharedNotes.length}) - ) + i18next.t("informations.foundNoteToSend", { nbNotes: newlySharedNotes.length }) ); - + const statusBarElement = plugin.addStatusBarItem(); - const isValid = await checkRepositoryValidityWithProperties(PublisherManager, monoRepo.frontmatter, newlySharedNotes.length); + const isValid = await checkRepositoryValidityWithProperties( + PublisherManager, + monoRepo.frontmatter, + newlySharedNotes.length + ); if (!isValid) return false; await PublisherManager.newBranch(monoRepo.frontmatter); await shareAllMarkedNotes( @@ -92,9 +104,9 @@ export async function shareNewNote( branchName, monoRepo, newlySharedNotes, - false, + false ); return; } - new Notice(i18next.t("informations.noNewNote") ); -} \ No newline at end of file + new Notice(i18next.t("informations.noNewNote")); +} diff --git a/src/commands/share/unique_note.ts b/src/commands/share/unique_note.ts index 0e637353..fec49513 100644 --- a/src/commands/share/unique_note.ts +++ b/src/commands/share/unique_note.ts @@ -4,8 +4,18 @@ import { Command, FrontMatterCache, Notice, Platform, TFile } from "obsidian"; import { GithubBranch } from "src/GitHub/branch"; import GithubPublisher from "src/main"; import { ListChangedFiles } from "src/settings/modals/list_changed"; -import { createLink, createListEdited, getSettingsOfMetadataExtractor, logs,notifError, publisherNotification } from "src/utils"; -import { checkRepositoryValidityWithProperties, isShared } from "src/utils/data_validation_test"; +import { + createLink, + createListEdited, + getSettingsOfMetadataExtractor, + logs, + notifError, + publisherNotification, +} from "src/utils"; +import { + checkRepositoryValidityWithProperties, + isShared, +} from "src/utils/data_validation_test"; import { frontmatterFromFile, getProperties } from "src/utils/parse_frontmatter"; import merge from "ts-deepmerge"; @@ -16,7 +26,10 @@ import merge from "ts-deepmerge"; * @param {GithubPublisher} plugin - The plugin instance * @return {Promise} */ -export async function shareOneNoteCallback(repo: Repository|null, plugin: GithubPublisher): Promise { +export async function shareOneNoteCallback( + repo: Repository | null, + plugin: GithubPublisher +): Promise { const id = repo ? `share-one-K${repo.smartKey}` : "share-one"; let name = i18next.t("commands.shareActiveFile"); const common = i18next.t("common.repository"); @@ -29,17 +42,9 @@ export async function shareOneNoteCallback(repo: Repository|null, plugin: Github checkCallback: (checking) => { const file = plugin.app.workspace.getActiveFile(); const frontmatter = frontmatterFromFile(file, plugin, repo); - if ( - file && frontmatter && isShared(frontmatter, plugin.settings, file, repo) - ) { + if (file && frontmatter && isShared(frontmatter, plugin.settings, file, repo)) { if (!checking) { - shareOneNote( - octokit, - file, - repo, - frontmatter, - file.basename, - ); + shareOneNote(octokit, file, repo, frontmatter, file.basename); } return true; } @@ -48,44 +53,45 @@ export async function shareOneNoteCallback(repo: Repository|null, plugin: Github } as Command; } - /** - * Share only **one** note and their embedded contents, including note and attachments - * @param {GithubBranch} PublisherManager - * @param {TFile} file - The file to share - * @param {Repository|null} repository - * @param {string} title The title from frontmatter + regex (if any) - * @returns {Promise} - */ + * Share only **one** note and their embedded contents, including note and attachments + * @param {GithubBranch} PublisherManager + * @param {TFile} file - The file to share + * @param {Repository|null} repository + * @param {string} title The title from frontmatter + regex (if any) + * @returns {Promise} + */ export async function shareOneNote( PublisherManager: GithubBranch, file: TFile, repository: Repository | null = null, sourceFrontmatter: FrontMatterCache | undefined | null, - title?: string, -): Promise { - const {settings, plugin} = PublisherManager; + title?: string +): Promise { + const { settings, plugin } = PublisherManager; const app = PublisherManager.plugin.app; let frontmatter = frontmatterFromFile(file, PublisherManager.plugin, null); - if (sourceFrontmatter && frontmatter) frontmatter = merge(sourceFrontmatter, frontmatter); + if (sourceFrontmatter && frontmatter) + frontmatter = merge(sourceFrontmatter, frontmatter); try { const prop = getProperties(plugin, repository, frontmatter); let isValid: boolean; if (prop instanceof Array) { const isValidArray = []; for (const repo of prop) { - isValidArray.push(await checkRepositoryValidityWithProperties(PublisherManager, repo)); + isValidArray.push( + await checkRepositoryValidityWithProperties(PublisherManager, repo) + ); } isValid = isValidArray.every((v) => v === true); } else isValid = await checkRepositoryValidityWithProperties(PublisherManager, prop); - + const multiRepo: MultiRepoProperties = { frontmatter: prop, - repository: repository + repository: repository, }; if (!isValid) return false; - if (!settings.github.dryRun.enable) - await PublisherManager.newBranch(prop); + if (!settings.github.dryRun.enable) await PublisherManager.newBranch(prop); const publishSuccess = await PublisherManager.publish( file, true, @@ -95,48 +101,34 @@ export async function shareOneNote( sourceFrontmatter ); if (publishSuccess) { - if ( - settings.upload.metadataExtractorPath.length > 0 && - Platform.isDesktop - ) { - const metadataExtractor = await getSettingsOfMetadataExtractor( - app, - settings - ); + if (settings.upload.metadataExtractorPath.length > 0 && Platform.isDesktop) { + const metadataExtractor = await getSettingsOfMetadataExtractor(app, settings); if (metadataExtractor) { - await PublisherManager.uploadMetadataExtractorFiles( - metadataExtractor, - prop - ); + await PublisherManager.uploadMetadataExtractorFiles(metadataExtractor, prop); } } const update = await PublisherManager.updateRepository( - prop, settings.github.dryRun.enable + prop, + settings.github.dryRun.enable ); if (update) { - await publisherNotification( - PublisherManager, - title, - settings, - prop - ); - await createLink( - file, - multiRepo, - plugin - ); + await publisherNotification(PublisherManager, title, settings, prop); + await createLink(file, multiRepo, plugin); if (settings.plugin.displayModalRepoEditing) { - const listEdited = createListEdited(publishSuccess.uploaded, publishSuccess.deleted, publishSuccess.error); + const listEdited = createListEdited( + publishSuccess.uploaded, + publishSuccess.deleted, + publishSuccess.error + ); new ListChangedFiles(app, listEdited).open(); } - } else { notifError(prop); } } } catch (error) { if (!(error instanceof DOMException)) { - logs({settings, e: true}, error); + logs({ settings, e: true }, error); notifError(getProperties(plugin, repository, frontmatter, true)); } } @@ -150,7 +142,10 @@ export async function shareOneNote( * @param {string} branchName * @return {Promise} */ -export async function shareActiveFile(plugin: GithubPublisher, repo: Repository | null): Promise { +export async function shareActiveFile( + plugin: GithubPublisher, + repo: Repository | null +): Promise { const file = plugin.app.workspace.getActiveFile(); const frontmatter = frontmatterFromFile(file, plugin, repo); if (file && frontmatter && isShared(frontmatter, plugin.settings, file, repo)) { @@ -158,9 +153,9 @@ export async function shareActiveFile(plugin: GithubPublisher, repo: Repository await plugin.reloadOctokit(repo?.smartKey), file, repo, - frontmatter, + frontmatter ); } else { new Notice(i18next.t("commands.runOtherRepo.noFile")); } -} \ No newline at end of file +} diff --git a/src/commands/suggest_other_repo_commands_modal.ts b/src/commands/suggest_other_repo_commands_modal.ts index 17024351..ca295c15 100644 --- a/src/commands/suggest_other_repo_commands_modal.ts +++ b/src/commands/suggest_other_repo_commands_modal.ts @@ -1,190 +1,224 @@ -import { FolderSettings, GitHubPublisherSettings, Repository } from "@interfaces"; -import i18next from "i18next"; -import { App, FuzzySuggestModal } from "obsidian"; -import {createLinkOnActiveFile, deleteCommands, repositoryValidityActiveFile, shareActiveFile, shareEditedOnly, uploadAllEditedNotes, uploadAllNotes, uploadNewNotes} from "src/commands"; -import GithubPublisher from "src/main"; -import { defaultRepo } from "src/utils/data_validation_test"; - - -interface GithubPublisherCommands { - commands: string; - name: string; -} - -/** - * @extends FuzzySuggestModal - * @category Command - * @category SuggestModal - * @category GithubPublisher - * @description This class is used to choose which repo to run the command on - */ - -export class ChooseWhichRepoToRun extends FuzzySuggestModal { - plugin: GithubPublisher; - branchName: string; - settings: GitHubPublisherSettings; - - constructor(app: App, plugin: GithubPublisher, branchName: string) { - super(app); - this.plugin = plugin; - this.branchName = branchName; - this.settings = plugin.settings; - } - - getItems(): Repository[] { - return this.settings.github.otherRepo; - } - getItemText(item: Repository): string { - return item.smartKey; - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - onChooseItem(item: Repository, evt: MouseEvent | KeyboardEvent): void { - new SuggestOtherRepoCommandsModal(this.plugin.app, this.plugin, this.branchName, item).open(); - } -} - -/** - * Just return the repo data - */ -export class ChooseRepoToRun extends FuzzySuggestModal { - plugin: GithubPublisher; - branchName: string; - keyToFind: string | null; - type: "folder" | "file"; - settings: GitHubPublisherSettings; - fileName: string | null; - onSubmit: (item: Repository) => void; - - constructor(app: App, plugin: GithubPublisher, keyToFind: null | string = null, branchName: string, type: "folder" | "file", fileName: string | null, onSubmit: (item: Repository) => void) { - super(app); - this.plugin = plugin; - this.branchName = branchName; - this.keyToFind = keyToFind; - this.onSubmit = onSubmit; - this.fileName = fileName; - this.type = type; - this.settings = plugin.settings; - } - - getItems(): Repository[] { - let repoFound: Repository[] = []; - const defRepo = defaultRepo(this.settings); - if (this.type === "file") { - if (this.settings.plugin.shareAll?.enable && !this.fileName?.startsWith(this.settings.plugin.shareAll?.excludedFileName)) { - repoFound.push(defRepo); - } - if (this.keyToFind) { - repoFound = repoFound.concat(this.settings.github.otherRepo.filter((repo: Repository) => repo.shareKey == this.keyToFind)); - if (this.keyToFind === defRepo.shareKey) { - repoFound.push(defRepo); - } - } - } - repoFound = repoFound.concat(this.settings.github.otherRepo.filter((repo: Repository) => repo.shareAll?.enable && !this.fileName?.startsWith(repo.shareAll?.excludedFileName))); - if (repoFound.length === 0) - return [defRepo, ...this.settings.github.otherRepo]; - repoFound.push(defRepo); - return [...new Set(repoFound)]; - } - getItemText(item: Repository): string { - return item.smartKey; - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - onChooseItem(item: Repository, evt: MouseEvent | KeyboardEvent): void { - this.onSubmit(item); - } -} - -/** - * @description This class call the commands on the chosen repo - * @extends FuzzySuggestModal - */ - -export class SuggestOtherRepoCommandsModal extends FuzzySuggestModal { - plugin: GithubPublisher; - branchName: string; - repo: Repository; - settings: GitHubPublisherSettings; - constructor(app: App, plugin: GithubPublisher, branchName: string, repo: Repository) { - super(app); - this.plugin = plugin; - this.branchName = branchName; - this.repo = repo; - this.settings = plugin.settings; - } - getItems(): GithubPublisherCommands[] { - const cmd = [ - { - commands: "shareAllMarkedNotes", - name: i18next.t("commands.uploadAllNotes") - }, - { - commands: "shareOneNote", - name: i18next.t("commands.shareActiveFile") - }, - { - commands: "shareNewNote", - name: i18next.t("commands.uploadNewNotes") - }, - { - commands: "shareAllEditedNotes", - name: i18next.t("commands.uploadAllNewEditedNote") - }, - { - commands: "shareOnlyEdited", - name: i18next.t("commands.uploadAllEditedNote") - }, - { - commands: "checkRepositoryValidity", - name: i18next.t("commands.checkValidity.title") - }, - ]; - if (this.settings.plugin.copyLink) { - cmd.push({ - commands: "createLink", - name: i18next.t("commands.copyLink.title"), - }); - } - if (this.settings.upload.autoclean.enable && this.settings.upload.behavior !== FolderSettings.fixed) { - cmd.push({ - commands: "deleteUnsharedDeletedNotes", - name: i18next.t("commands.publisherDeleteClean") - }); - } - return cmd; - - } - getItemText(item: GithubPublisherCommands): string { - return item.name; - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - onChooseItem(item: GithubPublisherCommands, evt: MouseEvent | KeyboardEvent): void { - switch (item.commands) { - case "shareAllMarkedNotes": - uploadAllNotes(this.plugin, this.repo, this.branchName); - break; - case "deleteUnsharedDeletedNotes": - deleteCommands(this.plugin, this.repo, this.branchName); - break; - case "shareNewNote": - uploadNewNotes(this.plugin, this.branchName, this.repo); - break; - case "shareAllEditedNotes": - uploadAllEditedNotes(this.plugin, this.branchName, this.repo); - break; - case "shareOnlyEdited": - shareEditedOnly(this.branchName, this.repo, this.plugin); - break; - case "shareOneNote": - shareActiveFile(this.plugin, this.repo); - break; - case "createLink": - createLinkOnActiveFile(this.repo, this.plugin); - break; - case "checkRepositoryValidity": - repositoryValidityActiveFile(this.plugin, this.repo); - break; - } - } -} - +import { FolderSettings, GitHubPublisherSettings, Repository } from "@interfaces"; +import i18next from "i18next"; +import { App, FuzzySuggestModal } from "obsidian"; +import { + createLinkOnActiveFile, + deleteCommands, + repositoryValidityActiveFile, + shareActiveFile, + shareEditedOnly, + uploadAllEditedNotes, + uploadAllNotes, + uploadNewNotes, +} from "src/commands"; +import GithubPublisher from "src/main"; +import { defaultRepo } from "src/utils/data_validation_test"; + +interface GithubPublisherCommands { + commands: string; + name: string; +} + +/** + * @extends FuzzySuggestModal + * @category Command + * @category SuggestModal + * @category GithubPublisher + * @description This class is used to choose which repo to run the command on + */ + +export class ChooseWhichRepoToRun extends FuzzySuggestModal { + plugin: GithubPublisher; + branchName: string; + settings: GitHubPublisherSettings; + + constructor(app: App, plugin: GithubPublisher, branchName: string) { + super(app); + this.plugin = plugin; + this.branchName = branchName; + this.settings = plugin.settings; + } + + getItems(): Repository[] { + return this.settings.github.otherRepo; + } + getItemText(item: Repository): string { + return item.smartKey; + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onChooseItem(item: Repository, evt: MouseEvent | KeyboardEvent): void { + new SuggestOtherRepoCommandsModal( + this.plugin.app, + this.plugin, + this.branchName, + item + ).open(); + } +} + +/** + * Just return the repo data + */ +export class ChooseRepoToRun extends FuzzySuggestModal { + plugin: GithubPublisher; + branchName: string; + keyToFind: string | null; + type: "folder" | "file"; + settings: GitHubPublisherSettings; + fileName: string | null; + onSubmit: (item: Repository) => void; + + constructor( + app: App, + plugin: GithubPublisher, + keyToFind: null | string = null, + branchName: string, + type: "folder" | "file", + fileName: string | null, + onSubmit: (item: Repository) => void + ) { + super(app); + this.plugin = plugin; + this.branchName = branchName; + this.keyToFind = keyToFind; + this.onSubmit = onSubmit; + this.fileName = fileName; + this.type = type; + this.settings = plugin.settings; + } + + getItems(): Repository[] { + let repoFound: Repository[] = []; + const defRepo = defaultRepo(this.settings); + if (this.type === "file") { + if ( + this.settings.plugin.shareAll?.enable && + !this.fileName?.startsWith(this.settings.plugin.shareAll?.excludedFileName) + ) { + repoFound.push(defRepo); + } + if (this.keyToFind) { + repoFound = repoFound.concat( + this.settings.github.otherRepo.filter( + (repo: Repository) => repo.shareKey == this.keyToFind + ) + ); + if (this.keyToFind === defRepo.shareKey) { + repoFound.push(defRepo); + } + } + } + repoFound = repoFound.concat( + this.settings.github.otherRepo.filter( + (repo: Repository) => + repo.shareAll?.enable && + !this.fileName?.startsWith(repo.shareAll?.excludedFileName) + ) + ); + if (repoFound.length === 0) return [defRepo, ...this.settings.github.otherRepo]; + repoFound.push(defRepo); + return [...new Set(repoFound)]; + } + getItemText(item: Repository): string { + return item.smartKey; + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onChooseItem(item: Repository, evt: MouseEvent | KeyboardEvent): void { + this.onSubmit(item); + } +} + +/** + * @description This class call the commands on the chosen repo + * @extends FuzzySuggestModal + */ + +export class SuggestOtherRepoCommandsModal extends FuzzySuggestModal { + plugin: GithubPublisher; + branchName: string; + repo: Repository; + settings: GitHubPublisherSettings; + constructor(app: App, plugin: GithubPublisher, branchName: string, repo: Repository) { + super(app); + this.plugin = plugin; + this.branchName = branchName; + this.repo = repo; + this.settings = plugin.settings; + } + getItems(): GithubPublisherCommands[] { + const cmd = [ + { + commands: "shareAllMarkedNotes", + name: i18next.t("commands.uploadAllNotes"), + }, + { + commands: "shareOneNote", + name: i18next.t("commands.shareActiveFile"), + }, + { + commands: "shareNewNote", + name: i18next.t("commands.uploadNewNotes"), + }, + { + commands: "shareAllEditedNotes", + name: i18next.t("commands.uploadAllNewEditedNote"), + }, + { + commands: "shareOnlyEdited", + name: i18next.t("commands.uploadAllEditedNote"), + }, + { + commands: "checkRepositoryValidity", + name: i18next.t("commands.checkValidity.title"), + }, + ]; + if (this.settings.plugin.copyLink) { + cmd.push({ + commands: "createLink", + name: i18next.t("commands.copyLink.title"), + }); + } + if ( + this.settings.upload.autoclean.enable && + this.settings.upload.behavior !== FolderSettings.fixed + ) { + cmd.push({ + commands: "deleteUnsharedDeletedNotes", + name: i18next.t("commands.publisherDeleteClean"), + }); + } + return cmd; + } + getItemText(item: GithubPublisherCommands): string { + return item.name; + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onChooseItem(item: GithubPublisherCommands, evt: MouseEvent | KeyboardEvent): void { + switch (item.commands) { + case "shareAllMarkedNotes": + uploadAllNotes(this.plugin, this.repo, this.branchName); + break; + case "deleteUnsharedDeletedNotes": + deleteCommands(this.plugin, this.repo, this.branchName); + break; + case "shareNewNote": + uploadNewNotes(this.plugin, this.branchName, this.repo); + break; + case "shareAllEditedNotes": + uploadAllEditedNotes(this.plugin, this.branchName, this.repo); + break; + case "shareOnlyEdited": + shareEditedOnly(this.branchName, this.repo, this.plugin); + break; + case "shareOneNote": + shareActiveFile(this.plugin, this.repo); + break; + case "createLink": + createLinkOnActiveFile(this.repo, this.plugin); + break; + case "checkRepositoryValidity": + repositoryValidityActiveFile(this.plugin, this.repo); + break; + } + } +} diff --git a/src/conversion/compiler/dataview.ts b/src/conversion/compiler/dataview.ts index d83eeaa2..593ec0dd 100644 --- a/src/conversion/compiler/dataview.ts +++ b/src/conversion/compiler/dataview.ts @@ -3,16 +3,29 @@ * @link https://github.com/oleeskild/obsidian-digital-garden/blob/main/src/compiler/DataviewCompiler.ts */ -import { GitHubPublisherSettings, LinkedNotes, MultiProperties,PropertiesConversion } from "@interfaces"; +import { + GitHubPublisherSettings, + LinkedNotes, + MultiProperties, + PropertiesConversion, +} from "@interfaces"; import i18next from "i18next"; -import { Component, FrontMatterCache, htmlToMarkdown,TFile } from "obsidian"; -import { DataviewApi,getAPI, isPluginEnabled,Literal, Success } from "obsidian-dataview"; -import { convertToInternalGithub, convertWikilinks, escapeRegex } from "src/conversion/links"; +import { Component, FrontMatterCache, htmlToMarkdown, TFile } from "obsidian"; +import { + DataviewApi, + getAPI, + isPluginEnabled, + Literal, + Success, +} from "obsidian-dataview"; +import { + convertToInternalGithub, + convertWikilinks, + escapeRegex, +} from "src/conversion/links"; import GithubPublisher from "src/main"; import { logs, notif } from "src/utils"; - - class DataviewCompiler { settings: GitHubPublisherSettings; properties: MultiProperties; @@ -20,7 +33,13 @@ class DataviewCompiler { dvApi: DataviewApi; sourceText: string; - constructor(settings: GitHubPublisherSettings, properties: MultiProperties, path: string, dvApi: DataviewApi, sourceText: string) { + constructor( + settings: GitHubPublisherSettings, + properties: MultiProperties, + path: string, + dvApi: DataviewApi, + sourceText: string + ) { this.settings = settings; this.properties = properties; this.path = path; @@ -28,21 +47,30 @@ class DataviewCompiler { this.sourceText = sourceText; } - dvJsMatch () { + dvJsMatch() { const dataviewJsPrefix = this.dvApi.settings.dataviewJsKeyword || "dataviewjs"; - const dataViewJsRegex = new RegExp(`\`\`\`${escapeRegex(dataviewJsPrefix)}\\s(.+?)\`\`\``, "gsm"); + const dataViewJsRegex = new RegExp( + `\`\`\`${escapeRegex(dataviewJsPrefix)}\\s(.+?)\`\`\``, + "gsm" + ); return this.sourceText.matchAll(dataViewJsRegex); } - dvInlineQueryMatches(){ + dvInlineQueryMatches() { const inlineQueryPrefix = this.dvApi.settings.inlineQueryPrefix || "="; - const inlineDataViewRegex = new RegExp(`\`${escapeRegex(inlineQueryPrefix)}(.+?)\``, "gsm"); + const inlineDataViewRegex = new RegExp( + `\`${escapeRegex(inlineQueryPrefix)}(.+?)\``, + "gsm" + ); return this.sourceText.matchAll(inlineDataViewRegex); } - + dvInlineJSMatches() { - const inlineJsQueryPrefix = this.dvApi.settings.inlineJsQueryPrefix ||"$="; - const inlineJsDataViewRegex = new RegExp(`\`${escapeRegex(inlineJsQueryPrefix)}(.+?)\``, "gsm"); + const inlineJsQueryPrefix = this.dvApi.settings.inlineJsQueryPrefix || "$="; + const inlineJsDataViewRegex = new RegExp( + `\`${escapeRegex(inlineJsQueryPrefix)}(.+?)\``, + "gsm" + ); return this.sourceText.matchAll(inlineJsDataViewRegex); } @@ -50,17 +78,20 @@ class DataviewCompiler { return { dataviewJsMatches: this.dvJsMatch(), inlineMatches: this.dvInlineQueryMatches(), - inlineJsMatches: this.dvInlineJSMatches() + inlineJsMatches: this.dvInlineJSMatches(), }; } /** * DQL Dataview - The SQL-like Dataview Query Language * Are in **code blocks** * @link https://blacksmithgu.github.io/obsidian-dataview/queries/dql-js-inline/#dataview-query-language-dql - */ + */ async DQLDataview(query: string) { - const {isInsideCallout, finalQuery} = sanitizeQuery(query); - const markdown = removeDataviewQueries(await this.dvApi.tryQueryMarkdown(finalQuery, this.path) as string, this.properties.frontmatter.general); + const { isInsideCallout, finalQuery } = sanitizeQuery(query); + const markdown = removeDataviewQueries( + (await this.dvApi.tryQueryMarkdown(finalQuery, this.path)) as string, + this.properties.frontmatter.general + ); if (isInsideCallout) { return surroundWithCalloutBlock(markdown); } @@ -84,15 +115,21 @@ class DataviewCompiler { * Syntax : `= query` * (the prefix can be changed in the settings) * @source https://blacksmithgu.github.io/obsidian-dataview/queries/dql-js-inline/#inline-dql - */ + */ async inlineDQLDataview(query: string) { let dataviewResult = this.dvApi.evaluateInline(query, this.path); if (dataviewResult.successful) { dataviewResult = dataviewResult as Success; - return removeDataviewQueries(dataviewResult.value, this.properties.frontmatter.general); + return removeDataviewQueries( + dataviewResult.value, + this.properties.frontmatter.general + ); } else { - return removeDataviewQueries(this.dvApi.settings.renderNullAs, this.properties.frontmatter.general); + return removeDataviewQueries( + this.dvApi.settings.renderNullAs, + this.properties.frontmatter.general + ); } } /** @@ -115,7 +152,10 @@ class DataviewCompiler { const component = new Component(); await this.dvApi.executeJs(evaluateQuery, div, component, this.path); component.load(); - return removeDataviewQueries(htmlToMarkdown(div.innerHTML), this.properties.frontmatter.general); + return removeDataviewQueries( + htmlToMarkdown(div.innerHTML), + this.properties.frontmatter.general + ); } } @@ -133,20 +173,20 @@ export async function convertDataviewQueries( path: string, frontmatter: FrontMatterCache | undefined | null, sourceFile: TFile, - properties: MultiProperties, + properties: MultiProperties ): Promise { const plugin = properties.plugin; const app = plugin.app; const settings = plugin.settings; let replacedText = text; - const dataViewRegex = /```dataview\s(.+?)```/gsm; + const dataViewRegex = /```dataview\s(.+?)```/gms; const isDataviewEnabled = app.plugins.plugins.dataview; if (!isDataviewEnabled || !isPluginEnabled(app)) return replacedText; const dvApi = getAPI(app); if (!dvApi || dvApi === undefined) return replacedText; const matches = text.matchAll(dataViewRegex); const compiler = new DataviewCompiler(settings, properties, path, dvApi, text); - const {dataviewJsMatches, inlineMatches, inlineJsMatches} = compiler.matches(); + const { dataviewJsMatches, inlineMatches, inlineJsMatches } = compiler.matches(); if (!matches && !inlineMatches && !dataviewJsMatches && !inlineJsMatches) { logs({ settings }, "No dataview queries found"); @@ -156,7 +196,7 @@ export async function convertDataviewQueries( /** * DQL Dataview - The SQL-like Dataview Query Language - */ + */ for (const queryBlock of matches) { try { const block = queryBlock[0]; @@ -215,7 +255,10 @@ export async function convertDataviewQueries( * @param {PropertiesConversion} frontmatterSettings the settings * @return {string} the text without dataview queries or the dataview queries in markdown */ -function removeDataviewQueries(dataviewMarkdown: Literal, frontmatterSettings: PropertiesConversion): string { +function removeDataviewQueries( + dataviewMarkdown: Literal, + frontmatterSettings: PropertiesConversion +): string { const toString = dataviewMarkdown?.toString(); return frontmatterSettings.dataview && dataviewMarkdown && toString ? toString : ""; } @@ -227,8 +270,8 @@ async function convertDataviewLinks( md: string, frontmatter: FrontMatterCache | undefined | null, sourceFile: TFile, - properties: MultiProperties): Promise { - + properties: MultiProperties +): Promise { const dataviewPath = getDataviewPath(md, properties.plugin); md = await convertToInternalGithub( md, @@ -236,7 +279,7 @@ async function convertDataviewLinks( sourceFile, frontmatter, properties - ); + ); return convertWikilinks( md, properties.frontmatter.general, @@ -253,7 +296,7 @@ export function getDataviewPath( markdown: string, plugin: GithubPublisher ): LinkedNotes[] { - const { settings} = plugin; + const { settings } = plugin; const vault = plugin.app.vault; if (!settings.conversion.dataview) { return []; @@ -275,7 +318,7 @@ export function getDataviewPath( linked, linkFrom, altText, - type: "link" + type: "link", }); } } @@ -288,7 +331,7 @@ export function getDataviewPath( * @param query {string} The query to sanitize * @returns {isInsideCallout: boolean, finalQuery: string} */ -function sanitizeQuery(query: string): {isInsideCallout: boolean, finalQuery: string} { +function sanitizeQuery(query: string): { isInsideCallout: boolean; finalQuery: string } { let isInsideCallout = false; const parts = query.split("\n"); const sanitized = []; @@ -302,7 +345,7 @@ function sanitizeQuery(query: string): {isInsideCallout: boolean, finalQuery: st } } const finalQuery = isInsideCallout ? sanitized.join("\n") : query; - return {isInsideCallout, finalQuery}; + return { isInsideCallout, finalQuery }; } /** @@ -314,4 +357,4 @@ function surroundWithCalloutBlock(input: string): string { const tmp = input.split("\n"); return ` ${tmp.join("\n> ")}`; -} \ No newline at end of file +} diff --git a/src/conversion/compiler/embeds.ts b/src/conversion/compiler/embeds.ts index ff2adc23..7caf68e2 100644 --- a/src/conversion/compiler/embeds.ts +++ b/src/conversion/compiler/embeds.ts @@ -6,10 +6,7 @@ * Each function is modified to fit the needs of this plugin, but citation are done in the code for each function */ -import { - GitHubPublisherSettings, - LinkedNotes, - MultiProperties} from "@interfaces"; +import { GitHubPublisherSettings, LinkedNotes, MultiProperties } from "@interfaces"; import { App, BlockSubpathResult, @@ -17,13 +14,17 @@ import { HeadingSubpathResult, parseLinktext, resolveSubpath, - TFile} from "obsidian"; + TFile, +} from "obsidian"; import { getAPI, Link } from "obsidian-dataview"; import { addToYaml } from "src/conversion"; -import { createRelativePath, getTitleField, regexOnFileName } from "src/conversion/file_path"; +import { + createRelativePath, + getTitleField, + regexOnFileName, +} from "src/conversion/file_path"; import GithubPublisher from "src/main"; -import {isShared} from "src/utils/data_validation_test"; - +import { isShared } from "src/utils/data_validation_test"; /** * Apply the indent to the text @@ -107,7 +108,6 @@ function extractSubpath( subpathResult: HeadingSubpathResult | BlockSubpathResult, cache: CachedMetadata ): string { - if (subpathResult.type === "block" && subpathResult.list && cache.listItems) { const targetItem = subpathResult.list; const ancestors = new Set([targetItem.position.start.line]); @@ -154,9 +154,10 @@ export async function bakeEmbeds( originalFile: TFile, ancestor: Set, properties: MultiProperties, - subpath: string|null, - linkedNotes: LinkedNotes[]): Promise { - const { plugin } = properties; + subpath: string | null, + linkedNotes: LinkedNotes[] +): Promise { + const { plugin } = properties; const { app, settings } = plugin; const { vault, metadataCache } = plugin.app; let text = await vault.cachedRead(originalFile); @@ -175,7 +176,7 @@ export async function bakeEmbeds( newAncestors.add(originalFile); let posOffset = 0; for (const embed of targets) { - const {path, subpath} = parseLinktext(embed.link); + const { path, subpath } = parseLinktext(embed.link); const linked = metadataCache.getFirstLinkpathDest(path, originalFile.path); if (linked === null || linked?.extension !== "md") continue; const start = embed.position.start.offset + posOffset; @@ -187,13 +188,25 @@ export async function bakeEmbeds( const replaceTarget = async (replacement: string) => { if (settings.embed.bake?.textAfter) { - let textAfter = await changeURL(settings.embed.bake?.textAfter, properties, linked, originalFile, linkedNotes); + let textAfter = await changeURL( + settings.embed.bake?.textAfter, + properties, + linked, + originalFile, + linkedNotes + ); textAfter = changeTitle(textAfter, linked, app, settings); const newLine = replacement.match(/[\s\n]/g) ? "" : "\n"; replacement = `${replacement}${newLine}${textAfter}`; } if (settings.embed.bake?.textBefore) { - let textBefore = await changeURL(settings.embed.bake?.textBefore, properties, linked, originalFile, linkedNotes); + let textBefore = await changeURL( + settings.embed.bake?.textBefore, + properties, + linked, + originalFile, + linkedNotes + ); textBefore = changeTitle(textBefore, linked, app, settings); replacement = `${textBefore}\n${replacement}`; } @@ -208,9 +221,12 @@ export async function bakeEmbeds( //do nothing continue; } - const baked = sanitizeBakedContent(await bakeEmbeds(linked, newAncestors, properties, subpath, linkedNotes)); + const baked = sanitizeBakedContent( + await bakeEmbeds(linked, newAncestors, properties, subpath, linkedNotes) + ); await replaceTarget( - listMatch ? applyIndent(stripFirstBullet(baked), listMatch[1]) : baked); + listMatch ? applyIndent(stripFirstBullet(baked), listMatch[1]) : baked + ); } return text; } @@ -225,17 +241,28 @@ export async function bakeEmbeds( * @param linkedNotes {LinkedNotes[]} The linked notes embedded in the file * @returns {Promise} */ -async function changeURL(textToAdd: string, properties: MultiProperties, linked: TFile, sourceFile: TFile, linkedNotes: LinkedNotes[]): Promise { +async function changeURL( + textToAdd: string, + properties: MultiProperties, + linked: TFile, + sourceFile: TFile, + linkedNotes: LinkedNotes[] +): Promise { const app = properties.plugin.app; const frontmatter = app.metadataCache.getFileCache(linked)?.frontmatter; if (!frontmatter) return textToAdd; const linkedNote = linkedNotes.find((note) => note.linked === linked); if (!linkedNote) return textToAdd; if (properties.frontmatter.general.convertInternalLinks) { - const relativePath = await createRelativePath(sourceFile, linkedNote, frontmatter, properties); - return textToAdd.replace(/\{{2}url\}{2}/gmi, relativePath); - } return textToAdd.replace(/\{{2}url\}{2}/gmi, linkedNote.linked.path); - + const relativePath = await createRelativePath( + sourceFile, + linkedNote, + frontmatter, + properties + ); + return textToAdd.replace(/\{{2}url\}{2}/gim, relativePath); + } + return textToAdd.replace(/\{{2}url\}{2}/gim, linkedNote.linked.path); } /** @@ -246,12 +273,20 @@ async function changeURL(textToAdd: string, properties: MultiProperties, linked: * @param settings {GitHubPublisherSettings} * @returns {string} */ -function changeTitle(textToAdd: string, linked: TFile, app: App, settings: GitHubPublisherSettings):string { +function changeTitle( + textToAdd: string, + linked: TFile, + app: App, + settings: GitHubPublisherSettings +): string { const title = linked.basename; const frontmatter = app.metadataCache.getFileCache(linked)?.frontmatter; - if (!frontmatter) return textToAdd.replace(/\{{2}title\}{2}/gmi, title); - const getTitle = regexOnFileName(getTitleField(frontmatter, linked, settings), settings).replace(".md", ""); - return textToAdd.replace(/\{{2}title\}{2}/gmi, getTitle); + if (!frontmatter) return textToAdd.replace(/\{{2}title\}{2}/gim, title); + const getTitle = regexOnFileName( + getTitleField(frontmatter, linked, settings), + settings + ).replace(".md", ""); + return textToAdd.replace(/\{{2}title\}{2}/gim, getTitle); } /** @@ -266,9 +301,9 @@ function changeTitle(textToAdd: string, linked: TFile, app: App, settings: GitHu export async function convertInlineDataview( text: string, plugin: GithubPublisher, - sourceFile: TFile, + sourceFile: TFile ): Promise { - const {settings, app} = plugin; + const { settings, app } = plugin; if ( settings.conversion.tags.fields.length === 0 || !app.plugins.enabledPlugins.has("dataview") @@ -298,8 +333,12 @@ export async function convertInlineDataview( stringifyField = dataviewExtract(item as Link, settings); if (stringifyField) valueToAdd.push(stringifyField); } else if ( - stringifyField && !settings.conversion.tags.exclude.includes(stringifyField.toString() as string) - ) valueToAdd.push(stringifyField.toString() as string); + stringifyField && + !settings.conversion.tags.exclude.includes( + stringifyField.toString() as string + ) + ) + valueToAdd.push(stringifyField.toString() as string); } } else if ( !settings.conversion.tags.exclude.includes(fieldValue.toString() as string) @@ -309,7 +348,10 @@ export async function convertInlineDataview( } } if (valueToAdd.length > 0) { - return addToYaml(text, valueToAdd.filter(Boolean), plugin, {properties: null, file: sourceFile}); + return addToYaml(text, valueToAdd.filter(Boolean), plugin, { + properties: null, + file: sourceFile, + }); } return text; } @@ -322,12 +364,13 @@ export async function convertInlineDataview( * @param {GitHubPublisherSettings} settings the global settings * @return {string | null} the display text by dataview */ -function dataviewExtract(fieldValue: Link, settings: GitHubPublisherSettings): string | null { +function dataviewExtract( + fieldValue: Link, + settings: GitHubPublisherSettings +): string | null { const basename = (name: string) => /([^/\\.]*)(\..*)?$/.exec(name)![1]; const filename = basename(fieldValue.path).toString(); - const display = fieldValue.display - ? fieldValue.display.toString() - : filename; + const display = fieldValue.display ? fieldValue.display.toString() : filename; if ( !settings.conversion.tags.exclude.includes(display) && !settings.conversion.tags.fields.includes(filename) @@ -336,4 +379,3 @@ function dataviewExtract(fieldValue: Link, settings: GitHubPublisherSettings): s } return null; } - diff --git a/src/conversion/compiler/excalidraw.ts b/src/conversion/compiler/excalidraw.ts index 1f7919c4..81bfe703 100644 --- a/src/conversion/compiler/excalidraw.ts +++ b/src/conversion/compiler/excalidraw.ts @@ -1,7 +1,4 @@ - - -import { App,TFile } from "obsidian"; - +import { App, TFile } from "obsidian"; export async function convertToHTMLSVG(file: TFile, app: App) { try { @@ -11,9 +8,8 @@ export async function convertToHTMLSVG(file: TFile, app: App) { const ea = excalidraw.ea; const svg = await ea.createSVG(file.path, ea.getExportSettings(true, true)); return svg.outerHTML as string; - } catch (e) { console.error(e); return null; } -} \ No newline at end of file +} diff --git a/src/conversion/file_path.ts b/src/conversion/file_path.ts index cba9ea21..8dd1a544 100644 --- a/src/conversion/file_path.ts +++ b/src/conversion/file_path.ts @@ -1,511 +1,557 @@ -import { - FIND_REGEX, - FolderSettings, - GitHubPublisherSettings, - LinkedNotes, - MultiProperties, - Properties, - PropertiesConversion, - Repository, -} from "@interfaces"; -import { - FrontMatterCache, - normalizePath, - TFile, - TFolder, - Vault, -} from "obsidian"; -import { createRegexFromText } from "src/conversion/find_and_replace_text"; -import GithubPublisher from "src/main"; -import { - logs, -} from "src/utils"; -import {checkIfRepoIsInAnother, isInternalShared, isShared} from "src/utils/data_validation_test"; -import { frontmatterFromFile, frontmatterSettingsRepository, getCategory, getFrontmatterSettings, getProperties } from "src/utils/parse_frontmatter"; -import merge from "ts-deepmerge"; - - -/** Search a link in the entire frontmatter value */ -/** Link will always be in the form of [[]] */ -export function linkIsInFormatter( - linkedFile: LinkedNotes, - frontmatter: FrontMatterCache | undefined | null, -): boolean { - if (frontmatter) { - for (const key in frontmatter) { - const wikiLinks = `[[${linkedFile.linkFrom}]]`; - if (frontmatter[key] === wikiLinks) { - return true; - } - } - } - return false; -} - -export function textIsInFrontmatter( - text: string, - frontmatter: FrontMatterCache | undefined | null, -): boolean { - - if (frontmatter) { - for (const key in frontmatter) { - if (frontmatter[key] === `[[${text}]]`) { - return true; - } - } - } - return false; -} - -/** - * Create relative path from a sourceFile to a targetPath. If the target file is a note, only share if the frontmatter sharekey is present and true - * @param {TFile} sourceFile the shared file containing all links, embed etc - * @param {LinkedNotes} targetFile The target file - * @param {FrontMatterCache | null} frontmatter FrontmatterCache or null - * @param properties - The properties of the source file - * @return {string} relative path - */ - -export async function createRelativePath( - sourceFile: TFile, - targetFile: LinkedNotes, - frontmatter: FrontMatterCache | null | undefined, - properties: MultiProperties, -): Promise { - const settings = properties.plugin.settings; - const shortRepo = properties.repository; - const sourcePath = getReceiptFolder(sourceFile, shortRepo, properties.plugin, properties.frontmatter.prop); - const frontmatterTarget = frontmatterFromFile(targetFile.linked, properties.plugin, properties.repository); - const targetRepo = getProperties(properties.plugin, shortRepo, frontmatterTarget); - const isFromAnotherRepo = checkIfRepoIsInAnother(properties.frontmatter.prop, targetRepo); - const shared = isInternalShared( - frontmatterTarget, - properties, - targetFile.linked, - ); - logs({settings}, `Shared: ${shared} for ${targetFile.linked.path}`); - if ( - targetFile.linked.extension === "md" && !targetFile.linked.name.includes("excalidraw") && (!isFromAnotherRepo || !shared) - ) { - return targetFile.destinationFilePath ? targetFile.destinationFilePath: targetFile.linked.basename; - } - if (targetFile.linked.path === sourceFile.path) { - return getReceiptFolder(targetFile.linked, shortRepo, properties.plugin, targetRepo).split("/").at(-1) as string; - } - - const frontmatterSettingsFromFile = getFrontmatterSettings(frontmatter, settings, shortRepo); - const frontmatterSettingsFromRepository = frontmatterSettingsRepository(properties.plugin, shortRepo); - const frontmatterSettings = merge(frontmatterSettingsFromRepository, frontmatterSettingsFromFile); - - const targetPath = - targetFile.linked.extension === "md" && !targetFile.linked.name.includes("excalidraw") - ? getReceiptFolder(targetFile.linked, shortRepo, properties.plugin, targetRepo) - : getImagePath( - targetFile.linked, - properties.plugin, - frontmatterSettings, - targetRepo - ); - const sourceList = sourcePath.split("/"); - const targetList = targetPath.split("/"); - - /** - * Exclude the first different element in the list - * @param {string[]} sourceList source list - * @param {string[]} targetList target list - * @returns {string[]} list with the first different element - */ - const excludeUtilDiff = ( - sourceList: string[], - targetList: string[] - ): string[] => { - let i = 0; - while (sourceList[i] === targetList[i]) { - i++; - } - return sourceList.slice(i); - }; - - const diffSourcePath = excludeUtilDiff(sourceList, targetList); - const diffTargetPath = excludeUtilDiff(targetList, sourceList); - const diffTarget = function (folderPath: string[]) { - const relativePath = []; - for (const folder of folderPath) { - if (folder != folderPath.at(-1)) { - relativePath.push(".."); - } - } - - return relativePath; - }; - const relativePath = diffTarget(diffSourcePath); - if (relativePath.length === 0) { - relativePath.push("."); - } - const relative = relativePath.concat(diffTargetPath).join("/"); - if (relative.trim() === "." || relative.trim() === "") { - //in case of errors - return getReceiptFolder( - targetFile.linked, - shortRepo, - properties.plugin, - targetRepo - ).split("/").at(-1) as string; - } - return relative; -} - -/** - * Check if the file is a folder note based on the global settings - Run for OBSIDIAN PATH option - * Change the filename in index.md if folder note - * @param {TFile} file Source file - * @param {Vault} vault - * @param {GitHubPublisherSettings} settings Global Settings - * @param {string} fileName the filename after reading the frontmatter - * @return {string} original file name or index.md - */ - -function folderNoteIndexOBS( - file: TFile, - vault: Vault, - settings: GitHubPublisherSettings, - fileName: string, -): string { - const index = settings.upload.folderNote.rename; - const folderParent = file.parent ? `/${file.parent.path}/` : "/" ; - const defaultPath = `${folderParent}${regexOnFileName(fileName, settings)}`; - if (!settings.upload.folderNote.enable) return defaultPath; - const parentFolderName = file.parent ? file.parent.name : ""; - if (fileName.replace(".md", "") === parentFolderName) return `/${file.parent!.path}/${index}`; - const outsideFolder = vault.getAbstractFileByPath( - file.path.replace(".md", "") - ); - if (outsideFolder && outsideFolder instanceof TFolder) return `/${outsideFolder.path}/${index}`; - return defaultPath; -} - -/** - * Create the path on hypothetical vault using the obsidian path and replacing the name by index.md if needed and removing the subfolder if needed - * @param {TFile} file Source file - * @param {Vault} vault - * @param {GitHubPublisherSettings} settings Global Settings - * @param {string} fileName file name - * @return {string} path - */ - -function createObsidianPath( - file: TFile, - settings: GitHubPublisherSettings, - vault: Vault, - fileName: string, - prop?: Properties, -): string { - fileName = folderNoteIndexOBS(file, vault, settings, fileName); - - const defaultFolder = prop?.path?.defaultName && prop.path.defaultName.length > 0 ? - prop.path.defaultName : settings.upload.defaultName.length > 0 ? - settings.upload.defaultName : ""; - const path = defaultFolder + fileName; - //remove last word from path splitted with / - let pathWithoutEnd = path.split("/").slice(0, -1).join("/"); - //get file name only - const fileNameOnly = path.split("/").at(-1) ?? ""; - pathWithoutEnd = regexOnPath(pathWithoutEnd, settings); - if (pathWithoutEnd.trim().length === 0) return fileNameOnly; - return (`${pathWithoutEnd}/${fileNameOnly}`).replace(/^\//, ""); -} - -/** - * Check if a file (using category frontmatter option) is a folder note, and rename it to index.md if it is - * @param {string} fileName file name - * @param {FrontMatterCache} frontmatter frontmatter - * @param {GitHubPublisherSettings} settings Settings - * @returns {string} renamed file name or original file name - */ - -function folderNoteIndexYAML( - fileName: string, - frontmatter: FrontMatterCache | undefined | null, - settings: GitHubPublisherSettings, - prop?: Properties, -): string { - const category = getCategory(frontmatter, settings, prop?.path); - const catSplit = category.split("/"); - const parentCatFolder = category.endsWith("/") ? catSplit.at(-2) as string : catSplit.at(-1) as string; - - if (!settings.upload.folderNote.enable) return regexOnFileName(fileName, settings); - if ( - fileName.replace(".md", "").toLowerCase() === - parentCatFolder?.toLowerCase() - ) - return settings.upload.folderNote.rename; - return regexOnFileName(fileName, settings); -} - -/** - * Create filepath based on settings and frontmatter for the github repository - * @param {GitHubPublisherSettings} settings Settings - * @param {FrontMatterCache} frontmatter frontmatter - * @param {string} fileName file name - * @returns {string} filepath - */ - -function createFrontmatterPath( - settings: GitHubPublisherSettings, - frontmatter: FrontMatterCache | null | undefined, - fileName: string, - prop?: Properties, -): string { - - const uploadSettings = settings.upload; - const folderCategory = getCategory(frontmatter, settings, prop?.path); - const path = prop?.path; - const folderNote = folderNoteIndexYAML(fileName, frontmatter, settings, prop); - const root = path?.rootFolder && path.rootFolder.length > 0 ? path.rootFolder : uploadSettings.rootFolder.length > 0 ? uploadSettings.rootFolder : undefined; - const folderRoot = root && !folderCategory.includes(root) ? `${root}/` : ""; - if (folderCategory.trim().length === 0) return folderNote; - const folderRegex = regexOnPath(folderRoot + folderCategory, settings); - if (folderRegex.trim().length === 0) return folderNote; - return (`${folderRegex}/${folderNote}`).replace(/^\//, ""); -} - -/** - * Apply a regex edition on the title. It can be used to remove special characters or to add a prefix or suffix - * ! Not applied on the index.md file (folder note) - * @param {string} fileName file name - * @param {GitHubPublisherSettings} settings Settings - * @return {string} edited file name - */ -export function regexOnFileName(fileName: string, settings: GitHubPublisherSettings): string { - const uploadSettings = settings.upload; - if (fileName === uploadSettings.folderNote.rename && uploadSettings.folderNote.enable || uploadSettings.replaceTitle.length === 0) return fileName; - const extension = fileName.match(/\.[0-9a-z]+$/i)?.at(-1) ?? ""; - fileName = fileName.replace(extension, ""); - for (const regexTitle of uploadSettings.replaceTitle) { - if (regexTitle.regex?.trim().length > 0) { - const toReplace = regexTitle.regex; - const replaceWith = regexTitle.replacement; - if (toReplace.match(FIND_REGEX)) { - const regex = createRegexFromText(toReplace); - fileName = fileName.replace( - regex, - replaceWith - ); - } else { - fileName = fileName.replaceAll( - toReplace, - replaceWith - ); - } - } - } - return `${fileName}${extension}`; -} - - -/** - * Allow to modify enterely the path of a file, using regex / string replace - * @param {string} path path - * @param {GitHubPublisherSettings} settings Settings - * @return {string} edited path - */ -export function regexOnPath(path: string, settings: GitHubPublisherSettings):string { - const uploadSettings = settings.upload; - if (uploadSettings.behavior === FolderSettings.fixed || uploadSettings.replacePath.length === 0) return path; - for (const regexTitle of uploadSettings.replacePath) { - if (regexTitle.regex.trim().length > 0) { - const toReplace = regexTitle.regex; - const replaceWith = regexTitle.replacement; - if (toReplace.match(FIND_REGEX)) { - const regex = createRegexFromText(toReplace); - path = path.replace( - regex, - replaceWith - ); - } else { - path = path.replaceAll( - toReplace, - replaceWith - ); - } - } - } - return path; -} - -/** - * Get the title field from frontmatter or file name - * @param {FrontMatterCache} frontmatter frontmatter - * @param {TFile} file file - * @param {GitHubPublisherSettings} settings Settings - * @returns {string} title - */ -export function getTitleField( - frontmatter: FrontMatterCache | undefined | null, - file: TFile, - settings: GitHubPublisherSettings -): string { - const fileName = file.name; - if ( - frontmatter && - settings.upload.frontmatterTitle.enable && - frontmatter[settings.upload.frontmatterTitle.key] && - frontmatter[settings.upload.frontmatterTitle.key] !== fileName - ) { - return `${frontmatter[settings.upload.frontmatterTitle.key]}.md`; - } - return fileName; -} - - - -/** - * Get the path where the file will be saved in the github repository - */ - -export function getReceiptFolder( - file: TFile, - otherRepo: Repository | null, - plugin: GithubPublisher, - prop?: Properties | Properties[], -): string { - const { vault} = plugin.app; - const settings = plugin.settings; - if (file.extension === "md") { - const frontmatter = frontmatterFromFile(file, plugin, otherRepo); - if (!prop) prop = getProperties(plugin, otherRepo, frontmatter); - prop = prop instanceof Array ? prop : [prop]; - let targetRepo = prop.find((repo) => repo.path?.smartkey === otherRepo?.smartKey || "default"); - if (!targetRepo) targetRepo = prop[0]; - const fileName = getTitleField(frontmatter, file, settings); - const editedFileName = regexOnFileName(fileName, settings); - if ( - !isShared(frontmatter, settings, file, otherRepo) - ) { - return normalizePath(fileName); - } - if (targetRepo.path?.override) { - const frontmatterPath = targetRepo.path.override; - if (frontmatterPath == "" || frontmatterPath == "/") { - return normalizePath(editedFileName); - } - return normalizePath(`${frontmatterPath}/${editedFileName}`); - } else if (targetRepo.path?.type === FolderSettings.yaml) { - return normalizePath(createFrontmatterPath(settings, frontmatter, fileName, targetRepo)); - } else if (targetRepo.path?.type === FolderSettings.obsidian) { - return normalizePath(createObsidianPath(file, settings, vault, fileName, targetRepo)); - } else { - return targetRepo.path?.defaultName && targetRepo.path.defaultName.length > 0 - ? normalizePath(`${targetRepo.path.defaultName}/${editedFileName}`) - : normalizePath(editedFileName); - } - } - return file.path; -} - - -/** - * Create filepath in github Repository based on settings and frontmatter for image - * @param {TFile} file : Source file - * @param {PropertiesConversion | null} sourceFrontmatter - * @return {string} the new filepath - */ -export function getImagePath( - file: TFile, - plugin: GithubPublisher, - sourceFrontmatter: PropertiesConversion | null, - repository: Properties | Properties[], -): string { - const settings = plugin.settings; - const overridePath = repository instanceof Array ? repository[0] : repository; - - const imagePath = createImagePath(file, settings, sourceFrontmatter, overridePath); - const path = regexOnPath(imagePath.path, settings); - const name = regexOnFileName(imagePath.name, settings); - return normalizePath(path.replace(file.name, name)); -} - - -/** - * Create filepath in github Repository based on settings and frontmatter for image - * @param {TFile} file : Source file - * @param {GitHubPublisherSettings} settings Settings - * @param {PropertiesConversion | null} sourceFrontmatter - * @return {string} the new filepath - */ - -function createImagePath(file: TFile, - settings: GitHubPublisherSettings, - sourceFrontmatter: PropertiesConversion | null, - overridePath?: Properties, -): { path: string, name: string } { - let fileName = file.name; - let filePath = file.path; - if (file.name.includes(".excalidraw")) { - fileName = fileName.replace(".excalidraw.md", ".svg"); - filePath = filePath.replace(".excalidraw.md", ".svg"); - } - const result : { path: string, name: string } = { path: filePath, name: fileName }; - const behavior = overridePath?.path?.type ? overridePath.path.type : settings.upload.behavior; - const rootFolder = overridePath?.path?.rootFolder ? overridePath.path.rootFolder : settings.upload.rootFolder; - const defaultFolderName = overridePath?.path?.defaultName ? overridePath.path.defaultName : settings.upload.defaultName; - if (sourceFrontmatter?.attachmentLinks && sourceFrontmatter.attachmentLinks.length > 0) { - result.path = normalizePath(`${sourceFrontmatter.attachmentLinks}/${fileName}`); - return result; - } - if (settings.embed.useObsidianFolder) { - if (behavior === FolderSettings.yaml) { - result.path = rootFolder.length > 0 ? normalizePath(`${rootFolder}/${filePath}`) : filePath; - } - else { - //no root, but default folder name - result.path = defaultFolderName.length > 0 ? normalizePath(`${defaultFolderName}/${filePath}`) : filePath; - } - result.path = applyOverriddenPath(fileName, result.path, settings).filePath; - return result; - } - const defaultImageFolder = overridePath?.path?.attachment?.folder ? overridePath.path?.attachment?.folder : settings.embed.folder; - //find in override - const overriddenPath = applyOverriddenPath(fileName, filePath, settings); - if (overriddenPath.overridden) { - result.path = overriddenPath.filePath; - } - else if (defaultImageFolder.length > 0) { - result.path = normalizePath(`${defaultImageFolder}/${fileName}`); - } else if (defaultFolderName.length > 0) { - result.path = normalizePath(`${defaultFolderName}/${fileName}`); - } else { - result.path = filePath; - } - return result; -} - -/** - * Override the path of an attachment using the settings (regex or string replace) - * @param fileName - The name of the file - * @param filePath - The (original) path of the file - * @param settings - The settings of the plugin - * @returns The new path of the file and whether it was overridden - */ -function applyOverriddenPath(fileName: string, filePath: string, settings: GitHubPublisherSettings): {filePath: string, overridden: boolean} { - let overridden = false; - const isOverridden = settings.embed.overrideAttachments.filter((override) => { - const isRegex = override.path.match(FIND_REGEX); - const regex = isRegex ? new RegExp(isRegex[1], isRegex[2]) : undefined; - return ( - regex?.test(filePath) - || filePath === override.path - || override.path.contains("{{all}}")) - && !override.destination.contains("{{default}}"); - }); - if (isOverridden.length > 0) { - overridden = true; - for (const override of isOverridden) { - const isRegex = override.path.match(FIND_REGEX); - const regex = isRegex ? new RegExp(isRegex[1], isRegex[2]) : null; - const dest = override.destination.replace("{{name}}", fileName); - filePath = regex ? normalizePath(filePath.replace(regex, dest)) : normalizePath(filePath.replace(override.path, dest)); - } - } - return {filePath, overridden}; -} \ No newline at end of file +import { + FIND_REGEX, + FolderSettings, + GitHubPublisherSettings, + LinkedNotes, + MultiProperties, + Properties, + PropertiesConversion, + Repository, +} from "@interfaces"; +import { FrontMatterCache, normalizePath, TFile, TFolder, Vault } from "obsidian"; +import { createRegexFromText } from "src/conversion/find_and_replace_text"; +import GithubPublisher from "src/main"; +import { logs } from "src/utils"; +import { + checkIfRepoIsInAnother, + isInternalShared, + isShared, +} from "src/utils/data_validation_test"; +import { + frontmatterFromFile, + frontmatterSettingsRepository, + getCategory, + getFrontmatterSettings, + getProperties, +} from "src/utils/parse_frontmatter"; +import merge from "ts-deepmerge"; + +/** Search a link in the entire frontmatter value */ +/** Link will always be in the form of [[]] */ +export function linkIsInFormatter( + linkedFile: LinkedNotes, + frontmatter: FrontMatterCache | undefined | null +): boolean { + if (frontmatter) { + for (const key in frontmatter) { + const wikiLinks = `[[${linkedFile.linkFrom}]]`; + if (frontmatter[key] === wikiLinks) { + return true; + } + } + } + return false; +} + +export function textIsInFrontmatter( + text: string, + frontmatter: FrontMatterCache | undefined | null +): boolean { + if (frontmatter) { + for (const key in frontmatter) { + if (frontmatter[key] === `[[${text}]]`) { + return true; + } + } + } + return false; +} + +/** + * Create relative path from a sourceFile to a targetPath. If the target file is a note, only share if the frontmatter sharekey is present and true + * @param {TFile} sourceFile the shared file containing all links, embed etc + * @param {LinkedNotes} targetFile The target file + * @param {FrontMatterCache | null} frontmatter FrontmatterCache or null + * @param properties - The properties of the source file + * @return {string} relative path + */ + +export async function createRelativePath( + sourceFile: TFile, + targetFile: LinkedNotes, + frontmatter: FrontMatterCache | null | undefined, + properties: MultiProperties +): Promise { + const settings = properties.plugin.settings; + const shortRepo = properties.repository; + const sourcePath = getReceiptFolder( + sourceFile, + shortRepo, + properties.plugin, + properties.frontmatter.prop + ); + const frontmatterTarget = frontmatterFromFile( + targetFile.linked, + properties.plugin, + properties.repository + ); + const targetRepo = getProperties(properties.plugin, shortRepo, frontmatterTarget); + const isFromAnotherRepo = checkIfRepoIsInAnother( + properties.frontmatter.prop, + targetRepo + ); + const shared = isInternalShared(frontmatterTarget, properties, targetFile.linked); + logs({ settings }, `Shared: ${shared} for ${targetFile.linked.path}`); + if ( + targetFile.linked.extension === "md" && + !targetFile.linked.name.includes("excalidraw") && + (!isFromAnotherRepo || !shared) + ) { + return targetFile.destinationFilePath + ? targetFile.destinationFilePath + : targetFile.linked.basename; + } + if (targetFile.linked.path === sourceFile.path) { + return getReceiptFolder(targetFile.linked, shortRepo, properties.plugin, targetRepo) + .split("/") + .at(-1) as string; + } + + const frontmatterSettingsFromFile = getFrontmatterSettings( + frontmatter, + settings, + shortRepo + ); + const frontmatterSettingsFromRepository = frontmatterSettingsRepository( + properties.plugin, + shortRepo + ); + const frontmatterSettings = merge( + frontmatterSettingsFromRepository, + frontmatterSettingsFromFile + ); + + const targetPath = + targetFile.linked.extension === "md" && !targetFile.linked.name.includes("excalidraw") + ? getReceiptFolder(targetFile.linked, shortRepo, properties.plugin, targetRepo) + : getImagePath( + targetFile.linked, + properties.plugin, + frontmatterSettings, + targetRepo + ); + const sourceList = sourcePath.split("/"); + const targetList = targetPath.split("/"); + + /** + * Exclude the first different element in the list + * @param {string[]} sourceList source list + * @param {string[]} targetList target list + * @returns {string[]} list with the first different element + */ + const excludeUtilDiff = (sourceList: string[], targetList: string[]): string[] => { + let i = 0; + while (sourceList[i] === targetList[i]) { + i++; + } + return sourceList.slice(i); + }; + + const diffSourcePath = excludeUtilDiff(sourceList, targetList); + const diffTargetPath = excludeUtilDiff(targetList, sourceList); + const diffTarget = function (folderPath: string[]) { + const relativePath = []; + for (const folder of folderPath) { + if (folder != folderPath.at(-1)) { + relativePath.push(".."); + } + } + + return relativePath; + }; + const relativePath = diffTarget(diffSourcePath); + if (relativePath.length === 0) { + relativePath.push("."); + } + const relative = relativePath.concat(diffTargetPath).join("/"); + if (relative.trim() === "." || relative.trim() === "") { + //in case of errors + return getReceiptFolder(targetFile.linked, shortRepo, properties.plugin, targetRepo) + .split("/") + .at(-1) as string; + } + return relative; +} + +/** + * Check if the file is a folder note based on the global settings - Run for OBSIDIAN PATH option + * Change the filename in index.md if folder note + * @param {TFile} file Source file + * @param {Vault} vault + * @param {GitHubPublisherSettings} settings Global Settings + * @param {string} fileName the filename after reading the frontmatter + * @return {string} original file name or index.md + */ + +function folderNoteIndexOBS( + file: TFile, + vault: Vault, + settings: GitHubPublisherSettings, + fileName: string +): string { + const index = settings.upload.folderNote.rename; + const folderParent = file.parent ? `/${file.parent.path}/` : "/"; + const defaultPath = `${folderParent}${regexOnFileName(fileName, settings)}`; + if (!settings.upload.folderNote.enable) return defaultPath; + const parentFolderName = file.parent ? file.parent.name : ""; + if (fileName.replace(".md", "") === parentFolderName) + return `/${file.parent!.path}/${index}`; + const outsideFolder = vault.getAbstractFileByPath(file.path.replace(".md", "")); + if (outsideFolder && outsideFolder instanceof TFolder) + return `/${outsideFolder.path}/${index}`; + return defaultPath; +} + +/** + * Create the path on hypothetical vault using the obsidian path and replacing the name by index.md if needed and removing the subfolder if needed + * @param {TFile} file Source file + * @param {Vault} vault + * @param {GitHubPublisherSettings} settings Global Settings + * @param {string} fileName file name + * @return {string} path + */ + +function createObsidianPath( + file: TFile, + settings: GitHubPublisherSettings, + vault: Vault, + fileName: string, + prop?: Properties +): string { + fileName = folderNoteIndexOBS(file, vault, settings, fileName); + + const defaultFolder = + prop?.path?.defaultName && prop.path.defaultName.length > 0 + ? prop.path.defaultName + : settings.upload.defaultName.length > 0 + ? settings.upload.defaultName + : ""; + const path = defaultFolder + fileName; + //remove last word from path splitted with / + let pathWithoutEnd = path.split("/").slice(0, -1).join("/"); + //get file name only + const fileNameOnly = path.split("/").at(-1) ?? ""; + pathWithoutEnd = regexOnPath(pathWithoutEnd, settings); + if (pathWithoutEnd.trim().length === 0) return fileNameOnly; + return `${pathWithoutEnd}/${fileNameOnly}`.replace(/^\//, ""); +} + +/** + * Check if a file (using category frontmatter option) is a folder note, and rename it to index.md if it is + * @param {string} fileName file name + * @param {FrontMatterCache} frontmatter frontmatter + * @param {GitHubPublisherSettings} settings Settings + * @returns {string} renamed file name or original file name + */ + +function folderNoteIndexYAML( + fileName: string, + frontmatter: FrontMatterCache | undefined | null, + settings: GitHubPublisherSettings, + prop?: Properties +): string { + const category = getCategory(frontmatter, settings, prop?.path); + const catSplit = category.split("/"); + const parentCatFolder = category.endsWith("/") + ? (catSplit.at(-2) as string) + : (catSplit.at(-1) as string); + + if (!settings.upload.folderNote.enable) return regexOnFileName(fileName, settings); + if (fileName.replace(".md", "").toLowerCase() === parentCatFolder?.toLowerCase()) + return settings.upload.folderNote.rename; + return regexOnFileName(fileName, settings); +} + +/** + * Create filepath based on settings and frontmatter for the github repository + * @param {GitHubPublisherSettings} settings Settings + * @param {FrontMatterCache} frontmatter frontmatter + * @param {string} fileName file name + * @returns {string} filepath + */ + +function createFrontmatterPath( + settings: GitHubPublisherSettings, + frontmatter: FrontMatterCache | null | undefined, + fileName: string, + prop?: Properties +): string { + const uploadSettings = settings.upload; + const folderCategory = getCategory(frontmatter, settings, prop?.path); + const path = prop?.path; + const folderNote = folderNoteIndexYAML(fileName, frontmatter, settings, prop); + const root = + path?.rootFolder && path.rootFolder.length > 0 + ? path.rootFolder + : uploadSettings.rootFolder.length > 0 + ? uploadSettings.rootFolder + : undefined; + const folderRoot = root && !folderCategory.includes(root) ? `${root}/` : ""; + if (folderCategory.trim().length === 0) return folderNote; + const folderRegex = regexOnPath(folderRoot + folderCategory, settings); + if (folderRegex.trim().length === 0) return folderNote; + return `${folderRegex}/${folderNote}`.replace(/^\//, ""); +} + +/** + * Apply a regex edition on the title. It can be used to remove special characters or to add a prefix or suffix + * ! Not applied on the index.md file (folder note) + * @param {string} fileName file name + * @param {GitHubPublisherSettings} settings Settings + * @return {string} edited file name + */ +export function regexOnFileName( + fileName: string, + settings: GitHubPublisherSettings +): string { + const uploadSettings = settings.upload; + if ( + (fileName === uploadSettings.folderNote.rename && uploadSettings.folderNote.enable) || + uploadSettings.replaceTitle.length === 0 + ) + return fileName; + const extension = fileName.match(/\.[0-9a-z]+$/i)?.at(-1) ?? ""; + fileName = fileName.replace(extension, ""); + for (const regexTitle of uploadSettings.replaceTitle) { + if (regexTitle.regex?.trim().length > 0) { + const toReplace = regexTitle.regex; + const replaceWith = regexTitle.replacement; + if (toReplace.match(FIND_REGEX)) { + const regex = createRegexFromText(toReplace); + fileName = fileName.replace(regex, replaceWith); + } else { + fileName = fileName.replaceAll(toReplace, replaceWith); + } + } + } + return `${fileName}${extension}`; +} + +/** + * Allow to modify enterely the path of a file, using regex / string replace + * @param {string} path path + * @param {GitHubPublisherSettings} settings Settings + * @return {string} edited path + */ +export function regexOnPath(path: string, settings: GitHubPublisherSettings): string { + const uploadSettings = settings.upload; + if ( + uploadSettings.behavior === FolderSettings.fixed || + uploadSettings.replacePath.length === 0 + ) + return path; + for (const regexTitle of uploadSettings.replacePath) { + if (regexTitle.regex.trim().length > 0) { + const toReplace = regexTitle.regex; + const replaceWith = regexTitle.replacement; + if (toReplace.match(FIND_REGEX)) { + const regex = createRegexFromText(toReplace); + path = path.replace(regex, replaceWith); + } else { + path = path.replaceAll(toReplace, replaceWith); + } + } + } + return path; +} + +/** + * Get the title field from frontmatter or file name + * @param {FrontMatterCache} frontmatter frontmatter + * @param {TFile} file file + * @param {GitHubPublisherSettings} settings Settings + * @returns {string} title + */ +export function getTitleField( + frontmatter: FrontMatterCache | undefined | null, + file: TFile, + settings: GitHubPublisherSettings +): string { + const fileName = file.name; + if ( + frontmatter && + settings.upload.frontmatterTitle.enable && + frontmatter[settings.upload.frontmatterTitle.key] && + frontmatter[settings.upload.frontmatterTitle.key] !== fileName + ) { + return `${frontmatter[settings.upload.frontmatterTitle.key]}.md`; + } + return fileName; +} + +/** + * Get the path where the file will be saved in the github repository + */ + +export function getReceiptFolder( + file: TFile, + otherRepo: Repository | null, + plugin: GithubPublisher, + prop?: Properties | Properties[] +): string { + const { vault } = plugin.app; + const settings = plugin.settings; + if (file.extension === "md") { + const frontmatter = frontmatterFromFile(file, plugin, otherRepo); + if (!prop) prop = getProperties(plugin, otherRepo, frontmatter); + prop = prop instanceof Array ? prop : [prop]; + let targetRepo = prop.find( + (repo) => repo.path?.smartkey === otherRepo?.smartKey || "default" + ); + if (!targetRepo) targetRepo = prop[0]; + const fileName = getTitleField(frontmatter, file, settings); + const editedFileName = regexOnFileName(fileName, settings); + if (!isShared(frontmatter, settings, file, otherRepo)) { + return normalizePath(fileName); + } + if (targetRepo.path?.override) { + const frontmatterPath = targetRepo.path.override; + if (frontmatterPath == "" || frontmatterPath == "/") { + return normalizePath(editedFileName); + } + return normalizePath(`${frontmatterPath}/${editedFileName}`); + } else if (targetRepo.path?.type === FolderSettings.yaml) { + return normalizePath( + createFrontmatterPath(settings, frontmatter, fileName, targetRepo) + ); + } else if (targetRepo.path?.type === FolderSettings.obsidian) { + return normalizePath( + createObsidianPath(file, settings, vault, fileName, targetRepo) + ); + } else { + return targetRepo.path?.defaultName && targetRepo.path.defaultName.length > 0 + ? normalizePath(`${targetRepo.path.defaultName}/${editedFileName}`) + : normalizePath(editedFileName); + } + } + return file.path; +} + +/** + * Create filepath in github Repository based on settings and frontmatter for image + * @param {TFile} file : Source file + * @param {PropertiesConversion | null} sourceFrontmatter + * @return {string} the new filepath + */ +export function getImagePath( + file: TFile, + plugin: GithubPublisher, + sourceFrontmatter: PropertiesConversion | null, + repository: Properties | Properties[] +): string { + const settings = plugin.settings; + const overridePath = repository instanceof Array ? repository[0] : repository; + + const imagePath = createImagePath(file, settings, sourceFrontmatter, overridePath); + const path = regexOnPath(imagePath.path, settings); + const name = regexOnFileName(imagePath.name, settings); + return normalizePath(path.replace(file.name, name)); +} + +/** + * Create filepath in github Repository based on settings and frontmatter for image + * @param {TFile} file : Source file + * @param {GitHubPublisherSettings} settings Settings + * @param {PropertiesConversion | null} sourceFrontmatter + * @return {string} the new filepath + */ + +function createImagePath( + file: TFile, + settings: GitHubPublisherSettings, + sourceFrontmatter: PropertiesConversion | null, + overridePath?: Properties +): { path: string; name: string } { + let fileName = file.name; + let filePath = file.path; + if (file.name.includes(".excalidraw")) { + fileName = fileName.replace(".excalidraw.md", ".svg"); + filePath = filePath.replace(".excalidraw.md", ".svg"); + } + const result: { path: string; name: string } = { + path: filePath, + name: fileName, + }; + const behavior = overridePath?.path?.type + ? overridePath.path.type + : settings.upload.behavior; + const rootFolder = overridePath?.path?.rootFolder + ? overridePath.path.rootFolder + : settings.upload.rootFolder; + const defaultFolderName = overridePath?.path?.defaultName + ? overridePath.path.defaultName + : settings.upload.defaultName; + if ( + sourceFrontmatter?.attachmentLinks && + sourceFrontmatter.attachmentLinks.length > 0 + ) { + result.path = normalizePath(`${sourceFrontmatter.attachmentLinks}/${fileName}`); + return result; + } + if (settings.embed.useObsidianFolder) { + if (behavior === FolderSettings.yaml) { + result.path = + rootFolder.length > 0 ? normalizePath(`${rootFolder}/${filePath}`) : filePath; + } else { + //no root, but default folder name + result.path = + defaultFolderName.length > 0 + ? normalizePath(`${defaultFolderName}/${filePath}`) + : filePath; + } + result.path = applyOverriddenPath(fileName, result.path, settings).filePath; + return result; + } + const defaultImageFolder = overridePath?.path?.attachment?.folder + ? overridePath.path?.attachment?.folder + : settings.embed.folder; + //find in override + const overriddenPath = applyOverriddenPath(fileName, filePath, settings); + if (overriddenPath.overridden) { + result.path = overriddenPath.filePath; + } else if (defaultImageFolder.length > 0) { + result.path = normalizePath(`${defaultImageFolder}/${fileName}`); + } else if (defaultFolderName.length > 0) { + result.path = normalizePath(`${defaultFolderName}/${fileName}`); + } else { + result.path = filePath; + } + return result; +} + +/** + * Override the path of an attachment using the settings (regex or string replace) + * @param fileName - The name of the file + * @param filePath - The (original) path of the file + * @param settings - The settings of the plugin + * @returns The new path of the file and whether it was overridden + */ +function applyOverriddenPath( + fileName: string, + filePath: string, + settings: GitHubPublisherSettings +): { filePath: string; overridden: boolean } { + let overridden = false; + const isOverridden = settings.embed.overrideAttachments.filter((override) => { + const isRegex = override.path.match(FIND_REGEX); + const regex = isRegex ? new RegExp(isRegex[1], isRegex[2]) : undefined; + return ( + (regex?.test(filePath) || + filePath === override.path || + override.path.contains("{{all}}")) && + !override.destination.contains("{{default}}") + ); + }); + if (isOverridden.length > 0) { + overridden = true; + for (const override of isOverridden) { + const isRegex = override.path.match(FIND_REGEX); + const regex = isRegex ? new RegExp(isRegex[1], isRegex[2]) : null; + const dest = override.destination.replace("{{name}}", fileName); + filePath = regex + ? normalizePath(filePath.replace(regex, dest)) + : normalizePath(filePath.replace(override.path, dest)); + } + } + return { filePath, overridden }; +} diff --git a/src/conversion/find_and_replace_text.ts b/src/conversion/find_and_replace_text.ts index 54a0b929..889e6ea2 100644 --- a/src/conversion/find_and_replace_text.ts +++ b/src/conversion/find_and_replace_text.ts @@ -1,102 +1,105 @@ - -import { FIND_REGEX, GitHubPublisherSettings } from "@interfaces"; -import { escapeRegex } from "src/conversion/links"; -import { logs } from "src/utils"; - -/** - * Convert a string to a regex object when the string is in the form of a regex (enclosed by /) - * @param toReplace {string} The string to be converted to a regex object - * @param withflag {string} The flags to be used in the regex object. If not provided, the flags will be extracted from the string. - * @returns RegExp The regex object - */ - -export function createRegexFromText(toReplace: string, withflag?: string): RegExp { - let flags = withflag; - if (!withflag) { - const flagsRegex = toReplace.match(/\/([gimy]+)$/); - flags = flagsRegex ? Array.from(new Set(flagsRegex[1].split(""))).join("") : ""; - } - return new RegExp(toReplace.replace(/\/(.+)\/.*/, "$1"), flags); -} - -/** - * Given a series of `censor` entries in Settings, this will loop through each - * then find and replace. - * @param {string} text The text to be censored. - * @param {GitHubPublisherSettings} settings Settings - * @param {boolean} after Whether to censor all or just the first match. - * @returns {string} The censored text. - */ -export default function findAndReplaceText( - text: string, - settings: GitHubPublisherSettings, - after?: boolean -): string { - if (!settings.conversion.censorText) { - return text; - } - const censoring = after ? settings.conversion.censorText.filter((censor) => censor.after) : settings.conversion.censorText.filter((censor) => !censor.after); - for (const censor of censoring) { - if (censor.entry.trim().length > 0) { - const toReplace = censor.entry; - const replaceWith = censor.replace; - if (toReplace.match(FIND_REGEX)) { - const regex = createRegexFromText(toReplace, censor.flags); - text = censor.inCodeBlocks ? text.replace(regex, replaceWith) : replaceText(text, regex, replaceWith, settings); - } else { - text = censor.inCodeBlocks ? text.replace(toReplace, replaceWith) : replaceText(text, toReplace, replaceWith, settings); - } - } - } - return text; -} - -/** - * Replace the String.prototype.replace() function with a function that will not replace text in code blocks. - * In `links` allow to exclude the replacement if the string is prepended by a backslash. - * So, enable the replacing for all text that is **not** in a codeblocks. - * @param fileContent {string} The entire file content - * @param pattern {string | RegExp} The string or regex to be replaced - * @param replaceWith {string} The string to replace with - * @param links {boolean} Whether to exclude the replacement if the string is prepended by a backslash. - * @returns {string} The file content with the replacements - */ -export function replaceText( - fileContent: string, - pattern: string | RegExp, - replaceWith: string, - settings: GitHubPublisherSettings, - links?: boolean):string { - let regexWithString: string ; - let regex: RegExp; - - if (pattern instanceof RegExp) { - regexWithString = "```[\\s\\S]*?```|`[^`]*`|"; - if (links) regexWithString += "\\\\?!?"; - regexWithString += pattern.source; - regex = new RegExp(regexWithString, `g${pattern.flags.replace("g", "")}`); - } else { - regexWithString = "```[\\s\\S]*?```|`[^`]*`|\\\\?!?"; - if (links) regexWithString += "\\\\?!?"; - regexWithString += escapeRegex(pattern); - regex = new RegExp(regexWithString, "g"); - } - return fileContent.replace(regex, (match) => { - if (match.match(/`[^`]*`/) || match.match(/```[\s\S]*?```/)) { - return match; - } else if (links && match.match(/^\\/)) { - return match; - } else { - try { - const replaceWithParsed = JSON.parse(`"${replaceWith}"`); - return match.replace(pattern, replaceWithParsed); - } - catch(e) { - logs({settings, e: true}, e); - return match.replace(pattern, replaceWith); - } - } - }); -} - - +import { FIND_REGEX, GitHubPublisherSettings } from "@interfaces"; +import { escapeRegex } from "src/conversion/links"; +import { logs } from "src/utils"; + +/** + * Convert a string to a regex object when the string is in the form of a regex (enclosed by /) + * @param toReplace {string} The string to be converted to a regex object + * @param withflag {string} The flags to be used in the regex object. If not provided, the flags will be extracted from the string. + * @returns RegExp The regex object + */ + +export function createRegexFromText(toReplace: string, withflag?: string): RegExp { + let flags = withflag; + if (!withflag) { + const flagsRegex = toReplace.match(/\/([gimy]+)$/); + flags = flagsRegex ? Array.from(new Set(flagsRegex[1].split(""))).join("") : ""; + } + return new RegExp(toReplace.replace(/\/(.+)\/.*/, "$1"), flags); +} + +/** + * Given a series of `censor` entries in Settings, this will loop through each + * then find and replace. + * @param {string} text The text to be censored. + * @param {GitHubPublisherSettings} settings Settings + * @param {boolean} after Whether to censor all or just the first match. + * @returns {string} The censored text. + */ +export default function findAndReplaceText( + text: string, + settings: GitHubPublisherSettings, + after?: boolean +): string { + if (!settings.conversion.censorText) { + return text; + } + const censoring = after + ? settings.conversion.censorText.filter((censor) => censor.after) + : settings.conversion.censorText.filter((censor) => !censor.after); + for (const censor of censoring) { + if (censor.entry.trim().length > 0) { + const toReplace = censor.entry; + const replaceWith = censor.replace; + if (toReplace.match(FIND_REGEX)) { + const regex = createRegexFromText(toReplace, censor.flags); + text = censor.inCodeBlocks + ? text.replace(regex, replaceWith) + : replaceText(text, regex, replaceWith, settings); + } else { + text = censor.inCodeBlocks + ? text.replace(toReplace, replaceWith) + : replaceText(text, toReplace, replaceWith, settings); + } + } + } + return text; +} + +/** + * Replace the String.prototype.replace() function with a function that will not replace text in code blocks. + * In `links` allow to exclude the replacement if the string is prepended by a backslash. + * So, enable the replacing for all text that is **not** in a codeblocks. + * @param fileContent {string} The entire file content + * @param pattern {string | RegExp} The string or regex to be replaced + * @param replaceWith {string} The string to replace with + * @param links {boolean} Whether to exclude the replacement if the string is prepended by a backslash. + * @returns {string} The file content with the replacements + */ +export function replaceText( + fileContent: string, + pattern: string | RegExp, + replaceWith: string, + settings: GitHubPublisherSettings, + links?: boolean +): string { + let regexWithString: string; + let regex: RegExp; + + if (pattern instanceof RegExp) { + regexWithString = "```[\\s\\S]*?```|`[^`]*`|"; + if (links) regexWithString += "\\\\?!?"; + regexWithString += pattern.source; + regex = new RegExp(regexWithString, `g${pattern.flags.replace("g", "")}`); + } else { + regexWithString = "```[\\s\\S]*?```|`[^`]*`|\\\\?!?"; + if (links) regexWithString += "\\\\?!?"; + regexWithString += escapeRegex(pattern); + regex = new RegExp(regexWithString, "g"); + } + return fileContent.replace(regex, (match) => { + if (match.match(/`[^`]*`/) || match.match(/```[\s\S]*?```/)) { + return match; + } else if (links && match.match(/^\\/)) { + return match; + } else { + try { + const replaceWithParsed = JSON.parse(`"${replaceWith}"`); + return match.replace(pattern, replaceWithParsed); + } catch (e) { + logs({ settings, e: true }, e); + return match.replace(pattern, replaceWith); + } + } + }); +} diff --git a/src/conversion/index.ts b/src/conversion/index.ts index 3b444d48..1ca83633 100644 --- a/src/conversion/index.ts +++ b/src/conversion/index.ts @@ -59,9 +59,7 @@ function tagsToYaml(toAdd: string[], settings: GitHubPublisherSettings, yaml: an toAdd = [ ...new Set([ ...toAdd, - ...yaml.tag.map((tag: string) => - tag.replaceAll("/", "_") - ), + ...yaml.tag.map((tag: string) => tag.replaceAll("/", "_")), ]), ]; delete yaml.tag; @@ -73,9 +71,7 @@ function tagsToYaml(toAdd: string[], settings: GitHubPublisherSettings, yaml: an try { yaml.tags = [ ...new Set([ - ...yaml.tags.map((tag: string) => - tag.replaceAll("/", "_") - ), + ...yaml.tags.map((tag: string) => tag.replaceAll("/", "_")), ...toAdd, ]), ]; @@ -97,22 +93,37 @@ function tagsToYaml(toAdd: string[], settings: GitHubPublisherSettings, yaml: an * @returns {Promise} the converted text */ -export function addToYaml(text: string, toAdd: string[], plugin: GithubPublisher, folderNoteParaMeters: { properties: MultiProperties | null, file: TFile}): string { +export function addToYaml( + text: string, + toAdd: string[], + plugin: GithubPublisher, + folderNoteParaMeters: { properties: MultiProperties | null; file: TFile } +): string { const properties = folderNoteParaMeters?.properties; - const {contentStart, exists, frontmatter} = getFrontMatterInfo(text); - if (!properties || (!properties.plugin.settings.conversion.tags.inline && !properties.plugin.settings.upload.folderNote.addTitle)) + const { contentStart, exists, frontmatter } = getFrontMatterInfo(text); + if ( + !properties || + (!properties.plugin.settings.conversion.tags.inline && + !properties.plugin.settings.upload.folderNote.addTitle) + ) return text; const { settings } = plugin; - let yamlObject = parseYaml(exists ? frontmatter:"{}"); + let yamlObject = parseYaml(exists ? frontmatter : "{}"); try { if (yamlObject && toAdd.length > 0) { yamlObject = tagsToYaml(toAdd, settings, yamlObject); } if (folderNoteParaMeters?.properties) { - yamlObject = titleToYaml(yamlObject, folderNoteParaMeters.properties, folderNoteParaMeters.file); + yamlObject = titleToYaml( + yamlObject, + folderNoteParaMeters.properties, + folderNoteParaMeters.file + ); } if (yamlObject && Object.keys(yamlObject).length > 0) { - return `---\n${stringifyYaml(yamlObject)}---\n${(exists ? text.slice(contentStart) : text)}`; + return `---\n${stringifyYaml(yamlObject)}---\n${ + exists ? text.slice(contentStart) : text + }`; } } catch (e) { new Notice(i18next.t("error.parseYaml")); @@ -134,7 +145,12 @@ function titleToYaml(yaml: any, properties: MultiProperties, file: TFile) { return yaml; } -function inlineTags(settings: GitHubPublisherSettings, file: TFile, metadataCache: MetadataCache, frontmatter: FrontMatterCache | undefined | null) { +function inlineTags( + settings: GitHubPublisherSettings, + file: TFile, + metadataCache: MetadataCache, + frontmatter: FrontMatterCache | undefined | null +) { if (!settings.conversion.tags.inline) { return []; } @@ -172,43 +188,33 @@ export async function processYaml( return addToYaml(text, toAdd, plugin, folderNoteParaMeters); } - /** * Main function to convert the text -*/ + */ export async function mainConverting( text: string, file: TFile, frontmatter: FrontMatterCache | undefined | null, linkedFiles: LinkedNotes[], - properties: MultiProperties, - + properties: MultiProperties ): Promise { const { plugin } = properties; if (properties.frontmatter.general.removeEmbed === "bake") text = await bakeEmbeds(file, new Set(), properties, null, linkedFiles); text = findAndReplaceText(text, plugin.settings, false); text = await processYaml(file, frontmatter, text, properties); - text = await convertToInternalGithub( + text = await convertToInternalGithub(text, linkedFiles, file, frontmatter, properties); + text = convertWikilinks( text, + properties.frontmatter.general, linkedFiles, - file, - frontmatter, - properties - ); - text = convertWikilinks(text, properties.frontmatter.general, linkedFiles, plugin.settings, frontmatter); - text = await convertDataviewQueries( - text, - file.path, - frontmatter, - file, - properties + plugin.settings, + frontmatter ); + text = await convertDataviewQueries(text, file.path, frontmatter, file, properties); text = await convertInlineDataview(text, plugin, file); text = addHardLineBreak(text, plugin.settings, properties.frontmatter.general); return findAndReplaceText(text, plugin.settings, true); } - - diff --git a/src/conversion/links.ts b/src/conversion/links.ts index 77ea1caf..fe02cad1 100644 --- a/src/conversion/links.ts +++ b/src/conversion/links.ts @@ -1,398 +1,425 @@ -import { - GitHubPublisherSettings, - LinkedNotes, - MultiProperties, - PropertiesConversion, -} from "@interfaces"; -import { FrontMatterCache, TFile } from "obsidian"; -import slugify from "slugify"; -import { createRelativePath, linkIsInFormatter, textIsInFrontmatter } from "src/conversion/file_path"; -import { replaceText } from "src/conversion/find_and_replace_text"; -import { isAttachment, noTextConversion } from "src/utils/data_validation_test"; - -type IsEmbed={ - cond: boolean; - char: string; -}; - -/** - * Convert wikilinks to markdown links - * Pretty cursed - * @param {string} fileContent the text to convert - * @param {PropertiesConversion} conditionConvert the frontmatter settings - * @param {GitHubPublisherSettings} settings global settings - * @param {LinkedNotes[]} linkedFiles the list of linked files - * @return {string} the converted text - */ - -export function convertWikilinks( - fileContent: string, - conditionConvert: PropertiesConversion, - linkedFiles: LinkedNotes[], - settings: GitHubPublisherSettings, - sourceFrontmatter: FrontMatterCache | undefined | null, -): string { - if (noTextConversion(conditionConvert)) return fileContent; - - const wikiRegex = /!?\[\[.*?\]\]/g; - const wikiMatches = fileContent.match(wikiRegex); - if (wikiMatches) { - const fileRegex = /(\[\[).*?([\]|])/; - for (const wikiMatch of wikiMatches) { - const fileMatch = wikiMatch.match(fileRegex); - const isEmbedBool = wikiMatch.startsWith("!"); - const isEmbed: IsEmbed = { - cond: isEmbedBool, - char: isEmbedBool ? "!" : "" - }; - if (fileMatch) { - const path = filepath(fileMatch[0]); - const fileName = sanitizeStrictFileName(fileMatch[0]); - const linkedFile = linkedFiles.find( - (item) => item.linkFrom.replace(/#.*/, "") === fileName - ); - const isNotAttachment = !isAttachment(fileName.trim(), settings.embed.unHandledObsidianExt); - - if (linkedFile && !linkIsInFormatter(linkedFile, sourceFrontmatter)) { - fileContent = isLinkedFile(linkedFile, conditionConvert, isEmbed, settings, isNotAttachment, fileContent, wikiMatch, path); - } else if (!path.startsWith("http") && !textIsInFrontmatter(path, sourceFrontmatter)) { - fileContent = strictStringConversion(isEmbed, isNotAttachment, wikiMatch, path, conditionConvert, settings, fileContent); - } - } - } - } - return fileContent; -} - -/** - * We need to sanitize the file to gain the filepath - * ! Note: Anchor are conservated - * @param {string} link the link to sanitize -*/ -function filepath(link: string) { - return link - .replaceAll("[", "") - .replaceAll("|", "") - .replaceAll("]", "") - .replaceAll("\\", ""); -} -/** - * In order to compare linked files with files that we have cached in - * memory, we have sanitize their link name to matching. - * - * For example, if we have a [[./wikilink.md|My Incredible Link Title]], - * we want to match `wikilink.md` with the cached files we have in - * memory. In this case, we'd strip [[, | and ./ to have wikilink.md as - * result. - * ! Note : It remove the `#anchor` too! - */ -function sanitizeStrictFileName(link: string) { - return link.replaceAll("[", "") - .replaceAll("|", "") - .replaceAll("]", "") - .replaceAll("\\", "") - .replaceAll("../", "") - .replaceAll("./", "") - .replace(/#.*/, ""); -} - -/** - * Convert the link to a markdown link using the linked notes from the Obsidian API - * @param linkedFile the file connected to the link - * @param conditionConvert the frontmatter settings - * @param isEmbed the embed and if it is an embed - * @param settings the global settings of the plugin - * @param isNotAttachment if the file linked is **not** an attachment - * @param fileContent the file content to convert - * @param wikiMatch the match for the wikilink - * @param fileName the file name to convert to a markdown link - * @returns {string} the converted text - */ -function isLinkedFile( - linkedFile: LinkedNotes, - conditionConvert: PropertiesConversion, - isEmbed: IsEmbed, - settings: GitHubPublisherSettings, - isNotAttachment: boolean, - fileContent: string, - wikiMatch: string, - fileName: string): string { - let altText: string; - let linkCreator = wikiMatch; - - if (linkedFile.linked.extension === "md") { - altText = linkedFile.altText ? linkedFile.altText : linkedFile.linked.basename; - altText = altText .replace("#", " > ").replace(/ > \^\w*/, ""); - } else altText = linkedFile.altText ? linkedFile.altText : ""; - - const removeEmbed = (conditionConvert.removeEmbed === "remove" || conditionConvert.removeEmbed === "bake") && - isEmbed.cond && - linkedFile.linked.extension === "md"; - if (isEmbed.cond && linkedFile.linked.extension === "md" && conditionConvert.removeEmbed === "links") { - isEmbed.char = `${conditionConvert.charEmbedLinks} `; - linkCreator = wikiMatch.replace("!", isEmbed.char); - } - if (conditionConvert.convertWiki) { - const altMatch = wikiMatch.match(/(\|).*(]])/); - const altCreator = fileName.split("/"); - let altLink = creatorAltLink( - altMatch as RegExpMatchArray, - altCreator, - fileName.split(".").at(-1) as string, - fileName - ); - - altLink = altLink - .replace("#", " > ") - .replace(/ > \^\w*/, ""); - linkCreator = createMarkdownLinks(fileName, isEmbed.char, altLink, settings); - } else { - const altMatch = wikiMatch.match(/(\|).*(]])/); - linkCreator = addAltForWikilinks(altMatch as RegExpMatchArray, linkCreator); - } - if ( - linkedFile.linked.extension === "md" && - !conditionConvert.links && - !isEmbed.cond - ) linkCreator = altText; - if ((!conditionConvert.attachment && !isNotAttachment) || removeEmbed) linkCreator = ""; - - return replaceText(fileContent, wikiMatch, linkCreator, settings, true); - - -} -/** - * Will strictly convert the the links without a found linked notes (usefull when links are already edited using the markdown conversion or regex) - * @param {IsEmbed} isEmbed if the link is an embed link - * @param {boolean} isNotAttachment if the file linked is **not** an attachment - * @param {string} wikiMatch the match for the wikilink - * @param {string} fileName the file name to convert to a markdown link - * @param {PropertiesConversion} conditionConvert the frontmatter settings - * @param {GitHubPublisherSettings} settings the global settings - * @param {string} fileContent the file content to convert - * @returns {string} the converted text - */ -function strictStringConversion( - isEmbed: IsEmbed, - isNotAttachment: boolean, - wikiMatch: string, - fileName: string, - conditionConvert: PropertiesConversion, - settings: GitHubPublisherSettings, - fileContent: string): string { - const altMatch = wikiMatch.match(/(\|).*(]])/); - const altCreator = fileName.split("/"); - - let altLink = creatorAltLink( - altMatch as RegExpMatchArray, - altCreator, - fileName.split(".").at(-1) as string, - fileName - ); - altLink = altLink - .replace("#", " > ") - .replace(/ > \^\w*/, ""); - const removeEmbed = - isNotAttachment && - conditionConvert.removeEmbed === "remove" && - isEmbed.cond; - let linkCreator = wikiMatch; - if (isEmbed.cond && conditionConvert.removeEmbed === "links" && isNotAttachment) { - isEmbed.char = `${conditionConvert.charEmbedLinks} `; - linkCreator = linkCreator.replace("!", isEmbed.char); - } - linkCreator = conditionConvert.convertWiki ? createMarkdownLinks(fileName, isEmbed.char, altLink, settings) : addAltForWikilinks(altMatch as RegExpMatchArray, linkCreator); - if ( - isNotAttachment && - !conditionConvert.links && - !isEmbed.cond - ) { - linkCreator = altLink; - } - if ( - (!conditionConvert.attachment && !isNotAttachment) || - removeEmbed - ) { - linkCreator = ""; - } - return replaceText(fileContent, wikiMatch, linkCreator, settings, true); -} - -/** - * If there are no altText (aka (.*)|), we create one based on the content in [[.*]], but we replace the # with >. - * @param {RegExpMatchArray} altMatch - The match for the altText if any - * @param {string} linkCreator - The links to edit - */ -function addAltForWikilinks(altMatch: RegExpMatchArray, linkCreator: string) { - if (!altMatch) { - const link = linkCreator.match(/\[{2}(.*)\]{2}/); - /** need group 1 because group 0 is the whole match */ - const altText = link ? link[1] - .replace("#", " > ") - .replace(/ > \^\w*/, "") - : ""; - return linkCreator.replace(/\[{2}(.*)\]{2}/, `[[$1|${altText}]]`); - } - return linkCreator; -} - -/** - * Convert the link to a markdown link - * Will sluggify the anchor by removing and readding it to the link - * @param fileName the file name to convert to a markdown link - * @param isEmbed the embed character - * @param altLink the alias - * @param settings the global settings - * @returns {string} the converted markdown link - */ -function createMarkdownLinks(fileName: string, isEmbed: string, altLink: string, settings: GitHubPublisherSettings): string { - const markdownName = isAttachment(fileName.trim(), settings.embed.unHandledObsidianExt) - ? fileName.trim() - : `${fileName.replace(/#.*/, "").trim()}.md`; - const anchorMatch = fileName.match(/(#.*)/); - let anchor = anchorMatch ? anchorMatch[0] : null; - const encodedURI = encodeURI(markdownName); - anchor = slugifyAnchor(anchor, settings); - return `${isEmbed}[${altLink}](${encodedURI}${anchor})`; -} - -/** - * Slugify the anchor based on the settings - * @param anchor {string | null} the anchor to slugify - * @param settings {GitHubPublisherSettings} - * @returns {string} the slugified anchor - */ -function slugifyAnchor(anchor: string | null, settings: GitHubPublisherSettings): string { - const slugifySetting = typeof(settings.conversion.links.slugify) === "string" ? settings.conversion.links.slugify : "disable"; - if (anchor && slugifySetting !== "disable") { - switch (settings.conversion.links.slugify) { - case "lower": - return anchor.toLowerCase().replaceAll(" ", "-"); - case "strict": - return `#${slugify(anchor, { lower: true, strict: true })}`; - - default: - return anchor; - } - - } - return anchor?.replaceAll(" ", "%20") ?? ""; -} - -/** - * Add alt text to links - * @param {string} link the link to add alt text to - * @param {LinkedNotes} linkedFile the linked file - * @returns {string} the link with alt text - */ - -function addAltText(link: string, linkedFile: LinkedNotes): string { - if (link.match(/\[{2}.*\]{2}/) && !link.match(/(\|).*(]])/)) { - return link.replace("|", "").replace("]]", `|${linkedFile.altText}]]`); - } - return link; -} - -/** - * Escape special characters in a string - * @param {string} filepath The string to escape - * @return {string} The escaped string - */ - -export function escapeRegex(filepath: string): string { - return filepath.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&"); -} - -/** - * Convert internal links with changing the path to the relative path in the github repository - * @param {string} fileContent the text to convert - * @param {LinkedNotes[]} linkedFiles the list of linked files in the file, include any internal links - * @param {TFile} sourceFile the file to convert - * @param {FrontMatterCache | undefined | null} frontmatter the frontmatter of the source file - * @param {MultiProperties} properties the properties of the source file - * @return {string} the converted text - */ -export async function convertToInternalGithub( - fileContent: string, - linkedFiles: LinkedNotes[], - sourceFile: TFile, - frontmatter: FrontMatterCache | undefined | null, - properties: MultiProperties, -): Promise { - const frontmatterSettings = properties.frontmatter.general; - const settings = properties.plugin.settings; - if (!frontmatterSettings.convertInternalLinks) return fileContent; - - for (const linkedFile of linkedFiles) { - let pathInGithub = await createRelativePath( - sourceFile, - linkedFile, - frontmatter, - properties, - ); - pathInGithub = pathInGithub.replace(".md", ""); - let anchor = linkedFile.anchor ? linkedFile.anchor : ""; - let linkInMarkdown = escapeRegex(linkedFile.linkFrom.replace(anchor, "")).replaceAll(" ", "%20") + anchor.replace("^", "\\^"); - linkInMarkdown = linkInMarkdown.replaceAll(" ", "%20"); - const escapedLinkedFile = escapeRegex(linkedFile.linkFrom); - const regexToReplace = new RegExp( - `(\\[{2}${escapedLinkedFile}(\\\\?\\|.*?)?\\]{2})|(\\[.*?\\]\\((${escapedLinkedFile}|${linkInMarkdown})\\))`, - "g" - ); - const matchedLink = fileContent.match(regexToReplace); - if (matchedLink) { - for (const link of matchedLink) { - const regToReplace = new RegExp(`((${escapedLinkedFile})|(${linkInMarkdown}))`); - let pathInGithubWithAnchor = pathInGithub; - if (linkedFile.anchor) { - pathInGithub = pathInGithub.replace(/#.*/, ""); - pathInGithubWithAnchor += linkedFile.anchor; - } - - let newLink = link.replace(regToReplace, pathInGithubWithAnchor); - if (link.match(/\[.*\]\(.*\)/)) { - if (linkedFile.linked.extension === "md" && !linkedFile.linked.name.includes("excalidraw")) { - anchor = slugifyAnchor(anchor, settings); - pathInGithub = `${pathInGithub.replaceAll(" ", "%20")}.md#${anchor}`; - pathInGithub = !pathInGithub.match(/(#.*)/) && !pathInGithub.endsWith(".md") ? - `${pathInGithub}.md` - : pathInGithub; - } - const altText = link.match(/\[(.*)\]/)![1]; - newLink = `[${altText}](${encodeURI(pathInGithub)})`; //encode to URI for compatibility with github - } - newLink = addAltText(newLink, linkedFile); - fileContent = replaceText(fileContent, link, newLink, settings, true); - } - } - } - return fileContent; -} - -/** - * Create the alt text for the link - * if no alt text is given, the alt text is the filename without the extension - * @param {RegExpMatchArray} altMatch the alt matched by the regex - * @param {string[]} altCreator The filename split by / - * @param {string} fileExtension the file extension - * @param {string} match the filename matched - * @return {string} the alt text - */ - -export function creatorAltLink( - altMatch: RegExpMatchArray, - altCreator: string[], - fileExtension: string, - match: string -): string { - if (altMatch) { - return altMatch[0].replace("]]", "").replace("|", ""); - } - if (fileExtension === "md") { - return altCreator.length > 1 - ? altCreator[altCreator.length - 1] - : altCreator[0]; //alt text based on filename for markdown files - } - return match.split("/").at(-1) as string; //alt text based on filename for other files -} - +import { + GitHubPublisherSettings, + LinkedNotes, + MultiProperties, + PropertiesConversion, +} from "@interfaces"; +import { FrontMatterCache, TFile } from "obsidian"; +import slugify from "slugify"; +import { + createRelativePath, + linkIsInFormatter, + textIsInFrontmatter, +} from "src/conversion/file_path"; +import { replaceText } from "src/conversion/find_and_replace_text"; +import { isAttachment, noTextConversion } from "src/utils/data_validation_test"; + +type IsEmbed = { + cond: boolean; + char: string; +}; + +/** + * Convert wikilinks to markdown links + * Pretty cursed + * @param {string} fileContent the text to convert + * @param {PropertiesConversion} conditionConvert the frontmatter settings + * @param {GitHubPublisherSettings} settings global settings + * @param {LinkedNotes[]} linkedFiles the list of linked files + * @return {string} the converted text + */ + +export function convertWikilinks( + fileContent: string, + conditionConvert: PropertiesConversion, + linkedFiles: LinkedNotes[], + settings: GitHubPublisherSettings, + sourceFrontmatter: FrontMatterCache | undefined | null +): string { + if (noTextConversion(conditionConvert)) return fileContent; + + const wikiRegex = /!?\[\[.*?\]\]/g; + const wikiMatches = fileContent.match(wikiRegex); + if (wikiMatches) { + const fileRegex = /(\[\[).*?([\]|])/; + for (const wikiMatch of wikiMatches) { + const fileMatch = wikiMatch.match(fileRegex); + const isEmbedBool = wikiMatch.startsWith("!"); + const isEmbed: IsEmbed = { + cond: isEmbedBool, + char: isEmbedBool ? "!" : "", + }; + if (fileMatch) { + const path = filepath(fileMatch[0]); + const fileName = sanitizeStrictFileName(fileMatch[0]); + const linkedFile = linkedFiles.find( + (item) => item.linkFrom.replace(/#.*/, "") === fileName + ); + const isNotAttachment = !isAttachment( + fileName.trim(), + settings.embed.unHandledObsidianExt + ); + + if (linkedFile && !linkIsInFormatter(linkedFile, sourceFrontmatter)) { + fileContent = isLinkedFile( + linkedFile, + conditionConvert, + isEmbed, + settings, + isNotAttachment, + fileContent, + wikiMatch, + path + ); + } else if ( + !path.startsWith("http") && + !textIsInFrontmatter(path, sourceFrontmatter) + ) { + fileContent = strictStringConversion( + isEmbed, + isNotAttachment, + wikiMatch, + path, + conditionConvert, + settings, + fileContent + ); + } + } + } + } + return fileContent; +} + +/** + * We need to sanitize the file to gain the filepath + * ! Note: Anchor are conservated + * @param {string} link the link to sanitize + */ +function filepath(link: string) { + return link + .replaceAll("[", "") + .replaceAll("|", "") + .replaceAll("]", "") + .replaceAll("\\", ""); +} +/** + * In order to compare linked files with files that we have cached in + * memory, we have sanitize their link name to matching. + * + * For example, if we have a [[./wikilink.md|My Incredible Link Title]], + * we want to match `wikilink.md` with the cached files we have in + * memory. In this case, we'd strip [[, | and ./ to have wikilink.md as + * result. + * ! Note : It remove the `#anchor` too! + */ +function sanitizeStrictFileName(link: string) { + return link + .replaceAll("[", "") + .replaceAll("|", "") + .replaceAll("]", "") + .replaceAll("\\", "") + .replaceAll("../", "") + .replaceAll("./", "") + .replace(/#.*/, ""); +} + +/** + * Convert the link to a markdown link using the linked notes from the Obsidian API + * @param linkedFile the file connected to the link + * @param conditionConvert the frontmatter settings + * @param isEmbed the embed and if it is an embed + * @param settings the global settings of the plugin + * @param isNotAttachment if the file linked is **not** an attachment + * @param fileContent the file content to convert + * @param wikiMatch the match for the wikilink + * @param fileName the file name to convert to a markdown link + * @returns {string} the converted text + */ +function isLinkedFile( + linkedFile: LinkedNotes, + conditionConvert: PropertiesConversion, + isEmbed: IsEmbed, + settings: GitHubPublisherSettings, + isNotAttachment: boolean, + fileContent: string, + wikiMatch: string, + fileName: string +): string { + let altText: string; + let linkCreator = wikiMatch; + + if (linkedFile.linked.extension === "md") { + altText = linkedFile.altText ? linkedFile.altText : linkedFile.linked.basename; + altText = altText.replace("#", " > ").replace(/ > \^\w*/, ""); + } else altText = linkedFile.altText ? linkedFile.altText : ""; + + const removeEmbed = + (conditionConvert.removeEmbed === "remove" || + conditionConvert.removeEmbed === "bake") && + isEmbed.cond && + linkedFile.linked.extension === "md"; + if ( + isEmbed.cond && + linkedFile.linked.extension === "md" && + conditionConvert.removeEmbed === "links" + ) { + isEmbed.char = `${conditionConvert.charEmbedLinks} `; + linkCreator = wikiMatch.replace("!", isEmbed.char); + } + if (conditionConvert.convertWiki) { + const altMatch = wikiMatch.match(/(\|).*(]])/); + const altCreator = fileName.split("/"); + let altLink = creatorAltLink( + altMatch as RegExpMatchArray, + altCreator, + fileName.split(".").at(-1) as string, + fileName + ); + + altLink = altLink.replace("#", " > ").replace(/ > \^\w*/, ""); + linkCreator = createMarkdownLinks(fileName, isEmbed.char, altLink, settings); + } else { + const altMatch = wikiMatch.match(/(\|).*(]])/); + linkCreator = addAltForWikilinks(altMatch as RegExpMatchArray, linkCreator); + } + if (linkedFile.linked.extension === "md" && !conditionConvert.links && !isEmbed.cond) + linkCreator = altText; + if ((!conditionConvert.attachment && !isNotAttachment) || removeEmbed) linkCreator = ""; + + return replaceText(fileContent, wikiMatch, linkCreator, settings, true); +} +/** + * Will strictly convert the the links without a found linked notes (usefull when links are already edited using the markdown conversion or regex) + * @param {IsEmbed} isEmbed if the link is an embed link + * @param {boolean} isNotAttachment if the file linked is **not** an attachment + * @param {string} wikiMatch the match for the wikilink + * @param {string} fileName the file name to convert to a markdown link + * @param {PropertiesConversion} conditionConvert the frontmatter settings + * @param {GitHubPublisherSettings} settings the global settings + * @param {string} fileContent the file content to convert + * @returns {string} the converted text + */ +function strictStringConversion( + isEmbed: IsEmbed, + isNotAttachment: boolean, + wikiMatch: string, + fileName: string, + conditionConvert: PropertiesConversion, + settings: GitHubPublisherSettings, + fileContent: string +): string { + const altMatch = wikiMatch.match(/(\|).*(]])/); + const altCreator = fileName.split("/"); + + let altLink = creatorAltLink( + altMatch as RegExpMatchArray, + altCreator, + fileName.split(".").at(-1) as string, + fileName + ); + altLink = altLink.replace("#", " > ").replace(/ > \^\w*/, ""); + const removeEmbed = + isNotAttachment && conditionConvert.removeEmbed === "remove" && isEmbed.cond; + let linkCreator = wikiMatch; + if (isEmbed.cond && conditionConvert.removeEmbed === "links" && isNotAttachment) { + isEmbed.char = `${conditionConvert.charEmbedLinks} `; + linkCreator = linkCreator.replace("!", isEmbed.char); + } + linkCreator = conditionConvert.convertWiki + ? createMarkdownLinks(fileName, isEmbed.char, altLink, settings) + : addAltForWikilinks(altMatch as RegExpMatchArray, linkCreator); + if (isNotAttachment && !conditionConvert.links && !isEmbed.cond) { + linkCreator = altLink; + } + if ((!conditionConvert.attachment && !isNotAttachment) || removeEmbed) { + linkCreator = ""; + } + return replaceText(fileContent, wikiMatch, linkCreator, settings, true); +} + +/** + * If there are no altText (aka (.*)|), we create one based on the content in [[.*]], but we replace the # with >. + * @param {RegExpMatchArray} altMatch - The match for the altText if any + * @param {string} linkCreator - The links to edit + */ +function addAltForWikilinks(altMatch: RegExpMatchArray, linkCreator: string) { + if (!altMatch) { + const link = linkCreator.match(/\[{2}(.*)\]{2}/); + /** need group 1 because group 0 is the whole match */ + const altText = link ? link[1].replace("#", " > ").replace(/ > \^\w*/, "") : ""; + return linkCreator.replace(/\[{2}(.*)\]{2}/, `[[$1|${altText}]]`); + } + return linkCreator; +} + +/** + * Convert the link to a markdown link + * Will sluggify the anchor by removing and readding it to the link + * @param fileName the file name to convert to a markdown link + * @param isEmbed the embed character + * @param altLink the alias + * @param settings the global settings + * @returns {string} the converted markdown link + */ +function createMarkdownLinks( + fileName: string, + isEmbed: string, + altLink: string, + settings: GitHubPublisherSettings +): string { + const markdownName = isAttachment(fileName.trim(), settings.embed.unHandledObsidianExt) + ? fileName.trim() + : `${fileName.replace(/#.*/, "").trim()}.md`; + const anchorMatch = fileName.match(/(#.*)/); + let anchor = anchorMatch ? anchorMatch[0] : null; + const encodedURI = encodeURI(markdownName); + anchor = slugifyAnchor(anchor, settings); + return `${isEmbed}[${altLink}](${encodedURI}${anchor})`; +} + +/** + * Slugify the anchor based on the settings + * @param anchor {string | null} the anchor to slugify + * @param settings {GitHubPublisherSettings} + * @returns {string} the slugified anchor + */ +function slugifyAnchor(anchor: string | null, settings: GitHubPublisherSettings): string { + const slugifySetting = + typeof settings.conversion.links.slugify === "string" + ? settings.conversion.links.slugify + : "disable"; + if (anchor && slugifySetting !== "disable") { + switch (settings.conversion.links.slugify) { + case "lower": + return anchor.toLowerCase().replaceAll(" ", "-"); + case "strict": + return `#${slugify(anchor, { lower: true, strict: true })}`; + + default: + return anchor; + } + } + return anchor?.replaceAll(" ", "%20") ?? ""; +} + +/** + * Add alt text to links + * @param {string} link the link to add alt text to + * @param {LinkedNotes} linkedFile the linked file + * @returns {string} the link with alt text + */ + +function addAltText(link: string, linkedFile: LinkedNotes): string { + if (link.match(/\[{2}.*\]{2}/) && !link.match(/(\|).*(]])/)) { + return link.replace("|", "").replace("]]", `|${linkedFile.altText}]]`); + } + return link; +} + +/** + * Escape special characters in a string + * @param {string} filepath The string to escape + * @return {string} The escaped string + */ + +export function escapeRegex(filepath: string): string { + return filepath.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&"); +} + +/** + * Convert internal links with changing the path to the relative path in the github repository + * @param {string} fileContent the text to convert + * @param {LinkedNotes[]} linkedFiles the list of linked files in the file, include any internal links + * @param {TFile} sourceFile the file to convert + * @param {FrontMatterCache | undefined | null} frontmatter the frontmatter of the source file + * @param {MultiProperties} properties the properties of the source file + * @return {string} the converted text + */ +export async function convertToInternalGithub( + fileContent: string, + linkedFiles: LinkedNotes[], + sourceFile: TFile, + frontmatter: FrontMatterCache | undefined | null, + properties: MultiProperties +): Promise { + const frontmatterSettings = properties.frontmatter.general; + const settings = properties.plugin.settings; + if (!frontmatterSettings.convertInternalLinks) return fileContent; + + for (const linkedFile of linkedFiles) { + let pathInGithub = await createRelativePath( + sourceFile, + linkedFile, + frontmatter, + properties + ); + pathInGithub = pathInGithub.replace(".md", ""); + let anchor = linkedFile.anchor ? linkedFile.anchor : ""; + let linkInMarkdown = + escapeRegex(linkedFile.linkFrom.replace(anchor, "")).replaceAll(" ", "%20") + + anchor.replace("^", "\\^"); + linkInMarkdown = linkInMarkdown.replaceAll(" ", "%20"); + const escapedLinkedFile = escapeRegex(linkedFile.linkFrom); + const regexToReplace = new RegExp( + `(\\[{2}${escapedLinkedFile}(\\\\?\\|.*?)?\\]{2})|(\\[.*?\\]\\((${escapedLinkedFile}|${linkInMarkdown})\\))`, + "g" + ); + const matchedLink = fileContent.match(regexToReplace); + if (matchedLink) { + for (const link of matchedLink) { + const regToReplace = new RegExp(`((${escapedLinkedFile})|(${linkInMarkdown}))`); + let pathInGithubWithAnchor = pathInGithub; + if (linkedFile.anchor) { + pathInGithub = pathInGithub.replace(/#.*/, ""); + pathInGithubWithAnchor += linkedFile.anchor; + } + + let newLink = link.replace(regToReplace, pathInGithubWithAnchor); + if (link.match(/\[.*\]\(.*\)/)) { + if ( + linkedFile.linked.extension === "md" && + !linkedFile.linked.name.includes("excalidraw") + ) { + anchor = slugifyAnchor(anchor, settings); + pathInGithub = `${pathInGithub.replaceAll(" ", "%20")}.md#${anchor}`; + pathInGithub = + !pathInGithub.match(/(#.*)/) && !pathInGithub.endsWith(".md") + ? `${pathInGithub}.md` + : pathInGithub; + } + const altText = link.match(/\[(.*)\]/)![1]; + newLink = `[${altText}](${encodeURI(pathInGithub)})`; //encode to URI for compatibility with github + } + newLink = addAltText(newLink, linkedFile); + fileContent = replaceText(fileContent, link, newLink, settings, true); + } + } + } + return fileContent; +} + +/** + * Create the alt text for the link + * if no alt text is given, the alt text is the filename without the extension + * @param {RegExpMatchArray} altMatch the alt matched by the regex + * @param {string[]} altCreator The filename split by / + * @param {string} fileExtension the file extension + * @param {string} match the filename matched + * @return {string} the alt text + */ + +export function creatorAltLink( + altMatch: RegExpMatchArray, + altCreator: string[], + fileExtension: string, + match: string +): string { + if (altMatch) { + return altMatch[0].replace("]]", "").replace("|", ""); + } + if (fileExtension === "md") { + return altCreator.length > 1 ? altCreator[altCreator.length - 1] : altCreator[0]; //alt text based on filename for markdown files + } + return match.split("/").at(-1) as string; //alt text based on filename for other files +} diff --git a/src/i18n/i18next.ts b/src/i18n/i18next.ts index ece9722a..cd1ee573 100644 --- a/src/i18n/i18next.ts +++ b/src/i18n/i18next.ts @@ -1,64 +1,68 @@ -import { moment } from "obsidian"; -import af from "src/i18n/locales/af.json"; -import ar from "src/i18n/locales/ar.json"; -import ca from "src/i18n/locales/ca.json"; -import cs from "src/i18n/locales/cs.json"; -import da from "src/i18n/locales/da.json"; -import de from "src/i18n/locales/de.json"; -import el from "src/i18n/locales/el.json"; -import en from "src/i18n/locales/en.json"; -import es from "src/i18n/locales/es.json"; -import fi from "src/i18n/locales/fi.json"; -import fr from "src/i18n/locales/fr.json"; -import he from "src/i18n/locales/he.json"; -import it from "src/i18n/locales/it.json"; -import ja from "src/i18n/locales/ja.json"; -import ko from "src/i18n/locales/ko.json"; -import nl from "src/i18n/locales/nl.json"; -import no from "src/i18n/locales/no.json"; -import pl from "src/i18n/locales/pl.json"; -import pt from "src/i18n/locales/pt.json"; -import ptBR from "src/i18n/locales/pt-BR.json"; -import ro from "src/i18n/locales/ro.json"; -import ru from "src/i18n/locales/ru.json"; -import sr from "src/i18n/locales/sr.json"; -import sv from "src/i18n/locales/sv.json"; -import tr from "src/i18n/locales/tr.json"; -import uk from "src/i18n/locales/uk.json"; -import vi from "src/i18n/locales/vi.json"; -import zhCN from "src/i18n/locales/zh-CN.json"; -import zhTW from "src/i18n/locales/zh-TW.json"; - -export const resources = { - en: { translation: en }, - fr: { translation: fr }, - af: { translation: af }, - ar: { translation: ar }, - ca: { translation: ca }, - cs: { translation: cs }, - da: { translation: da }, - de: { translation: de }, - el: { translation: el }, - es: { translation: es }, - fi: { translation: fi }, - he: { translation: he }, - it: { translation: it }, - ja: { translation: ja }, - ko: { translation: ko }, - nl: { translation: nl }, - no: { translation: no }, - pl: { translation: pl }, - pt: { translation: pt }, - "pt-BR": { translation: ptBR }, - ru: { translation: ru }, - ro: { translation: ro }, - sr: { translation: sr }, - sv: { translation: sv }, - tr: { translation: tr }, - uk: { translation: uk }, - vi: { translation: vi }, - "zh-TW": { translation: zhTW }, - "zh-CN": { translation: zhCN }, -} as const; - -export const translationLanguage = Object.keys(resources).find(i => i.toLocaleLowerCase() == moment.locale()) ? moment.locale() : "en"; +import { moment } from "obsidian"; +import af from "src/i18n/locales/af.json"; +import ar from "src/i18n/locales/ar.json"; +import ca from "src/i18n/locales/ca.json"; +import cs from "src/i18n/locales/cs.json"; +import da from "src/i18n/locales/da.json"; +import de from "src/i18n/locales/de.json"; +import el from "src/i18n/locales/el.json"; +import en from "src/i18n/locales/en.json"; +import es from "src/i18n/locales/es.json"; +import fi from "src/i18n/locales/fi.json"; +import fr from "src/i18n/locales/fr.json"; +import he from "src/i18n/locales/he.json"; +import it from "src/i18n/locales/it.json"; +import ja from "src/i18n/locales/ja.json"; +import ko from "src/i18n/locales/ko.json"; +import nl from "src/i18n/locales/nl.json"; +import no from "src/i18n/locales/no.json"; +import pl from "src/i18n/locales/pl.json"; +import pt from "src/i18n/locales/pt.json"; +import ptBR from "src/i18n/locales/pt-BR.json"; +import ro from "src/i18n/locales/ro.json"; +import ru from "src/i18n/locales/ru.json"; +import sr from "src/i18n/locales/sr.json"; +import sv from "src/i18n/locales/sv.json"; +import tr from "src/i18n/locales/tr.json"; +import uk from "src/i18n/locales/uk.json"; +import vi from "src/i18n/locales/vi.json"; +import zhCN from "src/i18n/locales/zh-CN.json"; +import zhTW from "src/i18n/locales/zh-TW.json"; + +export const resources = { + en: { translation: en }, + fr: { translation: fr }, + af: { translation: af }, + ar: { translation: ar }, + ca: { translation: ca }, + cs: { translation: cs }, + da: { translation: da }, + de: { translation: de }, + el: { translation: el }, + es: { translation: es }, + fi: { translation: fi }, + he: { translation: he }, + it: { translation: it }, + ja: { translation: ja }, + ko: { translation: ko }, + nl: { translation: nl }, + no: { translation: no }, + pl: { translation: pl }, + pt: { translation: pt }, + "pt-BR": { translation: ptBR }, + ru: { translation: ru }, + ro: { translation: ro }, + sr: { translation: sr }, + sv: { translation: sv }, + tr: { translation: tr }, + uk: { translation: uk }, + vi: { translation: vi }, + "zh-TW": { translation: zhTW }, + "zh-CN": { translation: zhCN }, +} as const; + +export const translationLanguage = Object.keys(resources).find( + (i) => i.toLocaleLowerCase() == moment.locale() +) + ? moment.locale() + : "en"; diff --git a/src/interfaces/constant.ts b/src/interfaces/constant.ts index 7d586e9f..7d7cd314 100644 --- a/src/interfaces/constant.ts +++ b/src/interfaces/constant.ts @@ -4,7 +4,6 @@ import { GitHubPublisherSettings } from "src/interfaces/main"; /** Find a regex encapsuled in // */ export const FIND_REGEX = /^\/(.*)\/[igmsuy]*$/; - /** * Just a constant for the token path * @type {string} TOKEN_PATH @@ -56,7 +55,7 @@ export const DEFAULT_SETTINGS: Partial = { addTitle: { enable: false, key: "title", - } + }, }, metadataExtractorPath: "", }, @@ -101,10 +100,10 @@ export const DEFAULT_SETTINGS: Partial = { toUri: true, slugify: "lower", applyRegex: [], - } + }, }, noticeError: false, displayModalRepoEditing: false, - setFrontmatterKey: "Set" - } -}; \ No newline at end of file + setFrontmatterKey: "Set", + }, +}; diff --git a/src/interfaces/enum.ts b/src/interfaces/enum.ts index f8f9bf23..5ea8cb40 100644 --- a/src/interfaces/enum.ts +++ b/src/interfaces/enum.ts @@ -1,4 +1,3 @@ - /** * @enum TypeOfEditRegex * @description Allow to set a value for the regex replace settings, to replace the path or the title of a file with a regex @@ -39,4 +38,4 @@ export enum FolderSettings { export enum GithubTiersVersion { free = "Github Free/Pro/Team (default)", entreprise = "Enterprise", -} \ No newline at end of file +} diff --git a/src/interfaces/icons.ts b/src/interfaces/icons.ts index 37c153c4..ab78aa6e 100644 --- a/src/interfaces/icons.ts +++ b/src/interfaces/icons.ts @@ -1,13 +1,20 @@ -export const LOADING_ICON = ""; +export const LOADING_ICON = + ''; -export const SUCCESS_ICON = ""; +export const SUCCESS_ICON = + ''; -export const HOURGLASS_ICON = ""; +export const HOURGLASS_ICON = + ''; -export const FOUND_ATTACHMENTS =""; +export const FOUND_ATTACHMENTS = + ''; -export const ERROR_ICONS = ""; +export const ERROR_ICONS = + ''; -export const BUG_ICONS = ""; +export const BUG_ICONS = + ''; -export const BUG_OFF_ICON = ""; \ No newline at end of file +export const BUG_OFF_ICON = + ''; diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index e826eb28..ade2a38c 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -1,10 +1,36 @@ //export interface in a file to prevent breaking & allow to easily find the interface -import { DEFAULT_SETTINGS,FIND_REGEX, TOKEN_PATH } from "src/interfaces/constant"; -import {EnumbSettingsTabId,FolderSettings, GithubTiersVersion, TypeOfEditRegex } from "src/interfaces/enum"; -import { Deleted, ListEditedFiles, UploadedFiles } from "src/interfaces/list_edited_files"; -import { GitHubPublisherSettings,GithubRepo,LinkedNotes, OverrideAttachments, Path, Properties,PropertiesConversion, RegexReplace, Repository, SetRepositoryFrontmatter, TextCleaner } from "src/interfaces/main"; -import { MonoProperties,MonoRepoProperties,MultiProperties, MultiRepoProperties } from "src/interfaces/properties"; +import { DEFAULT_SETTINGS, FIND_REGEX, TOKEN_PATH } from "src/interfaces/constant"; +import { + EnumbSettingsTabId, + FolderSettings, + GithubTiersVersion, + TypeOfEditRegex, +} from "src/interfaces/enum"; +import { + Deleted, + ListEditedFiles, + UploadedFiles, +} from "src/interfaces/list_edited_files"; +import { + GitHubPublisherSettings, + GithubRepo, + LinkedNotes, + OverrideAttachments, + Path, + Properties, + PropertiesConversion, + RegexReplace, + Repository, + SetRepositoryFrontmatter, + TextCleaner, +} from "src/interfaces/main"; +import { + MonoProperties, + MonoRepoProperties, + MultiProperties, + MultiRepoProperties, +} from "src/interfaces/properties"; import { MetadataExtractor } from "src/interfaces/settings"; import { ERROR_ICONS, FOUND_ATTACHMENTS, HOURGLASS_ICON, SUCCESS_ICON } from "./icons"; @@ -12,12 +38,16 @@ import { ERROR_ICONS, FOUND_ATTACHMENTS, HOURGLASS_ICON, SUCCESS_ICON } from "./ export { DEFAULT_SETTINGS, EnumbSettingsTabId, - ERROR_ICONS, FIND_REGEX, + ERROR_ICONS, + FIND_REGEX, FolderSettings, - FOUND_ATTACHMENTS, GithubTiersVersion, - HOURGLASS_ICON, SUCCESS_ICON, - TOKEN_PATH, - TypeOfEditRegex}; + FOUND_ATTACHMENTS, + GithubTiersVersion, + HOURGLASS_ICON, + SUCCESS_ICON, + TOKEN_PATH, + TypeOfEditRegex, +}; export type { Deleted, @@ -38,4 +68,5 @@ export type { Repository, SetRepositoryFrontmatter, TextCleaner, - UploadedFiles}; \ No newline at end of file + UploadedFiles, +}; diff --git a/src/interfaces/list_edited_files.ts b/src/interfaces/list_edited_files.ts index 7be8a5d0..af834133 100644 --- a/src/interfaces/list_edited_files.ts +++ b/src/interfaces/list_edited_files.ts @@ -1,4 +1,3 @@ - /** * Interface for list edited files * Allow to know which files was edited, deleted, added, unpublished, not deleted @@ -18,7 +17,7 @@ export interface ListEditedFiles { /** * Interface for uploaded files - * Used in ListEditedFiles to create it + * Used in ListEditedFiles to create it */ export interface UploadedFiles { /** Know if the file was updated on the repo or created */ @@ -29,7 +28,7 @@ export interface UploadedFiles { /** * Interface for deleted files - * Used in ListEditedFiles to create it + * Used in ListEditedFiles to create it * List deleted and undeleted file */ export interface Deleted { @@ -37,6 +36,3 @@ export interface Deleted { deleted: string[]; undeleted: string[]; } - - - diff --git a/src/interfaces/main.ts b/src/interfaces/main.ts index 1785c9a7..15c22a3a 100644 --- a/src/interfaces/main.ts +++ b/src/interfaces/main.ts @@ -1,7 +1,17 @@ import { FrontMatterCache, TFile } from "obsidian"; -import { EnumbSettingsTabId, FolderSettings,TypeOfEditRegex } from "./enum"; -import { Api, Conversion, CopyLink, Embed, GitHub, PluginBehavior, ShareAll, Upload, Workflow } from "./settings"; +import { EnumbSettingsTabId, FolderSettings, TypeOfEditRegex } from "./enum"; +import { + Api, + Conversion, + CopyLink, + Embed, + GitHub, + PluginBehavior, + ShareAll, + Upload, + Workflow, +} from "./settings"; /** * @interface RegexReplace @@ -73,9 +83,9 @@ export interface Repository { /** * Setting for copy link commands */ - copyLink: CopyLink, + copyLink: CopyLink; /** - * Allow to set a file for settings that override for example path, dataview, hardbreak... + * Allow to set a file for settings that override for example path, dataview, hardbreak... * Useful for autoclean settings, but also for other settings without needing to change the frontmatter and use the set function */ set: string | null; @@ -105,7 +115,6 @@ export interface GitHubPublisherSettings { plugin: PluginBehavior; } - /** * @interface LinkedNotes * @description Interface for the linked notes, with the file, the link from, the alt text and the destination file path @@ -130,7 +139,7 @@ export interface LinkedNotes { position?: { start: number; end: number; - } + }; } /** @@ -150,7 +159,7 @@ export interface ConvertedLink { /** * @interface GithubRepo - * @description File in the github Repository with the sha + * @description File in the github Repository with the sha */ export interface GithubRepo { file: string; @@ -240,9 +249,9 @@ export interface PropertiesConversion { includeLinks: boolean; } -/** A very important interface that handle a repository from the frontmatter and a lot of usefull settings that override the default plugin behavior, including {@link Path}. +/** A very important interface that handle a repository from the frontmatter and a lot of usefull settings that override the default plugin behavior, including {@link Path}. * Properties also handle {@link Repository} settings. -*/ + */ export interface Properties { /** Branch name */ branch: string; @@ -272,7 +281,7 @@ export interface Properties { folderName: string; /** If autoclean must perform in it */ autoclean: boolean; - } + }; } /** * Preset from the official repository @@ -282,17 +291,19 @@ export interface Preset { settings: GitHubPublisherSettings; } -export type SetRepositoryFrontmatter = {[repository: string] : FrontMatterCache | null | undefined}; +export type SetRepositoryFrontmatter = { + [repository: string]: FrontMatterCache | null | undefined; +}; /** * Override attachments settings, allowing force push and changing the destination */ export interface OverrideAttachments { - /** Path to override. + /** Path to override. * Support `{{all}}` special keys for handled all attachment */ path: string; /** Destination of the files, use `{{default}}` to send into their default repository */ destination: string; /** Force push the attachments */ forcePush: boolean; -} \ No newline at end of file +} diff --git a/src/interfaces/properties.ts b/src/interfaces/properties.ts index 8e608a35..a799b565 100644 --- a/src/interfaces/properties.ts +++ b/src/interfaces/properties.ts @@ -12,7 +12,7 @@ export interface MultiProperties { frontmatter: { general: PropertiesConversion; prop: Properties | Properties[]; - }, + }; repository: Repository | null; filepath: string; } @@ -27,10 +27,9 @@ export interface MonoProperties { general: PropertiesConversion; prop: Properties; source: FrontMatterCache | null | undefined; - }, + }; repository: Repository | null; filepath: string; - } /** @@ -50,4 +49,4 @@ export interface MonoRepoProperties { export interface MultiRepoProperties { frontmatter: Properties[] | Properties; repository: Repository | null; -} \ No newline at end of file +} diff --git a/src/interfaces/settings.ts b/src/interfaces/settings.ts index 00dcff5e..b2f05d3e 100644 --- a/src/interfaces/settings.ts +++ b/src/interfaces/settings.ts @@ -1,5 +1,10 @@ import { FolderSettings, GithubTiersVersion } from "src/interfaces/enum"; -import { OverrideAttachments,RegexReplace, Repository, TextCleaner } from "src/interfaces/main"; +import { + OverrideAttachments, + RegexReplace, + Repository, + TextCleaner, +} from "src/interfaces/main"; export type Api = { /** @@ -11,7 +16,7 @@ export type Api = { * The hostname of the API */ hostname: string; -} +}; export type Workflow = { /** @@ -23,7 +28,7 @@ export type Workflow = { * The github action needs to be set on "workflow_dispatch" to be able to be triggered */ name: string; -} +}; export type ShareAll = { enable: boolean; @@ -31,7 +36,7 @@ export type ShareAll = { * The name of the files to exclude */ excludedFileName: string; -} +}; export type CopyLink = { /** @@ -63,114 +68,111 @@ export type CopyLink = { applyRegex: { regex: string; replacement: string; - }[] - } -} + }[]; + }; +}; /** * The GitHub settings of the plugin */ export interface GitHub { - - /** The user that belongs to the repository, can be also a community (like obsidian-publisher) */ - user: string; - /** The name of the repository */ - repo: string; - /** - * The branch of the repository (default: main) - */ - branch: string; - /** - * The path where the token is stored (as the token is not saved in the settings directly, to prevent mistake and security issue) - * @default `%configDir%/plugins/%pluginID%/env` - */ - tokenPath: string; - /** - * If the PR should be automatically merged - */ - automaticallyMergePR: boolean; - /** - * Don't push the changes to the repository, or make any action on the repository - * It will use a local folder instead to mimic the behavior of the plugin - */ - dryRun: { - enable: boolean; - /** The folder name that mimic the folder, allow special keys to mimic multiple repository - * @key `%owner%` the owner of the repository (equivalent to `user`) - * @key `%repo%` the name of the repository - * @key `%branch%` the branch of the repository - */ - folderName: string; - } - /** - * The settings for the github API + /** The user that belongs to the repository, can be also a community (like obsidian-publisher) */ + user: string; + /** The name of the repository */ + repo: string; + /** + * The branch of the repository (default: main) + */ + branch: string; + /** + * The path where the token is stored (as the token is not saved in the settings directly, to prevent mistake and security issue) + * @default `%configDir%/plugins/%pluginID%/env` + */ + tokenPath: string; + /** + * If the PR should be automatically merged + */ + automaticallyMergePR: boolean; + /** + * Don't push the changes to the repository, or make any action on the repository + * It will use a local folder instead to mimic the behavior of the plugin + */ + dryRun: { + enable: boolean; + /** The folder name that mimic the folder, allow special keys to mimic multiple repository + * @key `%owner%` the owner of the repository (equivalent to `user`) + * @key `%repo%` the name of the repository + * @key `%branch%` the branch of the repository */ - api: Api, - /** workflow and action of the plugin in github */ - workflow: Workflow, - /** Enable the usage of different repository */ - otherRepo: Repository[]; - /** If the default repository is verified */ - verifiedRepo?: boolean; - /** The rate limit of the Github API */ - rateLimit: number; - + folderName: string; + }; + /** + * The settings for the github API + */ + api: Api; + /** workflow and action of the plugin in github */ + workflow: Workflow; + /** Enable the usage of different repository */ + otherRepo: Repository[]; + /** If the default repository is verified */ + verifiedRepo?: boolean; + /** The rate limit of the Github API */ + rateLimit: number; } /** * The settings for the upload settings tabs */ export interface Upload { - - /** The behavior of the folder settings - * - `yaml` : use a yaml frontmatter to set the path - * - `obsidian` : use the obsidian path - * - `fixed` : use a fixed folder and send all in it - */ - behavior: FolderSettings; - /** The default name of the folder - * Used only with `yaml` behavior, when the `category: XXX` is not set in the frontmatter. - */ - defaultName: string; - /** The root folder of the repository - * Used with all behavior - */ - rootFolder: string; - /** The "category" key name if used with YAML behavior */ - yamlFolderKey: string; - /** Generate the filename using a frontmatter key, to change it in the repository. - */ - frontmatterTitle: { + /** The behavior of the folder settings + * - `yaml` : use a yaml frontmatter to set the path + * - `obsidian` : use the obsidian path + * - `fixed` : use a fixed folder and send all in it + */ + behavior: FolderSettings; + /** The default name of the folder + * Used only with `yaml` behavior, when the `category: XXX` is not set in the frontmatter. + */ + defaultName: string; + /** The root folder of the repository + * Used with all behavior + */ + rootFolder: string; + /** The "category" key name if used with YAML behavior */ + yamlFolderKey: string; + /** Generate the filename using a frontmatter key, to change it in the repository. + */ + frontmatterTitle: { + enable: boolean; + /** @default `title` */ + key: string; + }; + /** Edit the title by using regex */ + replaceTitle: RegexReplace[]; + /** Edit the path by using regex; apply also on attachment path (will be applied on all path)*/ + replacePath: RegexReplace[]; + /** Auto remove file that was unshared (deleted in Obsidian or removed by set `share: false`) */ + autoclean: { + enable: boolean; + /** Prevent deleting files */ + excluded: string[]; + includeAttachments: boolean; + }; + /** Allow to set a folder note in `index.md` (example) when some settings are met + * For example, auto-rename to `index.md` when the file name and the folder name are the same. + */ + folderNote: { + enable: boolean; + /** The name of the index, by default `index.md`*/ + rename: string; + /** save the old title in the frontmatter */ + addTitle: { enable: boolean; - /** @default `title` */ key: string; - } - /** Edit the title by using regex */ - replaceTitle: RegexReplace[], - /** Edit the path by using regex; apply also on attachment path (will be applied on all path)*/ - replacePath: RegexReplace[], - /** Auto remove file that was unshared (deleted in Obsidian or removed by set `share: false`) */ - autoclean: { - enable: boolean; - /** Prevent deleting files */ - excluded: string[]; - includeAttachments: boolean; - } - /** Allow to set a folder note in `index.md` (example) when some settings are met - * For example, auto-rename to `index.md` when the file name and the folder name are the same. - */ - folderNote: { - enable: boolean; - /** The name of the index, by default `index.md`*/ - rename: string; - /** save the old title in the frontmatter */ - addTitle: { - enable: boolean; - key: string; - }; - } - /** The path to the metadata extractor plugin */ - metadataExtractorPath: string; + }; + }; + /** The path to the metadata extractor plugin */ + metadataExtractorPath: string; } export interface Conversion { @@ -188,7 +190,7 @@ export interface Conversion { exclude: string[]; /** add the fields value into tags too */ fields: string[]; - } + }; /** Settings for the links */ links: { /** Convert internal links to their proper path into Obsidian */ @@ -204,7 +206,7 @@ export interface Conversion { * @default `disable` */ slugify: "disable" | "strict" | "lower" | boolean; - } + }; } /** @@ -289,8 +291,8 @@ export interface PluginBehavior { * If disabled, the user will always return to the default tab when the settings are closed. */ saveTabId?: boolean; - /** Key used for "link" a frontmatter (overriding default settings) into another frontmatter - * @default `Set` + /** Key used for "link" a frontmatter (overriding default settings) into another frontmatter + * @default `Set` * @example `Set: [[frontmatter]]` */ setFrontmatterKey: string; diff --git a/src/main.ts b/src/main.ts index e0c3a4f3..7ce7fd86 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,307 +1,358 @@ -import { - DEFAULT_SETTINGS, - GitHubPublisherSettings, - GithubTiersVersion, - Repository, - SetRepositoryFrontmatter, -} from "@interfaces"; -import { Octokit } from "@octokit/core"; -import dedent from "dedent"; -import i18next from "i18next"; -import { FrontMatterCache, Menu, Plugin, TAbstractFile, TFile, TFolder } from "obsidian"; -import { - checkRepositoryValidityCallback, - createLinkCallback, - purgeNotesRemoteCallback, - refreshAllSets, - refreshOpenedSet, - shareEditedOnlyCallback, - shareOneNoteCallback, - uploadAllEditedNotesCallback, uploadAllNotesCallback, uploadNewNotesCallback -} from "src/commands"; -import { addMenuFile, addMenuFolder } from "src/commands/file_menu"; -import { ChooseWhichRepoToRun } from "src/commands/suggest_other_repo_commands_modal"; -import { getTitleField, regexOnFileName } from "src/conversion/file_path"; -import { GithubBranch } from "src/GitHub/branch"; -import { resources, translationLanguage } from "src/i18n/i18next"; -import { GithubPublisherSettingsTab } from "src/settings"; -import { migrateSettings, OldSettings } from "src/settings/migrate"; -import { createTokenPath, monkeyPatchConsole, notif } from "src/utils"; -import { checkRepositoryValidity, verifyRateLimitAPI } from "src/utils/data_validation_test"; -import merge from "ts-deepmerge"; - -/** - * Main class of the plugin - * @extends Plugin - */ - -export default class GithubPublisher extends Plugin { - settings!: GitHubPublisherSettings; - branchName: string = ""; - repositoryFrontmatter: SetRepositoryFrontmatter = {}; - - /** - * Get the title field of a file - * @param {TFile} file - The file to get the title field from - * @param {FrontMatterCache} frontmatter - The frontmatter of the file - * @return {string} - The title field of the file - */ - getTitleFieldForCommand(file: TFile, frontmatter: FrontMatterCache | undefined | null): string { - return regexOnFileName(getTitleField(frontmatter, file, this.settings), this.settings); - } - - async chargeAllCommands(repo: Repository | null, plugin: GithubPublisher) { - if (plugin.settings.plugin.copyLink.addCmd) { - this.addCommand(await createLinkCallback(repo, this)); - } - this.addCommand(await shareOneNoteCallback(repo, this)); - if (plugin.settings.upload.autoclean.enable) { - this.addCommand(await purgeNotesRemoteCallback(this, repo, this.branchName)); - } - this.addCommand(await uploadAllNotesCallback(this, repo, this.branchName)); - this.addCommand(await uploadNewNotesCallback(this, repo, this.branchName)); - this.addCommand(await uploadAllEditedNotesCallback(this, repo, this.branchName)); - this.addCommand(await shareEditedOnlyCallback(repo, this.branchName, this)); - this.addCommand(await checkRepositoryValidityCallback(this, repo)); - } - - cleanSpecificCommands(repo: Repository) { - const allCommands = this.app.commands.listCommands(); - for (const command of allCommands) { - if (command.id.startsWith("obsidian-mkdocs-publisher")) { - const publisherCMDsName = command.id.replace("obsidian-mkdocs-publisher:", "").split("-"); - //repo will be the last element of the array - const repoCmd = publisherCMDsName[publisherCMDsName.length - 1]; - if (repoCmd.startsWith("K") && repo.smartKey === repoCmd.replace("K", "")) { - this.app.commands.removeCommand(command.id); - } - } - } - } - - cleanOldCommands() { - const allRepo: Repository[] = this.settings.github?.otherRepo ?? []; - const allCommands = this.app.commands.listCommands(); - for (const command of allCommands) { - if (command.id.startsWith("obsidian-mkdocs-publisher")) { - const commandName = command.id.replace("obsidian-mkdocs-publisher:", ""); - //repo will be the last element of the array - const repoCmd = commandName.split("-")[commandName.split("-").length - 1]; - if (repoCmd.startsWith("K")) { - const repoIndex = allRepo.findIndex((repo) => repo.smartKey === repoCmd.replace("K", "")); - if (repoIndex === -1) { - this.app.commands.removeCommand(command.id); - } - } - if (!this.settings.upload.autoclean.enable && commandName === "delete-clean") { - this.app.commands.removeCommand(command.id); - } - } - } - } - - async reloadCommands() { - //compare old and new repo to delete old commands - const newRepo: Repository[] = this.settings.github?.otherRepo ?? []; - this.cleanOldCommands(); - for (const repo of newRepo) { - if (repo.createShortcuts) { - await this.chargeAllCommands(repo, this); - } else { - this.cleanSpecificCommands(repo); - } - } - } - - /** - * Read the env file to get the token of the plugin - * Form of the file: - * ``` - * GITHUB_TOKEN=token - * ``` - * @returns {Promise} - The token of the plugin - */ - - async loadToken(repo?: string): Promise { - if (repo=="default") repo = undefined; - const tokenPath = createTokenPath(this, this.settings.github.tokenPath); - - const tokenFileExists = await this.app.vault.adapter.exists(`${tokenPath}`); - if (!tokenFileExists) { - return ""; - } - try { - const tokenFile = await this.app.vault.adapter.read(`${tokenPath}`); - if (tokenPath.endsWith(".json")) { - const tokenJSON = JSON.parse(tokenFile); - const defaultToken = tokenJSON.GITHUB_PUBLISHER_TOKEN; - if (repo) - return tokenJSON.GITHUB_PUBLISHER_REPOS?.[repo] ?? defaultToken; - return defaultToken; - } - if (tokenFile) { - const defaultToken=tokenFile.split("\n").find((line) => line.startsWith("GITHUB_TOKEN"))?.split("=")[1] ?? ""; - if (repo) return tokenFile.split("\n").find((line) => line.startsWith(`${repo}_TOKEN`))?.split("=")[1] ?? defaultToken; - return defaultToken; - } - } catch (e) { - notif({ settings: this.settings, e: true }, e); - return ""; - } - return ""; - } - - /** - * Create a new instance of Octokit to load a new instance of GithubBranch - * @param {string} repo - The repository to load the Octokit for - */ - async reloadOctokit(repo?: string) { - const apiSettings = this.settings.github.api; - const token = await this.loadToken(repo); - const octokit = apiSettings.tiersForApi === GithubTiersVersion.entreprise && apiSettings.hostname.length > 0 ? new Octokit( - { - baseUrl: `${apiSettings.hostname}/api/v3`, - auth: token, - }) : new Octokit({ auth: token }); - return new GithubBranch( - octokit, - this, - ); - } - - - - /** - * Function called when the plugin is loaded - * @return {Promise} - */ - async onload(): Promise { - await i18next.init({ - lng: translationLanguage, - fallbackLng: "en", - resources, - returnNull: false, - returnEmptyString: false, - }); - - console.info(dedent(`[GITHUB PUBLISHER] v.${this.manifest.version} (lang: ${translationLanguage}) loaded. - * You can hide HTTP logs in the console with checking the "Hide network" in the console settings. - * See here: https://developer.chrome.com/docs/devtools/console/reference#network`)); - await this.loadSettings(); - - const oldSettings = this.settings; - await migrateSettings(oldSettings as unknown as OldSettings, this); - - this.branchName = - this.app.vault.getName().replaceAll(" ", "-").replaceAll(".", "-") + - "-" + - new Date().toLocaleDateString("en-US").replace(/\//g, "-"); - this.addSettingTab(new GithubPublisherSettingsTab(this.app, this, this.branchName)); - // verify rate limit - - if (!this.settings.github.verifiedRepo && (await this.loadToken()) !== "") { - const octokit = await this.reloadOctokit(); - this.settings.github.verifiedRepo = await checkRepositoryValidity(octokit, null, null, true); - this.settings.github.rateLimit = await verifyRateLimitAPI(octokit.octokit, this.settings, false); - await this.saveSettings(); - } - - for (const repository of this.settings.github.otherRepo) { - if (!repository.verifiedRepo && (await this.loadToken(repository.smartKey)) !== "") - { - const repoOctokit = await this.reloadOctokit(repository.smartKey); - repository.verifiedRepo = await checkRepositoryValidity(repoOctokit, repository, null, false); - repository.rateLimit = await verifyRateLimitAPI(repoOctokit.octokit, this.settings); - } - - if (repository.set) { - //take the file and update the frontmatter - const file = this.app.vault.getAbstractFileByPath(repository.set); - if (file && file instanceof TFile) { - const frontmatter = this.app.metadataCache.getFileCache(file)?.frontmatter; - if (frontmatter) { - this.repositoryFrontmatter[repository.smartKey] = frontmatter; - } - } - } - } - await this.saveSettings(); - - - this.registerEvent( - this.app.workspace.on("file-menu", (menu: Menu, folder: TAbstractFile) => { - if (this.settings.plugin.fileMenu && folder instanceof TFolder) { - addMenuFolder(menu, folder, this.branchName, this); - } else if (folder instanceof TFile) { - addMenuFile(this, folder, this.branchName, menu); - } - }) - ); - - this.registerEvent( - this.app.workspace.on("editor-menu", (menu, editor, view) => { - if (view.file) addMenuFile(this, view.file, this.branchName, menu); - }) - ); - await this.chargeAllCommands(null, this); - - this.addCommand({ - id: "check-rate-limit", - name: i18next.t("commands.checkValidity.rateLimit.command"), - callback: async () => { - const octokit = await this.reloadOctokit(); - this.settings.github.rateLimit = await verifyRateLimitAPI(octokit.octokit, this.settings); - await this.saveSettings(); - } - }); - - - if (this.settings.github.otherRepo.length > 0) { - this.addCommand({ - id: "run-cmd-for-repo", - name: i18next.t("commands.runOtherRepo.title"), - callback: async () => { - new ChooseWhichRepoToRun(this.app, this, this.branchName).open(); - } - }); - } - - const repoWithShortcuts = this.settings.github.otherRepo.filter((repo) => repo.createShortcuts); - for (const repo of repoWithShortcuts) { - await this.chargeAllCommands(repo, this); - } - this.addCommand(refreshOpenedSet(this)); - this.addCommand(refreshAllSets(this)); - - monkeyPatchConsole(this); - } - - /** - * Called when the plugin is disabled - */ - onunload() { - console.info("[Github Publisher] Unloaded"); - } - - /** - * Load the settings of the plugin - * Use merge methods to merge the default settings with the loaded settings as I use a lot of nested objects - * If the deep merge fails, I use the default method - */ - async loadSettings() { - const loadedData = await this.loadData(); - try { - this.settings = merge(DEFAULT_SETTINGS, loadedData) as unknown as GitHubPublisherSettings; - } catch (e) { - console.warn("[Github Publisher] Error while deep merging settings, using default loading method"); - this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); - } - } - - /** - * Save the settings of the plugin - */ - async saveSettings() { - await this.saveData(this.settings); - } -} +import { + DEFAULT_SETTINGS, + GitHubPublisherSettings, + GithubTiersVersion, + Repository, + SetRepositoryFrontmatter, +} from "@interfaces"; +import { Octokit } from "@octokit/core"; +import dedent from "dedent"; +import i18next from "i18next"; +import { FrontMatterCache, Menu, Plugin, TAbstractFile, TFile, TFolder } from "obsidian"; +import { + checkRepositoryValidityCallback, + createLinkCallback, + purgeNotesRemoteCallback, + refreshAllSets, + refreshOpenedSet, + shareEditedOnlyCallback, + shareOneNoteCallback, + uploadAllEditedNotesCallback, + uploadAllNotesCallback, + uploadNewNotesCallback, +} from "src/commands"; +import { addMenuFile, addMenuFolder } from "src/commands/file_menu"; +import { ChooseWhichRepoToRun } from "src/commands/suggest_other_repo_commands_modal"; +import { getTitleField, regexOnFileName } from "src/conversion/file_path"; +import { GithubBranch } from "src/GitHub/branch"; +import { resources, translationLanguage } from "src/i18n/i18next"; +import { GithubPublisherSettingsTab } from "src/settings"; +import { migrateSettings, OldSettings } from "src/settings/migrate"; +import { createTokenPath, monkeyPatchConsole, notif } from "src/utils"; +import { + checkRepositoryValidity, + verifyRateLimitAPI, +} from "src/utils/data_validation_test"; +import merge from "ts-deepmerge"; + +/** + * Main class of the plugin + * @extends Plugin + */ + +export default class GithubPublisher extends Plugin { + settings!: GitHubPublisherSettings; + branchName: string = ""; + repositoryFrontmatter: SetRepositoryFrontmatter = {}; + + /** + * Get the title field of a file + * @param {TFile} file - The file to get the title field from + * @param {FrontMatterCache} frontmatter - The frontmatter of the file + * @return {string} - The title field of the file + */ + getTitleFieldForCommand( + file: TFile, + frontmatter: FrontMatterCache | undefined | null + ): string { + return regexOnFileName( + getTitleField(frontmatter, file, this.settings), + this.settings + ); + } + + async chargeAllCommands(repo: Repository | null, plugin: GithubPublisher) { + if (plugin.settings.plugin.copyLink.addCmd) { + this.addCommand(await createLinkCallback(repo, this)); + } + this.addCommand(await shareOneNoteCallback(repo, this)); + if (plugin.settings.upload.autoclean.enable) { + this.addCommand(await purgeNotesRemoteCallback(this, repo, this.branchName)); + } + this.addCommand(await uploadAllNotesCallback(this, repo, this.branchName)); + this.addCommand(await uploadNewNotesCallback(this, repo, this.branchName)); + this.addCommand(await uploadAllEditedNotesCallback(this, repo, this.branchName)); + this.addCommand(await shareEditedOnlyCallback(repo, this.branchName, this)); + this.addCommand(await checkRepositoryValidityCallback(this, repo)); + } + + cleanSpecificCommands(repo: Repository) { + const allCommands = this.app.commands.listCommands(); + for (const command of allCommands) { + if (command.id.startsWith("obsidian-mkdocs-publisher")) { + const publisherCMDsName = command.id + .replace("obsidian-mkdocs-publisher:", "") + .split("-"); + //repo will be the last element of the array + const repoCmd = publisherCMDsName[publisherCMDsName.length - 1]; + if (repoCmd.startsWith("K") && repo.smartKey === repoCmd.replace("K", "")) { + this.app.commands.removeCommand(command.id); + } + } + } + } + + cleanOldCommands() { + const allRepo: Repository[] = this.settings.github?.otherRepo ?? []; + const allCommands = this.app.commands.listCommands(); + for (const command of allCommands) { + if (command.id.startsWith("obsidian-mkdocs-publisher")) { + const commandName = command.id.replace("obsidian-mkdocs-publisher:", ""); + //repo will be the last element of the array + const repoCmd = commandName.split("-")[commandName.split("-").length - 1]; + if (repoCmd.startsWith("K")) { + const repoIndex = allRepo.findIndex( + (repo) => repo.smartKey === repoCmd.replace("K", "") + ); + if (repoIndex === -1) { + this.app.commands.removeCommand(command.id); + } + } + if (!this.settings.upload.autoclean.enable && commandName === "delete-clean") { + this.app.commands.removeCommand(command.id); + } + } + } + } + + async reloadCommands() { + //compare old and new repo to delete old commands + const newRepo: Repository[] = this.settings.github?.otherRepo ?? []; + this.cleanOldCommands(); + for (const repo of newRepo) { + if (repo.createShortcuts) { + await this.chargeAllCommands(repo, this); + } else { + this.cleanSpecificCommands(repo); + } + } + } + + /** + * Read the env file to get the token of the plugin + * Form of the file: + * ``` + * GITHUB_TOKEN=token + * ``` + * @returns {Promise} - The token of the plugin + */ + + async loadToken(repo?: string): Promise { + if (repo == "default") repo = undefined; + const tokenPath = createTokenPath(this, this.settings.github.tokenPath); + + const tokenFileExists = await this.app.vault.adapter.exists(`${tokenPath}`); + if (!tokenFileExists) { + return ""; + } + try { + const tokenFile = await this.app.vault.adapter.read(`${tokenPath}`); + if (tokenPath.endsWith(".json")) { + const tokenJSON = JSON.parse(tokenFile); + const defaultToken = tokenJSON.GITHUB_PUBLISHER_TOKEN; + if (repo) return tokenJSON.GITHUB_PUBLISHER_REPOS?.[repo] ?? defaultToken; + return defaultToken; + } + if (tokenFile) { + const defaultToken = + tokenFile + .split("\n") + .find((line) => line.startsWith("GITHUB_TOKEN")) + ?.split("=")[1] ?? ""; + if (repo) + return ( + tokenFile + .split("\n") + .find((line) => line.startsWith(`${repo}_TOKEN`)) + ?.split("=")[1] ?? defaultToken + ); + return defaultToken; + } + } catch (e) { + notif({ settings: this.settings, e: true }, e); + return ""; + } + return ""; + } + + /** + * Create a new instance of Octokit to load a new instance of GithubBranch + * @param {string} repo - The repository to load the Octokit for + */ + async reloadOctokit(repo?: string) { + const apiSettings = this.settings.github.api; + const token = await this.loadToken(repo); + const octokit = + apiSettings.tiersForApi === GithubTiersVersion.entreprise && + apiSettings.hostname.length > 0 + ? new Octokit({ + baseUrl: `${apiSettings.hostname}/api/v3`, + auth: token, + }) + : new Octokit({ auth: token }); + return new GithubBranch(octokit, this); + } + + /** + * Function called when the plugin is loaded + * @return {Promise} + */ + async onload(): Promise { + await i18next.init({ + lng: translationLanguage, + fallbackLng: "en", + resources, + returnNull: false, + returnEmptyString: false, + }); + + console.info( + dedent(`[GITHUB PUBLISHER] v.${this.manifest.version} (lang: ${translationLanguage}) loaded. + * You can hide HTTP logs in the console with checking the "Hide network" in the console settings. + * See here: https://developer.chrome.com/docs/devtools/console/reference#network`) + ); + await this.loadSettings(); + + const oldSettings = this.settings; + await migrateSettings(oldSettings as unknown as OldSettings, this); + + this.branchName = + this.app.vault.getName().replaceAll(" ", "-").replaceAll(".", "-") + + "-" + + new Date().toLocaleDateString("en-US").replace(/\//g, "-"); + this.addSettingTab(new GithubPublisherSettingsTab(this.app, this, this.branchName)); + // verify rate limit + + if (!this.settings.github.verifiedRepo && (await this.loadToken()) !== "") { + const octokit = await this.reloadOctokit(); + this.settings.github.verifiedRepo = await checkRepositoryValidity( + octokit, + null, + null, + true + ); + this.settings.github.rateLimit = await verifyRateLimitAPI( + octokit.octokit, + this.settings, + false + ); + await this.saveSettings(); + } + + for (const repository of this.settings.github.otherRepo) { + if ( + !repository.verifiedRepo && + (await this.loadToken(repository.smartKey)) !== "" + ) { + const repoOctokit = await this.reloadOctokit(repository.smartKey); + repository.verifiedRepo = await checkRepositoryValidity( + repoOctokit, + repository, + null, + false + ); + repository.rateLimit = await verifyRateLimitAPI( + repoOctokit.octokit, + this.settings + ); + } + + if (repository.set) { + //take the file and update the frontmatter + const file = this.app.vault.getAbstractFileByPath(repository.set); + if (file && file instanceof TFile) { + const frontmatter = this.app.metadataCache.getFileCache(file)?.frontmatter; + if (frontmatter) { + this.repositoryFrontmatter[repository.smartKey] = frontmatter; + } + } + } + } + await this.saveSettings(); + + this.registerEvent( + this.app.workspace.on("file-menu", (menu: Menu, folder: TAbstractFile) => { + if (this.settings.plugin.fileMenu && folder instanceof TFolder) { + addMenuFolder(menu, folder, this.branchName, this); + } else if (folder instanceof TFile) { + addMenuFile(this, folder, this.branchName, menu); + } + }) + ); + + this.registerEvent( + this.app.workspace.on("editor-menu", (menu, editor, view) => { + if (view.file) addMenuFile(this, view.file, this.branchName, menu); + }) + ); + await this.chargeAllCommands(null, this); + + this.addCommand({ + id: "check-rate-limit", + name: i18next.t("commands.checkValidity.rateLimit.command"), + callback: async () => { + const octokit = await this.reloadOctokit(); + this.settings.github.rateLimit = await verifyRateLimitAPI( + octokit.octokit, + this.settings + ); + await this.saveSettings(); + }, + }); + + if (this.settings.github.otherRepo.length > 0) { + this.addCommand({ + id: "run-cmd-for-repo", + name: i18next.t("commands.runOtherRepo.title"), + callback: async () => { + new ChooseWhichRepoToRun(this.app, this, this.branchName).open(); + }, + }); + } + + const repoWithShortcuts = this.settings.github.otherRepo.filter( + (repo) => repo.createShortcuts + ); + for (const repo of repoWithShortcuts) { + await this.chargeAllCommands(repo, this); + } + this.addCommand(refreshOpenedSet(this)); + this.addCommand(refreshAllSets(this)); + + monkeyPatchConsole(this); + } + + /** + * Called when the plugin is disabled + */ + onunload() { + console.info("[Github Publisher] Unloaded"); + } + + /** + * Load the settings of the plugin + * Use merge methods to merge the default settings with the loaded settings as I use a lot of nested objects + * If the deep merge fails, I use the default method + */ + async loadSettings() { + const loadedData = await this.loadData(); + try { + this.settings = merge( + DEFAULT_SETTINGS, + loadedData + ) as unknown as GitHubPublisherSettings; + } catch (e) { + console.warn( + "[Github Publisher] Error while deep merging settings, using default loading method" + ); + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + } + } + + /** + * Save the settings of the plugin + */ + async saveSettings() { + await this.saveData(this.settings); + } +} diff --git a/src/settings.ts b/src/settings.ts index fd109d43..cdd03639 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,1418 +1,1425 @@ -import { - EnumbSettingsTabId, FolderSettings, GitHubPublisherSettings, GithubTiersVersion, Repository -} from "@interfaces"; -import i18next from "i18next"; -import { App, Notice, PluginSettingTab, setIcon, Setting } from "obsidian"; -import GithubPublisherPlugin from "src/main"; -import { - help, - KeyBasedOnSettings, - multipleRepoExplained, - supportMe, - usefulLinks -} from "src/settings/help"; -import { migrateToken } from "src/settings/migrate"; -import { ExportModal, ImportLoadPreset, ImportModal, loadAllPresets } from "src/settings/modals/import_export"; -import { ModalAddingNewRepository } from "src/settings/modals/manage_repo"; -import { AutoCleanPopup } from "src/settings/modals/popup"; -import { ModalRegexFilePathName, ModalRegexOnContents, OverrideAttachmentsModal } from "src/settings/modals/regex_edition"; -import { TokenEditPath } from "src/settings/modals/token_path"; -import { - autoCleanCondition, - folderHideShowSettings, showHideBasedOnFolder, -} from "src/settings/style"; -import { checkRepositoryValidity, verifyRateLimitAPI } from "src/utils/data_validation_test"; - - -export class GithubPublisherSettingsTab extends PluginSettingTab { - plugin: GithubPublisherPlugin; - settingsPage!: HTMLElement; - branchName: string; - settings: GitHubPublisherSettings; - - constructor(app: App, plugin: GithubPublisherPlugin, branchName: string) { - super(app, plugin); - this.plugin = plugin; - this.branchName = branchName; - this.settings = plugin.settings; - } - - display(): void { - const { containerEl } = this; - containerEl.empty(); - containerEl.addClass("github-publisher"); - const defaultTabId = EnumbSettingsTabId.github; - let savedId = this.settings.tabsID ?? defaultTabId; - if (this.settings.plugin.saveTabId != undefined && !this.settings.plugin.saveTabId) { //real false - this.settings.tabsID = defaultTabId; - savedId = defaultTabId; - this.plugin.saveSettings(); - } - - const PUBLISHER_TABS = { - "github-configuration": { - name: i18next.t("settings.github.title"), - icon: "cloud", - }, - "upload-configuration": { - name: i18next.t("settings.upload.title"), - icon: "upload", - }, - "text-conversion": { - name: i18next.t("settings.conversion.title"), - icon: "file-text", - }, - "embed-configuration": { - name: i18next.t("settings.embed.title"), - icon: "link", - }, - "plugin-settings": { - name: i18next.t("settings.plugin.title"), - icon: "gear", - }, - "help": { - name: i18next.t("settings.help.title"), - icon: "info", - }, - }; - - new Setting(containerEl) - .setClass("import-export") - .addButton((button) => { - button.setButtonText(i18next.t("modals.export.title")) - .onClick(() => { - new ExportModal(this.app, this.plugin).open(); - }); - } - ) - .addButton((button) => { - button.setButtonText(i18next.t("modals.import.title")) - .onClick(() => { - new ImportModal(this.app, this.plugin, this.settingsPage, this).open(); - }); - }) - .addButton((button) => { - button - .setButtonText(i18next.t("modals.import.presets.title")) - .setTooltip(i18next.t("modals.import.presets.desc")) - .onClick(async () => { - const octokit = await this.plugin.reloadOctokit(); - const presetLists = await loadAllPresets(octokit.octokit, this.plugin); - new ImportLoadPreset(this.app, this.plugin, presetLists, octokit.octokit, this).open(); - }); - }); - const tabBar = containerEl.createEl("nav", { - cls: "settings-tab-bar", - }); - - - - for (const [tabID, tabInfo] of Object.entries(PUBLISHER_TABS)) { - const tabEl = tabBar.createEl("div", { - cls: "settings-tab", - }); - const tabIcon = tabEl.createEl("div", { - cls: "settings-tab-icon", - }); - setIcon(tabIcon, tabInfo.icon); - tabEl.createEl("div", { - cls: "settings-tab-name", - text: tabInfo.name, - }); - if (tabID === savedId) - tabEl.addClass("settings-tab-active"); - - tabEl.addEventListener("click", async () => { - // @ts-ignore - for (const tabEl of tabBar.children) - tabEl.removeClass("settings-tab-active"); - - tabEl.addClass("settings-tab-active"); - this.renderSettingsPage(tabID); - }); - } - this.settingsPage = containerEl.createEl("div", { - cls: "settings-tab-page", - }); - this.renderSettingsPage(savedId); - - - } - - /** - * Render the settings tab - * @param {string} tabId - to know which tab to render - */ - async renderSettingsPage(tabId: string | EnumbSettingsTabId) { - if (this.settings.plugin.saveTabId || this.settings.plugin.saveTabId === undefined) { - this.settings.tabsID = tabId as EnumbSettingsTabId; - await this.plugin.saveSettings(); - } - this.settingsPage.empty(); - switch (tabId) { - case "github-configuration": - this.renderGithubConfiguration(); - break; - case "upload-configuration": - this.renderUploadConfiguration(); - break; - case "text-conversion": - this.renderTextConversion(); - break; - case "embed-configuration": - this.renderEmbedConfiguration(); - break; - case "plugin-settings": - this.renderPluginSettings(); - break; - case "help": - this.renderHelp(); - break; - } - } - - /** - * Render the github configuration tab - */ - renderGithubConfiguration() { - const githubSettings = this.settings.github; - new Setting(this.settingsPage) - .setName(i18next.t("settings.github.apiType.title")) - .setDesc(i18next.t("settings.github.apiType.desc")) - .addDropdown((dropdown) => { - dropdown - .addOption(GithubTiersVersion.free, i18next.t("settings.github.apiType.dropdown.free")) - .addOption(GithubTiersVersion.entreprise, i18next.t("settings.github.apiType.dropdown.enterprise")) - .setValue(githubSettings.api.tiersForApi) - .onChange(async (value) => { - githubSettings.api.tiersForApi = value as GithubTiersVersion; - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.github); - }); - }); - if (githubSettings.api.tiersForApi === GithubTiersVersion.entreprise) { - new Setting(this.settingsPage) - .setName(i18next.t("settings.github.apiType.hostname.title")) - .setDesc(i18next.t("settings.github.apiType.hostname.desc")) - .addText((text) => - text - .setPlaceholder("https://github.mycompany.com") - .setValue(githubSettings.api.hostname) - .onChange(async (value) => { - githubSettings.api.hostname = value.trim(); - await this.plugin.saveSettings(); - }) - ); - } - - - new Setting(this.settingsPage) - .setName(i18next.t("settings.github.username.title")) - .setDesc(i18next.t("settings.github.username.desc")) - .addText((text) => - text - .setPlaceholder( - i18next.t("settings.github.username.title") - ) - .setValue(githubSettings.user) - .onChange(async (value) => { - githubSettings.user = value.trim(); - await this.plugin.saveSettings(); - }) - ); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.github.repoName.title")) - .setDesc(i18next.t("settings.github.repoName.desc")) - .addText((text) => - text - .setPlaceholder(i18next.t("settings.github.repoName.placeholder")) - .setValue(githubSettings.repo) - .onChange(async (value) => { - githubSettings.repo = value.trim(); - await this.plugin.saveSettings(); - }) - ); - const desc_ghToken = document.createDocumentFragment(); - desc_ghToken.createEl("span", undefined, (span) => { - span.innerText = i18next.t("settings.github.ghToken.desc"); - span.createEl("a", undefined, (link) => { - link.innerText = `${i18next.t("common.here")}.`; - link.href = - "https://github.com/settings/tokens/new?scopes=repo,workflow"; - }); - }); - const tokenSettings = new Setting(this.settingsPage) - .setName(i18next.t("common.ghToken")) - .setDesc(desc_ghToken) - .addText(async (text) => { - const decryptedToken: string = await this.plugin.loadToken(); - text - .setPlaceholder("ghp_15457498545647987987112184") - .setValue(decryptedToken) - .onChange(async (value) => { - if (value.trim().length === 0) { - tokenSettings.controlEl.addClass("error"); - new Notice(i18next.t("settings.github.ghToken.error")); - } else { - tokenSettings.controlEl.removeClass("error"); - await migrateToken(this.plugin, value.trim()); - } - await this.plugin.saveSettings(); - }); - }) - .addExtraButton((button) => { - button - .setIcon("edit") - .setTooltip(i18next.t("settings.github.ghToken.button.tooltip")) - .onClick(async () => { - const token = await this.plugin.loadToken(); - new TokenEditPath(this.app, this.plugin, token).open(); - await this.plugin.saveSettings(); - - }); - }); - new Setting(this.settingsPage) - .setName(i18next.t("settings.github.branch.title")) - .setDesc(i18next.t("settings.github.branch.desc")) - .addText((text) => - text - .setPlaceholder("main") - .setValue(githubSettings.branch) - .onChange(async (value) => { - githubSettings.branch = value.trim(); - await this.plugin.saveSettings(); - }) - ); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.github.automaticallyMergePR")) - .addToggle((toggle) => - toggle - .setValue(githubSettings.automaticallyMergePR) - .onChange(async (value) => { - githubSettings.automaticallyMergePR = value; - await this.plugin.saveSettings(); - }) - ); - new Setting(this.settingsPage) - .setName(i18next.t("settings.github.dryRun.enable.title")) - .setDesc(i18next.t("settings.github.dryRun.enable.desc")) - .addToggle((toggle) => - toggle - .setValue(githubSettings.dryRun.enable) - .onChange(async (value) => { - githubSettings.dryRun.enable = value; - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.github); - }) - ); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.github.dryRun.folder.title")) - .setDesc(i18next.t("settings.github.dryRun.folder.desc")) - .addText((text) => - text - .setPlaceholder("github-publisher") - .setValue(githubSettings.dryRun.folderName) - .onChange(async (value) => { - githubSettings.dryRun.folderName = value.trim(); - await this.plugin.saveSettings(); - }) - ); - - - new Setting(this.settingsPage) - .setClass("no-display") - .addButton((button) => - button - .setButtonText(i18next.t("settings.github.testConnection")) - .setClass("connect-button") - .onClick(async () => { - const octokit = await this.plugin.reloadOctokit(); - this.settings.github.verifiedRepo = await checkRepositoryValidity(octokit, null, null); - this.settings.github.rateLimit = await verifyRateLimitAPI(octokit.octokit, this.settings); - await this.plugin.saveSettings(); - }) - ) - .addButton((button) => - button - .setButtonText(i18next.t("settings.github.smartRepo.button")) - .onClick(async () => { - const repository: Repository[] = this.copy(this.settings.github?.otherRepo ?? []); - new ModalAddingNewRepository(this.app, this.settings, this.branchName, this.plugin, repository, (async result => { - this.settings.github.otherRepo = result; - await this.plugin.saveSettings(); - this.plugin.reloadCommands(); - }) - ).open(); - })); - this.settingsPage.createEl("h3", { text: "GitHub Workflow" }); - new Setting(this.settingsPage) - .setName(i18next.t("settings.githubWorkflow.prRequest.title")) - .setDesc(i18next.t("settings.githubWorkflow.prRequest.desc")) - .addText((text) => - text - .setPlaceholder("[PUBLISHER] MERGE") - .setValue(githubSettings.workflow.commitMessage) - .onChange(async (value) => { - if (value.trim().length === 0) { - value = "[PUBLISHER] MERGE"; - new Notice(i18next.t("settings.githubWorkflow.prRequest.error")); - } - githubSettings.workflow.commitMessage = value; - await this.plugin.saveSettings(); - }) - ); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.githubWorkflow.githubAction.title")) - .setDesc( - i18next.t("settings.githubWorkflow.githubAction.desc") - ) - .addText((text) => { - text.setPlaceholder("ci") - .setValue(githubSettings.workflow.name) - .onChange(async (value) => { - if (value.length > 0) { - value = value.trim(); - const yamlEndings = [".yml", ".yaml"]; - if (!yamlEndings.some(ending => value.endsWith(ending))) { - value += yamlEndings[0]; - } - } - githubSettings.workflow.name = value; - await this.plugin.saveSettings(); - }); - }); - - - } - - /** - * Render the settings tab for the upload configuration - */ - renderUploadConfiguration() { - const uploadSettings = this.settings.upload; - new Setting(this.settingsPage) - .setName(i18next.t("settings.upload.folderBehavior.title")) - .setDesc(i18next.t("settings.upload.folderBehavior.desc")) - .addDropdown((dropDown) => { - dropDown - .addOptions({ - fixed: i18next.t( - "settings.upload.folderBehavior.fixedFolder"), - yaml: i18next.t("settings.upload.folderBehavior.yaml"), - obsidian: i18next.t( - "settings.upload.folderBehavior.obsidianPath"), - }) - .setValue(uploadSettings.behavior) - .onChange(async (value: string) => { - uploadSettings.behavior = value as FolderSettings; - await folderHideShowSettings( - frontmatterKeySettings, - rootFolderSettings, - autoCleanSetting, - value, - this.plugin); - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.upload); - }); - }); - - const defaultFolder = uploadSettings.behavior === FolderSettings.yaml ? - { - desc: i18next.t("settings.upload.defaultFolder.desc"), - title: i18next.t("settings.upload.defaultFolder.title"), - } - : { - desc: i18next.t("settings.upload.rootFolder.other"), - title: i18next.t("settings.upload.rootFolder.title"), - }; - - new Setting(this.settingsPage) - .setName(defaultFolder.title) - .setDesc(defaultFolder.desc) - .addText((text) => { - text.setPlaceholder(i18next.t("settings.upload.defaultFolder.placeholder")) - .setValue(uploadSettings.defaultName) - .onChange(async (value) => { - uploadSettings.defaultName = value.replace( - /\/$/, - "" - ); - await autoCleanCondition( - value, - autoCleanSetting, - this.plugin, - "defaultName", - this - ); - await this.plugin.saveSettings(); - }); - }); - - const frontmatterKeySettings = new Setting(this.settingsPage) - .setName(i18next.t("settings.upload.frontmatterKey.title")) - .setDesc(i18next.t("settings.upload.frontmatterKey.desc")) - .addText((text) => { - text.setPlaceholder(i18next.t("settings.upload.frontmatterKey.placeholder")) - .setValue(uploadSettings.yamlFolderKey) - .onChange(async (value) => { - uploadSettings.yamlFolderKey = value.trim(); - await this.plugin.saveSettings(); - }); - }); - const rootFolderSettings = new Setting(this.settingsPage) - .setName(i18next.t("settings.upload.rootFolder.title")) - .setDesc(i18next.t("settings.upload.rootFolder.desc")) - .addText((text) => { - text.setPlaceholder("docs") - .setValue(uploadSettings.rootFolder) - .onChange(async (value) => { - uploadSettings.rootFolder = value.replace( - /\/$/, - "" - ); - await autoCleanCondition( - value, - autoCleanSetting, - this.plugin, - "rootFolder", - this - ); - await this.plugin.saveSettings(); - }); - }); - const frontmatterTitleSet = new Setting(this.settingsPage) - .setName( - i18next.t("settings.upload.useFrontmatterTitle.title") - ) - .setDesc( - i18next.t("settings.upload.useFrontmatterTitle.desc") - ) - .setClass("title") - .addToggle((toggle) => { - toggle - .setValue(uploadSettings.frontmatterTitle.enable) - .onChange(async (value) => { - uploadSettings.frontmatterTitle.enable = value; - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.upload); - }); - }); - if (uploadSettings.frontmatterTitle.enable) { - frontmatterTitleSet.addText((text) => { - text.setPlaceholder("title") - .setValue(uploadSettings.frontmatterTitle.key) - .onChange(async (value) => { - uploadSettings.frontmatterTitle.key = value.trim(); - await this.plugin.saveSettings(); - }); - }); - } - - const desc = uploadSettings.behavior === FolderSettings.fixed ? i18next.t("settings.upload.regexFilePathTitle.title.titleOnly") : i18next.t("settings.upload.regexFilePathTitle.title.FolderPathTitle"); - - new Setting(this.settingsPage) - .setName(desc) - .setDesc( - i18next.t("settings.upload.regexFilePathTitle.desc") - ) - .addButton((button) => { - button - .setIcon("pencil") - .onClick(async () => { - let allRegex = uploadSettings.replaceTitle; - if (uploadSettings.behavior !== FolderSettings.fixed) { - allRegex = allRegex.concat(uploadSettings.replacePath); - } - new ModalRegexFilePathName(this.app, this.settings, this.copy(allRegex), (async result => { - uploadSettings.replacePath = result.filter(title => { return title.type === "path"; }); - uploadSettings.replaceTitle = result.filter(title => { return title.type === "title"; }); - await this.plugin.saveSettings(); - })).open(); - }); - }); - - const folderNoteSettings = new Setting(this.settingsPage) - .setName(i18next.t("settings.conversion.links.folderNote.title")) - .setDesc( - i18next.t("settings.conversion.links.folderNote.desc") - ) - .addToggle((toggle) => { - toggle - .setValue(uploadSettings.folderNote.enable) - .onChange(async (value) => { - uploadSettings.folderNote.enable = value; - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.upload); - }); - }); - - if (uploadSettings.folderNote.enable) { - - folderNoteSettings.addText((text) => { - text.setPlaceholder("folderNote") - .setValue(uploadSettings.folderNote.rename) - .onChange(async (value) => { - uploadSettings.folderNote.rename = value; - await this.plugin.saveSettings(); - - }); - }); - new Setting(this.settingsPage) - .setName(i18next.t("settings.upload.folderNote.addTitle.title")) - .addToggle((toggle) => { - toggle - .setValue(uploadSettings.folderNote.addTitle.enable) - .onChange(async (value) => { - uploadSettings.folderNote.addTitle.enable = value; - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.upload); - }); - }); - if (uploadSettings.folderNote.addTitle.enable) { - new Setting(this.settingsPage) - .setName(i18next.t("settings.upload.folderNote.addTitle.key")) - .addText((text) => { - text.setPlaceholder("title") - .setValue(uploadSettings.folderNote.addTitle.key) - .onChange(async (value) => { - uploadSettings.folderNote.addTitle.key = value; - await this.plugin.saveSettings(); - }); - }); - } - } - - showHideBasedOnFolder(this.settings, frontmatterKeySettings, rootFolderSettings, folderNoteSettings); - - if (this.app.plugins.getPlugin("metadata-extractor")) { - new Setting(this.settingsPage) - .setName( - i18next.t("settings.githubWorkflow.useMetadataExtractor.title") - ) - .setDesc( - i18next.t("settings.githubWorkflow.useMetadataExtractor.desc") - ) - .addText((text) => { - text.setPlaceholder("docs/_assets/metadata") - .setValue(uploadSettings.metadataExtractorPath) - .onChange(async (value) => { - uploadSettings.metadataExtractorPath = - value.trim(); - await this.plugin.saveSettings(); - }); - }); - } - - const autoCleanSetting = new Setting(this.settingsPage) - .setName(i18next.t("settings.githubWorkflow.autoCleanUp.title")) - .setDesc(i18next.t("settings.githubWorkflow.autoCleanUp.desc")) - .addToggle((toggle) => { - toggle - .setValue(uploadSettings.autoclean.enable) - .onChange(async (value) => { - if (value && (this.settings.upload.behavior === "yaml" && this.settings.upload.defaultName.length === 0 || this.settings.upload.rootFolder.length === 0)) - new AutoCleanPopup(this.app, this.settings, (result => { - uploadSettings.autoclean.enable = result; - this.renderSettingsPage(EnumbSettingsTabId.upload); - })).open(); - else - uploadSettings.autoclean.enable = value; - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.upload); - this.plugin.cleanOldCommands(); - await this.plugin.chargeAllCommands(null, this.plugin); - }); - }); - if (uploadSettings.autoclean.enable) { - new Setting(this.settingsPage) - .setName(i18next.t("settings.githubWorkflow.excludedFiles.title")) - .setDesc(i18next.t("settings.githubWorkflow.excludedFiles.desc")) - .addTextArea((textArea) => { - textArea - .setPlaceholder( - "docs/assets/js, docs/assets/logo, /\\.js$/" - ) - .setValue( - uploadSettings.autoclean.excluded.join(", ") - ) - .onChange(async (value) => { - uploadSettings.autoclean.excluded = value - .split(/[,\n]/) - .map((item) => item.trim()) - .filter((item) => item.length > 0); - await this.plugin.saveSettings(); - }); - }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.githubWorkflow.includeAttachments.title")) - .setDesc(i18next.t("settings.githubWorkflow.includeAttachments.desc")) - .addToggle((toggle) => { - toggle - .setValue(uploadSettings.autoclean.includeAttachments) - .onChange(async (value) => { - uploadSettings.autoclean.includeAttachments = value; - await this.plugin.saveSettings(); - }); - }); - } - - - folderHideShowSettings( - frontmatterKeySettings, - rootFolderSettings, - autoCleanSetting, - uploadSettings.behavior, - this.plugin, - ); - - } - - /** - * Render the settings page for the text conversion parameters - */ - renderTextConversion() { - const textSettings = this.settings.conversion; - this.settingsPage.createEl("p", { - text: i18next.t("settings.conversion.desc"), - }); - - this.settingsPage.createEl("h5", { - text: i18next.t("settings.conversion.links.title"), - }); - this.settingsPage.createEl("p", { - text: i18next.t("settings.conversion.links.desc"), - }); - - const shareAll = this.settings.plugin.shareAll?.enable ? ` ${i18next.t("settings.conversion.links.internals.shareAll")}` : ""; - const internalLinksDesc = document.createDocumentFragment(); - internalLinksDesc.createEl("p", {text: i18next.t("settings.conversion.links.internals.desc") + shareAll, cls: "no-margin"}); - internalLinksDesc.createEl("p", {text: i18next.t("settings.conversion.links.internals.dataview"), cls: "no-margin"}); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.conversion.links.internals.title")) - .setDesc(internalLinksDesc) - .addToggle((toggle) => { - toggle - .setValue(textSettings.links.internal) - .onChange(async (value) => { - textSettings.links.internal = value; - if (this.settings.plugin.shareAll?.enable) { - textSettings.links.unshared = true; - } - await this.plugin.saveSettings(); - this.renderSettingsPage("text-conversion"); - }); - }); - - if (textSettings.links.internal && !this.settings.plugin.shareAll?.enable) { - new Setting(this.settingsPage) - .setName( - i18next.t("settings.conversion.links.nonShared.title") - ) - .setDesc( - i18next.t("settings.conversion.links.nonShared.desc") - ) - .addToggle((toggle) => { - toggle - .setValue(textSettings.links.unshared) - .onChange(async (value) => { - textSettings.links.unshared = - value; - await this.plugin.saveSettings(); - }); - }); - } - - new Setting(this.settingsPage) - .setName(i18next.t("settings.conversion.links.wikilinks.title")) - .setDesc( - i18next.t("settings.conversion.links.wikilinks.desc") - ) - .addToggle((toggle) => { - toggle - .setValue(textSettings.links.wiki) - .onChange(async (value) => { - textSettings.links.wiki = value; - await this.plugin.saveSettings(); - this.renderSettingsPage("text-conversion"); - }); - }); - const slugifySetting = typeof (textSettings.links.slugify) == "boolean" ? textSettings.links.slugify ? "strict" : "disable" : textSettings.links.slugify; - if (textSettings.links.wiki || textSettings.links.internal) { - new Setting(this.settingsPage) - .setName(i18next.t("settings.conversion.links.slugify.title")) - .setDesc(i18next.t("settings.conversion.links.slugify.desc")) - .addDropdown((dropdown) => { - dropdown - .addOptions({ - disable: i18next.t("settings.conversion.links.slugify.disable"), - strict: i18next.t("settings.conversion.links.slugify.strict"), - lower: i18next.t("settings.conversion.links.slugify.lower"), - }) - .setValue(slugifySetting) - .onChange(async (value) => { - textSettings.links.slugify = ["disable", "strict", "lower"].includes(value) ? value as "disable" | "strict" | "lower" : "disable"; - await this.plugin.saveSettings(); - }); - }); - } - - - this.settingsPage.createEl("h5", { - text: i18next.t("settings.conversion.sectionTitle"), - }); - new Setting(this.settingsPage) - .setName(i18next.t("settings.conversion.hardBreak.title")) - .setDesc(i18next.t("settings.conversion.hardBreak.desc")) - .addToggle((toggle) => { - toggle - .setValue(textSettings.hardbreak) - .onChange(async (value) => { - textSettings.hardbreak = value; - await this.plugin.saveSettings(); - }); - }); - const isDataviewEnabled = this.app.plugins.plugins.dataview; - new Setting(this.settingsPage) - .setName(i18next.t("settings.conversion.dataview.title")) - .setDesc(i18next.t("settings.conversion.dataview.desc")) - .addToggle((toggle) => { - toggle - .setValue(textSettings.dataview && isDataviewEnabled && textSettings.links.internal) - .setDisabled(!isDataviewEnabled || !textSettings.links.internal) - .onChange(async (value) => { - textSettings.dataview = value; - await this.plugin.saveSettings(); - }); - }); - - - - new Setting(this.settingsPage) - .setName(i18next.t("settings.regexReplacing.modal.title.text")) - .setDesc(i18next.t("settings.regexReplacing.modal.desc")) - .addButton((button) => { - button - .setIcon("pencil") - .onClick(async () => { - new ModalRegexOnContents(this.app, this.copy(this.settings), (async result => { - this.settings.conversion.censorText = result.conversion.censorText; - await this.plugin.saveSettings(); - })).open(); - }); - }); - - this.settingsPage.createEl("h5", { text: "Tags" }); - new Setting(this.settingsPage) - .setName( - i18next.t("settings.conversion.tags.inlineTags.title") - ) - .setDesc( - i18next.t("settings.conversion.tags.inlineTags.desc") - ) - .addToggle((toggle) => { - toggle - .setValue(textSettings.tags.inline) - .onChange(async (value) => { - textSettings.tags.inline = value; - await this.plugin.saveSettings(); - }); - }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.conversion.tags.title")) - .setDesc(i18next.t("settings.conversion.tags.desc")) - .addTextArea((text) => { - text.inputEl.addClass("mid-height"); - text.setPlaceholder("field_name") - .setValue(textSettings.tags.fields.join(",")) - .onChange(async (value) => { - textSettings.tags.fields = value - .split(/[,\n]\W*/) - .map((item) => item.trim()) - .filter((item) => item.length > 0); - await this.plugin.saveSettings(); - }); - }); - new Setting(this.settingsPage) - .setName(i18next.t("settings.conversion.tags.exclude.title")) - .setDesc(i18next.t("settings.conversion.tags.exclude.desc")) - .addTextArea((text) => { - text - .setPlaceholder(i18next.t("settings.conversion.tags.exclude.placeholder")) - .setValue( - textSettings.tags.exclude.join(",") - ) - .onChange(async (value) => { - textSettings.tags.exclude = value - .split(/[,\n]\W*/) - .map((item) => item.trim()) - .filter((item) => item.length > 0); - await this.plugin.saveSettings(); - }); - }); - - - } - - /** - * Render the settings page for the embeds settings - */ - async renderEmbedConfiguration() { - this.settingsPage.empty(); - const embedSettings = this.settings.embed; - new Setting(this.settingsPage) - .setName(i18next.t("settings.embed.sendSimpleLinks.title")) - .setDesc(i18next.t("settings.embed.sendSimpleLinks.desc")) - .addToggle((toggle) => { - toggle - .setValue(embedSettings.sendSimpleLinks) - .onChange(async (value) => { - embedSettings.sendSimpleLinks = value; - await this.plugin.saveSettings(); - await this.renderEmbedConfiguration(); - }); - }); - this.settingsPage.createEl("h5", { text: i18next.t("settings.embed.attachment"), cls: "center" }); - - - new Setting(this.settingsPage) - .setName(i18next.t("settings.embed.transferImage.title")) - .addToggle((toggle) => { - toggle - .setValue(embedSettings.attachments) - .onChange(async (value) => { - embedSettings.attachments = value; - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.embed); - }); - }); - - if (embedSettings.attachments) { - new Setting(this.settingsPage) - .setName(i18next.t("settings.embed.imagePath.title")) - .setDesc(i18next.t("settings.embed.imagePath.desc")) - .addToggle((toggle) => { - toggle - .setValue(embedSettings.useObsidianFolder ?? false) - .onChange(async (value) => { - embedSettings.useObsidianFolder = value; - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.embed); - }); - }); - if (!embedSettings.useObsidianFolder) { - new Setting(this.settingsPage) - .setName(i18next.t("settings.embed.defaultImageFolder.title")) - .setDesc(i18next.t("settings.embed.defaultImageFolder.desc")) - .addText((text) => { - text.setPlaceholder("docs/images") - .setValue(embedSettings.folder) - .onChange(async (value) => { - embedSettings.folder = value.replace( - /\/$/, - "" - ); - await this.plugin.saveSettings(); - }); - }); - } - - new Setting(this.settingsPage) - .setName(i18next.t("settings.embed.overrides.modal.title")) - .setDesc(i18next.t("settings.embed.overrides.desc")) - .addButton((button) => { - button - .setIcon("pencil") - .onClick(async () => { - new OverrideAttachmentsModal(this.app, this.settings, this.copy(embedSettings.overrideAttachments), (async result => { - embedSettings.overrideAttachments = result; - await this.plugin.saveSettings(); - })).open(); - }); - }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.embeds.unHandledObsidianExt.title")) - .setDesc(i18next.t("settings.embeds.unHandledObsidianExt.desc")) - .addTextArea((text) => { - text.setPlaceholder("py, mdx") - .setValue(embedSettings.unHandledObsidianExt.join(", ")) - .onChange(async (value) => { - embedSettings.unHandledObsidianExt = value - .split(/[,\n]\W*/) - .map((item) => item.trim()) - .filter((item) => item.length > 0); - await this.plugin.saveSettings(); - }); - }); - } - - new Setting(this.settingsPage) - .setName(i18next.t("settings.embed.transferMetaFile.title")) - .setDesc(i18next.t("settings.embed.transferMetaFile.desc")) - .addTextArea((text) => { - text.setPlaceholder("banner") - .setValue( - embedSettings.keySendFile.join(", ") - ) - .onChange(async (value) => { - embedSettings.keySendFile = value - .split(/[,\n]\W*/) - .map((item) => item.trim()) - .filter((item) => item.length > 0); - await this.plugin.saveSettings(); - }); - }); - - this.settingsPage.createEl("h5", { text: i18next.t("settings.embed.notes"), cls: "center" }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.embed.transferNotes.title")) - .setDesc(i18next.t("settings.embed.transferNotes.desc")) - .addToggle((toggle) => { - toggle - .setValue(embedSettings.notes) - .onChange(async (value) => { - embedSettings.notes = value; - await this.plugin.saveSettings(); - await this.renderEmbedConfiguration(); - }); - }); - - if (embedSettings.notes) { - new Setting(this.settingsPage) - .setName(i18next.t("settings.embed.links.title")) - .setDesc(i18next.t("settings.embed.links.desc")) - .addDropdown((dropdown) => { - dropdown - .addOption("keep", i18next.t("settings.embed.links.dp.keep")) - .addOption("remove", i18next.t("settings.embed.links.dp.remove")) - .addOption("links", i18next.t("settings.embed.links.dp.links")) - .addOption("bake", i18next.t("settings.embed.links.dp.bake")) - .setValue(embedSettings.convertEmbedToLinks ?? "keep") - .onChange(async (value) => { - embedSettings.convertEmbedToLinks = value as "keep" | "remove" | "links" | "bake"; - await this.plugin.saveSettings(); - await this.renderEmbedConfiguration(); - }); - }); - - if (embedSettings.convertEmbedToLinks === "links") { - new Setting(this.settingsPage) - .setName(i18next.t("settings.embed.char.title")) - .setDesc(i18next.t("settings.embed.char.desc")) - .addText((text) => { - text.setPlaceholder("->") - .setValue(embedSettings.charConvert ?? "->") - .onChange(async (value) => { - embedSettings.charConvert = value; - await this.plugin.saveSettings(); - }); - }); - } else if (embedSettings.convertEmbedToLinks === "bake") { - if (!embedSettings.bake) { - embedSettings.bake = { - textBefore: "", - textAfter: "" - }; - await this.plugin.saveSettings(); - } - await this.plugin.saveSettings(); - this.settingsPage.createEl("h5", { text: i18next.t("settings.embed.bake.title"), cls: "border-bottom" }); - this.settingsPage.createEl("p", { text: i18next.t("settings.embed.bake.text") }); - this.settingsPage.createEl("p", undefined, (el) => { - el.createEl("span", { - text: i18next.t("settings.embed.bake.variable.desc"), - cls: ["bake"] - }) - .createEl("ul", undefined, (ul) => { - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "{{title}}" }); - li.createEl("span", { text: i18next.t("settings.embed.bake.variable.title") }); - }); - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "{{url}}" }); - li.createEl("span", { text: i18next.t("settings.embed.bake.variable.url") }); - }); - }); - }); - - this.settingsPage.createEl("p", { - text: `⚠️ ${i18next.t("settings.embed.bake.warning")}`, - cls: ["warning", "embed"] - }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.embed.bake.textBefore.title")) - .addTextArea((text) => { - text - .setValue(embedSettings.bake?.textBefore ?? "") - .onChange(async (value) => { - embedSettings.bake!.textBefore = value; - await this.plugin.saveSettings(); - }); - }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.embed.bake.textAfter.title")) - .addTextArea((text) => { - text - .setValue(embedSettings.bake?.textAfter ?? "") - .onChange(async (value) => { - embedSettings.bake!.textAfter = value; - await this.plugin.saveSettings(); - }); - }); - } - } - } - - /** - * Render the settings page for the plugin settings (general settings, as shareKey) - */ - renderPluginSettings() { - const pluginSettings = this.settings.plugin; - - this.settingsPage.createEl("h3", { text: i18next.t("settings.plugin.head.share") }); - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.shareKey.all.title")) - .setDesc(i18next.t("settings.plugin.shareKey.all.desc")) - .addToggle((toggle) => - toggle - .setValue(pluginSettings.shareAll?.enable ?? false) - .onChange(async (value) => { - pluginSettings.shareAll = { - enable: value, - excludedFileName: pluginSettings.shareAll?.excludedFileName ?? "DRAFT", - }; - if (value) this.settings.conversion.links.internal = true; - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.plugin); - }) - ); - if (!pluginSettings.shareAll || !pluginSettings.shareAll.enable) { - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.shareKey.title")) - .setDesc(i18next.t("settings.plugin.shareKey.desc")) - .addText((text) => - text - .setPlaceholder("share") - .setValue(pluginSettings.shareKey) - .onChange(async (value) => { - pluginSettings.shareKey = value.trim(); - await this.plugin.saveSettings(); - }) - ); - } else { - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.shareKey.excludedFileName.title")) - .addText((text) => - text - .setPlaceholder("DRAFT") - .setValue(pluginSettings.shareAll?.excludedFileName ?? "DRAFT") - .onChange(async (value) => { - pluginSettings.shareAll!.excludedFileName = value.trim(); - await this.plugin.saveSettings(); - }) - ); - } - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.excludedFolder.title")) - .setDesc(i18next.t("settings.plugin.excludedFolder.desc")) - .addTextArea((textArea) => - textArea - .setPlaceholder("_assets, Archive, /^_(.*)/gi") - .setValue(pluginSettings.excludedFolder.join(", ")) - .onChange(async (value) => { - pluginSettings.excludedFolder = value - .split(/[,\n]\W*/) - .map((item) => item.trim()) - .filter((item) => item.length > 0); - await this.plugin.saveSettings(); - }) - ); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.set.title")) - .setDesc(i18next.t("settings.plugin.set.desc")) - .addText((text) => - text - .setPlaceholder("Set") - .setValue(pluginSettings.setFrontmatterKey) - .onChange(async (value) => { - pluginSettings.setFrontmatterKey = value.trim(); - await this.plugin.saveSettings(); - }) - ); - - this.settingsPage.createEl("h3", { text: i18next.t("settings.plugin.head.menu") }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.fileMenu.title")) - .setDesc(i18next.t("settings.plugin.fileMenu.desc")) - .addToggle((toggle) => - toggle - .setValue(pluginSettings.fileMenu) - .onChange(async (value) => { - pluginSettings.fileMenu = value; - await this.plugin.saveSettings(); - }) - ); - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.editorMenu.title")) - .setDesc(i18next.t("settings.plugin.editorMenu.desc")) - .addToggle((toggle) => - toggle - .setValue(pluginSettings.editorMenu) - .onChange(async (value) => { - pluginSettings.editorMenu = value; - await this.plugin.saveSettings(); - }) - ); - this.settingsPage.createEl("h3", { text: i18next.t("settings.plugin.head.copyLinks") }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.copyLink.title")) - .setDesc(i18next.t("settings.plugin.copyLink.desc")) - .addToggle((toggle) => - toggle - .setValue(pluginSettings.copyLink.enable) - .onChange(async (value) => { - pluginSettings.copyLink.enable = value; - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.plugin); - - }) - ); - - if (pluginSettings.copyLink.enable) { - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.copyLink.baselink.title")) - .setDesc(i18next.t("settings.plugin.copyLink.baselink.desc")) - .addText((text) => { - text.setPlaceholder("my_blog.com") - .setValue(pluginSettings.copyLink.links) - .onChange(async (value) => { - pluginSettings.copyLink.links = value; - await this.plugin.saveSettings(); - }); - }); - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.copyLink.linkPathRemover.title")) - .setDesc( - i18next.t("settings.plugin.copyLink.linkPathRemover.desc") - ) - .addText((text) => { - text.setPlaceholder("docs") - .setValue(pluginSettings.copyLink.removePart.join(", ")) - .onChange(async (value) => { - pluginSettings.copyLink.removePart = value.split(/[,\n]\s*/).map((item) => item.trim()).filter((item) => item.length > 0); - await this.plugin.saveSettings(); - }); - }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.copyLink.toUri.title")) - .setDesc(i18next.t("settings.plugin.copyLink.toUri.desc")) - .addToggle((toggle) => - toggle - .setValue(pluginSettings.copyLink.transform.toUri) - .onChange(async (value) => { - pluginSettings.copyLink.transform.toUri = value; - await this.plugin.saveSettings(); - }) - ); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.copyLink.slugify.title")) - .addDropdown((dropdown) => { - dropdown - .addOptions({ - disable: i18next.t("settings.plugin.copyLink.slugify.disable"), - strict: i18next.t("settings.plugin.copyLink.slugify.strict"), - lower: i18next.t("settings.plugin.copyLink.slugify.lower"), - }) - .setValue(pluginSettings.copyLink.transform.slugify as "disable" | "strict" | "lower") - .onChange(async (value) => { - pluginSettings.copyLink.transform.slugify = value as "disable" | "strict" | "lower"; - await this.plugin.saveSettings(); - }); - }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.copyLink.applyRegex.title")) - .setHeading() - .setDesc(i18next.t("settings.plugin.copyLink.applyRegex.desc")) - .addExtraButton((button) => { - button - .setIcon("plus") - .onClick(async () => { - pluginSettings.copyLink.transform.applyRegex.push({ - regex: "", - replacement: "" - }); - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.plugin); - }); - }); - - for (const apply of pluginSettings.copyLink.transform.applyRegex) { - const regex = apply.regex; - const replacement = apply.replacement; - - new Setting(this.settingsPage) - .setClass("no-display") - .addText((text) => { - text - .setPlaceholder("regex") - .setValue(regex) - .onChange(async (value) => { - apply.regex = value; - await this.plugin.saveSettings(); - }); - }) - .setClass("max-width") - .addText((text) => { - text - .setPlaceholder("replacement") - .setValue(replacement) - .onChange(async (value) => { - apply.replacement = value; - await this.plugin.saveSettings(); - }); - }) - .setClass("max-width") - .addExtraButton(button => { - button.setIcon("trash") - .onClick(async () => { - pluginSettings.copyLink.transform.applyRegex = pluginSettings.copyLink.transform.applyRegex.filter((item) => item !== apply); - await this.plugin.saveSettings(); - this.renderSettingsPage(EnumbSettingsTabId.plugin); - }); - }); - } - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.copyLink.command.desc")) - .addToggle((toggle) => - toggle - .setValue(pluginSettings.copyLink.addCmd) - .onChange(async (value) => { - pluginSettings.copyLink.addCmd = value; - await this.plugin.saveSettings(); - }) - ); - } - - this.settingsPage.createEl("h3", { text: i18next.t("settings.plugin.head.other") }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.embedEditRepo.title")) - .setDesc(i18next.t("settings.plugin.embedEditRepo.desc")) - .addToggle((toggle) => - toggle - .setValue(pluginSettings.displayModalRepoEditing) - .onChange(async (value) => { - pluginSettings.displayModalRepoEditing = value; - await this.plugin.saveSettings(); - }) - ); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.saveTab.title")) - .setDesc(i18next.t("settings.plugin.saveTab.desc")) - .addToggle((toggle) => - toggle - .setValue(pluginSettings.saveTabId ?? true) - .onChange(async (value) => { - pluginSettings.saveTabId = value; - this.settings.tabsID = value ? EnumbSettingsTabId.plugin : EnumbSettingsTabId.github; - await this.plugin.saveSettings(); - }) - ); - - - this.settingsPage.createEl("h4", { text: i18next.t("settings.plugin.head.log") }); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.logNoticeHeader.title")) - .setDesc(i18next.t("settings.plugin.logNoticeHeader.desc")) - .addToggle((toggle) => - toggle - .setValue(pluginSettings.noticeError) - .onChange(async (value) => { - pluginSettings.noticeError = value; - await this.plugin.saveSettings(); - }) - ); - - new Setting(this.settingsPage) - .setName(i18next.t("settings.plugin.dev.title")) - .setDesc(i18next.t("settings.plugin.dev.desc")) - .addToggle((toggle) => - toggle - .setValue(pluginSettings.dev ?? false) - .onChange(async (value) => { - pluginSettings.dev = value; - await this.plugin.saveSettings(); - }) - ); - } - - /** - * Render the help page - */ - renderHelp() { - this.settingsPage.createEl("h2", { - text: i18next.t("settings.help.usefulLinks.title"), - }); - this.settingsPage.appendChild(usefulLinks()); - this.settingsPage.createEl("hr"); - this.settingsPage.createEl("h2", { - text: i18next.t("settings.help.frontmatter.title"), - }); - this.settingsPage.createEl("p", { - text: i18next.t("settings.help.frontmatter.desc"), - }); - this.settingsPage - .createEl("p", { - text: i18next.t("settings.help.frontmatter.nestedKey") - }) - .createEl("code", { - text: "key.subkey: value", - }) - .createEl("span", { - text: ".", - }); - this.settingsPage - .createEl("pre", { cls: "language-yaml" }) - .createEl("code", { - text: KeyBasedOnSettings(this.settings), - cls: "language-yaml", - }); - this.settingsPage.appendChild(help(this.settings)); - this.settingsPage.createEl("h2", { - text: i18next.t("settings.help.multiRepoHelp.title"), - }); - this.settingsPage.appendChild( - multipleRepoExplained(this.settings) - ); - this.settingsPage.appendChild(supportMe()); - } - - // eslint-disable-next-line - copy(object: any) { - try { - return JSON.parse(JSON.stringify(object)); - } catch (e) { - console.log("error with stringify for", object); - } - } -} +import { + EnumbSettingsTabId, + FolderSettings, + GitHubPublisherSettings, + GithubTiersVersion, + Repository, +} from "@interfaces"; +import i18next from "i18next"; +import { App, Notice, PluginSettingTab, setIcon, Setting } from "obsidian"; +import GithubPublisherPlugin from "src/main"; +import { + help, + KeyBasedOnSettings, + multipleRepoExplained, + supportMe, + usefulLinks, +} from "src/settings/help"; +import { migrateToken } from "src/settings/migrate"; +import { + ExportModal, + ImportLoadPreset, + ImportModal, + loadAllPresets, +} from "src/settings/modals/import_export"; +import { ModalAddingNewRepository } from "src/settings/modals/manage_repo"; +import { AutoCleanPopup } from "src/settings/modals/popup"; +import { + ModalRegexFilePathName, + ModalRegexOnContents, + OverrideAttachmentsModal, +} from "src/settings/modals/regex_edition"; +import { TokenEditPath } from "src/settings/modals/token_path"; +import { + autoCleanCondition, + folderHideShowSettings, + showHideBasedOnFolder, +} from "src/settings/style"; +import { + checkRepositoryValidity, + verifyRateLimitAPI, +} from "src/utils/data_validation_test"; + +export class GithubPublisherSettingsTab extends PluginSettingTab { + plugin: GithubPublisherPlugin; + settingsPage!: HTMLElement; + branchName: string; + settings: GitHubPublisherSettings; + + constructor(app: App, plugin: GithubPublisherPlugin, branchName: string) { + super(app, plugin); + this.plugin = plugin; + this.branchName = branchName; + this.settings = plugin.settings; + } + + display(): void { + const { containerEl } = this; + containerEl.empty(); + containerEl.addClass("github-publisher"); + const defaultTabId = EnumbSettingsTabId.github; + let savedId = this.settings.tabsID ?? defaultTabId; + if (this.settings.plugin.saveTabId != undefined && !this.settings.plugin.saveTabId) { + //real false + this.settings.tabsID = defaultTabId; + savedId = defaultTabId; + this.plugin.saveSettings(); + } + + const PUBLISHER_TABS = { + "github-configuration": { + name: i18next.t("settings.github.title"), + icon: "cloud", + }, + "upload-configuration": { + name: i18next.t("settings.upload.title"), + icon: "upload", + }, + "text-conversion": { + name: i18next.t("settings.conversion.title"), + icon: "file-text", + }, + "embed-configuration": { + name: i18next.t("settings.embed.title"), + icon: "link", + }, + "plugin-settings": { + name: i18next.t("settings.plugin.title"), + icon: "gear", + }, + help: { + name: i18next.t("settings.help.title"), + icon: "info", + }, + }; + + new Setting(containerEl) + .setClass("import-export") + .addButton((button) => { + button.setButtonText(i18next.t("modals.export.title")).onClick(() => { + new ExportModal(this.app, this.plugin).open(); + }); + }) + .addButton((button) => { + button.setButtonText(i18next.t("modals.import.title")).onClick(() => { + new ImportModal(this.app, this.plugin, this.settingsPage, this).open(); + }); + }) + .addButton((button) => { + button + .setButtonText(i18next.t("modals.import.presets.title")) + .setTooltip(i18next.t("modals.import.presets.desc")) + .onClick(async () => { + const octokit = await this.plugin.reloadOctokit(); + const presetLists = await loadAllPresets(octokit.octokit, this.plugin); + new ImportLoadPreset( + this.app, + this.plugin, + presetLists, + octokit.octokit, + this + ).open(); + }); + }); + const tabBar = containerEl.createEl("nav", { + cls: "settings-tab-bar", + }); + + for (const [tabID, tabInfo] of Object.entries(PUBLISHER_TABS)) { + const tabEl = tabBar.createEl("div", { + cls: "settings-tab", + }); + const tabIcon = tabEl.createEl("div", { + cls: "settings-tab-icon", + }); + setIcon(tabIcon, tabInfo.icon); + tabEl.createEl("div", { + cls: "settings-tab-name", + text: tabInfo.name, + }); + if (tabID === savedId) tabEl.addClass("settings-tab-active"); + + tabEl.addEventListener("click", async () => { + // @ts-ignore + for (const tabEl of tabBar.children) tabEl.removeClass("settings-tab-active"); + + tabEl.addClass("settings-tab-active"); + this.renderSettingsPage(tabID); + }); + } + this.settingsPage = containerEl.createEl("div", { + cls: "settings-tab-page", + }); + this.renderSettingsPage(savedId); + } + + /** + * Render the settings tab + * @param {string} tabId - to know which tab to render + */ + async renderSettingsPage(tabId: string | EnumbSettingsTabId) { + if (this.settings.plugin.saveTabId || this.settings.plugin.saveTabId === undefined) { + this.settings.tabsID = tabId as EnumbSettingsTabId; + await this.plugin.saveSettings(); + } + this.settingsPage.empty(); + switch (tabId) { + case "github-configuration": + this.renderGithubConfiguration(); + break; + case "upload-configuration": + this.renderUploadConfiguration(); + break; + case "text-conversion": + this.renderTextConversion(); + break; + case "embed-configuration": + this.renderEmbedConfiguration(); + break; + case "plugin-settings": + this.renderPluginSettings(); + break; + case "help": + this.renderHelp(); + break; + } + } + + /** + * Render the github configuration tab + */ + renderGithubConfiguration() { + const githubSettings = this.settings.github; + new Setting(this.settingsPage) + .setName(i18next.t("settings.github.apiType.title")) + .setDesc(i18next.t("settings.github.apiType.desc")) + .addDropdown((dropdown) => { + dropdown + .addOption( + GithubTiersVersion.free, + i18next.t("settings.github.apiType.dropdown.free") + ) + .addOption( + GithubTiersVersion.entreprise, + i18next.t("settings.github.apiType.dropdown.enterprise") + ) + .setValue(githubSettings.api.tiersForApi) + .onChange(async (value) => { + githubSettings.api.tiersForApi = value as GithubTiersVersion; + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.github); + }); + }); + if (githubSettings.api.tiersForApi === GithubTiersVersion.entreprise) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.github.apiType.hostname.title")) + .setDesc(i18next.t("settings.github.apiType.hostname.desc")) + .addText((text) => + text + .setPlaceholder("https://github.mycompany.com") + .setValue(githubSettings.api.hostname) + .onChange(async (value) => { + githubSettings.api.hostname = value.trim(); + await this.plugin.saveSettings(); + }) + ); + } + + new Setting(this.settingsPage) + .setName(i18next.t("settings.github.username.title")) + .setDesc(i18next.t("settings.github.username.desc")) + .addText((text) => + text + .setPlaceholder(i18next.t("settings.github.username.title")) + .setValue(githubSettings.user) + .onChange(async (value) => { + githubSettings.user = value.trim(); + await this.plugin.saveSettings(); + }) + ); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.github.repoName.title")) + .setDesc(i18next.t("settings.github.repoName.desc")) + .addText((text) => + text + .setPlaceholder(i18next.t("settings.github.repoName.placeholder")) + .setValue(githubSettings.repo) + .onChange(async (value) => { + githubSettings.repo = value.trim(); + await this.plugin.saveSettings(); + }) + ); + const desc_ghToken = document.createDocumentFragment(); + desc_ghToken.createEl("span", undefined, (span) => { + span.innerText = i18next.t("settings.github.ghToken.desc"); + span.createEl("a", undefined, (link) => { + link.innerText = `${i18next.t("common.here")}.`; + link.href = "https://github.com/settings/tokens/new?scopes=repo,workflow"; + }); + }); + const tokenSettings = new Setting(this.settingsPage) + .setName(i18next.t("common.ghToken")) + .setDesc(desc_ghToken) + .addText(async (text) => { + const decryptedToken: string = await this.plugin.loadToken(); + text + .setPlaceholder("ghp_15457498545647987987112184") + .setValue(decryptedToken) + .onChange(async (value) => { + if (value.trim().length === 0) { + tokenSettings.controlEl.addClass("error"); + new Notice(i18next.t("settings.github.ghToken.error")); + } else { + tokenSettings.controlEl.removeClass("error"); + await migrateToken(this.plugin, value.trim()); + } + await this.plugin.saveSettings(); + }); + }) + .addExtraButton((button) => { + button + .setIcon("edit") + .setTooltip(i18next.t("settings.github.ghToken.button.tooltip")) + .onClick(async () => { + const token = await this.plugin.loadToken(); + new TokenEditPath(this.app, this.plugin, token).open(); + await this.plugin.saveSettings(); + }); + }); + new Setting(this.settingsPage) + .setName(i18next.t("settings.github.branch.title")) + .setDesc(i18next.t("settings.github.branch.desc")) + .addText((text) => + text + .setPlaceholder("main") + .setValue(githubSettings.branch) + .onChange(async (value) => { + githubSettings.branch = value.trim(); + await this.plugin.saveSettings(); + }) + ); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.github.automaticallyMergePR")) + .addToggle((toggle) => + toggle.setValue(githubSettings.automaticallyMergePR).onChange(async (value) => { + githubSettings.automaticallyMergePR = value; + await this.plugin.saveSettings(); + }) + ); + new Setting(this.settingsPage) + .setName(i18next.t("settings.github.dryRun.enable.title")) + .setDesc(i18next.t("settings.github.dryRun.enable.desc")) + .addToggle((toggle) => + toggle.setValue(githubSettings.dryRun.enable).onChange(async (value) => { + githubSettings.dryRun.enable = value; + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.github); + }) + ); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.github.dryRun.folder.title")) + .setDesc(i18next.t("settings.github.dryRun.folder.desc")) + .addText((text) => + text + .setPlaceholder("github-publisher") + .setValue(githubSettings.dryRun.folderName) + .onChange(async (value) => { + githubSettings.dryRun.folderName = value.trim(); + await this.plugin.saveSettings(); + }) + ); + + new Setting(this.settingsPage) + .setClass("no-display") + .addButton((button) => + button + .setButtonText(i18next.t("settings.github.testConnection")) + .setClass("connect-button") + .onClick(async () => { + const octokit = await this.plugin.reloadOctokit(); + this.settings.github.verifiedRepo = await checkRepositoryValidity( + octokit, + null, + null + ); + this.settings.github.rateLimit = await verifyRateLimitAPI( + octokit.octokit, + this.settings + ); + await this.plugin.saveSettings(); + }) + ) + .addButton((button) => + button + .setButtonText(i18next.t("settings.github.smartRepo.button")) + .onClick(async () => { + const repository: Repository[] = this.copy( + this.settings.github?.otherRepo ?? [] + ); + new ModalAddingNewRepository( + this.app, + this.settings, + this.branchName, + this.plugin, + repository, + async (result) => { + this.settings.github.otherRepo = result; + await this.plugin.saveSettings(); + this.plugin.reloadCommands(); + } + ).open(); + }) + ); + this.settingsPage.createEl("h3", { text: "GitHub Workflow" }); + new Setting(this.settingsPage) + .setName(i18next.t("settings.githubWorkflow.prRequest.title")) + .setDesc(i18next.t("settings.githubWorkflow.prRequest.desc")) + .addText((text) => + text + .setPlaceholder("[PUBLISHER] MERGE") + .setValue(githubSettings.workflow.commitMessage) + .onChange(async (value) => { + if (value.trim().length === 0) { + value = "[PUBLISHER] MERGE"; + new Notice(i18next.t("settings.githubWorkflow.prRequest.error")); + } + githubSettings.workflow.commitMessage = value; + await this.plugin.saveSettings(); + }) + ); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.githubWorkflow.githubAction.title")) + .setDesc(i18next.t("settings.githubWorkflow.githubAction.desc")) + .addText((text) => { + text + .setPlaceholder("ci") + .setValue(githubSettings.workflow.name) + .onChange(async (value) => { + if (value.length > 0) { + value = value.trim(); + const yamlEndings = [".yml", ".yaml"]; + if (!yamlEndings.some((ending) => value.endsWith(ending))) { + value += yamlEndings[0]; + } + } + githubSettings.workflow.name = value; + await this.plugin.saveSettings(); + }); + }); + } + + /** + * Render the settings tab for the upload configuration + */ + renderUploadConfiguration() { + const uploadSettings = this.settings.upload; + new Setting(this.settingsPage) + .setName(i18next.t("settings.upload.folderBehavior.title")) + .setDesc(i18next.t("settings.upload.folderBehavior.desc")) + .addDropdown((dropDown) => { + dropDown + .addOptions({ + fixed: i18next.t("settings.upload.folderBehavior.fixedFolder"), + yaml: i18next.t("settings.upload.folderBehavior.yaml"), + obsidian: i18next.t("settings.upload.folderBehavior.obsidianPath"), + }) + .setValue(uploadSettings.behavior) + .onChange(async (value: string) => { + uploadSettings.behavior = value as FolderSettings; + await folderHideShowSettings( + frontmatterKeySettings, + rootFolderSettings, + autoCleanSetting, + value, + this.plugin + ); + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.upload); + }); + }); + + const defaultFolder = + uploadSettings.behavior === FolderSettings.yaml + ? { + desc: i18next.t("settings.upload.defaultFolder.desc"), + title: i18next.t("settings.upload.defaultFolder.title"), + } + : { + desc: i18next.t("settings.upload.rootFolder.other"), + title: i18next.t("settings.upload.rootFolder.title"), + }; + + new Setting(this.settingsPage) + .setName(defaultFolder.title) + .setDesc(defaultFolder.desc) + .addText((text) => { + text + .setPlaceholder(i18next.t("settings.upload.defaultFolder.placeholder")) + .setValue(uploadSettings.defaultName) + .onChange(async (value) => { + uploadSettings.defaultName = value.replace(/\/$/, ""); + await autoCleanCondition( + value, + autoCleanSetting, + this.plugin, + "defaultName", + this + ); + await this.plugin.saveSettings(); + }); + }); + + const frontmatterKeySettings = new Setting(this.settingsPage) + .setName(i18next.t("settings.upload.frontmatterKey.title")) + .setDesc(i18next.t("settings.upload.frontmatterKey.desc")) + .addText((text) => { + text + .setPlaceholder(i18next.t("settings.upload.frontmatterKey.placeholder")) + .setValue(uploadSettings.yamlFolderKey) + .onChange(async (value) => { + uploadSettings.yamlFolderKey = value.trim(); + await this.plugin.saveSettings(); + }); + }); + const rootFolderSettings = new Setting(this.settingsPage) + .setName(i18next.t("settings.upload.rootFolder.title")) + .setDesc(i18next.t("settings.upload.rootFolder.desc")) + .addText((text) => { + text + .setPlaceholder("docs") + .setValue(uploadSettings.rootFolder) + .onChange(async (value) => { + uploadSettings.rootFolder = value.replace(/\/$/, ""); + await autoCleanCondition( + value, + autoCleanSetting, + this.plugin, + "rootFolder", + this + ); + await this.plugin.saveSettings(); + }); + }); + const frontmatterTitleSet = new Setting(this.settingsPage) + .setName(i18next.t("settings.upload.useFrontmatterTitle.title")) + .setDesc(i18next.t("settings.upload.useFrontmatterTitle.desc")) + .setClass("title") + .addToggle((toggle) => { + toggle + .setValue(uploadSettings.frontmatterTitle.enable) + .onChange(async (value) => { + uploadSettings.frontmatterTitle.enable = value; + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.upload); + }); + }); + if (uploadSettings.frontmatterTitle.enable) { + frontmatterTitleSet.addText((text) => { + text + .setPlaceholder("title") + .setValue(uploadSettings.frontmatterTitle.key) + .onChange(async (value) => { + uploadSettings.frontmatterTitle.key = value.trim(); + await this.plugin.saveSettings(); + }); + }); + } + + const desc = + uploadSettings.behavior === FolderSettings.fixed + ? i18next.t("settings.upload.regexFilePathTitle.title.titleOnly") + : i18next.t("settings.upload.regexFilePathTitle.title.FolderPathTitle"); + + new Setting(this.settingsPage) + .setName(desc) + .setDesc(i18next.t("settings.upload.regexFilePathTitle.desc")) + .addButton((button) => { + button.setIcon("pencil").onClick(async () => { + let allRegex = uploadSettings.replaceTitle; + if (uploadSettings.behavior !== FolderSettings.fixed) { + allRegex = allRegex.concat(uploadSettings.replacePath); + } + new ModalRegexFilePathName( + this.app, + this.settings, + this.copy(allRegex), + async (result) => { + uploadSettings.replacePath = result.filter((title) => { + return title.type === "path"; + }); + uploadSettings.replaceTitle = result.filter((title) => { + return title.type === "title"; + }); + await this.plugin.saveSettings(); + } + ).open(); + }); + }); + + const folderNoteSettings = new Setting(this.settingsPage) + .setName(i18next.t("settings.conversion.links.folderNote.title")) + .setDesc(i18next.t("settings.conversion.links.folderNote.desc")) + .addToggle((toggle) => { + toggle.setValue(uploadSettings.folderNote.enable).onChange(async (value) => { + uploadSettings.folderNote.enable = value; + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.upload); + }); + }); + + if (uploadSettings.folderNote.enable) { + folderNoteSettings.addText((text) => { + text + .setPlaceholder("folderNote") + .setValue(uploadSettings.folderNote.rename) + .onChange(async (value) => { + uploadSettings.folderNote.rename = value; + await this.plugin.saveSettings(); + }); + }); + new Setting(this.settingsPage) + .setName(i18next.t("settings.upload.folderNote.addTitle.title")) + .addToggle((toggle) => { + toggle + .setValue(uploadSettings.folderNote.addTitle.enable) + .onChange(async (value) => { + uploadSettings.folderNote.addTitle.enable = value; + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.upload); + }); + }); + if (uploadSettings.folderNote.addTitle.enable) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.upload.folderNote.addTitle.key")) + .addText((text) => { + text + .setPlaceholder("title") + .setValue(uploadSettings.folderNote.addTitle.key) + .onChange(async (value) => { + uploadSettings.folderNote.addTitle.key = value; + await this.plugin.saveSettings(); + }); + }); + } + } + + showHideBasedOnFolder( + this.settings, + frontmatterKeySettings, + rootFolderSettings, + folderNoteSettings + ); + + if (this.app.plugins.getPlugin("metadata-extractor")) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.githubWorkflow.useMetadataExtractor.title")) + .setDesc(i18next.t("settings.githubWorkflow.useMetadataExtractor.desc")) + .addText((text) => { + text + .setPlaceholder("docs/_assets/metadata") + .setValue(uploadSettings.metadataExtractorPath) + .onChange(async (value) => { + uploadSettings.metadataExtractorPath = value.trim(); + await this.plugin.saveSettings(); + }); + }); + } + + const autoCleanSetting = new Setting(this.settingsPage) + .setName(i18next.t("settings.githubWorkflow.autoCleanUp.title")) + .setDesc(i18next.t("settings.githubWorkflow.autoCleanUp.desc")) + .addToggle((toggle) => { + toggle.setValue(uploadSettings.autoclean.enable).onChange(async (value) => { + if ( + value && + ((this.settings.upload.behavior === "yaml" && + this.settings.upload.defaultName.length === 0) || + this.settings.upload.rootFolder.length === 0) + ) + new AutoCleanPopup(this.app, this.settings, (result) => { + uploadSettings.autoclean.enable = result; + this.renderSettingsPage(EnumbSettingsTabId.upload); + }).open(); + else uploadSettings.autoclean.enable = value; + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.upload); + this.plugin.cleanOldCommands(); + await this.plugin.chargeAllCommands(null, this.plugin); + }); + }); + if (uploadSettings.autoclean.enable) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.githubWorkflow.excludedFiles.title")) + .setDesc(i18next.t("settings.githubWorkflow.excludedFiles.desc")) + .addTextArea((textArea) => { + textArea + .setPlaceholder("docs/assets/js, docs/assets/logo, /\\.js$/") + .setValue(uploadSettings.autoclean.excluded.join(", ")) + .onChange(async (value) => { + uploadSettings.autoclean.excluded = value + .split(/[,\n]/) + .map((item) => item.trim()) + .filter((item) => item.length > 0); + await this.plugin.saveSettings(); + }); + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.githubWorkflow.includeAttachments.title")) + .setDesc(i18next.t("settings.githubWorkflow.includeAttachments.desc")) + .addToggle((toggle) => { + toggle + .setValue(uploadSettings.autoclean.includeAttachments) + .onChange(async (value) => { + uploadSettings.autoclean.includeAttachments = value; + await this.plugin.saveSettings(); + }); + }); + } + + folderHideShowSettings( + frontmatterKeySettings, + rootFolderSettings, + autoCleanSetting, + uploadSettings.behavior, + this.plugin + ); + } + + /** + * Render the settings page for the text conversion parameters + */ + renderTextConversion() { + const textSettings = this.settings.conversion; + this.settingsPage.createEl("p", { + text: i18next.t("settings.conversion.desc"), + }); + + this.settingsPage.createEl("h5", { + text: i18next.t("settings.conversion.links.title"), + }); + this.settingsPage.createEl("p", { + text: i18next.t("settings.conversion.links.desc"), + }); + + const shareAll = this.settings.plugin.shareAll?.enable + ? ` ${i18next.t("settings.conversion.links.internals.shareAll")}` + : ""; + const internalLinksDesc = document.createDocumentFragment(); + internalLinksDesc.createEl("p", { + text: i18next.t("settings.conversion.links.internals.desc") + shareAll, + cls: "no-margin", + }); + internalLinksDesc.createEl("p", { + text: i18next.t("settings.conversion.links.internals.dataview"), + cls: "no-margin", + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.conversion.links.internals.title")) + .setDesc(internalLinksDesc) + .addToggle((toggle) => { + toggle.setValue(textSettings.links.internal).onChange(async (value) => { + textSettings.links.internal = value; + if (this.settings.plugin.shareAll?.enable) { + textSettings.links.unshared = true; + } + await this.plugin.saveSettings(); + this.renderSettingsPage("text-conversion"); + }); + }); + + if (textSettings.links.internal && !this.settings.plugin.shareAll?.enable) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.conversion.links.nonShared.title")) + .setDesc(i18next.t("settings.conversion.links.nonShared.desc")) + .addToggle((toggle) => { + toggle.setValue(textSettings.links.unshared).onChange(async (value) => { + textSettings.links.unshared = value; + await this.plugin.saveSettings(); + }); + }); + } + + new Setting(this.settingsPage) + .setName(i18next.t("settings.conversion.links.wikilinks.title")) + .setDesc(i18next.t("settings.conversion.links.wikilinks.desc")) + .addToggle((toggle) => { + toggle.setValue(textSettings.links.wiki).onChange(async (value) => { + textSettings.links.wiki = value; + await this.plugin.saveSettings(); + this.renderSettingsPage("text-conversion"); + }); + }); + const slugifySetting = + typeof textSettings.links.slugify == "boolean" + ? textSettings.links.slugify + ? "strict" + : "disable" + : textSettings.links.slugify; + if (textSettings.links.wiki || textSettings.links.internal) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.conversion.links.slugify.title")) + .setDesc(i18next.t("settings.conversion.links.slugify.desc")) + .addDropdown((dropdown) => { + dropdown + .addOptions({ + disable: i18next.t("settings.conversion.links.slugify.disable"), + strict: i18next.t("settings.conversion.links.slugify.strict"), + lower: i18next.t("settings.conversion.links.slugify.lower"), + }) + .setValue(slugifySetting) + .onChange(async (value) => { + textSettings.links.slugify = ["disable", "strict", "lower"].includes(value) + ? (value as "disable" | "strict" | "lower") + : "disable"; + await this.plugin.saveSettings(); + }); + }); + } + + this.settingsPage.createEl("h5", { + text: i18next.t("settings.conversion.sectionTitle"), + }); + new Setting(this.settingsPage) + .setName(i18next.t("settings.conversion.hardBreak.title")) + .setDesc(i18next.t("settings.conversion.hardBreak.desc")) + .addToggle((toggle) => { + toggle.setValue(textSettings.hardbreak).onChange(async (value) => { + textSettings.hardbreak = value; + await this.plugin.saveSettings(); + }); + }); + const isDataviewEnabled = this.app.plugins.plugins.dataview; + new Setting(this.settingsPage) + .setName(i18next.t("settings.conversion.dataview.title")) + .setDesc(i18next.t("settings.conversion.dataview.desc")) + .addToggle((toggle) => { + toggle + .setValue( + textSettings.dataview && isDataviewEnabled && textSettings.links.internal + ) + .setDisabled(!isDataviewEnabled || !textSettings.links.internal) + .onChange(async (value) => { + textSettings.dataview = value; + await this.plugin.saveSettings(); + }); + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.regexReplacing.modal.title.text")) + .setDesc(i18next.t("settings.regexReplacing.modal.desc")) + .addButton((button) => { + button.setIcon("pencil").onClick(async () => { + new ModalRegexOnContents(this.app, this.copy(this.settings), async (result) => { + this.settings.conversion.censorText = result.conversion.censorText; + await this.plugin.saveSettings(); + }).open(); + }); + }); + + this.settingsPage.createEl("h5", { text: "Tags" }); + new Setting(this.settingsPage) + .setName(i18next.t("settings.conversion.tags.inlineTags.title")) + .setDesc(i18next.t("settings.conversion.tags.inlineTags.desc")) + .addToggle((toggle) => { + toggle.setValue(textSettings.tags.inline).onChange(async (value) => { + textSettings.tags.inline = value; + await this.plugin.saveSettings(); + }); + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.conversion.tags.title")) + .setDesc(i18next.t("settings.conversion.tags.desc")) + .addTextArea((text) => { + text.inputEl.addClass("mid-height"); + text + .setPlaceholder("field_name") + .setValue(textSettings.tags.fields.join(",")) + .onChange(async (value) => { + textSettings.tags.fields = value + .split(/[,\n]\W*/) + .map((item) => item.trim()) + .filter((item) => item.length > 0); + await this.plugin.saveSettings(); + }); + }); + new Setting(this.settingsPage) + .setName(i18next.t("settings.conversion.tags.exclude.title")) + .setDesc(i18next.t("settings.conversion.tags.exclude.desc")) + .addTextArea((text) => { + text + .setPlaceholder(i18next.t("settings.conversion.tags.exclude.placeholder")) + .setValue(textSettings.tags.exclude.join(",")) + .onChange(async (value) => { + textSettings.tags.exclude = value + .split(/[,\n]\W*/) + .map((item) => item.trim()) + .filter((item) => item.length > 0); + await this.plugin.saveSettings(); + }); + }); + } + + /** + * Render the settings page for the embeds settings + */ + async renderEmbedConfiguration() { + this.settingsPage.empty(); + const embedSettings = this.settings.embed; + new Setting(this.settingsPage) + .setName(i18next.t("settings.embed.sendSimpleLinks.title")) + .setDesc(i18next.t("settings.embed.sendSimpleLinks.desc")) + .addToggle((toggle) => { + toggle.setValue(embedSettings.sendSimpleLinks).onChange(async (value) => { + embedSettings.sendSimpleLinks = value; + await this.plugin.saveSettings(); + await this.renderEmbedConfiguration(); + }); + }); + this.settingsPage.createEl("h5", { + text: i18next.t("settings.embed.attachment"), + cls: "center", + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.embed.transferImage.title")) + .addToggle((toggle) => { + toggle.setValue(embedSettings.attachments).onChange(async (value) => { + embedSettings.attachments = value; + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.embed); + }); + }); + + if (embedSettings.attachments) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.embed.imagePath.title")) + .setDesc(i18next.t("settings.embed.imagePath.desc")) + .addToggle((toggle) => { + toggle + .setValue(embedSettings.useObsidianFolder ?? false) + .onChange(async (value) => { + embedSettings.useObsidianFolder = value; + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.embed); + }); + }); + if (!embedSettings.useObsidianFolder) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.embed.defaultImageFolder.title")) + .setDesc(i18next.t("settings.embed.defaultImageFolder.desc")) + .addText((text) => { + text + .setPlaceholder("docs/images") + .setValue(embedSettings.folder) + .onChange(async (value) => { + embedSettings.folder = value.replace(/\/$/, ""); + await this.plugin.saveSettings(); + }); + }); + } + + new Setting(this.settingsPage) + .setName(i18next.t("settings.embed.overrides.modal.title")) + .setDesc(i18next.t("settings.embed.overrides.desc")) + .addButton((button) => { + button.setIcon("pencil").onClick(async () => { + new OverrideAttachmentsModal( + this.app, + this.settings, + this.copy(embedSettings.overrideAttachments), + async (result) => { + embedSettings.overrideAttachments = result; + await this.plugin.saveSettings(); + } + ).open(); + }); + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.embeds.unHandledObsidianExt.title")) + .setDesc(i18next.t("settings.embeds.unHandledObsidianExt.desc")) + .addTextArea((text) => { + text + .setPlaceholder("py, mdx") + .setValue(embedSettings.unHandledObsidianExt.join(", ")) + .onChange(async (value) => { + embedSettings.unHandledObsidianExt = value + .split(/[,\n]\W*/) + .map((item) => item.trim()) + .filter((item) => item.length > 0); + await this.plugin.saveSettings(); + }); + }); + } + + new Setting(this.settingsPage) + .setName(i18next.t("settings.embed.transferMetaFile.title")) + .setDesc(i18next.t("settings.embed.transferMetaFile.desc")) + .addTextArea((text) => { + text + .setPlaceholder("banner") + .setValue(embedSettings.keySendFile.join(", ")) + .onChange(async (value) => { + embedSettings.keySendFile = value + .split(/[,\n]\W*/) + .map((item) => item.trim()) + .filter((item) => item.length > 0); + await this.plugin.saveSettings(); + }); + }); + + this.settingsPage.createEl("h5", { + text: i18next.t("settings.embed.notes"), + cls: "center", + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.embed.transferNotes.title")) + .setDesc(i18next.t("settings.embed.transferNotes.desc")) + .addToggle((toggle) => { + toggle.setValue(embedSettings.notes).onChange(async (value) => { + embedSettings.notes = value; + await this.plugin.saveSettings(); + await this.renderEmbedConfiguration(); + }); + }); + + if (embedSettings.notes) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.embed.links.title")) + .setDesc(i18next.t("settings.embed.links.desc")) + .addDropdown((dropdown) => { + dropdown + .addOption("keep", i18next.t("settings.embed.links.dp.keep")) + .addOption("remove", i18next.t("settings.embed.links.dp.remove")) + .addOption("links", i18next.t("settings.embed.links.dp.links")) + .addOption("bake", i18next.t("settings.embed.links.dp.bake")) + .setValue(embedSettings.convertEmbedToLinks ?? "keep") + .onChange(async (value) => { + embedSettings.convertEmbedToLinks = value as + | "keep" + | "remove" + | "links" + | "bake"; + await this.plugin.saveSettings(); + await this.renderEmbedConfiguration(); + }); + }); + + if (embedSettings.convertEmbedToLinks === "links") { + new Setting(this.settingsPage) + .setName(i18next.t("settings.embed.char.title")) + .setDesc(i18next.t("settings.embed.char.desc")) + .addText((text) => { + text + .setPlaceholder("->") + .setValue(embedSettings.charConvert ?? "->") + .onChange(async (value) => { + embedSettings.charConvert = value; + await this.plugin.saveSettings(); + }); + }); + } else if (embedSettings.convertEmbedToLinks === "bake") { + if (!embedSettings.bake) { + embedSettings.bake = { + textBefore: "", + textAfter: "", + }; + await this.plugin.saveSettings(); + } + await this.plugin.saveSettings(); + this.settingsPage.createEl("h5", { + text: i18next.t("settings.embed.bake.title"), + cls: "border-bottom", + }); + this.settingsPage.createEl("p", { text: i18next.t("settings.embed.bake.text") }); + this.settingsPage.createEl("p", undefined, (el) => { + el.createEl("span", { + text: i18next.t("settings.embed.bake.variable.desc"), + cls: ["bake"], + }).createEl("ul", undefined, (ul) => { + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "{{title}}" }); + li.createEl("span", { + text: i18next.t("settings.embed.bake.variable.title"), + }); + }); + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "{{url}}" }); + li.createEl("span", { + text: i18next.t("settings.embed.bake.variable.url"), + }); + }); + }); + }); + + this.settingsPage.createEl("p", { + text: `⚠️ ${i18next.t("settings.embed.bake.warning")}`, + cls: ["warning", "embed"], + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.embed.bake.textBefore.title")) + .addTextArea((text) => { + text + .setValue(embedSettings.bake?.textBefore ?? "") + .onChange(async (value) => { + embedSettings.bake!.textBefore = value; + await this.plugin.saveSettings(); + }); + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.embed.bake.textAfter.title")) + .addTextArea((text) => { + text.setValue(embedSettings.bake?.textAfter ?? "").onChange(async (value) => { + embedSettings.bake!.textAfter = value; + await this.plugin.saveSettings(); + }); + }); + } + } + } + + /** + * Render the settings page for the plugin settings (general settings, as shareKey) + */ + renderPluginSettings() { + const pluginSettings = this.settings.plugin; + + this.settingsPage.createEl("h3", { text: i18next.t("settings.plugin.head.share") }); + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.shareKey.all.title")) + .setDesc(i18next.t("settings.plugin.shareKey.all.desc")) + .addToggle((toggle) => + toggle + .setValue(pluginSettings.shareAll?.enable ?? false) + .onChange(async (value) => { + pluginSettings.shareAll = { + enable: value, + excludedFileName: pluginSettings.shareAll?.excludedFileName ?? "DRAFT", + }; + if (value) this.settings.conversion.links.internal = true; + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.plugin); + }) + ); + if (!pluginSettings.shareAll || !pluginSettings.shareAll.enable) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.shareKey.title")) + .setDesc(i18next.t("settings.plugin.shareKey.desc")) + .addText((text) => + text + .setPlaceholder("share") + .setValue(pluginSettings.shareKey) + .onChange(async (value) => { + pluginSettings.shareKey = value.trim(); + await this.plugin.saveSettings(); + }) + ); + } else { + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.shareKey.excludedFileName.title")) + .addText((text) => + text + .setPlaceholder("DRAFT") + .setValue(pluginSettings.shareAll?.excludedFileName ?? "DRAFT") + .onChange(async (value) => { + pluginSettings.shareAll!.excludedFileName = value.trim(); + await this.plugin.saveSettings(); + }) + ); + } + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.excludedFolder.title")) + .setDesc(i18next.t("settings.plugin.excludedFolder.desc")) + .addTextArea((textArea) => + textArea + .setPlaceholder("_assets, Archive, /^_(.*)/gi") + .setValue(pluginSettings.excludedFolder.join(", ")) + .onChange(async (value) => { + pluginSettings.excludedFolder = value + .split(/[,\n]\W*/) + .map((item) => item.trim()) + .filter((item) => item.length > 0); + await this.plugin.saveSettings(); + }) + ); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.set.title")) + .setDesc(i18next.t("settings.plugin.set.desc")) + .addText((text) => + text + .setPlaceholder("Set") + .setValue(pluginSettings.setFrontmatterKey) + .onChange(async (value) => { + pluginSettings.setFrontmatterKey = value.trim(); + await this.plugin.saveSettings(); + }) + ); + + this.settingsPage.createEl("h3", { text: i18next.t("settings.plugin.head.menu") }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.fileMenu.title")) + .setDesc(i18next.t("settings.plugin.fileMenu.desc")) + .addToggle((toggle) => + toggle.setValue(pluginSettings.fileMenu).onChange(async (value) => { + pluginSettings.fileMenu = value; + await this.plugin.saveSettings(); + }) + ); + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.editorMenu.title")) + .setDesc(i18next.t("settings.plugin.editorMenu.desc")) + .addToggle((toggle) => + toggle.setValue(pluginSettings.editorMenu).onChange(async (value) => { + pluginSettings.editorMenu = value; + await this.plugin.saveSettings(); + }) + ); + this.settingsPage.createEl("h3", { + text: i18next.t("settings.plugin.head.copyLinks"), + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.copyLink.title")) + .setDesc(i18next.t("settings.plugin.copyLink.desc")) + .addToggle((toggle) => + toggle.setValue(pluginSettings.copyLink.enable).onChange(async (value) => { + pluginSettings.copyLink.enable = value; + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.plugin); + }) + ); + + if (pluginSettings.copyLink.enable) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.copyLink.baselink.title")) + .setDesc(i18next.t("settings.plugin.copyLink.baselink.desc")) + .addText((text) => { + text + .setPlaceholder("my_blog.com") + .setValue(pluginSettings.copyLink.links) + .onChange(async (value) => { + pluginSettings.copyLink.links = value; + await this.plugin.saveSettings(); + }); + }); + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.copyLink.linkPathRemover.title")) + .setDesc(i18next.t("settings.plugin.copyLink.linkPathRemover.desc")) + .addText((text) => { + text + .setPlaceholder("docs") + .setValue(pluginSettings.copyLink.removePart.join(", ")) + .onChange(async (value) => { + pluginSettings.copyLink.removePart = value + .split(/[,\n]\s*/) + .map((item) => item.trim()) + .filter((item) => item.length > 0); + await this.plugin.saveSettings(); + }); + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.copyLink.toUri.title")) + .setDesc(i18next.t("settings.plugin.copyLink.toUri.desc")) + .addToggle((toggle) => + toggle + .setValue(pluginSettings.copyLink.transform.toUri) + .onChange(async (value) => { + pluginSettings.copyLink.transform.toUri = value; + await this.plugin.saveSettings(); + }) + ); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.copyLink.slugify.title")) + .addDropdown((dropdown) => { + dropdown + .addOptions({ + disable: i18next.t("settings.plugin.copyLink.slugify.disable"), + strict: i18next.t("settings.plugin.copyLink.slugify.strict"), + lower: i18next.t("settings.plugin.copyLink.slugify.lower"), + }) + .setValue( + pluginSettings.copyLink.transform.slugify as "disable" | "strict" | "lower" + ) + .onChange(async (value) => { + pluginSettings.copyLink.transform.slugify = value as + | "disable" + | "strict" + | "lower"; + await this.plugin.saveSettings(); + }); + }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.copyLink.applyRegex.title")) + .setHeading() + .setDesc(i18next.t("settings.plugin.copyLink.applyRegex.desc")) + .addExtraButton((button) => { + button.setIcon("plus").onClick(async () => { + pluginSettings.copyLink.transform.applyRegex.push({ + regex: "", + replacement: "", + }); + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.plugin); + }); + }); + + for (const apply of pluginSettings.copyLink.transform.applyRegex) { + const regex = apply.regex; + const replacement = apply.replacement; + + new Setting(this.settingsPage) + .setClass("no-display") + .addText((text) => { + text + .setPlaceholder("regex") + .setValue(regex) + .onChange(async (value) => { + apply.regex = value; + await this.plugin.saveSettings(); + }); + }) + .setClass("max-width") + .addText((text) => { + text + .setPlaceholder("replacement") + .setValue(replacement) + .onChange(async (value) => { + apply.replacement = value; + await this.plugin.saveSettings(); + }); + }) + .setClass("max-width") + .addExtraButton((button) => { + button.setIcon("trash").onClick(async () => { + pluginSettings.copyLink.transform.applyRegex = + pluginSettings.copyLink.transform.applyRegex.filter( + (item) => item !== apply + ); + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.plugin); + }); + }); + } + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.copyLink.command.desc")) + .addToggle((toggle) => + toggle.setValue(pluginSettings.copyLink.addCmd).onChange(async (value) => { + pluginSettings.copyLink.addCmd = value; + await this.plugin.saveSettings(); + }) + ); + } + + this.settingsPage.createEl("h3", { text: i18next.t("settings.plugin.head.other") }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.embedEditRepo.title")) + .setDesc(i18next.t("settings.plugin.embedEditRepo.desc")) + .addToggle((toggle) => + toggle + .setValue(pluginSettings.displayModalRepoEditing) + .onChange(async (value) => { + pluginSettings.displayModalRepoEditing = value; + await this.plugin.saveSettings(); + }) + ); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.saveTab.title")) + .setDesc(i18next.t("settings.plugin.saveTab.desc")) + .addToggle((toggle) => + toggle.setValue(pluginSettings.saveTabId ?? true).onChange(async (value) => { + pluginSettings.saveTabId = value; + this.settings.tabsID = value + ? EnumbSettingsTabId.plugin + : EnumbSettingsTabId.github; + await this.plugin.saveSettings(); + }) + ); + + this.settingsPage.createEl("h4", { text: i18next.t("settings.plugin.head.log") }); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.logNoticeHeader.title")) + .setDesc(i18next.t("settings.plugin.logNoticeHeader.desc")) + .addToggle((toggle) => + toggle.setValue(pluginSettings.noticeError).onChange(async (value) => { + pluginSettings.noticeError = value; + await this.plugin.saveSettings(); + }) + ); + + new Setting(this.settingsPage) + .setName(i18next.t("settings.plugin.dev.title")) + .setDesc(i18next.t("settings.plugin.dev.desc")) + .addToggle((toggle) => + toggle.setValue(pluginSettings.dev ?? false).onChange(async (value) => { + pluginSettings.dev = value; + await this.plugin.saveSettings(); + }) + ); + } + + /** + * Render the help page + */ + renderHelp() { + this.settingsPage.createEl("h2", { + text: i18next.t("settings.help.usefulLinks.title"), + }); + this.settingsPage.appendChild(usefulLinks()); + this.settingsPage.createEl("hr"); + this.settingsPage.createEl("h2", { + text: i18next.t("settings.help.frontmatter.title"), + }); + this.settingsPage.createEl("p", { + text: i18next.t("settings.help.frontmatter.desc"), + }); + this.settingsPage + .createEl("p", { + text: i18next.t("settings.help.frontmatter.nestedKey"), + }) + .createEl("code", { + text: "key.subkey: value", + }) + .createEl("span", { + text: ".", + }); + this.settingsPage.createEl("pre", { cls: "language-yaml" }).createEl("code", { + text: KeyBasedOnSettings(this.settings), + cls: "language-yaml", + }); + this.settingsPage.appendChild(help(this.settings)); + this.settingsPage.createEl("h2", { + text: i18next.t("settings.help.multiRepoHelp.title"), + }); + this.settingsPage.appendChild(multipleRepoExplained(this.settings)); + this.settingsPage.appendChild(supportMe()); + } + + // eslint-disable-next-line + copy(object: any) { + try { + return JSON.parse(JSON.stringify(object)); + } catch (e) { + console.log("error with stringify for", object); + } + } +} diff --git a/src/settings/help.ts b/src/settings/help.ts index 670b0cc2..ea9895af 100644 --- a/src/settings/help.ts +++ b/src/settings/help.ts @@ -1,457 +1,525 @@ -import { FolderSettings, GitHubPublisherSettings } from "@interfaces"; -import i18next from "i18next"; -import { normalizePath } from "obsidian"; -import { regexOnPath } from "src/conversion/file_path"; - -function spanAtRule(text: string, code: DocumentFragment, br: boolean = true): HTMLElement { - if (br) code.createEl("br"); - return code.createEl("span", { text, cls: ["token", "key", "atrule"] }); -} - -function spanBoolean(text: boolean, code: DocumentFragment): HTMLElement { - const textToString = text ? "true" : "false"; - return code.createEl("span", { text: textToString, cls: ["token", "boolean", "important"] }); -} - -function spanComment(text: string, code: DocumentFragment): HTMLElement { - return code.createEl("span", { text, cls: ["token", "comment"] }); -} - -function spanString(text: string, code: DocumentFragment): HTMLElement { - return code.createEl("span", { text, cls: ["token", "string"] }); -} - -function spanCategory(settings: GitHubPublisherSettings, code: DocumentFragment) { - if (settings.upload.behavior === FolderSettings.yaml) { - const defaultPath = settings.upload.defaultName.length > 0 ? `${settings.upload.defaultName}` : "/"; - return { - rule: spanAtRule(settings.upload.yamlFolderKey.length > 0 ? `${settings.upload.yamlFolderKey}: ` : "category: ", code), - token: spanString(normalizePath(defaultPath), code), - }; - } -} - -/** - * Export the YAML help to create an example of yaml with the value based on the Settings - */ - -export function KeyBasedOnSettings(settings: GitHubPublisherSettings): DocumentFragment { - const code = document.createDocumentFragment(); - const defaultPath = settings.upload.defaultName.length > 0 ? `${settings.upload.defaultName}` : "/"; - let path = settings.upload.behavior === FolderSettings.yaml ? `${settings.upload.rootFolder.length > 0 ? settings.upload.rootFolder : ""}/${defaultPath}/file.md` : `${defaultPath}/file.md`; - path = normalizePath(regexOnPath(path, settings)); - - spanAtRule(`${settings.plugin.shareKey}: `, code, false); - spanBoolean(true, code); - spanCategory(settings, code); - spanAtRule("path: ", code); - spanString(path, code); - spanComment(" #given as an example path", code); - spanAtRule("links: ", code); - spanAtRule(" mdlinks: ", code); - spanBoolean(settings.conversion.links.wiki, code); - spanAtRule(" convert: ", code); - spanBoolean(true, code); - spanAtRule(" internals: ", code); - spanBoolean(settings.conversion.links.internal, code); - spanAtRule(" nonShared: ", code); - spanBoolean(settings.conversion.links.unshared, code); - spanAtRule("embed: ", code); - spanAtRule(" send: ", code); - spanBoolean(settings.embed.notes, code); - spanAtRule(" remove: ", code); - spanString(settings.embed.convertEmbedToLinks, code); - spanAtRule(" char: ", code); - spanString(settings.embed.charConvert, code); - spanAtRule("attachment: ", code); - spanAtRule(" send: ", code); - spanBoolean(settings.embed.attachments, code); - spanAtRule(" folder: ", code); - spanString(settings.embed.folder, code); - spanAtRule("dataview: ", code); - spanBoolean(settings.conversion.dataview, code); - spanAtRule("hardBreak: ", code); - spanBoolean(settings.conversion.hardbreak, code); - spanAtRule("includeLinks: ", code); - spanBoolean(settings.embed.sendSimpleLinks, code); - if (settings.github.otherRepo.length > 0) { - spanAtRule("shortRepo: ", code); - spanString(settings.github.otherRepo[0].smartKey.length > 0 ? settings.github.otherRepo[0].smartKey : "smartkey", code); - } - spanAtRule("repo: ", code); - spanAtRule(" owner: ", code); - spanString(settings.github.user, code); - spanAtRule(" repo: ", code); - spanString(settings.github.repo, code); - spanAtRule(" branch: ", code); - spanString(settings.github.branch, code); - spanAtRule(" autoclean: ", code); - spanBoolean(settings.upload.autoclean.enable, code); - spanAtRule("copylink: ", code); - spanAtRule(" base: ", code); - spanString(settings.plugin.copyLink.links.length > 0 ? settings.plugin.copyLink.links : `https://${settings.github.repo}.github.io/${settings.github.repo}`, code); - const removePart = settings.plugin.copyLink.removePart.map(val => `"${val}"`).join(", "); - if (removePart.length > 0) { - spanAtRule(" remove: ", code); - spanString(removePart, code); - } - return code; -} - -/** - * Create the contents of the help settings tab - */ -export function help(settings: GitHubPublisherSettings) { - const explanation = document.createDocumentFragment(); - explanation.createEl("ul", undefined, (span) => { - span.createEl("li", undefined, (span) => { - span.createEl("code", { - text: `${settings.plugin.shareKey}${i18next.t("common.points")}`, - cls: "code-title", - }); - span.createEl("span", { - text: `${i18next.t("settings.help.frontmatter.share.title")}`, - }); - span.createEl("ul", undefined, (l) => { - l.createEl("span", { text: i18next.t("settings.help.frontmatter.share.other") }); - }); - }); - span.createEl("li", undefined, (span) => { - span.createEl("code", { - text: `path${i18next.t("common.points")}`, - cls: "code-title", - }); - span.createEl("span", { - text: ` ${i18next.t("settings.help.frontmatter.path")}`, - }); - }); - span.createEl("li", undefined, (span) => { - span.createEl("code", { text: `links${i18next.t("common.points")}`, cls: "code-title" }); - }); - span.createEl("ul", undefined, (l) => { - l.createEl("li", undefined, (p) => { - p.createEl("code", { text: "mdlinks" }); - p.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.mdlinks") - }`, - }); - p.createEl("code", { - text: " [[markdown|alias]] ", - }); - p.createEl("span", { text: i18next.t("common.in")}); - p.createEl("code", { text: " [alias](markdown) " }); - }); - l.createEl("li", undefined, (p) => { - p.createEl("code", { text: "convert" }); - p.createEl("span", undefined, (span) => { - span.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t( - "settings.help.frontmatter.convert.enableOrDisable" - ) - } `, - }); - span.createEl("code", { text: " [[link]] " }); - span.createEl("span", { - text: i18next.t("common.or") - }); - span.createEl("code", { text: " [](link) " }); - span.createEl("span", { - text: i18next.t("settings.help.frontmatter.convert.syntax") - }); - }); - }); - l.createEl("li", undefined, (p) => { - p.createEl("code", { text: "internals" }); - p.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.internals")}` - }); - }); - l.createEl("li", undefined, (p) => { - p.createEl("code", { text: "nonShared" }); - p.createEl("span", { text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.nonShared")}` }); - }); - }); - span.createEl("li", { text: `embed${i18next.t("common.points")}`, cls: "code code-title" }); - span.createEl("ul", undefined, (l) => { - l.createEl("li", undefined, (p) => { - p.createEl("code", { text: "send" }); - p.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.embed.send") - }`, - }); - }); - l.createEl("li", undefined, (p) => { - p.createEl("code", { text: "remove" }); - p.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.embed.remove.desc") - }`, - }); - p.createEl("ul", undefined, (ul) => { - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "remove | true" }); - li.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.embed.remove.remove")}` - }, - ); - }); - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "keep | false" }); - li.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.embed.remove.keep")}` - }, - ); - }); - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "links" }); - li.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.embed.remove.links")}` - }, - ); - }); - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "bake" }); - li.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.embed.remove.bake")}` - }, - ); - }); - }); - }); - l.createEl("li", undefined, (p) => { - p.createEl("code", { text: "char" }); - p.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.embed.char")}`, - }); - }); - }); - span.createEl("li", { text: `attachment${i18next.t("common.points")}`, cls: "code code-title" }); - span.createEl("ul", undefined, (l) => { - l.createEl("li", undefined, (span) => { - span.createEl("code", { text: "send" }); - span.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t( - "settings.help.frontmatter.attachment.send" - ) - }`, - }); - }); - l.createEl("li", undefined, (p) => { - p.createEl("code", { text: "folder" }); - p.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t( - "settings.help.frontmatter.attachment.folder" - ) - }`, - }); - }); - }); - span.createEl("li", undefined, (span) => { - span.createEl("code", { text: "dataview", cls: "code-title" }); - span.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.dataview")}`, - }); - }); - span.createEl("li", undefined, (span) => { - span.createEl("code", { text: "hardbreak", cls: "code-title" }); - span.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.hardBreak") - }`, - }); - }); - span.createEl("li", undefined, (span) => { - span.createEl("code", { text: "includeLinks", cls: "code-title" }); - span.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.includeLinks")}`, - }); - span.createEl("code", { text: "[[markdown]]", cls: "code-title" }); - span.createEl("span", { text: i18next.t("common.or")}); - span.createEl("code", { text: "[](markdown)", cls: "code-title" }); - - }); - span.createEl("li", undefined, (span) => { - span.createEl("code", { text: "shortRepo", cls: "code-title" }); - span.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.shortRepo")}`, - }); - }); - span.createEl("li", undefined, (span) => { - span.createEl("code", { text: "repo", cls: "code-title" }); - span.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.repo.desc")}`, - }); - span.createEl("ul", undefined, (ul) => { - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "owner" }); - li.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.repo.owner")}`, - }); - }); - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "repo" }); - li.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.github.repoName.title")}`, - }); - }); - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "branch" }); - li.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t( - "settings.help.frontmatter.repo.branch" - )}`, - }); - }); - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "autoclean" }); - li.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.autoclean")}`, - }); - }); - }); - }); - span.createEl("li", undefined, (span) => { - span.createEl("code", { - text: `${settings.upload.frontmatterTitle.key}`, - cls: "code-title", - }); - span.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.titleKey")}`, - }); - }); - span.createEl("li", undefined, (span) => { - span.createEl("code", { text: "baseLink", cls: "code-title" }); - span.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.baselink.desc")}`, - }); - span.createEl("code", { text: "copylink:", cls: "code-title" }); - span.createEl("ul", undefined, (ul) => { - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "base" }); - li.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.plugin.copyLink.baselink.title")}`, - }); - }); - ul.createEl("li", undefined, (li) => { - li.createEl("code", { text: "remove" }); - li.createEl("span", { - text: `${i18next.t("common.points")}${i18next.t("settings.help.frontmatter.baselink.remove")}`, - }); - }); - }); - }); - }); - return explanation; -} - -/** - * Create the useful links section in the settings help tab - * @return {DocumentFragment} - */ -export function usefulLinks(): DocumentFragment { - const usefulLinks = document.createDocumentFragment(); - usefulLinks.createEl("ul", undefined, (el) => { - el.createEl("li", undefined, (el) => { - el.createEl("a", { - text: i18next.t("settings.help.usefulLinks.documentation"), - href: i18next.t("settings.help.usefulLinks.links"), - }); - }); - el.createEl("li", undefined, (el) => { - el.createEl("a", { - text: i18next.t("common.repository"), - href: "https://github.com/ObsidianPublisher/obsidian-github-publisher", - }); - }); - el.createEl("li", undefined, (el) => { - el.createEl("a", { - text: i18next.t("settings.help.usefulLinks.issue"), - href: "https://github.com/ObsidianPublisher/obsidian-github-publisher/issues", - }); - }); - el.createEl("li", undefined, (el) => { - el.createEl("a", { - text: i18next.t("settings.help.usefulLinks.discussion"), - href: "https://github.com/ObsidianPublisher/obsidian-github-publisher/discussions", - }); - }); - }); - return usefulLinks; -} - -/** - * Create the explanation of multiple repo for the help tab - * @param {GitHubPublisherSettings} settings - * @return {DocumentFragment} - */ -export function multipleRepoExplained( - settings: GitHubPublisherSettings -): DocumentFragment { - const multipleRepoExplained = document.createDocumentFragment(); - multipleRepoExplained.createEl("p", undefined, (el) => { - el.createEl("span", { - text: i18next.t("settings.help.multiRepoHelp.desc"), - }); - el.createEl("code", { text: "multipleRepo" }); - el.createEl("span", { - text: ` ${i18next.t("settings.help.multiRepoHelp.desc2")}`, - }); - el.createEl("ul", undefined, (el) => { - el.createEl("li", { text: "owner" }).addClass("code"); - el.createEl("li", { text: "repo" }).addClass("code"); - el.createEl("li", { text: "branch" }).addClass("code"); - el.createEl("li", { text: "autoclean" }).addClass("code"); - }); - el.createEl("span", { - text: i18next.t("settings.help.multiRepoHelp.exampleDesc"), - }); - }); - const code = document.createDocumentFragment(); - spanAtRule("multipleRepo: ", code, false); - spanAtRule(" - owner: ", code); - spanString(settings.github.user, code); - spanAtRule(" repo: ", code); - spanString(settings.github.repo, code); - spanAtRule(" branch: ", code); - spanString(settings.github.branch, code); - spanAtRule(" autoclean: ", code); - spanBoolean(false, code); - spanAtRule(" - owner: ", code); - spanString(settings.github.user, code); - spanAtRule(" repo: ", code); - spanString("my_second_blog", code); - spanAtRule(" branch: ", code); - spanString("master", code); - spanAtRule(" autoclean: ", code); - spanBoolean(false, code); - multipleRepoExplained - .createEl("pre", { cls: "language-yaml" }) - .createEl("code", { text: code, cls: "language-yaml" }); - - return multipleRepoExplained; -} - -/** - * Add the link for kofi in the help tab - * @return {DocumentFragment} - */ - -export function supportMe(): DocumentFragment { - const supportMe = document.createDocumentFragment(); - supportMe.createEl("p", undefined, (el) => { - el.createEl("a", undefined, (el) => { - el.createEl("img", undefined, (img) => { - img.setAttr( - "src", - "https://storage.ko-fi.com/cdn/kofi2.png?v=3" - ); - img.setAttr("alt", "Buy Me A Coffee"); - img.setAttr( - "style", - "height: 60px !important;width: 217px !important;" - ); - }); - el.setAttr("href", "https://ko-fi.com/lisandra_dev"); - }); - el.setAttr("style", "text-align: center;"); - }); - return supportMe; -} +import { FolderSettings, GitHubPublisherSettings } from "@interfaces"; +import i18next from "i18next"; +import { normalizePath } from "obsidian"; +import { regexOnPath } from "src/conversion/file_path"; + +function spanAtRule( + text: string, + code: DocumentFragment, + br: boolean = true +): HTMLElement { + if (br) code.createEl("br"); + return code.createEl("span", { text, cls: ["token", "key", "atrule"] }); +} + +function spanBoolean(text: boolean, code: DocumentFragment): HTMLElement { + const textToString = text ? "true" : "false"; + return code.createEl("span", { + text: textToString, + cls: ["token", "boolean", "important"], + }); +} + +function spanComment(text: string, code: DocumentFragment): HTMLElement { + return code.createEl("span", { text, cls: ["token", "comment"] }); +} + +function spanString(text: string, code: DocumentFragment): HTMLElement { + return code.createEl("span", { text, cls: ["token", "string"] }); +} + +function spanCategory(settings: GitHubPublisherSettings, code: DocumentFragment) { + if (settings.upload.behavior === FolderSettings.yaml) { + const defaultPath = + settings.upload.defaultName.length > 0 ? `${settings.upload.defaultName}` : "/"; + return { + rule: spanAtRule( + settings.upload.yamlFolderKey.length > 0 + ? `${settings.upload.yamlFolderKey}: ` + : "category: ", + code + ), + token: spanString(normalizePath(defaultPath), code), + }; + } +} + +/** + * Export the YAML help to create an example of yaml with the value based on the Settings + */ + +export function KeyBasedOnSettings(settings: GitHubPublisherSettings): DocumentFragment { + const code = document.createDocumentFragment(); + const defaultPath = + settings.upload.defaultName.length > 0 ? `${settings.upload.defaultName}` : "/"; + let path = + settings.upload.behavior === FolderSettings.yaml + ? `${ + settings.upload.rootFolder.length > 0 ? settings.upload.rootFolder : "" + }/${defaultPath}/file.md` + : `${defaultPath}/file.md`; + path = normalizePath(regexOnPath(path, settings)); + + spanAtRule(`${settings.plugin.shareKey}: `, code, false); + spanBoolean(true, code); + spanCategory(settings, code); + spanAtRule("path: ", code); + spanString(path, code); + spanComment(" #given as an example path", code); + spanAtRule("links: ", code); + spanAtRule(" mdlinks: ", code); + spanBoolean(settings.conversion.links.wiki, code); + spanAtRule(" convert: ", code); + spanBoolean(true, code); + spanAtRule(" internals: ", code); + spanBoolean(settings.conversion.links.internal, code); + spanAtRule(" nonShared: ", code); + spanBoolean(settings.conversion.links.unshared, code); + spanAtRule("embed: ", code); + spanAtRule(" send: ", code); + spanBoolean(settings.embed.notes, code); + spanAtRule(" remove: ", code); + spanString(settings.embed.convertEmbedToLinks, code); + spanAtRule(" char: ", code); + spanString(settings.embed.charConvert, code); + spanAtRule("attachment: ", code); + spanAtRule(" send: ", code); + spanBoolean(settings.embed.attachments, code); + spanAtRule(" folder: ", code); + spanString(settings.embed.folder, code); + spanAtRule("dataview: ", code); + spanBoolean(settings.conversion.dataview, code); + spanAtRule("hardBreak: ", code); + spanBoolean(settings.conversion.hardbreak, code); + spanAtRule("includeLinks: ", code); + spanBoolean(settings.embed.sendSimpleLinks, code); + if (settings.github.otherRepo.length > 0) { + spanAtRule("shortRepo: ", code); + spanString( + settings.github.otherRepo[0].smartKey.length > 0 + ? settings.github.otherRepo[0].smartKey + : "smartkey", + code + ); + } + spanAtRule("repo: ", code); + spanAtRule(" owner: ", code); + spanString(settings.github.user, code); + spanAtRule(" repo: ", code); + spanString(settings.github.repo, code); + spanAtRule(" branch: ", code); + spanString(settings.github.branch, code); + spanAtRule(" autoclean: ", code); + spanBoolean(settings.upload.autoclean.enable, code); + spanAtRule("copylink: ", code); + spanAtRule(" base: ", code); + spanString( + settings.plugin.copyLink.links.length > 0 + ? settings.plugin.copyLink.links + : `https://${settings.github.repo}.github.io/${settings.github.repo}`, + code + ); + const removePart = settings.plugin.copyLink.removePart + .map((val) => `"${val}"`) + .join(", "); + if (removePart.length > 0) { + spanAtRule(" remove: ", code); + spanString(removePart, code); + } + return code; +} + +/** + * Create the contents of the help settings tab + */ +export function help(settings: GitHubPublisherSettings) { + const explanation = document.createDocumentFragment(); + explanation.createEl("ul", undefined, (span) => { + span.createEl("li", undefined, (span) => { + span.createEl("code", { + text: `${settings.plugin.shareKey}${i18next.t("common.points")}`, + cls: "code-title", + }); + span.createEl("span", { + text: `${i18next.t("settings.help.frontmatter.share.title")}`, + }); + span.createEl("ul", undefined, (l) => { + l.createEl("span", { text: i18next.t("settings.help.frontmatter.share.other") }); + }); + }); + span.createEl("li", undefined, (span) => { + span.createEl("code", { + text: `path${i18next.t("common.points")}`, + cls: "code-title", + }); + span.createEl("span", { + text: ` ${i18next.t("settings.help.frontmatter.path")}`, + }); + }); + span.createEl("li", undefined, (span) => { + span.createEl("code", { + text: `links${i18next.t("common.points")}`, + cls: "code-title", + }); + }); + span.createEl("ul", undefined, (l) => { + l.createEl("li", undefined, (p) => { + p.createEl("code", { text: "mdlinks" }); + p.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.mdlinks" + )}`, + }); + p.createEl("code", { + text: " [[markdown|alias]] ", + }); + p.createEl("span", { text: i18next.t("common.in") }); + p.createEl("code", { text: " [alias](markdown) " }); + }); + l.createEl("li", undefined, (p) => { + p.createEl("code", { text: "convert" }); + p.createEl("span", undefined, (span) => { + span.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.convert.enableOrDisable" + )} `, + }); + span.createEl("code", { text: " [[link]] " }); + span.createEl("span", { + text: i18next.t("common.or"), + }); + span.createEl("code", { text: " [](link) " }); + span.createEl("span", { + text: i18next.t("settings.help.frontmatter.convert.syntax"), + }); + }); + }); + l.createEl("li", undefined, (p) => { + p.createEl("code", { text: "internals" }); + p.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.internals" + )}`, + }); + }); + l.createEl("li", undefined, (p) => { + p.createEl("code", { text: "nonShared" }); + p.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.nonShared" + )}`, + }); + }); + }); + span.createEl("li", { + text: `embed${i18next.t("common.points")}`, + cls: "code code-title", + }); + span.createEl("ul", undefined, (l) => { + l.createEl("li", undefined, (p) => { + p.createEl("code", { text: "send" }); + p.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.embed.send" + )}`, + }); + }); + l.createEl("li", undefined, (p) => { + p.createEl("code", { text: "remove" }); + p.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.embed.remove.desc" + )}`, + }); + p.createEl("ul", undefined, (ul) => { + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "remove | true" }); + li.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.embed.remove.remove" + )}`, + }); + }); + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "keep | false" }); + li.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.embed.remove.keep" + )}`, + }); + }); + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "links" }); + li.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.embed.remove.links" + )}`, + }); + }); + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "bake" }); + li.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.embed.remove.bake" + )}`, + }); + }); + }); + }); + l.createEl("li", undefined, (p) => { + p.createEl("code", { text: "char" }); + p.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.embed.char" + )}`, + }); + }); + }); + span.createEl("li", { + text: `attachment${i18next.t("common.points")}`, + cls: "code code-title", + }); + span.createEl("ul", undefined, (l) => { + l.createEl("li", undefined, (span) => { + span.createEl("code", { text: "send" }); + span.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.attachment.send" + )}`, + }); + }); + l.createEl("li", undefined, (p) => { + p.createEl("code", { text: "folder" }); + p.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.attachment.folder" + )}`, + }); + }); + }); + span.createEl("li", undefined, (span) => { + span.createEl("code", { text: "dataview", cls: "code-title" }); + span.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.dataview" + )}`, + }); + }); + span.createEl("li", undefined, (span) => { + span.createEl("code", { text: "hardbreak", cls: "code-title" }); + span.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.hardBreak" + )}`, + }); + }); + span.createEl("li", undefined, (span) => { + span.createEl("code", { text: "includeLinks", cls: "code-title" }); + span.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.includeLinks" + )}`, + }); + span.createEl("code", { text: "[[markdown]]", cls: "code-title" }); + span.createEl("span", { text: i18next.t("common.or") }); + span.createEl("code", { text: "[](markdown)", cls: "code-title" }); + }); + span.createEl("li", undefined, (span) => { + span.createEl("code", { text: "shortRepo", cls: "code-title" }); + span.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.shortRepo" + )}`, + }); + }); + span.createEl("li", undefined, (span) => { + span.createEl("code", { text: "repo", cls: "code-title" }); + span.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.repo.desc" + )}`, + }); + span.createEl("ul", undefined, (ul) => { + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "owner" }); + li.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.repo.owner" + )}`, + }); + }); + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "repo" }); + li.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.github.repoName.title" + )}`, + }); + }); + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "branch" }); + li.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.repo.branch" + )}`, + }); + }); + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "autoclean" }); + li.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.autoclean" + )}`, + }); + }); + }); + }); + span.createEl("li", undefined, (span) => { + span.createEl("code", { + text: `${settings.upload.frontmatterTitle.key}`, + cls: "code-title", + }); + span.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.titleKey" + )}`, + }); + }); + span.createEl("li", undefined, (span) => { + span.createEl("code", { text: "baseLink", cls: "code-title" }); + span.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.baselink.desc" + )}`, + }); + span.createEl("code", { text: "copylink:", cls: "code-title" }); + span.createEl("ul", undefined, (ul) => { + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "base" }); + li.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.plugin.copyLink.baselink.title" + )}`, + }); + }); + ul.createEl("li", undefined, (li) => { + li.createEl("code", { text: "remove" }); + li.createEl("span", { + text: `${i18next.t("common.points")}${i18next.t( + "settings.help.frontmatter.baselink.remove" + )}`, + }); + }); + }); + }); + }); + return explanation; +} + +/** + * Create the useful links section in the settings help tab + * @return {DocumentFragment} + */ +export function usefulLinks(): DocumentFragment { + const usefulLinks = document.createDocumentFragment(); + usefulLinks.createEl("ul", undefined, (el) => { + el.createEl("li", undefined, (el) => { + el.createEl("a", { + text: i18next.t("settings.help.usefulLinks.documentation"), + href: i18next.t("settings.help.usefulLinks.links"), + }); + }); + el.createEl("li", undefined, (el) => { + el.createEl("a", { + text: i18next.t("common.repository"), + href: "https://github.com/ObsidianPublisher/obsidian-github-publisher", + }); + }); + el.createEl("li", undefined, (el) => { + el.createEl("a", { + text: i18next.t("settings.help.usefulLinks.issue"), + href: "https://github.com/ObsidianPublisher/obsidian-github-publisher/issues", + }); + }); + el.createEl("li", undefined, (el) => { + el.createEl("a", { + text: i18next.t("settings.help.usefulLinks.discussion"), + href: "https://github.com/ObsidianPublisher/obsidian-github-publisher/discussions", + }); + }); + }); + return usefulLinks; +} + +/** + * Create the explanation of multiple repo for the help tab + * @param {GitHubPublisherSettings} settings + * @return {DocumentFragment} + */ +export function multipleRepoExplained( + settings: GitHubPublisherSettings +): DocumentFragment { + const multipleRepoExplained = document.createDocumentFragment(); + multipleRepoExplained.createEl("p", undefined, (el) => { + el.createEl("span", { + text: i18next.t("settings.help.multiRepoHelp.desc"), + }); + el.createEl("code", { text: "multipleRepo" }); + el.createEl("span", { + text: ` ${i18next.t("settings.help.multiRepoHelp.desc2")}`, + }); + el.createEl("ul", undefined, (el) => { + el.createEl("li", { text: "owner" }).addClass("code"); + el.createEl("li", { text: "repo" }).addClass("code"); + el.createEl("li", { text: "branch" }).addClass("code"); + el.createEl("li", { text: "autoclean" }).addClass("code"); + }); + el.createEl("span", { + text: i18next.t("settings.help.multiRepoHelp.exampleDesc"), + }); + }); + const code = document.createDocumentFragment(); + spanAtRule("multipleRepo: ", code, false); + spanAtRule(" - owner: ", code); + spanString(settings.github.user, code); + spanAtRule(" repo: ", code); + spanString(settings.github.repo, code); + spanAtRule(" branch: ", code); + spanString(settings.github.branch, code); + spanAtRule(" autoclean: ", code); + spanBoolean(false, code); + spanAtRule(" - owner: ", code); + spanString(settings.github.user, code); + spanAtRule(" repo: ", code); + spanString("my_second_blog", code); + spanAtRule(" branch: ", code); + spanString("master", code); + spanAtRule(" autoclean: ", code); + spanBoolean(false, code); + multipleRepoExplained + .createEl("pre", { cls: "language-yaml" }) + .createEl("code", { text: code, cls: "language-yaml" }); + + return multipleRepoExplained; +} + +/** + * Add the link for kofi in the help tab + * @return {DocumentFragment} + */ + +export function supportMe(): DocumentFragment { + const supportMe = document.createDocumentFragment(); + supportMe.createEl("p", undefined, (el) => { + el.createEl("a", undefined, (el) => { + el.createEl("img", undefined, (img) => { + img.setAttr("src", "https://storage.ko-fi.com/cdn/kofi2.png?v=3"); + img.setAttr("alt", "Buy Me A Coffee"); + img.setAttr("style", "height: 60px !important;width: 217px !important;"); + }); + el.setAttr("href", "https://ko-fi.com/lisandra_dev"); + }); + el.setAttr("style", "text-align: center;"); + }); + return supportMe; +} diff --git a/src/settings/migrate.ts b/src/settings/migrate.ts index b1e6d42b..93bbd994 100644 --- a/src/settings/migrate.ts +++ b/src/settings/migrate.ts @@ -1,353 +1,399 @@ -import { FolderSettings, GithubTiersVersion, TextCleaner, TOKEN_PATH, TypeOfEditRegex } from "@interfaces"; -import i18next from "i18next"; -import { normalizePath } from "obsidian"; -import GithubPublisher from "src/main"; -import { createTokenPath, logs } from "src/utils"; - -export interface OldSettings { - githubRepo: string; - githubName: string; - GhToken: string; - githubBranch: string; - shareKey: string; - excludedFolder: string[]; - fileMenu: boolean; - editorMenu: boolean; - downloadedFolder: string; - folderDefaultName: string; - yamlFolderKey: string; - rootFolder: string; - workflowName: string; - customCommitMsg: string; - embedImage: boolean; - defaultImageFolder: string; - autoCleanUp: boolean; - autoCleanUpExcluded: string[]; - folderNote: boolean; - folderNoteRename: string; - migrateWikiLinks: boolean; - migrateForGithub: boolean; - subFolder: string; - embedNotes: boolean; - copyLink: boolean; - mainLink: string; - linkRemover: string; - hardBreak: boolean; - logNotice: boolean; - migrateDataview: boolean; - useFrontmatterTitle: boolean; - censorText: TextCleaner[]; - inlineTags: boolean; - dataviewFields: string[]; - frontmatterTitleKey: string; - excludeDataviewValue: string[]; - metadataFileFields: string[]; - shareExternalModified: boolean; - automaticallyMergePR: boolean; - metadataExtractorPath: string; - migrateInternalNonShared: boolean; - frontmatterTitleRegex: string; - frontmatterTitleReplacement: string; - tiersForApi: GithubTiersVersion; - hostname: string; -} - -export async function migrateSettings(old: OldSettings, plugin: GithubPublisher, imported?: boolean) { - if (plugin.settings.plugin.migrated && !imported) { - return; - } - await migrateOldSettings(plugin, old); - await migrateReplaceTitle(plugin); - await migrateSubFolder(plugin); - await migrateCensor(plugin); - await migrateWorFlow(plugin); - await migrateToken(plugin); - await migrateOtherRepository(plugin); - plugin.settings.plugin.migrated = true; - await plugin.saveSettings(); -} - -async function migrateReplaceTitle(plugin: GithubPublisher) { - if ((plugin.settings.upload.replaceTitle instanceof Array)) { - return; - } - logs({ settings: plugin.settings }, i18next.t("informations.migrating.fileReplace")); - plugin.settings.upload.replaceTitle = [plugin.settings.upload.replaceTitle]; - await plugin.saveSettings(); -} - -async function migrateSubFolder(plugin: GithubPublisher) { - //@ts-ignore - if (plugin.settings.upload.subFolder && (!plugin.settings.upload.replacePath.find((e) => e.regex === `/${plugin.settings.upload.subFolder}`))) { - logs({ settings: plugin.settings }, i18next.t("informations.migrating.subFolder")); - //@ts-ignore - if (plugin.settings.upload.subFolder.length > 0) { - plugin.settings.upload.replacePath.push({ - //@ts-ignore - regex: "/" + plugin.settings.upload.subFolder, - replacement: "", - type: TypeOfEditRegex.path - }); - } - //delete plugin.settings.upload.subFolder from settings; - //@ts-ignore - delete plugin.settings.upload.subFolder; - await plugin.saveSettings(); - } - -} - - -async function migrateCensor(plugin: GithubPublisher) { - for (const censor of plugin.settings.conversion.censorText) { - if (censor.flags) { - //enclose regex in / / and add flags - censor.entry = "/" + censor.entry + "/" + censor.flags; - delete censor.flags; - await plugin.saveSettings(); - } - } -} - -async function migrateWorFlow(plugin: GithubPublisher) { - logs({ settings: plugin.settings }, "Migrating workflow"); - //@ts-ignore - if (!plugin.settings.github.worflow) { - return; - } - //@ts-ignore - const worflow = plugin.settings.github.worflow; - plugin.settings.github.workflow = { - //@ts-ignore - name: worflow.workflowName, - //@ts-ignore - commitMessage: worflow.customCommitMsg, - }; - //@ts-ignore - delete plugin.settings.github.worflow; - await plugin.saveSettings(); -} - -export async function migrateToken(plugin: GithubPublisher, token?: string, repo?: string) { - logs({ settings: plugin.settings }, "migrating token"); - const tokenPath = createTokenPath(plugin, plugin.settings.github.tokenPath); - //@ts-ignore - if (plugin.settings.github.token && !token) { - logs({ settings: plugin.settings }, `Moving the GitHub Token in the file : ${tokenPath}`); - //@ts-ignore - token = plugin.settings.github.token; - //@ts-ignore - delete plugin.settings.github.token; - await plugin.saveSettings(); - } - if (token === undefined) { - return; - } - logs({ settings: plugin.settings }, `Moving the GitHub Token in the file : ${tokenPath} for ${repo ?? "default"} repository`); - const exists = await plugin.app.vault.adapter.exists(tokenPath); - if (!exists) { - await plugin.app.vault.adapter.mkdir(normalizePath(tokenPath).split("/").slice(0, -1).join("/")); - } - if (tokenPath.endsWith(".json")) { - const envToken = repo ? { - "GITHUB_PUBLISHER_REPOS" : { - [repo] : token - } - } : { - GITHUB_PUBLISHER_TOKEN: token - }; - if (!exists) { - await plugin.app.vault.adapter.write(tokenPath, JSON.stringify(envToken, null, 2)); - return; - } - const oldToken = JSON.parse(await plugin.app.vault.adapter.read(tokenPath)); - const newToken = { ...oldToken, ...envToken }; - await plugin.app.vault.adapter.write(tokenPath, JSON.stringify(newToken, null, 2)); - - } else { - const envToken = repo ? `${repo}_TOKEN=${token}` : `GITHUB_TOKEN=${token}`; - if (!exists) { - await plugin.app.vault.adapter.write(tokenPath, envToken); - return; - } - const oldToken = (await plugin.app.vault.adapter.read(tokenPath)).split("\n"); - //search if old token is already in the file, if yes, replace it - for (let i = 0; i < oldToken.length; i++) { - if (oldToken[i].startsWith("GITHUB_TOKEN") && !repo) { - oldToken[i] = envToken; - break; - } else if (oldToken[i].startsWith(`${repo}_TOKEN`) && repo) { - oldToken[i] = envToken; - break; - } else if (i === oldToken.length - 1) { - oldToken.push(envToken); - } - } - await plugin.app.vault.adapter.write(tokenPath, oldToken.join("\n")); - - } - return; -} - - -async function migrateOtherRepository(plugin: GithubPublisher) { - logs({ settings: plugin.settings }, "Configuring other repositories"); - const otherRepo = plugin.settings.github?.otherRepo ?? []; - for (const repo of otherRepo) { - const workflow = { - //@ts-ignore - name: plugin.settings.github.worflow?.workflowName ?? plugin.settings.github.workflow.name, - //@ts-ignore - commitMessage: plugin.settings.github.worflow?.customCommitMsg ?? plugin.settings.github.workflow.commitMessage, - }; - if (!repo.workflow) { - repo.workflow = workflow; - await plugin.saveSettings(); - } - //@ts-ignore - if (repo.worflow) { - //@ts-ignore - const worflow = repo.worflow; - if (worflow.workflowName) { - repo.workflow.name = worflow.workflowName; - } - if (worflow.customCommitMsg) { - repo.workflow.commitMessage = worflow.customCommitMsg; - } - //@ts-ignore - delete repo.worflow; - await plugin.saveSettings(); - } - if (!repo.copyLink) { - repo.copyLink = { - links: "", - removePart: [], - transform: { - toUri: false, - slugify: "disable", - applyRegex: [] - } - }; - await plugin.saveSettings(); - } - } -} - -async function migrateOldSettings(plugin: GithubPublisher, old: OldSettings) { - if (!Object.keys(old).includes("editorMenu")) { - return; - } - logs({ settings: plugin.settings }, i18next.t("informations.migrating.oldSettings")); - plugin.settings = { - github: - { - user: old.githubName ? old.githubName : plugin.settings.github.user ? plugin.settings.github.user : "", - repo: old.githubRepo ? old.githubRepo : plugin.settings.github.repo ? plugin.settings.github.repo : "", - branch: old.githubBranch, - automaticallyMergePR: old.automaticallyMergePR, - tokenPath: TOKEN_PATH, - api: { - tiersForApi: old.tiersForApi, - hostname: old.hostname, - }, - workflow: { - name: old.workflowName, - commitMessage: old.customCommitMsg ?? plugin.settings.github.workflow.commitMessage ?? "[PUBLISHER] MERGE", - }, - otherRepo: [], - rateLimit: 0, - verifiedRepo: false, - dryRun: { - enable: false, - folderName: "", - } - }, - upload: { - behavior: old.downloadedFolder as FolderSettings, - defaultName: old.folderDefaultName, - rootFolder: old.rootFolder, - yamlFolderKey: old.yamlFolderKey, - frontmatterTitle: { - enable: old.useFrontmatterTitle, - key: old.frontmatterTitleKey, - }, - replaceTitle: [{ - regex: old.frontmatterTitleRegex, - replacement: old.frontmatterTitleReplacement, - type: TypeOfEditRegex.title - }], - replacePath: [ - { - regex: old.subFolder, - replacement: "", - type: TypeOfEditRegex.path - } - ], - autoclean: { - enable: old.autoCleanUp, - excluded: old.autoCleanUpExcluded, - includeAttachments: old.autoCleanUp, - }, - folderNote: { - enable: old.folderNote, - rename: old.folderNoteRename, - addTitle: { - enable: old.folderNote, - key: old.frontmatterTitleKey, - } - }, - metadataExtractorPath: old.metadataExtractorPath, - }, - conversion: { - hardbreak: old.hardBreak, - dataview: old.migrateDataview, - censorText: old.censorText, - tags: { - inline: old.inlineTags, - exclude: old.excludeDataviewValue, - fields: old.dataviewFields, - }, - links: { - internal: old.migrateForGithub, - unshared: old.migrateInternalNonShared, - wiki: old.migrateWikiLinks, - slugify: false, - }, - }, - embed: { - attachments: old.embedImage, - keySendFile: old.metadataFileFields, - notes: old.embedNotes, - folder: old.defaultImageFolder, - charConvert: "->", - convertEmbedToLinks: "keep", - overrideAttachments: [], - unHandledObsidianExt: [], - sendSimpleLinks: true, - }, - plugin: { - shareKey: old.shareKey, - fileMenu: old.fileMenu, - editorMenu: old.editorMenu, - excludedFolder: old.excludedFolder, - copyLink: { - enable: old.copyLink, - links: old.mainLink, - removePart: old.linkRemover.split(/[,\n]\W*/).map((s) => s.trim()), - addCmd: false, - transform: { - toUri: true, - slugify: "lower", - applyRegex: [], - } - }, - noticeError: old.logNotice, - displayModalRepoEditing: false, - setFrontmatterKey: "Set" - } - }; - //@ts-ignore - const token = old.GhToken ? old.GhToken : plugin.settings.github.token ? plugin.settings.github.token : undefined; - await migrateToken(plugin, token); - await plugin.saveSettings(); -} +import { + FolderSettings, + GithubTiersVersion, + TextCleaner, + TOKEN_PATH, + TypeOfEditRegex, +} from "@interfaces"; +import i18next from "i18next"; +import { normalizePath } from "obsidian"; +import GithubPublisher from "src/main"; +import { createTokenPath, logs } from "src/utils"; + +export interface OldSettings { + githubRepo: string; + githubName: string; + GhToken: string; + githubBranch: string; + shareKey: string; + excludedFolder: string[]; + fileMenu: boolean; + editorMenu: boolean; + downloadedFolder: string; + folderDefaultName: string; + yamlFolderKey: string; + rootFolder: string; + workflowName: string; + customCommitMsg: string; + embedImage: boolean; + defaultImageFolder: string; + autoCleanUp: boolean; + autoCleanUpExcluded: string[]; + folderNote: boolean; + folderNoteRename: string; + migrateWikiLinks: boolean; + migrateForGithub: boolean; + subFolder: string; + embedNotes: boolean; + copyLink: boolean; + mainLink: string; + linkRemover: string; + hardBreak: boolean; + logNotice: boolean; + migrateDataview: boolean; + useFrontmatterTitle: boolean; + censorText: TextCleaner[]; + inlineTags: boolean; + dataviewFields: string[]; + frontmatterTitleKey: string; + excludeDataviewValue: string[]; + metadataFileFields: string[]; + shareExternalModified: boolean; + automaticallyMergePR: boolean; + metadataExtractorPath: string; + migrateInternalNonShared: boolean; + frontmatterTitleRegex: string; + frontmatterTitleReplacement: string; + tiersForApi: GithubTiersVersion; + hostname: string; +} + +export async function migrateSettings( + old: OldSettings, + plugin: GithubPublisher, + imported?: boolean +) { + if (plugin.settings.plugin.migrated && !imported) { + return; + } + await migrateOldSettings(plugin, old); + await migrateReplaceTitle(plugin); + await migrateSubFolder(plugin); + await migrateCensor(plugin); + await migrateWorFlow(plugin); + await migrateToken(plugin); + await migrateOtherRepository(plugin); + plugin.settings.plugin.migrated = true; + await plugin.saveSettings(); +} + +async function migrateReplaceTitle(plugin: GithubPublisher) { + if (plugin.settings.upload.replaceTitle instanceof Array) { + return; + } + logs({ settings: plugin.settings }, i18next.t("informations.migrating.fileReplace")); + plugin.settings.upload.replaceTitle = [plugin.settings.upload.replaceTitle]; + await plugin.saveSettings(); +} + +async function migrateSubFolder(plugin: GithubPublisher) { + //@ts-ignore + if ( + plugin.settings.upload.subFolder && + !plugin.settings.upload.replacePath.find( + (e) => e.regex === `/${plugin.settings.upload.subFolder}` + ) + ) { + logs({ settings: plugin.settings }, i18next.t("informations.migrating.subFolder")); + //@ts-ignore + if (plugin.settings.upload.subFolder.length > 0) { + plugin.settings.upload.replacePath.push({ + //@ts-ignore + regex: "/" + plugin.settings.upload.subFolder, + replacement: "", + type: TypeOfEditRegex.path, + }); + } + //delete plugin.settings.upload.subFolder from settings; + //@ts-ignore + delete plugin.settings.upload.subFolder; + await plugin.saveSettings(); + } +} + +async function migrateCensor(plugin: GithubPublisher) { + for (const censor of plugin.settings.conversion.censorText) { + if (censor.flags) { + //enclose regex in / / and add flags + censor.entry = "/" + censor.entry + "/" + censor.flags; + delete censor.flags; + await plugin.saveSettings(); + } + } +} + +async function migrateWorFlow(plugin: GithubPublisher) { + logs({ settings: plugin.settings }, "Migrating workflow"); + //@ts-ignore + if (!plugin.settings.github.worflow) { + return; + } + //@ts-ignore + const worflow = plugin.settings.github.worflow; + plugin.settings.github.workflow = { + //@ts-ignore + name: worflow.workflowName, + //@ts-ignore + commitMessage: worflow.customCommitMsg, + }; + //@ts-ignore + delete plugin.settings.github.worflow; + await plugin.saveSettings(); +} + +export async function migrateToken( + plugin: GithubPublisher, + token?: string, + repo?: string +) { + logs({ settings: plugin.settings }, "migrating token"); + const tokenPath = createTokenPath(plugin, plugin.settings.github.tokenPath); + //@ts-ignore + if (plugin.settings.github.token && !token) { + logs( + { settings: plugin.settings }, + `Moving the GitHub Token in the file : ${tokenPath}` + ); + //@ts-ignore + token = plugin.settings.github.token; + //@ts-ignore + delete plugin.settings.github.token; + await plugin.saveSettings(); + } + if (token === undefined) { + return; + } + logs( + { settings: plugin.settings }, + `Moving the GitHub Token in the file : ${tokenPath} for ${ + repo ?? "default" + } repository` + ); + const exists = await plugin.app.vault.adapter.exists(tokenPath); + if (!exists) { + await plugin.app.vault.adapter.mkdir( + normalizePath(tokenPath).split("/").slice(0, -1).join("/") + ); + } + if (tokenPath.endsWith(".json")) { + const envToken = repo + ? { + GITHUB_PUBLISHER_REPOS: { + [repo]: token, + }, + } + : { + GITHUB_PUBLISHER_TOKEN: token, + }; + if (!exists) { + await plugin.app.vault.adapter.write(tokenPath, JSON.stringify(envToken, null, 2)); + return; + } + const oldToken = JSON.parse(await plugin.app.vault.adapter.read(tokenPath)); + const newToken = { ...oldToken, ...envToken }; + await plugin.app.vault.adapter.write(tokenPath, JSON.stringify(newToken, null, 2)); + } else { + const envToken = repo ? `${repo}_TOKEN=${token}` : `GITHUB_TOKEN=${token}`; + if (!exists) { + await plugin.app.vault.adapter.write(tokenPath, envToken); + return; + } + const oldToken = (await plugin.app.vault.adapter.read(tokenPath)).split("\n"); + //search if old token is already in the file, if yes, replace it + for (let i = 0; i < oldToken.length; i++) { + if (oldToken[i].startsWith("GITHUB_TOKEN") && !repo) { + oldToken[i] = envToken; + break; + } else if (oldToken[i].startsWith(`${repo}_TOKEN`) && repo) { + oldToken[i] = envToken; + break; + } else if (i === oldToken.length - 1) { + oldToken.push(envToken); + } + } + await plugin.app.vault.adapter.write(tokenPath, oldToken.join("\n")); + } + return; +} + +async function migrateOtherRepository(plugin: GithubPublisher) { + logs({ settings: plugin.settings }, "Configuring other repositories"); + const otherRepo = plugin.settings.github?.otherRepo ?? []; + for (const repo of otherRepo) { + const workflow = { + //@ts-ignore + name: + plugin.settings.github.worflow?.workflowName ?? + plugin.settings.github.workflow.name, + //@ts-ignore + commitMessage: + plugin.settings.github.worflow?.customCommitMsg ?? + plugin.settings.github.workflow.commitMessage, + }; + if (!repo.workflow) { + repo.workflow = workflow; + await plugin.saveSettings(); + } + //@ts-ignore + if (repo.worflow) { + //@ts-ignore + const worflow = repo.worflow; + if (worflow.workflowName) { + repo.workflow.name = worflow.workflowName; + } + if (worflow.customCommitMsg) { + repo.workflow.commitMessage = worflow.customCommitMsg; + } + //@ts-ignore + delete repo.worflow; + await plugin.saveSettings(); + } + if (!repo.copyLink) { + repo.copyLink = { + links: "", + removePart: [], + transform: { + toUri: false, + slugify: "disable", + applyRegex: [], + }, + }; + await plugin.saveSettings(); + } + } +} + +async function migrateOldSettings(plugin: GithubPublisher, old: OldSettings) { + if (!Object.keys(old).includes("editorMenu")) { + return; + } + logs({ settings: plugin.settings }, i18next.t("informations.migrating.oldSettings")); + plugin.settings = { + github: { + user: old.githubName + ? old.githubName + : plugin.settings.github.user + ? plugin.settings.github.user + : "", + repo: old.githubRepo + ? old.githubRepo + : plugin.settings.github.repo + ? plugin.settings.github.repo + : "", + branch: old.githubBranch, + automaticallyMergePR: old.automaticallyMergePR, + tokenPath: TOKEN_PATH, + api: { + tiersForApi: old.tiersForApi, + hostname: old.hostname, + }, + workflow: { + name: old.workflowName, + commitMessage: + old.customCommitMsg ?? + plugin.settings.github.workflow.commitMessage ?? + "[PUBLISHER] MERGE", + }, + otherRepo: [], + rateLimit: 0, + verifiedRepo: false, + dryRun: { + enable: false, + folderName: "", + }, + }, + upload: { + behavior: old.downloadedFolder as FolderSettings, + defaultName: old.folderDefaultName, + rootFolder: old.rootFolder, + yamlFolderKey: old.yamlFolderKey, + frontmatterTitle: { + enable: old.useFrontmatterTitle, + key: old.frontmatterTitleKey, + }, + replaceTitle: [ + { + regex: old.frontmatterTitleRegex, + replacement: old.frontmatterTitleReplacement, + type: TypeOfEditRegex.title, + }, + ], + replacePath: [ + { + regex: old.subFolder, + replacement: "", + type: TypeOfEditRegex.path, + }, + ], + autoclean: { + enable: old.autoCleanUp, + excluded: old.autoCleanUpExcluded, + includeAttachments: old.autoCleanUp, + }, + folderNote: { + enable: old.folderNote, + rename: old.folderNoteRename, + addTitle: { + enable: old.folderNote, + key: old.frontmatterTitleKey, + }, + }, + metadataExtractorPath: old.metadataExtractorPath, + }, + conversion: { + hardbreak: old.hardBreak, + dataview: old.migrateDataview, + censorText: old.censorText, + tags: { + inline: old.inlineTags, + exclude: old.excludeDataviewValue, + fields: old.dataviewFields, + }, + links: { + internal: old.migrateForGithub, + unshared: old.migrateInternalNonShared, + wiki: old.migrateWikiLinks, + slugify: false, + }, + }, + embed: { + attachments: old.embedImage, + keySendFile: old.metadataFileFields, + notes: old.embedNotes, + folder: old.defaultImageFolder, + charConvert: "->", + convertEmbedToLinks: "keep", + overrideAttachments: [], + unHandledObsidianExt: [], + sendSimpleLinks: true, + }, + plugin: { + shareKey: old.shareKey, + fileMenu: old.fileMenu, + editorMenu: old.editorMenu, + excludedFolder: old.excludedFolder, + copyLink: { + enable: old.copyLink, + links: old.mainLink, + removePart: old.linkRemover.split(/[,\n]\W*/).map((s) => s.trim()), + addCmd: false, + transform: { + toUri: true, + slugify: "lower", + applyRegex: [], + }, + }, + noticeError: old.logNotice, + displayModalRepoEditing: false, + setFrontmatterKey: "Set", + }, + }; + //@ts-ignore + const token = old.GhToken + ? old.GhToken + : plugin.settings.github.token + ? plugin.settings.github.token + : undefined; + await migrateToken(plugin, token); + await plugin.saveSettings(); +} diff --git a/src/settings/modals/import_export.ts b/src/settings/modals/import_export.ts index dfb31013..1e6352d8 100644 --- a/src/settings/modals/import_export.ts +++ b/src/settings/modals/import_export.ts @@ -1,395 +1,447 @@ -import {GitHubPublisherSettings, Preset} from "@interfaces/main"; -import {Octokit} from "@octokit/core"; -import i18next from "i18next"; -import { - App, - ButtonComponent, - FuzzySuggestModal, - Modal, - Notice, - Platform, - Setting, - TextAreaComponent} from "obsidian"; -import GithubPublisher from "src/main"; -import {GithubPublisherSettingsTab} from "src/settings"; -import { migrateSettings,OldSettings } from "src/settings/migrate"; -import {logs, notif} from "src/utils"; - - -export type SettingValue = number | string | boolean | unknown; - -function clone(obj: GitHubPublisherSettings): GitHubPublisherSettings { - return JSON.parse(JSON.stringify(obj)); -} -/** - * - * Credit : Style Settings Plugin - * URL : https://github.com/mgmeyers/obsidian-github-publisher - **/ - -export class ImportModal extends Modal { - plugin: GithubPublisher; - settingsPage: HTMLElement; - settingsTab: GithubPublisherSettingsTab; - settings: GitHubPublisherSettings; - constructor(app: App, plugin: GithubPublisher, settingsPage: HTMLElement, settingsTab: GithubPublisherSettingsTab) { - super(app); - this.plugin = plugin; - this.settingsPage = settingsPage; - this.settingsTab = settingsTab; - this.settings = plugin.settings; - } - - async censorRepositoryData(original: GitHubPublisherSettings) { - logs({settings: original}, "original settings:", original); - this.settings.plugin.dev = original.plugin.dev; - this.settings.plugin.migrated = original.plugin.migrated; - this.settings.plugin.displayModalRepoEditing = original.plugin.displayModalRepoEditing; - this.settings.plugin.noticeError = original.plugin.noticeError; - this.settings.plugin.copyLink.addCmd = original.plugin.copyLink.addCmd; - this.settings.plugin.fileMenu = original.plugin.fileMenu; - this.settings.plugin.editorMenu = original.plugin.editorMenu; - this.plugin.settings.github.repo = original.github.repo; - this.plugin.settings.github.user = original.github.user; - this.plugin.settings.github.otherRepo = original.github.otherRepo; - await this.plugin.saveSettings(); - } - - onOpen() { - const {contentEl} = this; - - new Setting(contentEl) - .setName(i18next.t("modals.import.title") ) - .setDesc(i18next.t("modals.import.desc") ); - - new Setting(contentEl).then((setting) => { - // Build an error message container - const errorSpan = createSpan({ - cls: "github-publisher-import-error", - text: i18next.t("modals.import.error.span") , - }); - setting.nameEl.appendChild(errorSpan); - const importAndClose = async (str: string) => { - if (str) { - try { - let importedSettings = JSON.parse(str); - if (Object.keys(importedSettings).includes("editorMenu")) { - //need to convert old settings to new settings - const oldSettings = importedSettings as unknown as OldSettings; - await migrateSettings(oldSettings, this.plugin, true); - logs({settings: this.plugin.settings}, i18next.t("informations.migrating.oldSettings")); - } else { - logs({settings: this.plugin.settings}, i18next.t("informations.migrating.normalFormat")); - importedSettings = importedSettings as unknown as GitHubPublisherSettings; - //create a copy of actual settings - const actualSettings = clone(this.plugin.settings); - if (!(importedSettings.upload.replaceTitle instanceof Array)) { - importedSettings.upload.replaceTitle = [importedSettings.upload.replaceTitle]; - } - - for (const [key, value] of Object.entries(importedSettings)) { - // @ts-ignore - this.plugin.settings[key] = value; - } - await this.censorRepositoryData(actualSettings); - await this.plugin.saveSettings(); - } - this.close(); - } catch (e) { - errorSpan.addClass("active"); - errorSpan.setText(`${i18next.t("modals.import.error.span")}${e}`); - } - } else { - errorSpan.addClass("active"); - errorSpan.setText(`${i18next.t("modals.import.error.span")}: ${i18next.t("modals.import.error.isEmpty")}`); - } - }; - setting.controlEl.createEl( - "input", - { - cls: "github-publisher-import-input", - attr: { - id: "github-publisher-import-input", - name: "github-publisher-import-input", - type: "file", - accept: ".json", - }, - }, - (importInput) => { - // Set up a FileReader so we can parse the file contents - importInput.addEventListener("change", (e) => { - const reader = new FileReader(); - - reader.onload = async (e: ProgressEvent) => { - await importAndClose(e.target!.result!.toString().trim()); - }; - - reader.readAsText((e.target as HTMLInputElement).files![0]); - }); - } - ); - - // Build a label we will style as a link - setting.controlEl.createEl("label", { - cls: "github-publisher-import-label", - text: i18next.t("modals.import.importFromFile") , - attr: { - for: "github-publisher-import-input", - }, - }); - - const textArea = new TextAreaComponent(contentEl) - .setPlaceholder(i18next.t("modals.import.paste") ).then((ta) => { - const saveButton = new ButtonComponent(contentEl).setButtonText(i18next.t("common.save") ).onClick(async () => { - await importAndClose(ta.getValue().trim()); - }); - saveButton.buttonEl.addClass("github-publisher-import-save-button"); - }); - textArea.inputEl.addClass("github-publisher-import-textarea"); - }); - } - - onClose() { - const { contentEl } = this; - contentEl.empty(); - this.settingsPage.empty(); - // @ts-ignore - let openedTab = this.plugin.settings.tabsID ?? document.querySelector(".settings-tab.settings-tab-active") ? document.querySelector(".settings-tab.settings-tab-active .settings-tab-name").innerText : i18next.t("settings.github.title") ; - openedTab = openedTab.trim(); - switch (openedTab) { - case i18next.t("settings.github.title") : - this.settingsTab.renderGithubConfiguration(); - break; - case i18next.t("settings.upload.title"): - this.settingsTab.renderUploadConfiguration(); - break; - case i18next.t("settings.conversion.title") : - this.settingsTab.renderTextConversion(); - break; - case i18next.t("settings.embed.title") : - this.settingsTab.renderEmbedConfiguration(); - break; - case i18next.t("settings.plugin.title") : - this.settingsTab.renderPluginSettings(); - break; - case i18next.t("settings.help.title") : - this.settingsTab.renderHelp(); - break; - } - - } -} - - -export class ExportModal extends Modal { - plugin: GithubPublisher; - - constructor(app: App, plugin: GithubPublisher) { - super(app); - this.plugin = plugin; - } - - censorGithubSettingsData(censuredSettings: GitHubPublisherSettings) { - const cloneCensored = Object(censuredSettings); - const { github } = cloneCensored; - if (cloneCensored.tabsID) - delete cloneCensored.tabsID; - if (github) { - delete github.repo; - delete github.user; - delete github.otherRepo; - delete github.rateLimit; - } - delete cloneCensored.plugin.dev; - delete cloneCensored.plugin.migrated; - delete cloneCensored.plugin.displayModalRepoEditing; - delete cloneCensored.plugin.noticeError; - delete cloneCensored.plugin.copyLink.addCmd; - delete cloneCensored.plugin.fileMenu; - delete cloneCensored.plugin.editorMenu; - return cloneCensored; - } - - onOpen() { - const {contentEl, modalEl} = this; - modalEl.addClass("modal-github-publisher"); - new Setting(contentEl) - .setName(i18next.t("modals.export.title") ) - .setDesc(i18next.t("modals.export.desc") ) - .then((setting) => { - //create a copy of the settings object - const censuredSettings = this.censorGithubSettingsData(clone(this.plugin.settings)); - const output = JSON.stringify(censuredSettings, null, 2); - setting.controlEl.createEl("a", - { - cls: "github-publisher-copy", - text: i18next.t("modals.export.copy") , - href: "#", - }, - (copyButton) => { - const textArea = new TextAreaComponent(contentEl).setValue(output).then((textarea) => { - copyButton.addEventListener("click", (e) => { - e.preventDefault(); - - // Select the textarea contents and copy them to the clipboard - textarea.inputEl.select(); - textarea.inputEl.setSelectionRange(0, 99999); - document.execCommand("copy"); - - copyButton.addClass("success"); - - setTimeout(() => { - // If the button is still in the dom, remove the success class - if (copyButton.parentNode) { - copyButton.removeClass("success"); - } - }, 2000); - }); - }); - textArea.inputEl.addClass("github-publisher-export-textarea"); - }); - - if (Platform.isDesktop) { - setting.controlEl.createEl("a", { - cls: "github-publisher-download", - text: i18next.t("modals.export.download") , - attr: { - download: "github-publisher.json", - href: `data:application/json;charset=utf-8,${encodeURIComponent(output)}`, - }, - }); - } else if (Platform.isMobile) { - setting.addButton((b) => - b - .setButtonText(i18next.t("modals.export.download") ) - .onClick(() => { - // Can't use the method above on mobile, so we'll just open a new tab - //create a temporary file - this.app.vault.adapter.write(`${this.app.vault.configDir}/plugins/obsidian-mkdocs-publisher/._tempSettings.json`, output); - //open the file with default application - //eslint-disable-next-line - (this.app as any).openWithDefaultApp(`${this.app.vault.configDir}/plugins/obsidian-mkdocs-publisher/._tempSettings.json`); - })); - } - }); - } - - onClose() { - try{ - this.app.vault.adapter.trashSystem(`${this.app.vault.configDir}/plugins/obsidian-mkdocs-publisher/._tempSettings.json`); - }catch(e){ - logs({settings: this.plugin.settings}, "Error while deleting temporary file", e); - } - const {contentEl} = this; - contentEl.empty(); - } -} - - -export class ImportLoadPreset extends FuzzySuggestModal { - octokit: Octokit; - plugin: GithubPublisher; - presetList: Preset[]; - page: GithubPublisherSettingsTab; - settings: GitHubPublisherSettings; - - - constructor(app: App, plugin: GithubPublisher, presetList: Preset[], octokit: Octokit, page: GithubPublisherSettingsTab) { - super(app); - this.plugin = plugin; - this.presetList = presetList; - this.octokit = octokit; - this.page = page; - this.settings = plugin.settings; - } - - getItems(): Preset[] { - return this.presetList; - } - - getItemText(item: Preset): string { - return item.name; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - onChooseItem(item: Preset, evt: MouseEvent | KeyboardEvent): void { - const presetSettings = item.settings; - logs({settings: presetSettings},"onChooseItem"); - try { - const original = clone(this.plugin.settings); - if (!(presetSettings.upload.replaceTitle instanceof Array)) { - presetSettings.upload.replaceTitle = [presetSettings.upload.replaceTitle]; - } - - for (const [key, value] of Object.entries(presetSettings)) { - // @ts-ignore - this.settings[key] = value; - } - this.settings.plugin.dev = original.plugin.dev; - this.settings.plugin.migrated = original.plugin.migrated; - this.settings.plugin.displayModalRepoEditing = original.plugin.displayModalRepoEditing; - this.settings.plugin.noticeError = original.plugin.noticeError; - this.settings.plugin.copyLink.addCmd = original.plugin.copyLink.addCmd; - this.settings.plugin.fileMenu = original.plugin.fileMenu; - this.settings.plugin.editorMenu = original.plugin.editorMenu; - this.settings.github.repo = original.github.repo; - this.settings.github.user = original.github.user; - this.settings.github.otherRepo = original.github.otherRepo; - this.settings.github.rateLimit = original.github.rateLimit; - this.settings.tabsID = original.tabsID; - - this.plugin.saveSettings(); - this.page.renderSettingsPage("github-configuration"); - - } catch (e) { - new Notice(i18next.t("modals.import.error.span") + e); - notif({settings: this.settings}, "onChooseItem", e); - } - } -} - -export async function loadAllPresets(octokit: Octokit, plugin: GithubPublisher): Promise { - //load from gitHub repository - try { - const githubPreset = await octokit.request("GET /repos/{owner}/{repo}/contents/{path}", { - owner: "ObsidianPublisher", - repo: "plugin-presets", - path: "presets", - }); - - //create a list - const presetList: Preset[] = []; - if (!Array.isArray(githubPreset.data)) { - return presetList; - } - logs({settings: plugin.settings}, "LoadAllPreset", githubPreset); - for (const preset of githubPreset.data) { - if (preset.name.endsWith(".json")) { - const presetName = preset.name.replace(".json", ""); - presetList.push({ - name: presetName, - settings: await loadPresetContent(preset.path, octokit, plugin), - }); - } - } - return presetList; - } catch (e) { - notif({settings: plugin.settings, e: true}, "Couldn't load preset. Error:", e); - return []; - } -} - -export async function loadPresetContent(path: string, octokit: Octokit, plugin: GithubPublisher): Promise { - const presetContent = await octokit.request("GET /repos/{owner}/{repo}/contents/{path}", { - owner: "ObsidianPublisher", - repo: "plugin-presets", - path, - }); - // @ts-ignore - if (!presetContent.data?.content) { - return plugin.settings; - } - // @ts-ignore - const presetContentDecoded = atob(presetContent.data.content); - return JSON.parse(presetContentDecoded) as unknown as GitHubPublisherSettings; -} +import { GitHubPublisherSettings, Preset } from "@interfaces/main"; +import { Octokit } from "@octokit/core"; +import i18next from "i18next"; +import { + App, + ButtonComponent, + FuzzySuggestModal, + Modal, + Notice, + Platform, + Setting, + TextAreaComponent, +} from "obsidian"; +import GithubPublisher from "src/main"; +import { GithubPublisherSettingsTab } from "src/settings"; +import { migrateSettings, OldSettings } from "src/settings/migrate"; +import { logs, notif } from "src/utils"; + +export type SettingValue = number | string | boolean | unknown; + +function clone(obj: GitHubPublisherSettings): GitHubPublisherSettings { + return JSON.parse(JSON.stringify(obj)); +} +/** + * + * Credit : Style Settings Plugin + * URL : https://github.com/mgmeyers/obsidian-github-publisher + **/ + +export class ImportModal extends Modal { + plugin: GithubPublisher; + settingsPage: HTMLElement; + settingsTab: GithubPublisherSettingsTab; + settings: GitHubPublisherSettings; + constructor( + app: App, + plugin: GithubPublisher, + settingsPage: HTMLElement, + settingsTab: GithubPublisherSettingsTab + ) { + super(app); + this.plugin = plugin; + this.settingsPage = settingsPage; + this.settingsTab = settingsTab; + this.settings = plugin.settings; + } + + async censorRepositoryData(original: GitHubPublisherSettings) { + logs({ settings: original }, "original settings:", original); + this.settings.plugin.dev = original.plugin.dev; + this.settings.plugin.migrated = original.plugin.migrated; + this.settings.plugin.displayModalRepoEditing = + original.plugin.displayModalRepoEditing; + this.settings.plugin.noticeError = original.plugin.noticeError; + this.settings.plugin.copyLink.addCmd = original.plugin.copyLink.addCmd; + this.settings.plugin.fileMenu = original.plugin.fileMenu; + this.settings.plugin.editorMenu = original.plugin.editorMenu; + this.plugin.settings.github.repo = original.github.repo; + this.plugin.settings.github.user = original.github.user; + this.plugin.settings.github.otherRepo = original.github.otherRepo; + await this.plugin.saveSettings(); + } + + onOpen() { + const { contentEl } = this; + + new Setting(contentEl) + .setName(i18next.t("modals.import.title")) + .setDesc(i18next.t("modals.import.desc")); + + new Setting(contentEl).then((setting) => { + // Build an error message container + const errorSpan = createSpan({ + cls: "github-publisher-import-error", + text: i18next.t("modals.import.error.span"), + }); + setting.nameEl.appendChild(errorSpan); + const importAndClose = async (str: string) => { + if (str) { + try { + let importedSettings = JSON.parse(str); + if (Object.keys(importedSettings).includes("editorMenu")) { + //need to convert old settings to new settings + const oldSettings = importedSettings as unknown as OldSettings; + await migrateSettings(oldSettings, this.plugin, true); + logs( + { settings: this.plugin.settings }, + i18next.t("informations.migrating.oldSettings") + ); + } else { + logs( + { settings: this.plugin.settings }, + i18next.t("informations.migrating.normalFormat") + ); + importedSettings = importedSettings as unknown as GitHubPublisherSettings; + //create a copy of actual settings + const actualSettings = clone(this.plugin.settings); + if (!(importedSettings.upload.replaceTitle instanceof Array)) { + importedSettings.upload.replaceTitle = [ + importedSettings.upload.replaceTitle, + ]; + } + + for (const [key, value] of Object.entries(importedSettings)) { + // @ts-ignore + this.plugin.settings[key] = value; + } + await this.censorRepositoryData(actualSettings); + await this.plugin.saveSettings(); + } + this.close(); + } catch (e) { + errorSpan.addClass("active"); + errorSpan.setText(`${i18next.t("modals.import.error.span")}${e}`); + } + } else { + errorSpan.addClass("active"); + errorSpan.setText( + `${i18next.t("modals.import.error.span")}: ${i18next.t( + "modals.import.error.isEmpty" + )}` + ); + } + }; + setting.controlEl.createEl( + "input", + { + cls: "github-publisher-import-input", + attr: { + id: "github-publisher-import-input", + name: "github-publisher-import-input", + type: "file", + accept: ".json", + }, + }, + (importInput) => { + // Set up a FileReader so we can parse the file contents + importInput.addEventListener("change", (e) => { + const reader = new FileReader(); + + reader.onload = async (e: ProgressEvent) => { + await importAndClose(e.target!.result!.toString().trim()); + }; + + reader.readAsText((e.target as HTMLInputElement).files![0]); + }); + } + ); + + // Build a label we will style as a link + setting.controlEl.createEl("label", { + cls: "github-publisher-import-label", + text: i18next.t("modals.import.importFromFile"), + attr: { + for: "github-publisher-import-input", + }, + }); + + const textArea = new TextAreaComponent(contentEl) + .setPlaceholder(i18next.t("modals.import.paste")) + .then((ta) => { + const saveButton = new ButtonComponent(contentEl) + .setButtonText(i18next.t("common.save")) + .onClick(async () => { + await importAndClose(ta.getValue().trim()); + }); + saveButton.buttonEl.addClass("github-publisher-import-save-button"); + }); + textArea.inputEl.addClass("github-publisher-import-textarea"); + }); + } + + onClose() { + const { contentEl } = this; + contentEl.empty(); + this.settingsPage.empty(); + // @ts-ignore + let openedTab = + this.plugin.settings.tabsID ?? + document.querySelector(".settings-tab.settings-tab-active") + ? document.querySelector(".settings-tab.settings-tab-active .settings-tab-name") + .innerText + : i18next.t("settings.github.title"); + openedTab = openedTab.trim(); + switch (openedTab) { + case i18next.t("settings.github.title"): + this.settingsTab.renderGithubConfiguration(); + break; + case i18next.t("settings.upload.title"): + this.settingsTab.renderUploadConfiguration(); + break; + case i18next.t("settings.conversion.title"): + this.settingsTab.renderTextConversion(); + break; + case i18next.t("settings.embed.title"): + this.settingsTab.renderEmbedConfiguration(); + break; + case i18next.t("settings.plugin.title"): + this.settingsTab.renderPluginSettings(); + break; + case i18next.t("settings.help.title"): + this.settingsTab.renderHelp(); + break; + } + } +} + +export class ExportModal extends Modal { + plugin: GithubPublisher; + + constructor(app: App, plugin: GithubPublisher) { + super(app); + this.plugin = plugin; + } + + censorGithubSettingsData(censuredSettings: GitHubPublisherSettings) { + const cloneCensored = Object(censuredSettings); + const { github } = cloneCensored; + if (cloneCensored.tabsID) delete cloneCensored.tabsID; + if (github) { + delete github.repo; + delete github.user; + delete github.otherRepo; + delete github.rateLimit; + } + delete cloneCensored.plugin.dev; + delete cloneCensored.plugin.migrated; + delete cloneCensored.plugin.displayModalRepoEditing; + delete cloneCensored.plugin.noticeError; + delete cloneCensored.plugin.copyLink.addCmd; + delete cloneCensored.plugin.fileMenu; + delete cloneCensored.plugin.editorMenu; + return cloneCensored; + } + + onOpen() { + const { contentEl, modalEl } = this; + modalEl.addClass("modal-github-publisher"); + new Setting(contentEl) + .setName(i18next.t("modals.export.title")) + .setDesc(i18next.t("modals.export.desc")) + .then((setting) => { + //create a copy of the settings object + const censuredSettings = this.censorGithubSettingsData( + clone(this.plugin.settings) + ); + const output = JSON.stringify(censuredSettings, null, 2); + setting.controlEl.createEl( + "a", + { + cls: "github-publisher-copy", + text: i18next.t("modals.export.copy"), + href: "#", + }, + (copyButton) => { + const textArea = new TextAreaComponent(contentEl) + .setValue(output) + .then((textarea) => { + copyButton.addEventListener("click", (e) => { + e.preventDefault(); + + // Select the textarea contents and copy them to the clipboard + textarea.inputEl.select(); + textarea.inputEl.setSelectionRange(0, 99999); + document.execCommand("copy"); + + copyButton.addClass("success"); + + setTimeout(() => { + // If the button is still in the dom, remove the success class + if (copyButton.parentNode) { + copyButton.removeClass("success"); + } + }, 2000); + }); + }); + textArea.inputEl.addClass("github-publisher-export-textarea"); + } + ); + + if (Platform.isDesktop) { + setting.controlEl.createEl("a", { + cls: "github-publisher-download", + text: i18next.t("modals.export.download"), + attr: { + download: "github-publisher.json", + href: `data:application/json;charset=utf-8,${encodeURIComponent(output)}`, + }, + }); + } else if (Platform.isMobile) { + setting.addButton((b) => + b.setButtonText(i18next.t("modals.export.download")).onClick(() => { + // Can't use the method above on mobile, so we'll just open a new tab + //create a temporary file + this.app.vault.adapter.write( + `${this.app.vault.configDir}/plugins/obsidian-mkdocs-publisher/._tempSettings.json`, + output + ); + //open the file with default application + //eslint-disable-next-line + (this.app as any).openWithDefaultApp( + `${this.app.vault.configDir}/plugins/obsidian-mkdocs-publisher/._tempSettings.json` + ); + }) + ); + } + }); + } + + onClose() { + try { + this.app.vault.adapter.trashSystem( + `${this.app.vault.configDir}/plugins/obsidian-mkdocs-publisher/._tempSettings.json` + ); + } catch (e) { + logs({ settings: this.plugin.settings }, "Error while deleting temporary file", e); + } + const { contentEl } = this; + contentEl.empty(); + } +} + +export class ImportLoadPreset extends FuzzySuggestModal { + octokit: Octokit; + plugin: GithubPublisher; + presetList: Preset[]; + page: GithubPublisherSettingsTab; + settings: GitHubPublisherSettings; + + constructor( + app: App, + plugin: GithubPublisher, + presetList: Preset[], + octokit: Octokit, + page: GithubPublisherSettingsTab + ) { + super(app); + this.plugin = plugin; + this.presetList = presetList; + this.octokit = octokit; + this.page = page; + this.settings = plugin.settings; + } + + getItems(): Preset[] { + return this.presetList; + } + + getItemText(item: Preset): string { + return item.name; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onChooseItem(item: Preset, evt: MouseEvent | KeyboardEvent): void { + const presetSettings = item.settings; + logs({ settings: presetSettings }, "onChooseItem"); + try { + const original = clone(this.plugin.settings); + if (!(presetSettings.upload.replaceTitle instanceof Array)) { + presetSettings.upload.replaceTitle = [presetSettings.upload.replaceTitle]; + } + + for (const [key, value] of Object.entries(presetSettings)) { + // @ts-ignore + this.settings[key] = value; + } + this.settings.plugin.dev = original.plugin.dev; + this.settings.plugin.migrated = original.plugin.migrated; + this.settings.plugin.displayModalRepoEditing = + original.plugin.displayModalRepoEditing; + this.settings.plugin.noticeError = original.plugin.noticeError; + this.settings.plugin.copyLink.addCmd = original.plugin.copyLink.addCmd; + this.settings.plugin.fileMenu = original.plugin.fileMenu; + this.settings.plugin.editorMenu = original.plugin.editorMenu; + this.settings.github.repo = original.github.repo; + this.settings.github.user = original.github.user; + this.settings.github.otherRepo = original.github.otherRepo; + this.settings.github.rateLimit = original.github.rateLimit; + this.settings.tabsID = original.tabsID; + + this.plugin.saveSettings(); + this.page.renderSettingsPage("github-configuration"); + } catch (e) { + new Notice(i18next.t("modals.import.error.span") + e); + notif({ settings: this.settings }, "onChooseItem", e); + } + } +} + +export async function loadAllPresets( + octokit: Octokit, + plugin: GithubPublisher +): Promise { + //load from gitHub repository + try { + const githubPreset = await octokit.request( + "GET /repos/{owner}/{repo}/contents/{path}", + { + owner: "ObsidianPublisher", + repo: "plugin-presets", + path: "presets", + } + ); + + //create a list + const presetList: Preset[] = []; + if (!Array.isArray(githubPreset.data)) { + return presetList; + } + logs({ settings: plugin.settings }, "LoadAllPreset", githubPreset); + for (const preset of githubPreset.data) { + if (preset.name.endsWith(".json")) { + const presetName = preset.name.replace(".json", ""); + presetList.push({ + name: presetName, + settings: await loadPresetContent(preset.path, octokit, plugin), + }); + } + } + return presetList; + } catch (e) { + notif({ settings: plugin.settings, e: true }, "Couldn't load preset. Error:", e); + return []; + } +} + +export async function loadPresetContent( + path: string, + octokit: Octokit, + plugin: GithubPublisher +): Promise { + const presetContent = await octokit.request( + "GET /repos/{owner}/{repo}/contents/{path}", + { + owner: "ObsidianPublisher", + repo: "plugin-presets", + path, + } + ); + // @ts-ignore + if (!presetContent.data?.content) { + return plugin.settings; + } + // @ts-ignore + const presetContentDecoded = atob(presetContent.data.content); + return JSON.parse(presetContentDecoded) as unknown as GitHubPublisherSettings; +} diff --git a/src/settings/modals/list_changed.ts b/src/settings/modals/list_changed.ts index b7cc071b..42673929 100644 --- a/src/settings/modals/list_changed.ts +++ b/src/settings/modals/list_changed.ts @@ -1,77 +1,98 @@ -import { Deleted, ListEditedFiles } from "@interfaces"; -import i18next from "i18next"; -import { App,Modal } from "obsidian"; - - -export class ListChangedFiles extends Modal { - listChanged: ListEditedFiles | Deleted; - constructor(app: App, listChanged: ListEditedFiles | Deleted) { - super(app); - this.listChanged = listChanged ; - } - - displayListOfFile(toDisplay: string[], contentEl: HTMLElement) { - if (!toDisplay.length) return; - const ul = contentEl.createEl("ul"); - toDisplay.forEach((file) => { - let emoji = "❓"; - const ext = file.split(".").pop() ?? ""; - if (["md"].includes(ext)) { - emoji = "🗒️"; - } else if ([".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"].includes(`.${ext}`)) { - emoji = "🖼️"; - } else if ([".mp3", ".wav", ".ogg", ".flac", ".aac"].includes(`.${ext}`)) { - emoji = "🎵"; - } else if ([".mp4", ".avi", ".mov", ".mkv", ".webm"].includes(`.${ext}`)) { - emoji = "🎥"; - } else if ([".pdf"].includes(`.${ext}`)) { - emoji = "📄"; - } - const li = ul.createEl("li"); - li.createEl("span", { text: emoji, cls: "emoji" }); - li.createEl("code", { text: file, cls: "code-title"}); - }); - } - - onOpen() { - /** - * a modal is open and the user can read what files was : - * - added - * - modified - * - deleted - * - Not uploaded - */ - const { contentEl } = this; - contentEl.empty(); - contentEl.addClasses(["github-publisher", "modals", "list-changed"]); - contentEl.createEl("h2", { text: i18next.t("modals.listChangedFiles.title"), cls: "success"}); - if (Object.keys(this.listChanged).contains("edited")) { - this.listChanged = this.listChanged as ListEditedFiles; - contentEl.createEl("h3", { text: `📤 ${i18next.t("modals.listChangedFiles.added")}`}); - this.displayListOfFile(this.listChanged.added, contentEl); - contentEl.createEl("h3", { text: `✒️ ${i18next.t("modals.listChangedFiles.edited")}`}); - this.displayListOfFile(this.listChanged.edited, contentEl); - contentEl.createEl("h3", { text: `🗑️ ${i18next.t("modals.listChangedFiles.deleted")}`}); - this.displayListOfFile(this.listChanged.deleted, contentEl); - - contentEl.createEl("h2", { text: `❌ ${i18next.t("modals.listChangedFiles.error")}`, cls: "error"}); - contentEl.createEl("h3", { text: `📤 ${i18next.t("modals.listChangedFiles.unpublished")}`}); - this.displayListOfFile(this.listChanged.unpublished, contentEl); - contentEl.createEl("h3", { text: `♻️ ${i18next.t("modals.listChangedFiles.notDeleted")}`}); - this.displayListOfFile(this.listChanged.notDeleted, contentEl); - } else { - this.listChanged = this.listChanged as Deleted; - contentEl.createEl("h3", { text: `🗑️ ${i18next.t("modals.listChangedFiles.deleted")}`}); - this.displayListOfFile(this.listChanged.deleted, contentEl); - contentEl.createEl("h3", { text: `❌ ${i18next.t("modals.listChangedFiles.error")}`, cls: "error"}); - contentEl.createEl("h3", { text: `♻️ ${i18next.t("modals.listChangedFiles.notDeleted")}`}); - this.displayListOfFile(this.listChanged.undeleted, contentEl); - } - } - - onClose() { - const { contentEl } = this; - contentEl.empty(); - } - -} +import { Deleted, ListEditedFiles } from "@interfaces"; +import i18next from "i18next"; +import { App, Modal } from "obsidian"; + +export class ListChangedFiles extends Modal { + listChanged: ListEditedFiles | Deleted; + constructor(app: App, listChanged: ListEditedFiles | Deleted) { + super(app); + this.listChanged = listChanged; + } + + displayListOfFile(toDisplay: string[], contentEl: HTMLElement) { + if (!toDisplay.length) return; + const ul = contentEl.createEl("ul"); + toDisplay.forEach((file) => { + let emoji = "❓"; + const ext = file.split(".").pop() ?? ""; + if (["md"].includes(ext)) { + emoji = "🗒️"; + } else if ([".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"].includes(`.${ext}`)) { + emoji = "🖼️"; + } else if ([".mp3", ".wav", ".ogg", ".flac", ".aac"].includes(`.${ext}`)) { + emoji = "🎵"; + } else if ([".mp4", ".avi", ".mov", ".mkv", ".webm"].includes(`.${ext}`)) { + emoji = "🎥"; + } else if ([".pdf"].includes(`.${ext}`)) { + emoji = "📄"; + } + const li = ul.createEl("li"); + li.createEl("span", { text: emoji, cls: "emoji" }); + li.createEl("code", { text: file, cls: "code-title" }); + }); + } + + onOpen() { + /** + * a modal is open and the user can read what files was : + * - added + * - modified + * - deleted + * - Not uploaded + */ + const { contentEl } = this; + contentEl.empty(); + contentEl.addClasses(["github-publisher", "modals", "list-changed"]); + contentEl.createEl("h2", { + text: i18next.t("modals.listChangedFiles.title"), + cls: "success", + }); + if (Object.keys(this.listChanged).contains("edited")) { + this.listChanged = this.listChanged as ListEditedFiles; + contentEl.createEl("h3", { + text: `📤 ${i18next.t("modals.listChangedFiles.added")}`, + }); + this.displayListOfFile(this.listChanged.added, contentEl); + contentEl.createEl("h3", { + text: `✒️ ${i18next.t("modals.listChangedFiles.edited")}`, + }); + this.displayListOfFile(this.listChanged.edited, contentEl); + contentEl.createEl("h3", { + text: `🗑️ ${i18next.t("modals.listChangedFiles.deleted")}`, + }); + this.displayListOfFile(this.listChanged.deleted, contentEl); + + contentEl.createEl("h2", { + text: `❌ ${i18next.t("modals.listChangedFiles.error")}`, + cls: "error", + }); + contentEl.createEl("h3", { + text: `📤 ${i18next.t("modals.listChangedFiles.unpublished")}`, + }); + this.displayListOfFile(this.listChanged.unpublished, contentEl); + contentEl.createEl("h3", { + text: `♻️ ${i18next.t("modals.listChangedFiles.notDeleted")}`, + }); + this.displayListOfFile(this.listChanged.notDeleted, contentEl); + } else { + this.listChanged = this.listChanged as Deleted; + contentEl.createEl("h3", { + text: `🗑️ ${i18next.t("modals.listChangedFiles.deleted")}`, + }); + this.displayListOfFile(this.listChanged.deleted, contentEl); + contentEl.createEl("h3", { + text: `❌ ${i18next.t("modals.listChangedFiles.error")}`, + cls: "error", + }); + contentEl.createEl("h3", { + text: `♻️ ${i18next.t("modals.listChangedFiles.notDeleted")}`, + }); + this.displayListOfFile(this.listChanged.undeleted, contentEl); + } + } + + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +} diff --git a/src/settings/modals/manage_repo.ts b/src/settings/modals/manage_repo.ts index 40b17852..5346dc46 100644 --- a/src/settings/modals/manage_repo.ts +++ b/src/settings/modals/manage_repo.ts @@ -1,611 +1,654 @@ -import {GitHubPublisherSettings, GithubTiersVersion, Repository} from "@interfaces"; -import i18next from "i18next"; -import {AbstractInputSuggest, App, Modal, Notice, Setting, TFile} from "obsidian"; -import GithubPublisher from "src/main"; -import { migrateToken } from "src/settings/migrate"; -import {checkRepositoryValidity, verifyRateLimitAPI} from "src/utils/data_validation_test"; - - -class SetClassSuggester extends AbstractInputSuggest { - plugin: GithubPublisher; - constructor(private inputEl: HTMLInputElement, plugin: GithubPublisher, private onSubmit: (value: TFile) => void) { - super(plugin.app, inputEl); - this.plugin = plugin; - } - - renderSuggestion(value: TFile, el: HTMLElement): void { - el.setText(value.path); - } - - getSuggestions(query: string): TFile[] { - return this.plugin.app.vault.getFiles().filter((file) => { - if (file.extension === "md" && file.path.toLowerCase().contains(query.toLowerCase())) { - const frontmatter = this.plugin.app.metadataCache.getFileCache(file)?.frontmatter; - if (frontmatter) return true; - } - return false; - }); - } - - //eslint-disable-next-line @typescript-eslint/no-unused-vars - selectSuggestion(value: TFile, evt: MouseEvent | KeyboardEvent): void { - this.onSubmit(value); - this.inputEl.value = value.path; - this.inputEl.focus(); - this.inputEl.trigger("input"); - this.close(); - } -} - -/** - * @description This class is used to add a new repo to the settings in the "otherRepo" in the github setting section - * It will list all the repo in the settings and allow the user to add a new one, edit or delete an existing one - * @extends Modal - * @param {App} app - the Obsidian App - * @param {GitHubPublisherSettings} settings - the plugin settings - * @param {string} branchName - the branch name - * @param {GithubPublisher} plugin - the plugin - * @param {Repository[]} repository - the list of repo in the settings - * @param {(result: Repository[]) => void} onSubmit - the function to call when the modal is submitted - */ - -export class ModalAddingNewRepository extends Modal { - settings: GitHubPublisherSettings; - plugin: GithubPublisher; - branchName: string; - repository: Repository[]; - onSubmit: (result: Repository[]) => void; - - constructor( - app: App, - settings: GitHubPublisherSettings, - branchName: string, - plugin: GithubPublisher, - repository: Repository[], - onSubmit: (result: Repository[]) => void) { - super(app); - this.settings = settings; - this.repository = repository; - this.plugin = plugin; - this.onSubmit = onSubmit; - this.branchName = branchName; - } - - onOpen() { - const {contentEl} = this; - contentEl.empty(); - contentEl.addClasses(["github-publisher", "modals", "manage-repo", "add"]); - contentEl.createEl("h2", {text: i18next.t("settings.github.smartRepo.modals.title")}); - contentEl.createEl("p", {text: i18next.t("settings.github.smartRepo.modals.desc")}); - contentEl.createEl("p", {text: i18next.t("settings.github.smartRepo.modals.frontmatterInfo")}); - contentEl.createEl("p", {text: i18next.t("settings.plugin.shareKey.otherRepo")}); - - - const defaultRepository: Repository = { - smartKey: "smartkey", - user: this.settings.github.user, - repo: this.settings.github.repo, - branch: this.settings.github.branch, - automaticallyMergePR: this.settings.github.automaticallyMergePR, - api: { - tiersForApi: this.settings.github.api.tiersForApi, - hostname: this.settings.github.api.hostname, - }, - workflow: { - commitMessage: this.settings.github.workflow.commitMessage, - name: "", - }, - createShortcuts: false, - shareKey: this.settings.plugin.shareKey, - copyLink: { - links: this.settings.plugin.copyLink.links, - removePart: [], - transform: { - toUri: this.settings.plugin.copyLink.transform.toUri, - slugify: this.settings.plugin.copyLink.transform.slugify, - applyRegex: this.settings.plugin.copyLink.transform.applyRegex - } - }, - set: null - }; - - new Setting(contentEl) - .setClass("max-width") - .setClass("display-none") - .addButton((button) => { - button - - .setButtonText(i18next.t("common.add", {things: i18next.t("settings.github.smartRepo.modals.newRepo").toLowerCase()})) - .onClick(() => { - this.repository.push(defaultRepository); - this.onOpen(); - }); - }); - - for (const repo of this.repository) { - const sett = new Setting(contentEl) - .setClass("max-width") - .setClass("display-none") - .addText((text) => { - text - .setPlaceholder("smartKey") - .setValue(repo.smartKey) - .onChange((value) => { - repo.smartKey = value.toLowerCase(); - sett.controlEl.setAttribute("smartKey", value.toLowerCase()); - }); - - }) - - .addExtraButton((btn) => { - btn - .setIcon("trash") - .onClick(() => { - this.repository.splice(this.repository.indexOf(repo), 1); - this.onOpen(); - }); - }) - .addExtraButton((btn) => { - btn - .setIcon("pencil") - .onClick(() => { - new ModalEditingRepository(this.app, repo, this.plugin, this.branchName, (result) => { - this.repository[this.repository.indexOf(repo)] = result; - }).open(); - }); - }); - - } - new Setting(contentEl) - .addButton((button) => { - button - .setButtonText(i18next.t("common.save")) - .setCta() - .onClick(() => { - const error = this.foundError(); - const input = error.repo.length > 0 ? this.containerEl.querySelector(`[smartkey="${error.repo}"] input`) : contentEl.querySelector("[placeholder=\"smartKey\"] input"); - if (error.type === "None") { - //remove error - input?.classList?.remove("error"); - this.onSubmit(this.repository); - this.close(); - } - input?.classList?.add("error"); - if (error.type === "duplicate") { - new Notice(i18next.t("settings.github.smartRepo.modals.duplicate")); - } else if (error.type === "default") { - new Notice(i18next.t("settings.github.smartRepo.modals.default")); - } else if (error.type === "empty") { - new Notice(i18next.t("settings.github.smartRepo.modals.empty")); - } - }); - }); - } - - foundError(): { repo: string; type: "duplicate" | "default" | "empty" | "None"} { - for (const repo of this.repository) { - if (this.plugin.settings.github.otherRepo.filter((r) => r.smartKey === repo.smartKey).length > 1) { - return {repo: repo.smartKey, type: "duplicate"}; - } else if (repo.smartKey === "default") { - return {repo: repo.smartKey, type: "default"}; - } else if (repo.smartKey.length === 0) { - return {repo: "", type: "empty"}; - } - } - return {repo: "", type: "None"}; - - } - - - onClose() { - const { contentEl } = this; - const error = this.foundError(); - if (error.type === "empty") { - //rename the repo - const repo = this.repository.filter((r) => r.smartKey === error.repo); - for (let i = 0; i < repo.length; i++) { - repo[i].smartKey = `smartkey-${i}`; - } - new Notice(`${i18next.t("settings.github.smartRepo.modals.empty")} ${i18next.t("common.rename")}`); - } else if (error.type === "duplicate") { - //rename the repo - const repo = this.repository.filter((r) => r.smartKey === error.repo); - for (let i = 0; i < repo.length; i++) { - repo[i].smartKey = `${repo[i].smartKey}-${i}`; - } - new Notice(`${i18next.t("settings.github.smartRepo.modals.duplicate")} ${i18next.t("common.rename")}`); - } else if (error.type === "default") { - //rename the repo - const repo = this.repository.filter((r) => r.smartKey === error.repo); - for (const r of repo) { - const randomKey = Math.random().toString(36).substring(2, 8); - r.smartKey = `${r.smartKey}-${randomKey}`; - } - new Notice(`${i18next.t("settings.github.smartRepo.modals.default")} ${i18next.t("common.rename")}`); - } - this.onSubmit(this.repository); - contentEl.empty(); - } -} - -/** - * @description Called by the ModalAddingNewRepository class, this class is used to edit an existing repo - * @extends Modal - * @param {App} app - The Obsidian App instance - * @param {Repository} repository - The repository to edit - * @param {GithubPublisher} GithubPublisher - The GithubPublisher instance - * @param {string} brancheName - The name of the branch (for validation) - * @param {function} onSubmit - The function to call when the modal is closed (to save the changes) - */ - -class ModalEditingRepository extends Modal { - repository: Repository; - branchName: string; - plugin: GithubPublisher; - onSubmit: (result: Repository) => void; - - constructor( - app: App, - repository: Repository, - GithubPublisher: GithubPublisher, - brancheName: string, - onSubmit: (result: Repository) => void) { - super(app); - this.repository = repository; - this.onSubmit = onSubmit; - this.branchName = brancheName; - this.plugin = GithubPublisher; - } - - onOpen() { - const {contentEl} = this; - contentEl.empty(); - contentEl.addClasses(["github-publisher", "modals", "manage-repo" , "edit"]); - new Setting(contentEl) - .setClass("no-display") - .addButton((button) => - button - .setCta() - .setButtonText(i18next.t("common.save")) - .onClick(async () => { - this.onSubmit(this.repository); - this.close(); - }) - ) - .addButton((button) => - button - .setWarning() - .setButtonText(i18next.t("common.cancel")) - .onClick(async () => { - this.close(); - }) - ); - contentEl.createEl("h2", {text: i18next.t("common.edit", {things: this.repository.smartKey})}); - - new Setting(contentEl) - .setName(i18next.t("settings.github.apiType.title")) - .setDesc(i18next.t("settings.github.apiType.desc")) - .addDropdown((dropdown) => { - dropdown - .addOption(GithubTiersVersion.free, i18next.t("settings.github.apiType.dropdown.free")) - .addOption(GithubTiersVersion.entreprise, i18next.t("settings.github.apiType.dropdown.enterprise")) - .setValue(this.repository.api.tiersForApi) - .onChange((value) => { - this.repository.api.tiersForApi = value as GithubTiersVersion; - this.onOpen(); - }); - }); - if (this.repository.api.tiersForApi === GithubTiersVersion.entreprise) { - new Setting(contentEl) - .setName(i18next.t("settings.github.apiType.hostname.title")) - .setDesc(i18next.t("settings.github.apiType.hostname.desc")) - .addText((text) => - text - .setPlaceholder("https://github.mycompany.com") - .setValue(this.repository.api.hostname) - .onChange(async (value) => { - this.repository.api.hostname = value.trim(); - }) - ); - } - - new Setting(contentEl) - .setName(i18next.t("settings.github.username.title")) - .setDesc(i18next.t("settings.github.username.desc")) - .addText((text) => - text - .setPlaceholder( - i18next.t("settings.github.username.title") - ) - .setValue(this.repository.user) - .onChange(async (value) => { - this.repository.user = value.trim(); - }) - ); - - new Setting(contentEl) - .setName(i18next.t("settings.github.repoName.title")) - .setDesc(i18next.t("settings.github.repoName.desc")) - .addText((text) => - text - .setPlaceholder(i18next.t("settings.github.repoName.placeholder")) - .setValue(this.repository.repo) - .onChange(async (value) => { - this.repository.repo = value.trim(); - }) - ); - - new Setting(contentEl) - .setName(i18next.t("settings.github.branch.title")) - .setDesc(i18next.t("settings.github.branch.desc")) - .addText((text) => - text - .setPlaceholder("main") - .setValue(this.repository.branch) - .onChange(async (value) => { - this.repository.branch = value.trim(); - }) - ); - const desc_ghToken = document.createDocumentFragment(); - desc_ghToken.createEl("span", undefined, (span) => { - span.innerText = i18next.t("settings.github.ghToken.desc"); - span.createEl("a", undefined, (link) => { - link.innerText = `${i18next.t("common.here")}.`; - link.href = - "https://github.com/settings/tokens/new?scopes=repo,workflow"; - }); - }); - new Setting(contentEl) - .setName(i18next.t("common.ghToken")) - .setDesc(desc_ghToken) - .addText(async (text) => { - const decryptedToken: string = await this.plugin.loadToken(this.repository.smartKey); - text - .setPlaceholder("ghp_1234567890") - .setValue(decryptedToken) - .onChange(async (value) => { - await migrateToken(this.plugin, value.trim(), this.repository.smartKey); - }); - }); - new Setting(contentEl) - .setName(i18next.t("settings.github.automaticallyMergePR")) - .addToggle((toggle) => - toggle - .setValue(this.repository.automaticallyMergePR) - .onChange(async (value) => { - this.repository.automaticallyMergePR = value; - }) - ); - new Setting(contentEl) - .setClass("no-display") - .addButton((button) => - button - .setButtonText(i18next.t("settings.github.testConnection")) - .setClass("connect") - .onClick(async () => { - const octokit = await this.plugin.reloadOctokit(this.repository.smartKey); - this.repository.verifiedRepo = await checkRepositoryValidity( - octokit, - this.repository, - null); - this.plugin.settings.github.rateLimit = await verifyRateLimitAPI(octokit.octokit, this.plugin.settings); - }) - ); - new Setting(contentEl) - .setName(i18next.t("settings.github.smartRepo.modals.shortcuts.title")) - .setDesc(i18next.t("settings.github.smartRepo.modals.shortcuts.desc")) - .addToggle((toggle) => - toggle - .setValue(this.repository.createShortcuts) - .onChange(async (value) => { - this.repository.createShortcuts = value; - }) - ); - - contentEl.createEl("h3", { text: "GitHub Workflow" }); - new Setting(contentEl) - .setName(i18next.t("settings.githubWorkflow.prRequest.title")) - .setDesc(i18next.t("settings.githubWorkflow.prRequest.desc")) - .addText((text) => - text - .setPlaceholder("[PUBLISHER] MERGE") - .setValue(this.repository.workflow.commitMessage) - .onChange(async (value) => { - if (value.trim().length === 0) { - value = "[PUBLISHER] MERGE"; - new Notice(i18next.t("settings.githubWorkflow.prRequest.error")); - } - this.repository.workflow.commitMessage = value; - }) - ); - new Setting(contentEl) - .setName(i18next.t("settings.githubWorkflow.githubAction.title")) - .setDesc( - i18next.t("settings.githubWorkflow.githubAction.desc") - ) - .addText((text) => { - text.setPlaceholder("ci") - .setValue(this.repository.workflow.name) - .onChange(async (value) => { - if (value.length > 0) { - value = value.trim(); - const yamlEndings = [".yml", ".yaml"]; - if (! yamlEndings.some(ending => value.endsWith(ending))) { - value += yamlEndings[0]; - } - } - this.repository.workflow.name = value; - }); - }); - - contentEl.createEl("h3", { text: i18next.t("settings.github.smartRepo.modals.otherConfig") }); - - new Setting(contentEl) - .setName(i18next.t("settings.plugin.setImport.title")) - .setDesc(i18next.t("settings.plugin.setImport.desc")) - .addSearch((search) => { - search - .setValue(this.repository.set ?? "") - .setPlaceholder("path/to/file.md"); - new SetClassSuggester(search.inputEl, this.plugin, (result) => { - this.repository.set = result.path; - const frontmatter = this.plugin.app.metadataCache.getFileCache(result)?.frontmatter; - this.plugin.repositoryFrontmatter[this.repository.smartKey] = frontmatter; - }); - }); - - new Setting(contentEl) - .setName(i18next.t("settings.plugin.shareKey.all.title")) - .setDesc(i18next.t("settings.plugin.shareKey.all.desc")) - .addToggle((toggle) => - toggle - .setValue(this.repository.shareAll?.enable ?? false) - .onChange(async (value) => { - this.repository.shareAll = { - enable: value, - excludedFileName: this.plugin.settings.plugin.shareAll?.excludedFileName ?? "DRAFT" - }; - this.onOpen(); - }) - ); - if (!this.repository.shareAll || !this.repository.shareAll.enable) { - new Setting(contentEl) - .setName(i18next.t("settings.plugin.shareKey.title")) - .setDesc(i18next.t("settings.plugin.shareKey.desc")) - .addText((text) => - text - .setPlaceholder("share") - .setValue(this.repository.shareKey) - .onChange(async (value) => { - this.repository.shareKey = value.trim(); - - }) - ); - } else { - new Setting(contentEl) - .setName(i18next.t("settings.plugin.shareKey.excludedFileName.title")) - .addText((text) => - text - .setPlaceholder("DRAFT") - .setValue(this.repository.shareAll?.excludedFileName ?? this.plugin.settings.plugin.shareAll?.excludedFileName ?? "DRAFT") - .onChange(async (value) => { - this.repository.shareAll!.excludedFileName = value.trim(); - }) - ); - } - - if (this.plugin.settings.plugin.copyLink.enable) { - new Setting(contentEl) - .setName(i18next.t("settings.plugin.copyLink.baselink.title")) - .setDesc(i18next.t("settings.plugin.copyLink.baselink.desc")) - .addText((text) => - text - .setPlaceholder(this.plugin.settings.plugin.copyLink.links) - .setValue(this.repository.copyLink.links) - .onChange(async (value) => { - this.repository.copyLink.links = value.trim(); - }) - ); - - new Setting(contentEl) - .setName(i18next.t("settings.plugin.copyLink.linkPathRemover.title")) - .setDesc( - i18next.t("settings.plugin.copyLink.linkPathRemover.desc") - ) - .addText((text) => { - text.setPlaceholder("docs") - .setValue(this.repository.copyLink.removePart.join(", ")) - .onChange(async (value) => { - this.repository.copyLink.removePart = value.split(/[,\n]\s*/).map((item) => item.trim()).filter((item) => item.length > 0); - - }); - }); - - new Setting(contentEl) - .setName(i18next.t("settings.plugin.copyLink.toUri.title")) - .setDesc(i18next.t("settings.plugin.copyLink.toUri.desc")) - .addToggle((toggle) => - toggle - .setValue(this.repository.copyLink.transform.toUri) - .onChange(async (value) => { - this.repository.copyLink.transform.toUri = value; - - }) - ); - - new Setting(contentEl) - .setName(i18next.t("settings.plugin.copyLink.slugify.title")) - .addDropdown((dropdown) => { - dropdown - .addOptions({ - disable: i18next.t("settings.plugin.copyLink.slugify.disable"), - strict: i18next.t("settings.plugin.copyLink.slugify.strict"), - lower: i18next.t("settings.plugin.copyLink.slugify.lower"), - }) - .setValue(this.repository.copyLink.transform.slugify as "disable" | "strict" | "lower") - .onChange(async (value) => { - this.repository.copyLink.transform.slugify = value as "disable" | "strict" | "lower"; - - }); - }); - - new Setting(contentEl) - .setName(i18next.t("settings.plugin.copyLink.applyRegex.title")) - .setHeading() - .setDesc(i18next.t("settings.plugin.copyLink.applyRegex.desc")) - .addExtraButton((button) => { - button - .setIcon("plus") - .onClick(async () => { - this.repository.copyLink.transform.applyRegex.push({ - regex: "", - replacement: "" - }); - - this.onOpen(); - }); - }); - - for (const apply of this.repository.copyLink.transform.applyRegex) { - const regex = apply.regex; - const replacement = apply.replacement; - - new Setting(contentEl) - .setClass("no-display") - .addText((text) => { - text - .setPlaceholder("regex") - .setValue(regex) - .onChange(async (value) => { - apply.regex = value; - - }); - }) - .setClass("max-width") - .addText((text) => { - text - .setPlaceholder("replacement") - .setValue(replacement) - .onChange(async (value) => { - apply.replacement = value; - - }); - }) - .setClass("max-width") - .addExtraButton(button => { - button.setIcon("trash") - .onClick(async () => { - this.repository.copyLink.transform.applyRegex = this.repository.copyLink.transform.applyRegex.filter((item) => item !== apply); - - this.onOpen(); - }); - }); - } - } - } - onClose() { - const {contentEl} = this; - contentEl.empty(); - - } -} +import { GitHubPublisherSettings, GithubTiersVersion, Repository } from "@interfaces"; +import i18next from "i18next"; +import { AbstractInputSuggest, App, Modal, Notice, Setting, TFile } from "obsidian"; +import GithubPublisher from "src/main"; +import { migrateToken } from "src/settings/migrate"; +import { + checkRepositoryValidity, + verifyRateLimitAPI, +} from "src/utils/data_validation_test"; + +class SetClassSuggester extends AbstractInputSuggest { + plugin: GithubPublisher; + constructor( + private inputEl: HTMLInputElement, + plugin: GithubPublisher, + private onSubmit: (value: TFile) => void + ) { + super(plugin.app, inputEl); + this.plugin = plugin; + } + + renderSuggestion(value: TFile, el: HTMLElement): void { + el.setText(value.path); + } + + getSuggestions(query: string): TFile[] { + return this.plugin.app.vault.getFiles().filter((file) => { + if ( + file.extension === "md" && + file.path.toLowerCase().contains(query.toLowerCase()) + ) { + const frontmatter = this.plugin.app.metadataCache.getFileCache(file)?.frontmatter; + if (frontmatter) return true; + } + return false; + }); + } + + //eslint-disable-next-line @typescript-eslint/no-unused-vars + selectSuggestion(value: TFile, evt: MouseEvent | KeyboardEvent): void { + this.onSubmit(value); + this.inputEl.value = value.path; + this.inputEl.focus(); + this.inputEl.trigger("input"); + this.close(); + } +} + +/** + * @description This class is used to add a new repo to the settings in the "otherRepo" in the github setting section + * It will list all the repo in the settings and allow the user to add a new one, edit or delete an existing one + * @extends Modal + * @param {App} app - the Obsidian App + * @param {GitHubPublisherSettings} settings - the plugin settings + * @param {string} branchName - the branch name + * @param {GithubPublisher} plugin - the plugin + * @param {Repository[]} repository - the list of repo in the settings + * @param {(result: Repository[]) => void} onSubmit - the function to call when the modal is submitted + */ + +export class ModalAddingNewRepository extends Modal { + settings: GitHubPublisherSettings; + plugin: GithubPublisher; + branchName: string; + repository: Repository[]; + onSubmit: (result: Repository[]) => void; + + constructor( + app: App, + settings: GitHubPublisherSettings, + branchName: string, + plugin: GithubPublisher, + repository: Repository[], + onSubmit: (result: Repository[]) => void + ) { + super(app); + this.settings = settings; + this.repository = repository; + this.plugin = plugin; + this.onSubmit = onSubmit; + this.branchName = branchName; + } + + onOpen() { + const { contentEl } = this; + contentEl.empty(); + contentEl.addClasses(["github-publisher", "modals", "manage-repo", "add"]); + contentEl.createEl("h2", { + text: i18next.t("settings.github.smartRepo.modals.title"), + }); + contentEl.createEl("p", { text: i18next.t("settings.github.smartRepo.modals.desc") }); + contentEl.createEl("p", { + text: i18next.t("settings.github.smartRepo.modals.frontmatterInfo"), + }); + contentEl.createEl("p", { text: i18next.t("settings.plugin.shareKey.otherRepo") }); + + const defaultRepository: Repository = { + smartKey: "smartkey", + user: this.settings.github.user, + repo: this.settings.github.repo, + branch: this.settings.github.branch, + automaticallyMergePR: this.settings.github.automaticallyMergePR, + api: { + tiersForApi: this.settings.github.api.tiersForApi, + hostname: this.settings.github.api.hostname, + }, + workflow: { + commitMessage: this.settings.github.workflow.commitMessage, + name: "", + }, + createShortcuts: false, + shareKey: this.settings.plugin.shareKey, + copyLink: { + links: this.settings.plugin.copyLink.links, + removePart: [], + transform: { + toUri: this.settings.plugin.copyLink.transform.toUri, + slugify: this.settings.plugin.copyLink.transform.slugify, + applyRegex: this.settings.plugin.copyLink.transform.applyRegex, + }, + }, + set: null, + }; + + new Setting(contentEl) + .setClass("max-width") + .setClass("display-none") + .addButton((button) => { + button + + .setButtonText( + i18next.t("common.add", { + things: i18next.t("settings.github.smartRepo.modals.newRepo").toLowerCase(), + }) + ) + .onClick(() => { + this.repository.push(defaultRepository); + this.onOpen(); + }); + }); + + for (const repo of this.repository) { + const sett = new Setting(contentEl) + .setClass("max-width") + .setClass("display-none") + .addText((text) => { + text + .setPlaceholder("smartKey") + .setValue(repo.smartKey) + .onChange((value) => { + repo.smartKey = value.toLowerCase(); + sett.controlEl.setAttribute("smartKey", value.toLowerCase()); + }); + }) + + .addExtraButton((btn) => { + btn.setIcon("trash").onClick(() => { + this.repository.splice(this.repository.indexOf(repo), 1); + this.onOpen(); + }); + }) + .addExtraButton((btn) => { + btn.setIcon("pencil").onClick(() => { + new ModalEditingRepository( + this.app, + repo, + this.plugin, + this.branchName, + (result) => { + this.repository[this.repository.indexOf(repo)] = result; + } + ).open(); + }); + }); + } + new Setting(contentEl).addButton((button) => { + button + .setButtonText(i18next.t("common.save")) + .setCta() + .onClick(() => { + const error = this.foundError(); + const input = + error.repo.length > 0 + ? this.containerEl.querySelector(`[smartkey="${error.repo}"] input`) + : contentEl.querySelector('[placeholder="smartKey"] input'); + if (error.type === "None") { + //remove error + input?.classList?.remove("error"); + this.onSubmit(this.repository); + this.close(); + } + input?.classList?.add("error"); + if (error.type === "duplicate") { + new Notice(i18next.t("settings.github.smartRepo.modals.duplicate")); + } else if (error.type === "default") { + new Notice(i18next.t("settings.github.smartRepo.modals.default")); + } else if (error.type === "empty") { + new Notice(i18next.t("settings.github.smartRepo.modals.empty")); + } + }); + }); + } + + foundError(): { repo: string; type: "duplicate" | "default" | "empty" | "None" } { + for (const repo of this.repository) { + if ( + this.plugin.settings.github.otherRepo.filter((r) => r.smartKey === repo.smartKey) + .length > 1 + ) { + return { repo: repo.smartKey, type: "duplicate" }; + } else if (repo.smartKey === "default") { + return { repo: repo.smartKey, type: "default" }; + } else if (repo.smartKey.length === 0) { + return { repo: "", type: "empty" }; + } + } + return { repo: "", type: "None" }; + } + + onClose() { + const { contentEl } = this; + const error = this.foundError(); + if (error.type === "empty") { + //rename the repo + const repo = this.repository.filter((r) => r.smartKey === error.repo); + for (let i = 0; i < repo.length; i++) { + repo[i].smartKey = `smartkey-${i}`; + } + new Notice( + `${i18next.t("settings.github.smartRepo.modals.empty")} ${i18next.t( + "common.rename" + )}` + ); + } else if (error.type === "duplicate") { + //rename the repo + const repo = this.repository.filter((r) => r.smartKey === error.repo); + for (let i = 0; i < repo.length; i++) { + repo[i].smartKey = `${repo[i].smartKey}-${i}`; + } + new Notice( + `${i18next.t("settings.github.smartRepo.modals.duplicate")} ${i18next.t( + "common.rename" + )}` + ); + } else if (error.type === "default") { + //rename the repo + const repo = this.repository.filter((r) => r.smartKey === error.repo); + for (const r of repo) { + const randomKey = Math.random().toString(36).substring(2, 8); + r.smartKey = `${r.smartKey}-${randomKey}`; + } + new Notice( + `${i18next.t("settings.github.smartRepo.modals.default")} ${i18next.t( + "common.rename" + )}` + ); + } + this.onSubmit(this.repository); + contentEl.empty(); + } +} + +/** + * @description Called by the ModalAddingNewRepository class, this class is used to edit an existing repo + * @extends Modal + * @param {App} app - The Obsidian App instance + * @param {Repository} repository - The repository to edit + * @param {GithubPublisher} GithubPublisher - The GithubPublisher instance + * @param {string} brancheName - The name of the branch (for validation) + * @param {function} onSubmit - The function to call when the modal is closed (to save the changes) + */ + +class ModalEditingRepository extends Modal { + repository: Repository; + branchName: string; + plugin: GithubPublisher; + onSubmit: (result: Repository) => void; + + constructor( + app: App, + repository: Repository, + GithubPublisher: GithubPublisher, + brancheName: string, + onSubmit: (result: Repository) => void + ) { + super(app); + this.repository = repository; + this.onSubmit = onSubmit; + this.branchName = brancheName; + this.plugin = GithubPublisher; + } + + onOpen() { + const { contentEl } = this; + contentEl.empty(); + contentEl.addClasses(["github-publisher", "modals", "manage-repo", "edit"]); + new Setting(contentEl) + .setClass("no-display") + .addButton((button) => + button + .setCta() + .setButtonText(i18next.t("common.save")) + .onClick(async () => { + this.onSubmit(this.repository); + this.close(); + }) + ) + .addButton((button) => + button + .setWarning() + .setButtonText(i18next.t("common.cancel")) + .onClick(async () => { + this.close(); + }) + ); + contentEl.createEl("h2", { + text: i18next.t("common.edit", { things: this.repository.smartKey }), + }); + + new Setting(contentEl) + .setName(i18next.t("settings.github.apiType.title")) + .setDesc(i18next.t("settings.github.apiType.desc")) + .addDropdown((dropdown) => { + dropdown + .addOption( + GithubTiersVersion.free, + i18next.t("settings.github.apiType.dropdown.free") + ) + .addOption( + GithubTiersVersion.entreprise, + i18next.t("settings.github.apiType.dropdown.enterprise") + ) + .setValue(this.repository.api.tiersForApi) + .onChange((value) => { + this.repository.api.tiersForApi = value as GithubTiersVersion; + this.onOpen(); + }); + }); + if (this.repository.api.tiersForApi === GithubTiersVersion.entreprise) { + new Setting(contentEl) + .setName(i18next.t("settings.github.apiType.hostname.title")) + .setDesc(i18next.t("settings.github.apiType.hostname.desc")) + .addText((text) => + text + .setPlaceholder("https://github.mycompany.com") + .setValue(this.repository.api.hostname) + .onChange(async (value) => { + this.repository.api.hostname = value.trim(); + }) + ); + } + + new Setting(contentEl) + .setName(i18next.t("settings.github.username.title")) + .setDesc(i18next.t("settings.github.username.desc")) + .addText((text) => + text + .setPlaceholder(i18next.t("settings.github.username.title")) + .setValue(this.repository.user) + .onChange(async (value) => { + this.repository.user = value.trim(); + }) + ); + + new Setting(contentEl) + .setName(i18next.t("settings.github.repoName.title")) + .setDesc(i18next.t("settings.github.repoName.desc")) + .addText((text) => + text + .setPlaceholder(i18next.t("settings.github.repoName.placeholder")) + .setValue(this.repository.repo) + .onChange(async (value) => { + this.repository.repo = value.trim(); + }) + ); + + new Setting(contentEl) + .setName(i18next.t("settings.github.branch.title")) + .setDesc(i18next.t("settings.github.branch.desc")) + .addText((text) => + text + .setPlaceholder("main") + .setValue(this.repository.branch) + .onChange(async (value) => { + this.repository.branch = value.trim(); + }) + ); + const desc_ghToken = document.createDocumentFragment(); + desc_ghToken.createEl("span", undefined, (span) => { + span.innerText = i18next.t("settings.github.ghToken.desc"); + span.createEl("a", undefined, (link) => { + link.innerText = `${i18next.t("common.here")}.`; + link.href = "https://github.com/settings/tokens/new?scopes=repo,workflow"; + }); + }); + new Setting(contentEl) + .setName(i18next.t("common.ghToken")) + .setDesc(desc_ghToken) + .addText(async (text) => { + const decryptedToken: string = await this.plugin.loadToken( + this.repository.smartKey + ); + text + .setPlaceholder("ghp_1234567890") + .setValue(decryptedToken) + .onChange(async (value) => { + await migrateToken(this.plugin, value.trim(), this.repository.smartKey); + }); + }); + new Setting(contentEl) + .setName(i18next.t("settings.github.automaticallyMergePR")) + .addToggle((toggle) => + toggle.setValue(this.repository.automaticallyMergePR).onChange(async (value) => { + this.repository.automaticallyMergePR = value; + }) + ); + new Setting(contentEl).setClass("no-display").addButton((button) => + button + .setButtonText(i18next.t("settings.github.testConnection")) + .setClass("connect") + .onClick(async () => { + const octokit = await this.plugin.reloadOctokit(this.repository.smartKey); + this.repository.verifiedRepo = await checkRepositoryValidity( + octokit, + this.repository, + null + ); + this.plugin.settings.github.rateLimit = await verifyRateLimitAPI( + octokit.octokit, + this.plugin.settings + ); + }) + ); + new Setting(contentEl) + .setName(i18next.t("settings.github.smartRepo.modals.shortcuts.title")) + .setDesc(i18next.t("settings.github.smartRepo.modals.shortcuts.desc")) + .addToggle((toggle) => + toggle.setValue(this.repository.createShortcuts).onChange(async (value) => { + this.repository.createShortcuts = value; + }) + ); + + contentEl.createEl("h3", { text: "GitHub Workflow" }); + new Setting(contentEl) + .setName(i18next.t("settings.githubWorkflow.prRequest.title")) + .setDesc(i18next.t("settings.githubWorkflow.prRequest.desc")) + .addText((text) => + text + .setPlaceholder("[PUBLISHER] MERGE") + .setValue(this.repository.workflow.commitMessage) + .onChange(async (value) => { + if (value.trim().length === 0) { + value = "[PUBLISHER] MERGE"; + new Notice(i18next.t("settings.githubWorkflow.prRequest.error")); + } + this.repository.workflow.commitMessage = value; + }) + ); + new Setting(contentEl) + .setName(i18next.t("settings.githubWorkflow.githubAction.title")) + .setDesc(i18next.t("settings.githubWorkflow.githubAction.desc")) + .addText((text) => { + text + .setPlaceholder("ci") + .setValue(this.repository.workflow.name) + .onChange(async (value) => { + if (value.length > 0) { + value = value.trim(); + const yamlEndings = [".yml", ".yaml"]; + if (!yamlEndings.some((ending) => value.endsWith(ending))) { + value += yamlEndings[0]; + } + } + this.repository.workflow.name = value; + }); + }); + + contentEl.createEl("h3", { + text: i18next.t("settings.github.smartRepo.modals.otherConfig"), + }); + + new Setting(contentEl) + .setName(i18next.t("settings.plugin.setImport.title")) + .setDesc(i18next.t("settings.plugin.setImport.desc")) + .addSearch((search) => { + search.setValue(this.repository.set ?? "").setPlaceholder("path/to/file.md"); + new SetClassSuggester(search.inputEl, this.plugin, (result) => { + this.repository.set = result.path; + const frontmatter = + this.plugin.app.metadataCache.getFileCache(result)?.frontmatter; + this.plugin.repositoryFrontmatter[this.repository.smartKey] = frontmatter; + }); + }); + + new Setting(contentEl) + .setName(i18next.t("settings.plugin.shareKey.all.title")) + .setDesc(i18next.t("settings.plugin.shareKey.all.desc")) + .addToggle((toggle) => + toggle + .setValue(this.repository.shareAll?.enable ?? false) + .onChange(async (value) => { + this.repository.shareAll = { + enable: value, + excludedFileName: + this.plugin.settings.plugin.shareAll?.excludedFileName ?? "DRAFT", + }; + this.onOpen(); + }) + ); + if (!this.repository.shareAll || !this.repository.shareAll.enable) { + new Setting(contentEl) + .setName(i18next.t("settings.plugin.shareKey.title")) + .setDesc(i18next.t("settings.plugin.shareKey.desc")) + .addText((text) => + text + .setPlaceholder("share") + .setValue(this.repository.shareKey) + .onChange(async (value) => { + this.repository.shareKey = value.trim(); + }) + ); + } else { + new Setting(contentEl) + .setName(i18next.t("settings.plugin.shareKey.excludedFileName.title")) + .addText((text) => + text + .setPlaceholder("DRAFT") + .setValue( + this.repository.shareAll?.excludedFileName ?? + this.plugin.settings.plugin.shareAll?.excludedFileName ?? + "DRAFT" + ) + .onChange(async (value) => { + this.repository.shareAll!.excludedFileName = value.trim(); + }) + ); + } + + if (this.plugin.settings.plugin.copyLink.enable) { + new Setting(contentEl) + .setName(i18next.t("settings.plugin.copyLink.baselink.title")) + .setDesc(i18next.t("settings.plugin.copyLink.baselink.desc")) + .addText((text) => + text + .setPlaceholder(this.plugin.settings.plugin.copyLink.links) + .setValue(this.repository.copyLink.links) + .onChange(async (value) => { + this.repository.copyLink.links = value.trim(); + }) + ); + + new Setting(contentEl) + .setName(i18next.t("settings.plugin.copyLink.linkPathRemover.title")) + .setDesc(i18next.t("settings.plugin.copyLink.linkPathRemover.desc")) + .addText((text) => { + text + .setPlaceholder("docs") + .setValue(this.repository.copyLink.removePart.join(", ")) + .onChange(async (value) => { + this.repository.copyLink.removePart = value + .split(/[,\n]\s*/) + .map((item) => item.trim()) + .filter((item) => item.length > 0); + }); + }); + + new Setting(contentEl) + .setName(i18next.t("settings.plugin.copyLink.toUri.title")) + .setDesc(i18next.t("settings.plugin.copyLink.toUri.desc")) + .addToggle((toggle) => + toggle + .setValue(this.repository.copyLink.transform.toUri) + .onChange(async (value) => { + this.repository.copyLink.transform.toUri = value; + }) + ); + + new Setting(contentEl) + .setName(i18next.t("settings.plugin.copyLink.slugify.title")) + .addDropdown((dropdown) => { + dropdown + .addOptions({ + disable: i18next.t("settings.plugin.copyLink.slugify.disable"), + strict: i18next.t("settings.plugin.copyLink.slugify.strict"), + lower: i18next.t("settings.plugin.copyLink.slugify.lower"), + }) + .setValue( + this.repository.copyLink.transform.slugify as "disable" | "strict" | "lower" + ) + .onChange(async (value) => { + this.repository.copyLink.transform.slugify = value as + | "disable" + | "strict" + | "lower"; + }); + }); + + new Setting(contentEl) + .setName(i18next.t("settings.plugin.copyLink.applyRegex.title")) + .setHeading() + .setDesc(i18next.t("settings.plugin.copyLink.applyRegex.desc")) + .addExtraButton((button) => { + button.setIcon("plus").onClick(async () => { + this.repository.copyLink.transform.applyRegex.push({ + regex: "", + replacement: "", + }); + + this.onOpen(); + }); + }); + + for (const apply of this.repository.copyLink.transform.applyRegex) { + const regex = apply.regex; + const replacement = apply.replacement; + + new Setting(contentEl) + .setClass("no-display") + .addText((text) => { + text + .setPlaceholder("regex") + .setValue(regex) + .onChange(async (value) => { + apply.regex = value; + }); + }) + .setClass("max-width") + .addText((text) => { + text + .setPlaceholder("replacement") + .setValue(replacement) + .onChange(async (value) => { + apply.replacement = value; + }); + }) + .setClass("max-width") + .addExtraButton((button) => { + button.setIcon("trash").onClick(async () => { + this.repository.copyLink.transform.applyRegex = + this.repository.copyLink.transform.applyRegex.filter( + (item) => item !== apply + ); + + this.onOpen(); + }); + }); + } + } + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +} diff --git a/src/settings/modals/popup.ts b/src/settings/modals/popup.ts index def63f25..e59099a0 100644 --- a/src/settings/modals/popup.ts +++ b/src/settings/modals/popup.ts @@ -2,18 +2,24 @@ import { GitHubPublisherSettings } from "@interfaces/main"; import i18next from "i18next"; import { App, Modal, Setting } from "obsidian"; - export class AutoCleanPopup extends Modal { settings: GitHubPublisherSettings; onSubmit: (enable: boolean) => void; - constructor(app: App, settings: GitHubPublisherSettings, onSubmit: (enable: boolean) => void) { + constructor( + app: App, + settings: GitHubPublisherSettings, + onSubmit: (enable: boolean) => void + ) { super(app); this.onSubmit = onSubmit; this.settings = settings; } whatIsEmpty() { - if (this.settings.upload.behavior === "yaml" && this.settings.upload.defaultName.length === 0) { + if ( + this.settings.upload.behavior === "yaml" && + this.settings.upload.defaultName.length === 0 + ) { return i18next.t("common.defaultName"); } else if (this.settings.upload.rootFolder.length === 0) { return i18next.t("common.rootFolder"); @@ -24,10 +30,20 @@ export class AutoCleanPopup extends Modal { const { contentEl } = this; contentEl.empty(); contentEl.addClasses(["github-publisher", "modals", "popup"]); - contentEl.createEl("h2", { text: i18next.t("settings.githubWorkflow.autoCleanUp.popup.title") }); - contentEl.createEl("p", { text: i18next.t("settings.githubWorkflow.autoCleanUp.popup.desc", {what: this.whatIsEmpty()})}); - contentEl.createEl("p", { text: i18next.t("settings.githubWorkflow.autoCleanUp.popup.exclude")}); - contentEl.createEl("p", { text: i18next.t("settings.githubWorkflow.autoCleanUp.popup.sure") }); + contentEl.createEl("h2", { + text: i18next.t("settings.githubWorkflow.autoCleanUp.popup.title"), + }); + contentEl.createEl("p", { + text: i18next.t("settings.githubWorkflow.autoCleanUp.popup.desc", { + what: this.whatIsEmpty(), + }), + }); + contentEl.createEl("p", { + text: i18next.t("settings.githubWorkflow.autoCleanUp.popup.exclude"), + }); + contentEl.createEl("p", { + text: i18next.t("settings.githubWorkflow.autoCleanUp.popup.sure"), + }); new Setting(contentEl) .setClass("no-display") @@ -41,17 +57,15 @@ export class AutoCleanPopup extends Modal { }); }) .addButton((button) => { - button - .setButtonText(i18next.t("common.no")) - .onClick(() => { - this.onSubmit(false); - this.close(); - }); - }); + button.setButtonText(i18next.t("common.no")).onClick(() => { + this.onSubmit(false); + this.close(); + }); + }); } onClose() { const { contentEl } = this; contentEl.empty(); } -} \ No newline at end of file +} diff --git a/src/settings/modals/regex_edition.ts b/src/settings/modals/regex_edition.ts index 7f264fb1..1f29c11b 100644 --- a/src/settings/modals/regex_edition.ts +++ b/src/settings/modals/regex_edition.ts @@ -1,512 +1,565 @@ -import {FolderSettings, GitHubPublisherSettings, OverrideAttachments, RegexReplace, TextCleaner, TypeOfEditRegex} from "@interfaces"; -import i18next from "i18next"; -import {App, Modal, Notice, Setting} from "obsidian"; -import { escapeRegex } from "src/conversion/links"; - -function isRegexValid(regexString: string) { - try { - new RegExp(regexString); - return { - error: null, - isValid: true - }; - } - catch (e) { - return { - error: e, - isValid: false - }; - } -} - -export class OverrideAttachmentsModal extends Modal { - settings: GitHubPublisherSettings; - allOverrides: OverrideAttachments[]; - onSubmit: (result: OverrideAttachments[]) => void; - constructor( - app: App, - settings: GitHubPublisherSettings, - allOverrides : OverrideAttachments[], - onSubmit: (result: OverrideAttachments[]) => void) { - super(app); - this.allOverrides = allOverrides; - this.settings = settings; - this.onSubmit = onSubmit; - } - - forbiddenValue(value: string): {value: string, isForbidden: boolean} { - if (!isRegexValid(value).isValid) { - const error = isRegexValid(value).error; - new Notice(i18next.t("settings.regexReplacing.invalidRegex", {e: error})); - return { - value: "", - isForbidden: true - }; - } else if (value.match(/[\\><:"|?*]/) && !value.match(/^\/(.*)\/[gmisuvdy]*$/)) { - new Notice(i18next.t("settings.regexReplacing.forbiddenValue", { what: i18next.t("common.path.folder"), forbiddenChar: value.match(/[\\><:"|?*]/)![0] })); - return { - value: "", - isForbidden: true - }; - } - return { - value, - isForbidden: false - }; - } - onOpen(): void { - const { contentEl } = this; - contentEl.empty(); - contentEl.addClasses(["github-publisher", "modals", "regex", "file-path-name"]); - contentEl.createEl("h2", { text: i18next.t("settings.embed.overrides.modal.title") }); - contentEl.createEl("p", { text: i18next.t("settings.regexReplacing.modal.desc") }); - contentEl.createEl("h3", {text: i18next.t("settings.regexReplacing.modal.keywords")}); - const ul = contentEl.createEl("ul", {cls: "keywords"}); - ul.createEl("li", {text: i18next.t("settings.embed.forcePush.all")}); - ul.createEl("li", {text: i18next.t("settings.embed.forcePush.default")}); - ul.createEl("li", {text: i18next.t("settings.regexReplacing.modal.name")}); - contentEl.createEl("h3", {text: i18next.t("settings.regexReplacing.modal.force")}); - contentEl.createEl("p", { text: i18next.t("settings.embed.forcePush.info") }); - - if (!this.settings.embed.overrideAttachments) { - this.settings.embed.overrideAttachments = []; - } - for (const override of this.allOverrides) { - const sett = new Setting(contentEl) - .setClass("entry") - .addText((text) => { - text.setPlaceholder(i18next.t("settings.embed.overrides.modal.path")) - .setValue(override.path) - .onChange((value) => { - override.path = value; - sett.controlEl.setAttribute("value", value); - }); - }) - .addText((text) => { - text.setPlaceholder(i18next.t("settings.embed.overrides.modal.dest")) - .setValue(override.destination) - .onChange((value) => { - override.destination = value; - sett.controlEl.setAttribute("replace", value); - }); - }) - .addToggle((toggle) => { - toggle - .setTooltip(i18next.t("settings.embed.forcePush.title")) - .setValue(override.forcePush) - .onChange((value) => { - override.forcePush = value; - }); - }); - sett.controlEl.setAttribute("value", override.path); - sett.controlEl.setAttribute("replace", override.destination); - sett.addExtraButton((button) => { - button - .setIcon("trash") - .onClick(() => { - this.allOverrides.splice(this.allOverrides.indexOf(override), 1); - this.onOpen(); - }); - }); - } - - new Setting(contentEl) - .addButton((button) => { - button - .setIcon("plus") - .onClick(() => { - this.allOverrides.push({ - path: "", - destination: "", - forcePush: false - }); - this.onOpen(); - }); - }) - .addButton((button) => { - button - .setButtonText(i18next.t("common.save")) - .onClick(() => { - const canBeValidated: boolean[] = []; - this.allOverrides.forEach((override) => { - const isForbiddenEntry = this.forbiddenValue(override.path); - const isForbiddenReplace = this.forbiddenValue(override.destination); - canBeValidated.push(isForbiddenEntry.isForbidden); - canBeValidated.push(isForbiddenReplace.isForbidden); - if (isForbiddenEntry.isForbidden || isForbiddenReplace.isForbidden) { - override.path = isForbiddenEntry.value as string; - override.destination = isForbiddenReplace.value as string; - const faultyInputValue = contentEl.querySelector(`[value="${escapeRegex(override.path)}"] input`); - const faultyInputReplace = contentEl.querySelector(`[replace="${escapeRegex(override.destination)}"] input`); - faultyInputValue?.classList.add("error"); - faultyInputReplace?.classList.add("error"); - } - }); - if (!canBeValidated.includes(true)) { - //remove empty regex - this.onSubmit(this.allOverrides); - this.close(); - } - }); - }); - } - onClose(): void { - const { contentEl } = this; - contentEl.empty(); - } -} - -export class ModalRegexFilePathName extends Modal { - settings: GitHubPublisherSettings; - allRegex: RegexReplace[]; - onSubmit: (result: RegexReplace[]) => void; - constructor( - app: App, - settings: GitHubPublisherSettings, - allRegex : RegexReplace[], - onSubmit: (result: RegexReplace[]) => void) { - super(app); - this.allRegex = allRegex; - this.settings = settings; - this.onSubmit = onSubmit; - } - - classValue(allRegex: RegexReplace[]) { - this.settings.upload.replacePath = allRegex.filter((regex) => { - return regex.type === TypeOfEditRegex.path; - }); - this.settings.upload.replaceTitle = allRegex.filter((regex) => { - return regex.type === TypeOfEditRegex.title; - } - ); - } - - forbiddenValue(value: string, type: TypeOfEditRegex): {value: string, isForbidden: boolean} { - const regexSpecialDontExclude = /\/(.*)(\\[dwstrnvfb0cxup])(.*)\//i; - let onWhat = type === TypeOfEditRegex.path ? i18next.t("common.path.folder") : i18next.t("common.path.file"); - onWhat = onWhat.toLowerCase(); - let isForbidden = false; - if (value == "/") { - new Notice(i18next.t("settings.regexReplacing.forbiddenValue", {what: onWhat, forbiddenChar: value})); - value = ""; - isForbidden = true; - } else if (!isRegexValid(value).isValid) { - const error = isRegexValid(value).error; - new Notice(i18next.t("settings.regexReplacing.invalidRegex", {e: error})); - isForbidden = true; - } - else if ( - (value.match(/[><:"|?*]|(\\\/)|(^\w+\/\w+)|(\\)/)) && (type === TypeOfEditRegex.title) && !(value.match(regexSpecialDontExclude)) - ) { - new Notice(i18next.t("settings.regexReplacing.forbiddenValue", {what: onWhat, forbiddenChar: value.match(/[><:"|?*]|(\\\/)|(^\w+\/\w+)|(\\)/)![0]})); - value = ""; - isForbidden = true; - } else if (type === TypeOfEditRegex.path) { - if (value.match(/[\\><:"|?*]/) && !value.match(/^\/(.*)\/[gmisuvdy]*$/)){ - new Notice(i18next.t("settings.regexReplacing.forbiddenValue", { what: onWhat, forbiddenChar: value.match(/[\\><:"|?*]/)![0]})); - value = ""; - isForbidden = true; - } else if (value.match(/(^\w+\/\w+)|(\\\/)/) && !(value.match(regexSpecialDontExclude))) { - new Notice(i18next.t("settings.regexReplacing.warningPath")); - } - } - return { - value, - isForbidden - }; - } - - onOpen() { - const {contentEl} = this; - contentEl.empty(); - contentEl.addClasses(["github-publisher", "modals", "regex", "file-path-name"]); - if (this.settings.upload.behavior === FolderSettings.fixed) { - contentEl.createEl("h2", { text: i18next.t("settings.regexReplacing.modal.title.only")}); - } else { - contentEl.createEl("h2", { text: i18next.t("settings.regexReplacing.modal.title.all")}); - } - if (!this.settings.upload.replacePath) { - this.settings.upload.replacePath = []; - } - else if (!this.settings.upload.replaceTitle) { - this.settings.upload.replaceTitle = []; - } - this.settings.upload.replacePath.forEach((title) => { - if (!title.type) { - title.type = TypeOfEditRegex.path; - } - }); - this.settings.upload.replaceTitle.forEach((title) => { - if (!title.type) { - title.type = TypeOfEditRegex.title; - } - }); - - for (const title of this.allRegex) { - const sett = new Setting(contentEl) - .setClass("entry") - .addText((text) => { - text.setPlaceholder(i18next.t("regex.entry")) - .setValue(title.regex) - .onChange((value) => { - title.regex = value; - sett.controlEl.setAttribute("value", value); - }); - }) - .addText((text) => { - text.setPlaceholder(i18next.t("regex.replace")) - .setValue(title.replacement) - .onChange((value) => { - title.replacement = value; - sett.controlEl.setAttribute("replace", value); - }); - }); - sett.controlEl.setAttribute("value", title.regex); - sett.controlEl.setAttribute("replace", title.replacement); - if (this.settings.upload.behavior !== FolderSettings.fixed) { - sett.addDropdown((dropdown) => { - dropdown - .addOption("path", i18next.t("common.path.folder")) - .addOption("title", i18next.t("common.path.file")) - .setValue(title.type) - .onChange((value) => { - title.type = value as TypeOfEditRegex; - }); - }); - } else { - sett - .addButton((button) => { - button.buttonEl.classList.add("disabled"); - button.setButtonText(i18next.t("common.path.file")); - }); - } - sett.addExtraButton((button) => { - button - .setIcon("trash") - .onClick(() => { - this.allRegex.splice(this.allRegex.indexOf(title), 1); - this.onOpen(); - }); - }); - } - new Setting(contentEl) - .addButton((button) => { - button - .setIcon("plus") - .onClick(() => { - this.allRegex.push({ - regex: "", - replacement: "", - type: TypeOfEditRegex.title - }); - this.onOpen(); - }); - }) - .addButton((button) => { - button - .setButtonText(i18next.t("common.save")) - .onClick(() => { - const canBeValidated: boolean[] = []; - this.allRegex.forEach((title) => { - if (!title.regex) - title.regex = ""; - if (!title.replacement) - title.replacement = ""; - const isForbiddenEntry = this.forbiddenValue(title.regex, title.type); - if (title.regex.length === 0) { - new Notice(i18next.t("settings.regexReplacing.emptyRegex")); - isForbiddenEntry.isForbidden = true; - isForbiddenEntry.value = ""; - } - - const isForbiddenReplace = this.forbiddenValue(title.replacement, title.type); - canBeValidated.push(isForbiddenEntry.isForbidden); - canBeValidated.push(isForbiddenReplace.isForbidden); - if (isForbiddenEntry.isForbidden || isForbiddenReplace.isForbidden) { - title.regex = isForbiddenEntry.value as string; - title.replacement = isForbiddenReplace.value as string; - const faultyInputValue = contentEl.querySelector(`[value="${escapeRegex(title.regex)}"] input`); - const faultyInputReplace = contentEl.querySelector(`[replace="${escapeRegex(title.replacement)}"] input`); - faultyInputValue?.classList.add("error"); - faultyInputReplace?.classList.add("error"); - } - - }); - if (!canBeValidated.includes(true)) { - //remove empty regex - - this.onSubmit(this.allRegex); - this.close(); - } - }); - }); - } - - onClose() { - const {contentEl} = this; - contentEl.empty(); - } -} - -export class ModalRegexOnContents extends Modal { - settings: GitHubPublisherSettings; - onSubmit: (settings: GitHubPublisherSettings) => void; - constructor( - app: App, - settings: GitHubPublisherSettings, - onSubmit: (settings: GitHubPublisherSettings) => void) { - super(app); - this.settings = settings; - this.onSubmit = onSubmit; - } - - onOpen() { - const {contentEl} = this; - contentEl.empty(); - contentEl.addClasses(["github-publisher", "modals", "regex", "on-contents"]); - contentEl - .createEl("p", { - text: i18next.t("settings.regexReplacing.modal.title.text") , - }) - .createEl("p", { - text: i18next.t("settings.regexReplacing.modal.desc") , - }) - .createEl("p", { - text: i18next.t("settings.regexReplacing.empty")}); - - for (const censorText of this.settings.conversion.censorText) { - const afterIcon = censorText.after ? "arrow-up-wide-narrow" : "arrow-down-wide-narrow"; - const inCodeBlocks = censorText?.inCodeBlocks ? "code" : "scan"; - const moment = censorText.after ? i18next.t("common.after").toLowerCase() : i18next.t("common.before").toLowerCase(); - const desc = i18next.t("settings.regexReplacing.momentReplaceRegex", {moment}); - const toolTipCode = censorText.inCodeBlocks ? i18next.t("settings.regexReplacing.inCodeBlocks.runIn") : i18next.t("settings.regexReplacing.inCodeBlocks.runOut"); - const sett = new Setting(contentEl); - const isLastInList = this.settings.conversion.censorText.indexOf(censorText) === this.settings.conversion.censorText.length - 1; - const isFirstInList = this.settings.conversion.censorText.indexOf(censorText) === 0; - sett.addExtraButton((btn) => { - btn - .setDisabled(isFirstInList) - .setIcon("arrow-up") - .onClick(async () => { - const index = this.settings.conversion.censorText.indexOf(censorText); - const newIndex = index - 1; - this.settings.conversion.censorText.splice(index, 1); - this.settings.conversion.censorText.splice(newIndex, 0, censorText); - this.onOpen(); - }); - btn.extraSettingsEl.classList.add("padding-0"); - }); - - sett.addExtraButton((btn) => { - btn - .setDisabled(isLastInList) - .setIcon("arrow-down") - .onClick(async () => { - const index = this.settings.conversion.censorText.indexOf(censorText); - const newIndex = index + 1; - this.settings.conversion.censorText.splice(index, 1); - this.settings.conversion.censorText.splice(newIndex, 0, censorText); - this.onOpen(); - }); - btn.extraSettingsEl.classList.add("padding-0"); - }); - - sett.setClass("entry") - .addText((text) => { - text.setPlaceholder(i18next.t( - "regex.entry") - ) - .setValue(censorText.entry) - .onChange(async (value) => { - censorText.entry = value; - sett.controlEl.setAttribute("value", value); - }); - }) - .addText((text) => { - text.setPlaceholder(i18next.t("regex.replace")) - .setValue(censorText.replace) - .onChange(async (value) => { - censorText.replace = value; - }); - }) - - .addExtraButton((btn) => { - btn.setIcon("trash") - .setTooltip(i18next.t("common.delete", {things: "Regex"})) - .onClick(async () => { - this.settings.conversion.censorText.splice( - this.settings.conversion.censorText.indexOf( - censorText - ), - 1 - ); - this.onOpen(); - }); - }) - .addExtraButton((btn) => { - btn - .setTooltip(desc) - .setIcon(afterIcon) - .onClick(async () => { - censorText.after = !censorText.after; - this.onOpen(); - }); - }) - .addExtraButton((btn) => { - btn - .setTooltip(toolTipCode) - .setIcon(inCodeBlocks) - .onClick(async () => { - censorText.inCodeBlocks = !censorText.inCodeBlocks; - this.onOpen(); - }); - }); - sett.controlEl.setAttribute("value", censorText.entry); - sett.controlEl.classList.add("regex"); - } - new Setting(contentEl) - .addButton((btn) => { - btn - .setIcon("plus") - .setTooltip(i18next.t("common.add", {things: "Regex"})) - - .onClick(async () => { - const censorText: TextCleaner = { - entry: "", - replace: "", - flags: "", - after: false, - }; - this.settings.conversion.censorText.push(censorText); - this.onOpen(); - }); - }) - .addButton((button) => { - button - .setButtonText(i18next.t("common.save")) - .setCta() - .onClick(() => { - const canBeValidated: boolean[] = []; - for (const censor of this.settings.conversion.censorText) { - if (!isRegexValid(censor.entry).isValid) { - new Notice(i18next.t("settings.regexReplacing.invalidRegex", {e: isRegexValid(censor.entry).error})); - //add error class to faulty input - const faultyInput = contentEl.querySelector(`[value="${escapeRegex(censor.entry)}"] input`); - faultyInput?.classList.add("error"); - canBeValidated.push(false); - } - } if (!canBeValidated.includes(false)) { - this.onSubmit(this.settings); - this.close(); - } - }); - }); - } - - onClose() { - const {contentEl} = this; - contentEl.empty(); - } -} - - +import { + FolderSettings, + GitHubPublisherSettings, + OverrideAttachments, + RegexReplace, + TextCleaner, + TypeOfEditRegex, +} from "@interfaces"; +import i18next from "i18next"; +import { App, Modal, Notice, Setting } from "obsidian"; +import { escapeRegex } from "src/conversion/links"; + +function isRegexValid(regexString: string) { + try { + new RegExp(regexString); + return { + error: null, + isValid: true, + }; + } catch (e) { + return { + error: e, + isValid: false, + }; + } +} + +export class OverrideAttachmentsModal extends Modal { + settings: GitHubPublisherSettings; + allOverrides: OverrideAttachments[]; + onSubmit: (result: OverrideAttachments[]) => void; + constructor( + app: App, + settings: GitHubPublisherSettings, + allOverrides: OverrideAttachments[], + onSubmit: (result: OverrideAttachments[]) => void + ) { + super(app); + this.allOverrides = allOverrides; + this.settings = settings; + this.onSubmit = onSubmit; + } + + forbiddenValue(value: string): { value: string; isForbidden: boolean } { + if (!isRegexValid(value).isValid) { + const error = isRegexValid(value).error; + new Notice(i18next.t("settings.regexReplacing.invalidRegex", { e: error })); + return { + value: "", + isForbidden: true, + }; + } else if (value.match(/[\\><:"|?*]/) && !value.match(/^\/(.*)\/[gmisuvdy]*$/)) { + new Notice( + i18next.t("settings.regexReplacing.forbiddenValue", { + what: i18next.t("common.path.folder"), + forbiddenChar: value.match(/[\\><:"|?*]/)![0], + }) + ); + return { + value: "", + isForbidden: true, + }; + } + return { + value, + isForbidden: false, + }; + } + onOpen(): void { + const { contentEl } = this; + contentEl.empty(); + contentEl.addClasses(["github-publisher", "modals", "regex", "file-path-name"]); + contentEl.createEl("h2", { text: i18next.t("settings.embed.overrides.modal.title") }); + contentEl.createEl("p", { text: i18next.t("settings.regexReplacing.modal.desc") }); + contentEl.createEl("h3", { + text: i18next.t("settings.regexReplacing.modal.keywords"), + }); + const ul = contentEl.createEl("ul", { cls: "keywords" }); + ul.createEl("li", { text: i18next.t("settings.embed.forcePush.all") }); + ul.createEl("li", { text: i18next.t("settings.embed.forcePush.default") }); + ul.createEl("li", { text: i18next.t("settings.regexReplacing.modal.name") }); + contentEl.createEl("h3", { text: i18next.t("settings.regexReplacing.modal.force") }); + contentEl.createEl("p", { text: i18next.t("settings.embed.forcePush.info") }); + + if (!this.settings.embed.overrideAttachments) { + this.settings.embed.overrideAttachments = []; + } + for (const override of this.allOverrides) { + const sett = new Setting(contentEl) + .setClass("entry") + .addText((text) => { + text + .setPlaceholder(i18next.t("settings.embed.overrides.modal.path")) + .setValue(override.path) + .onChange((value) => { + override.path = value; + sett.controlEl.setAttribute("value", value); + }); + }) + .addText((text) => { + text + .setPlaceholder(i18next.t("settings.embed.overrides.modal.dest")) + .setValue(override.destination) + .onChange((value) => { + override.destination = value; + sett.controlEl.setAttribute("replace", value); + }); + }) + .addToggle((toggle) => { + toggle + .setTooltip(i18next.t("settings.embed.forcePush.title")) + .setValue(override.forcePush) + .onChange((value) => { + override.forcePush = value; + }); + }); + sett.controlEl.setAttribute("value", override.path); + sett.controlEl.setAttribute("replace", override.destination); + sett.addExtraButton((button) => { + button.setIcon("trash").onClick(() => { + this.allOverrides.splice(this.allOverrides.indexOf(override), 1); + this.onOpen(); + }); + }); + } + + new Setting(contentEl) + .addButton((button) => { + button.setIcon("plus").onClick(() => { + this.allOverrides.push({ + path: "", + destination: "", + forcePush: false, + }); + this.onOpen(); + }); + }) + .addButton((button) => { + button.setButtonText(i18next.t("common.save")).onClick(() => { + const canBeValidated: boolean[] = []; + this.allOverrides.forEach((override) => { + const isForbiddenEntry = this.forbiddenValue(override.path); + const isForbiddenReplace = this.forbiddenValue(override.destination); + canBeValidated.push(isForbiddenEntry.isForbidden); + canBeValidated.push(isForbiddenReplace.isForbidden); + if (isForbiddenEntry.isForbidden || isForbiddenReplace.isForbidden) { + override.path = isForbiddenEntry.value as string; + override.destination = isForbiddenReplace.value as string; + const faultyInputValue = contentEl.querySelector( + `[value="${escapeRegex(override.path)}"] input` + ); + const faultyInputReplace = contentEl.querySelector( + `[replace="${escapeRegex(override.destination)}"] input` + ); + faultyInputValue?.classList.add("error"); + faultyInputReplace?.classList.add("error"); + } + }); + if (!canBeValidated.includes(true)) { + //remove empty regex + this.onSubmit(this.allOverrides); + this.close(); + } + }); + }); + } + onClose(): void { + const { contentEl } = this; + contentEl.empty(); + } +} + +export class ModalRegexFilePathName extends Modal { + settings: GitHubPublisherSettings; + allRegex: RegexReplace[]; + onSubmit: (result: RegexReplace[]) => void; + constructor( + app: App, + settings: GitHubPublisherSettings, + allRegex: RegexReplace[], + onSubmit: (result: RegexReplace[]) => void + ) { + super(app); + this.allRegex = allRegex; + this.settings = settings; + this.onSubmit = onSubmit; + } + + classValue(allRegex: RegexReplace[]) { + this.settings.upload.replacePath = allRegex.filter((regex) => { + return regex.type === TypeOfEditRegex.path; + }); + this.settings.upload.replaceTitle = allRegex.filter((regex) => { + return regex.type === TypeOfEditRegex.title; + }); + } + + forbiddenValue( + value: string, + type: TypeOfEditRegex + ): { value: string; isForbidden: boolean } { + const regexSpecialDontExclude = /\/(.*)(\\[dwstrnvfb0cxup])(.*)\//i; + let onWhat = + type === TypeOfEditRegex.path + ? i18next.t("common.path.folder") + : i18next.t("common.path.file"); + onWhat = onWhat.toLowerCase(); + let isForbidden = false; + if (value == "/") { + new Notice( + i18next.t("settings.regexReplacing.forbiddenValue", { + what: onWhat, + forbiddenChar: value, + }) + ); + value = ""; + isForbidden = true; + } else if (!isRegexValid(value).isValid) { + const error = isRegexValid(value).error; + new Notice(i18next.t("settings.regexReplacing.invalidRegex", { e: error })); + isForbidden = true; + } else if ( + value.match(/[><:"|?*]|(\\\/)|(^\w+\/\w+)|(\\)/) && + type === TypeOfEditRegex.title && + !value.match(regexSpecialDontExclude) + ) { + new Notice( + i18next.t("settings.regexReplacing.forbiddenValue", { + what: onWhat, + forbiddenChar: value.match(/[><:"|?*]|(\\\/)|(^\w+\/\w+)|(\\)/)![0], + }) + ); + value = ""; + isForbidden = true; + } else if (type === TypeOfEditRegex.path) { + if (value.match(/[\\><:"|?*]/) && !value.match(/^\/(.*)\/[gmisuvdy]*$/)) { + new Notice( + i18next.t("settings.regexReplacing.forbiddenValue", { + what: onWhat, + forbiddenChar: value.match(/[\\><:"|?*]/)![0], + }) + ); + value = ""; + isForbidden = true; + } else if ( + value.match(/(^\w+\/\w+)|(\\\/)/) && + !value.match(regexSpecialDontExclude) + ) { + new Notice(i18next.t("settings.regexReplacing.warningPath")); + } + } + return { + value, + isForbidden, + }; + } + + onOpen() { + const { contentEl } = this; + contentEl.empty(); + contentEl.addClasses(["github-publisher", "modals", "regex", "file-path-name"]); + if (this.settings.upload.behavior === FolderSettings.fixed) { + contentEl.createEl("h2", { + text: i18next.t("settings.regexReplacing.modal.title.only"), + }); + } else { + contentEl.createEl("h2", { + text: i18next.t("settings.regexReplacing.modal.title.all"), + }); + } + if (!this.settings.upload.replacePath) { + this.settings.upload.replacePath = []; + } else if (!this.settings.upload.replaceTitle) { + this.settings.upload.replaceTitle = []; + } + this.settings.upload.replacePath.forEach((title) => { + if (!title.type) { + title.type = TypeOfEditRegex.path; + } + }); + this.settings.upload.replaceTitle.forEach((title) => { + if (!title.type) { + title.type = TypeOfEditRegex.title; + } + }); + + for (const title of this.allRegex) { + const sett = new Setting(contentEl) + .setClass("entry") + .addText((text) => { + text + .setPlaceholder(i18next.t("regex.entry")) + .setValue(title.regex) + .onChange((value) => { + title.regex = value; + sett.controlEl.setAttribute("value", value); + }); + }) + .addText((text) => { + text + .setPlaceholder(i18next.t("regex.replace")) + .setValue(title.replacement) + .onChange((value) => { + title.replacement = value; + sett.controlEl.setAttribute("replace", value); + }); + }); + sett.controlEl.setAttribute("value", title.regex); + sett.controlEl.setAttribute("replace", title.replacement); + if (this.settings.upload.behavior !== FolderSettings.fixed) { + sett.addDropdown((dropdown) => { + dropdown + .addOption("path", i18next.t("common.path.folder")) + .addOption("title", i18next.t("common.path.file")) + .setValue(title.type) + .onChange((value) => { + title.type = value as TypeOfEditRegex; + }); + }); + } else { + sett.addButton((button) => { + button.buttonEl.classList.add("disabled"); + button.setButtonText(i18next.t("common.path.file")); + }); + } + sett.addExtraButton((button) => { + button.setIcon("trash").onClick(() => { + this.allRegex.splice(this.allRegex.indexOf(title), 1); + this.onOpen(); + }); + }); + } + new Setting(contentEl) + .addButton((button) => { + button.setIcon("plus").onClick(() => { + this.allRegex.push({ + regex: "", + replacement: "", + type: TypeOfEditRegex.title, + }); + this.onOpen(); + }); + }) + .addButton((button) => { + button.setButtonText(i18next.t("common.save")).onClick(() => { + const canBeValidated: boolean[] = []; + this.allRegex.forEach((title) => { + if (!title.regex) title.regex = ""; + if (!title.replacement) title.replacement = ""; + const isForbiddenEntry = this.forbiddenValue(title.regex, title.type); + if (title.regex.length === 0) { + new Notice(i18next.t("settings.regexReplacing.emptyRegex")); + isForbiddenEntry.isForbidden = true; + isForbiddenEntry.value = ""; + } + + const isForbiddenReplace = this.forbiddenValue(title.replacement, title.type); + canBeValidated.push(isForbiddenEntry.isForbidden); + canBeValidated.push(isForbiddenReplace.isForbidden); + if (isForbiddenEntry.isForbidden || isForbiddenReplace.isForbidden) { + title.regex = isForbiddenEntry.value as string; + title.replacement = isForbiddenReplace.value as string; + const faultyInputValue = contentEl.querySelector( + `[value="${escapeRegex(title.regex)}"] input` + ); + const faultyInputReplace = contentEl.querySelector( + `[replace="${escapeRegex(title.replacement)}"] input` + ); + faultyInputValue?.classList.add("error"); + faultyInputReplace?.classList.add("error"); + } + }); + if (!canBeValidated.includes(true)) { + //remove empty regex + + this.onSubmit(this.allRegex); + this.close(); + } + }); + }); + } + + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +} + +export class ModalRegexOnContents extends Modal { + settings: GitHubPublisherSettings; + onSubmit: (settings: GitHubPublisherSettings) => void; + constructor( + app: App, + settings: GitHubPublisherSettings, + onSubmit: (settings: GitHubPublisherSettings) => void + ) { + super(app); + this.settings = settings; + this.onSubmit = onSubmit; + } + + onOpen() { + const { contentEl } = this; + contentEl.empty(); + contentEl.addClasses(["github-publisher", "modals", "regex", "on-contents"]); + contentEl + .createEl("p", { + text: i18next.t("settings.regexReplacing.modal.title.text"), + }) + .createEl("p", { + text: i18next.t("settings.regexReplacing.modal.desc"), + }) + .createEl("p", { + text: i18next.t("settings.regexReplacing.empty"), + }); + + for (const censorText of this.settings.conversion.censorText) { + const afterIcon = censorText.after + ? "arrow-up-wide-narrow" + : "arrow-down-wide-narrow"; + const inCodeBlocks = censorText?.inCodeBlocks ? "code" : "scan"; + const moment = censorText.after + ? i18next.t("common.after").toLowerCase() + : i18next.t("common.before").toLowerCase(); + const desc = i18next.t("settings.regexReplacing.momentReplaceRegex", { moment }); + const toolTipCode = censorText.inCodeBlocks + ? i18next.t("settings.regexReplacing.inCodeBlocks.runIn") + : i18next.t("settings.regexReplacing.inCodeBlocks.runOut"); + const sett = new Setting(contentEl); + const isLastInList = + this.settings.conversion.censorText.indexOf(censorText) === + this.settings.conversion.censorText.length - 1; + const isFirstInList = this.settings.conversion.censorText.indexOf(censorText) === 0; + sett.addExtraButton((btn) => { + btn + .setDisabled(isFirstInList) + .setIcon("arrow-up") + .onClick(async () => { + const index = this.settings.conversion.censorText.indexOf(censorText); + const newIndex = index - 1; + this.settings.conversion.censorText.splice(index, 1); + this.settings.conversion.censorText.splice(newIndex, 0, censorText); + this.onOpen(); + }); + btn.extraSettingsEl.classList.add("padding-0"); + }); + + sett.addExtraButton((btn) => { + btn + .setDisabled(isLastInList) + .setIcon("arrow-down") + .onClick(async () => { + const index = this.settings.conversion.censorText.indexOf(censorText); + const newIndex = index + 1; + this.settings.conversion.censorText.splice(index, 1); + this.settings.conversion.censorText.splice(newIndex, 0, censorText); + this.onOpen(); + }); + btn.extraSettingsEl.classList.add("padding-0"); + }); + + sett + .setClass("entry") + .addText((text) => { + text + .setPlaceholder(i18next.t("regex.entry")) + .setValue(censorText.entry) + .onChange(async (value) => { + censorText.entry = value; + sett.controlEl.setAttribute("value", value); + }); + }) + .addText((text) => { + text + .setPlaceholder(i18next.t("regex.replace")) + .setValue(censorText.replace) + .onChange(async (value) => { + censorText.replace = value; + }); + }) + + .addExtraButton((btn) => { + btn + .setIcon("trash") + .setTooltip(i18next.t("common.delete", { things: "Regex" })) + .onClick(async () => { + this.settings.conversion.censorText.splice( + this.settings.conversion.censorText.indexOf(censorText), + 1 + ); + this.onOpen(); + }); + }) + .addExtraButton((btn) => { + btn + .setTooltip(desc) + .setIcon(afterIcon) + .onClick(async () => { + censorText.after = !censorText.after; + this.onOpen(); + }); + }) + .addExtraButton((btn) => { + btn + .setTooltip(toolTipCode) + .setIcon(inCodeBlocks) + .onClick(async () => { + censorText.inCodeBlocks = !censorText.inCodeBlocks; + this.onOpen(); + }); + }); + sett.controlEl.setAttribute("value", censorText.entry); + sett.controlEl.classList.add("regex"); + } + new Setting(contentEl) + .addButton((btn) => { + btn + .setIcon("plus") + .setTooltip(i18next.t("common.add", { things: "Regex" })) + + .onClick(async () => { + const censorText: TextCleaner = { + entry: "", + replace: "", + flags: "", + after: false, + }; + this.settings.conversion.censorText.push(censorText); + this.onOpen(); + }); + }) + .addButton((button) => { + button + .setButtonText(i18next.t("common.save")) + .setCta() + .onClick(() => { + const canBeValidated: boolean[] = []; + for (const censor of this.settings.conversion.censorText) { + if (!isRegexValid(censor.entry).isValid) { + new Notice( + i18next.t("settings.regexReplacing.invalidRegex", { + e: isRegexValid(censor.entry).error, + }) + ); + //add error class to faulty input + const faultyInput = contentEl.querySelector( + `[value="${escapeRegex(censor.entry)}"] input` + ); + faultyInput?.classList.add("error"); + canBeValidated.push(false); + } + } + if (!canBeValidated.includes(false)) { + this.onSubmit(this.settings); + this.close(); + } + }); + }); + } + + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +} diff --git a/src/settings/modals/token_path.ts b/src/settings/modals/token_path.ts index 05af47f5..b484ebf7 100644 --- a/src/settings/modals/token_path.ts +++ b/src/settings/modals/token_path.ts @@ -1,113 +1,112 @@ -import { TOKEN_PATH } from "@interfaces"; -import i18next from "i18next"; -import { App, Modal, Notice,Setting } from "obsidian"; -import GithubPublisher from "src/main"; -import { migrateToken } from "src/settings/migrate"; -import { createTokenPath, logs } from "src/utils"; - -export class TokenEditPath extends Modal { - plugin: GithubPublisher; - token: string; - tokenPath: string; - - constructor(app: App, plugin: GithubPublisher, token: string) { - super(app); - this.plugin = plugin; - this.token = token; - this.tokenPath = ""; - } - - onOpen() { - const {contentEl} = this; - contentEl.empty(); - contentEl.addClasses(["github-publisher", "modals", "token-path"]); - - const defaultPath = createTokenPath(this.plugin, TOKEN_PATH); - const desc = contentEl.createEl("p", undefined, (p) => { - p.appendText(i18next.t("settings.github.ghToken.button.description")); - const div = p.createDiv({text: i18next.t("settings.github.ghToken.button.default")}); - div.createEl("code", {text: ` ${defaultPath}`}, (code) => { - code.classList.add("cm-inline-code"); - code.style.fontFamily = "var(--font-monospace)"; - }); - }); - desc.createEl("br"); - desc.createEl("p", {text: i18next.t("settings.github.ghToken.button.variables")}); - desc.createEl("ul", undefined, (span) => { - span.createEl("li", undefined, (li) => { - li.createEl("code", {text: "%configDir%"}, (code) => { - code.classList.add("cm-inline-code"); - code.style.fontFamily = "var(--font-monospace)"; - }); - li.createEl("span", undefined, (e) => { - e.appendText(`${i18next.t("settings.github.ghToken.button.configDir")} (`); - e.createEl("code", {text: this.app.vault.configDir}, (code) => { - code.classList.add("cm-inline-code"); - code.style.fontFamily = "var(--font-monospace)"; - }); - e.appendText(")"); - }); - }); - span.createEl("li", undefined, (li) => { - li.createEl("code", {text: "%pluginID%"}, (code) => { - code.classList.add("cm-inline-code"); - code.style.fontFamily = "var(--font-monospace)"; - }); - li.createEl("span", undefined, (e) => { - e.appendText(`${i18next.t("settings.github.ghToken.button.pluginID")} (`); - e.createEl("code", {text: this.plugin.manifest.id}, (code) => { - code.classList.add("cm-inline-code"); - code.style.fontFamily = "var(--font-monospace)"; - }); - e.appendText(")"); - }); - }); - }); - - const input = new Setting(contentEl) - .setClass("display-none") - .setClass("max-width") - .addText((text) => { - const path = this.plugin.settings.github.tokenPath ?? defaultPath; - text - .setPlaceholder(defaultPath) - .setValue(path) - .onChange(async (value) => { - let path = value.trim(); - if (path.length === 0) { - path = defaultPath; - } - this.plugin.settings.github.tokenPath = path; - this.tokenPath = path; - }); - }); - - - new Setting(contentEl) - .addButton((button) => { - button.setButtonText(i18next.t("common.save")) - .onClick(async () => { - try { - await this.plugin.saveSettings(); - await migrateToken(this.plugin, this.token); - this.close(); - } catch (e) { - input.controlEl.querySelector("input")!.style.border = "1px solid red"; - new Notice(i18next.t("error.reading-token-file")); - this.tokenPath="error"; - logs({settings: this.plugin.settings, e: true}, e); - } - }); - }); - } - - async onClose() { - const {contentEl} = this; - contentEl.empty(); - if (this.tokenPath === "error") { - this.plugin.settings.github.tokenPath = TOKEN_PATH; - await this.plugin.saveSettings(); - await migrateToken(this.plugin, this.token); - } - } -} \ No newline at end of file +import { TOKEN_PATH } from "@interfaces"; +import i18next from "i18next"; +import { App, Modal, Notice, Setting } from "obsidian"; +import GithubPublisher from "src/main"; +import { migrateToken } from "src/settings/migrate"; +import { createTokenPath, logs } from "src/utils"; + +export class TokenEditPath extends Modal { + plugin: GithubPublisher; + token: string; + tokenPath: string; + + constructor(app: App, plugin: GithubPublisher, token: string) { + super(app); + this.plugin = plugin; + this.token = token; + this.tokenPath = ""; + } + + onOpen() { + const { contentEl } = this; + contentEl.empty(); + contentEl.addClasses(["github-publisher", "modals", "token-path"]); + + const defaultPath = createTokenPath(this.plugin, TOKEN_PATH); + const desc = contentEl.createEl("p", undefined, (p) => { + p.appendText(i18next.t("settings.github.ghToken.button.description")); + const div = p.createDiv({ + text: i18next.t("settings.github.ghToken.button.default"), + }); + div.createEl("code", { text: ` ${defaultPath}` }, (code) => { + code.classList.add("cm-inline-code"); + code.style.fontFamily = "var(--font-monospace)"; + }); + }); + desc.createEl("br"); + desc.createEl("p", { text: i18next.t("settings.github.ghToken.button.variables") }); + desc.createEl("ul", undefined, (span) => { + span.createEl("li", undefined, (li) => { + li.createEl("code", { text: "%configDir%" }, (code) => { + code.classList.add("cm-inline-code"); + code.style.fontFamily = "var(--font-monospace)"; + }); + li.createEl("span", undefined, (e) => { + e.appendText(`${i18next.t("settings.github.ghToken.button.configDir")} (`); + e.createEl("code", { text: this.app.vault.configDir }, (code) => { + code.classList.add("cm-inline-code"); + code.style.fontFamily = "var(--font-monospace)"; + }); + e.appendText(")"); + }); + }); + span.createEl("li", undefined, (li) => { + li.createEl("code", { text: "%pluginID%" }, (code) => { + code.classList.add("cm-inline-code"); + code.style.fontFamily = "var(--font-monospace)"; + }); + li.createEl("span", undefined, (e) => { + e.appendText(`${i18next.t("settings.github.ghToken.button.pluginID")} (`); + e.createEl("code", { text: this.plugin.manifest.id }, (code) => { + code.classList.add("cm-inline-code"); + code.style.fontFamily = "var(--font-monospace)"; + }); + e.appendText(")"); + }); + }); + }); + + const input = new Setting(contentEl) + .setClass("display-none") + .setClass("max-width") + .addText((text) => { + const path = this.plugin.settings.github.tokenPath ?? defaultPath; + text + .setPlaceholder(defaultPath) + .setValue(path) + .onChange(async (value) => { + let path = value.trim(); + if (path.length === 0) { + path = defaultPath; + } + this.plugin.settings.github.tokenPath = path; + this.tokenPath = path; + }); + }); + + new Setting(contentEl).addButton((button) => { + button.setButtonText(i18next.t("common.save")).onClick(async () => { + try { + await this.plugin.saveSettings(); + await migrateToken(this.plugin, this.token); + this.close(); + } catch (e) { + input.controlEl.querySelector("input")!.style.border = "1px solid red"; + new Notice(i18next.t("error.reading-token-file")); + this.tokenPath = "error"; + logs({ settings: this.plugin.settings, e: true }, e); + } + }); + }); + } + + async onClose() { + const { contentEl } = this; + contentEl.empty(); + if (this.tokenPath === "error") { + this.plugin.settings.github.tokenPath = TOKEN_PATH; + await this.plugin.saveSettings(); + await migrateToken(this.plugin, this.token); + } + } +} diff --git a/src/settings/style.ts b/src/settings/style.ts index 0f87fdf8..4b5e6517 100644 --- a/src/settings/style.ts +++ b/src/settings/style.ts @@ -1,171 +1,165 @@ -import {EnumbSettingsTabId, FolderSettings, GitHubPublisherSettings} from "@interfaces"; -import i18next from "i18next"; -import { Notice, Setting } from "obsidian"; -import GithubPublisher from "src/main"; -import { GithubPublisherSettingsTab } from "src/settings"; -/** - * show a settings - * @param {Setting} containerEl setting to show - */ - -export function showSettings(containerEl: Setting) { - for (const [type, elem] of Object.entries(containerEl)) { - if (type != "components") { - elem.show(); - } - } -} - - -/** - * Hide a settings - * @param {Setting} containerEl settings to hide - */ - -export function hideSettings(containerEl: Setting) { - for (const [type, elem] of Object.entries(containerEl)) { - if (type != "components") { - elem.hide(); - } - } -} - - -export function showHideBasedOnFolder(settings: GitHubPublisherSettings, frontmatterKeySettings: Setting, rootFolderSettings: Setting, folderNoteSettings: Setting) { - const upload = settings.upload; - if (upload.behavior === FolderSettings.yaml) { - showSettings(frontmatterKeySettings); - showSettings(rootFolderSettings); - showSettings(folderNoteSettings); - } else { - hideSettings(frontmatterKeySettings); - hideSettings(rootFolderSettings); - if ( - upload.behavior === - FolderSettings.obsidian - ) { - showSettings(folderNoteSettings); - } else { - hideSettings(folderNoteSettings); - } - } -} - - -/** - * Show or hide the autoclean settings - */ - -export async function autoCleanCondition( - value: string, - autoCleanSetting: Setting, - plugin: GithubPublisher, - what: "rootFolder" | "defaultName" = "defaultName", - settingsTab: GithubPublisherSettingsTab -) { - const settings = plugin.settings.upload; - const translation = what === "rootFolder" ? i18next.t("common.rootFolder") : i18next.t("common.defaultName"); - if (value.length === 0 && settings.defaultName) { - if (settings.autoclean.enable) - new Notice(i18next.t("error.autoClean", {what: translation})); - settings.autoclean.enable = false; - await plugin.saveSettings(); - // @ts-ignore - autoCleanSetting.components[0].toggleEl.classList.remove("is-enabled"); - settingsTab.renderSettingsPage(EnumbSettingsTabId.upload); - } - if ( - value.length === 0 && - settings.behavior !== FolderSettings.yaml - ) { - if (settings.autoclean.enable) - new Notice(i18next.t("error.autoClean", {what: i18next.t("common.defaultName")}),); - settings.autoclean.enable = false; - // @ts-ignore - autoCleanSetting.components[0].toggleEl.classList.remove("is-enabled"); - settingsTab.renderSettingsPage(EnumbSettingsTabId.upload); - } - if (settings.autoclean.enable) { - // @ts-ignore - autoCleanSetting.components[0].toggleEl.classList.add("is-enabled"); - } -} - -/** - * Show or hide settings based on the value of the folder settings - * Will : - * @example - * If the settings is set to YAML: - * - Show the frontmatterKey setting (to set the category key) - * - Show the rootFolder settings - * - Check the condition to show or hide the autoClean settings (with checking the length of the defaultFolder) - * - * @example - * - If obsidian path or fixed folder : hide the frontmatterKey setting and the rootFolder settings - */ - -export async function folderHideShowSettings( - frontmatterKeySettings: Setting, - rootFolderSettings: Setting, - autoCleanSetting: Setting, - value: string, - plugin: GithubPublisher, -) { - const settings = plugin.settings.upload; - if (value === FolderSettings.yaml) { - showSettings(frontmatterKeySettings); - showSettings(rootFolderSettings); - return; - } - if (settings.defaultName.length > 0 && settings.autoclean.enable) { - // @ts-ignore - autoCleanSetting.components[0].toggleEl.classList.add( - "is-enabled" - ); - } - hideSettings(frontmatterKeySettings); - hideSettings(rootFolderSettings); -} - -/** - * show or hide with disabling the autoclean settings based on the condition - * @param {boolean} condition - * @param {Setting} autoCleanSetting - * @param {GithubPublisher} plugin - */ - -export function autoCleanUpSettingsOnCondition( - condition: boolean, - autoCleanSetting: Setting, - plugin: GithubPublisher -) { - const settings = plugin.settings.upload; - if (condition) { - // @ts-ignore - autoCleanSetting.components[0].toggleEl.classList.remove("is-enabled"); - settings.autoclean.enable = false; - plugin.saveSettings().then(); - return; - } - if (settings.autoclean.enable) { - // @ts-ignore - autoCleanSetting.components[0].toggleEl.classList.add("is-enabled"); - } -} - -/** - * Show or hide the settings based on the condition - * @param {string | boolean} condition - * @param {Setting} toDisplay the Settings to display - */ - -export function shortcutsHideShow( - condition: string | boolean, - toDisplay: Setting -) { - if (condition) { - showSettings(toDisplay); - } else { - hideSettings(toDisplay); - } -} +import { EnumbSettingsTabId, FolderSettings, GitHubPublisherSettings } from "@interfaces"; +import i18next from "i18next"; +import { Notice, Setting } from "obsidian"; +import GithubPublisher from "src/main"; +import { GithubPublisherSettingsTab } from "src/settings"; +/** + * show a settings + * @param {Setting} containerEl setting to show + */ + +export function showSettings(containerEl: Setting) { + for (const [type, elem] of Object.entries(containerEl)) { + if (type != "components") { + elem.show(); + } + } +} + +/** + * Hide a settings + * @param {Setting} containerEl settings to hide + */ + +export function hideSettings(containerEl: Setting) { + for (const [type, elem] of Object.entries(containerEl)) { + if (type != "components") { + elem.hide(); + } + } +} + +export function showHideBasedOnFolder( + settings: GitHubPublisherSettings, + frontmatterKeySettings: Setting, + rootFolderSettings: Setting, + folderNoteSettings: Setting +) { + const upload = settings.upload; + if (upload.behavior === FolderSettings.yaml) { + showSettings(frontmatterKeySettings); + showSettings(rootFolderSettings); + showSettings(folderNoteSettings); + } else { + hideSettings(frontmatterKeySettings); + hideSettings(rootFolderSettings); + if (upload.behavior === FolderSettings.obsidian) { + showSettings(folderNoteSettings); + } else { + hideSettings(folderNoteSettings); + } + } +} + +/** + * Show or hide the autoclean settings + */ + +export async function autoCleanCondition( + value: string, + autoCleanSetting: Setting, + plugin: GithubPublisher, + what: "rootFolder" | "defaultName" = "defaultName", + settingsTab: GithubPublisherSettingsTab +) { + const settings = plugin.settings.upload; + const translation = + what === "rootFolder" + ? i18next.t("common.rootFolder") + : i18next.t("common.defaultName"); + if (value.length === 0 && settings.defaultName) { + if (settings.autoclean.enable) + new Notice(i18next.t("error.autoClean", { what: translation })); + settings.autoclean.enable = false; + await plugin.saveSettings(); + // @ts-ignore + autoCleanSetting.components[0].toggleEl.classList.remove("is-enabled"); + settingsTab.renderSettingsPage(EnumbSettingsTabId.upload); + } + if (value.length === 0 && settings.behavior !== FolderSettings.yaml) { + if (settings.autoclean.enable) + new Notice(i18next.t("error.autoClean", { what: i18next.t("common.defaultName") })); + settings.autoclean.enable = false; + // @ts-ignore + autoCleanSetting.components[0].toggleEl.classList.remove("is-enabled"); + settingsTab.renderSettingsPage(EnumbSettingsTabId.upload); + } + if (settings.autoclean.enable) { + // @ts-ignore + autoCleanSetting.components[0].toggleEl.classList.add("is-enabled"); + } +} + +/** + * Show or hide settings based on the value of the folder settings + * Will : + * @example + * If the settings is set to YAML: + * - Show the frontmatterKey setting (to set the category key) + * - Show the rootFolder settings + * - Check the condition to show or hide the autoClean settings (with checking the length of the defaultFolder) + * + * @example + * - If obsidian path or fixed folder : hide the frontmatterKey setting and the rootFolder settings + */ + +export async function folderHideShowSettings( + frontmatterKeySettings: Setting, + rootFolderSettings: Setting, + autoCleanSetting: Setting, + value: string, + plugin: GithubPublisher +) { + const settings = plugin.settings.upload; + if (value === FolderSettings.yaml) { + showSettings(frontmatterKeySettings); + showSettings(rootFolderSettings); + return; + } + if (settings.defaultName.length > 0 && settings.autoclean.enable) { + // @ts-ignore + autoCleanSetting.components[0].toggleEl.classList.add("is-enabled"); + } + hideSettings(frontmatterKeySettings); + hideSettings(rootFolderSettings); +} + +/** + * show or hide with disabling the autoclean settings based on the condition + * @param {boolean} condition + * @param {Setting} autoCleanSetting + * @param {GithubPublisher} plugin + */ + +export function autoCleanUpSettingsOnCondition( + condition: boolean, + autoCleanSetting: Setting, + plugin: GithubPublisher +) { + const settings = plugin.settings.upload; + if (condition) { + // @ts-ignore + autoCleanSetting.components[0].toggleEl.classList.remove("is-enabled"); + settings.autoclean.enable = false; + plugin.saveSettings().then(); + return; + } + if (settings.autoclean.enable) { + // @ts-ignore + autoCleanSetting.components[0].toggleEl.classList.add("is-enabled"); + } +} + +/** + * Show or hide the settings based on the condition + * @param {string | boolean} condition + * @param {Setting} toDisplay the Settings to display + */ + +export function shortcutsHideShow(condition: string | boolean, toDisplay: Setting) { + if (condition) { + showSettings(toDisplay); + } else { + hideSettings(toDisplay); + } +} diff --git a/src/utils/data_validation_test.ts b/src/utils/data_validation_test.ts index 022be170..086b9d1f 100644 --- a/src/utils/data_validation_test.ts +++ b/src/utils/data_validation_test.ts @@ -1,12 +1,24 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import {FIND_REGEX, GitHubPublisherSettings, GithubTiersVersion, MultiProperties, Properties, PropertiesConversion, Repository} from "@interfaces"; +import { + FIND_REGEX, + GitHubPublisherSettings, + GithubTiersVersion, + MultiProperties, + Properties, + PropertiesConversion, + Repository, +} from "@interfaces"; import { Octokit } from "@octokit/core"; import i18next from "i18next"; -import { FrontMatterCache, normalizePath,Notice, TFile, TFolder} from "obsidian"; -import {GithubBranch} from "src/GitHub/branch"; +import { FrontMatterCache, normalizePath, Notice, TFile, TFolder } from "obsidian"; +import { GithubBranch } from "src/GitHub/branch"; import GithubPublisher from "src/main"; -import { notif} from "src/utils"; -import { frontmatterFromFile, getLinkedFrontmatter, getProperties } from "src/utils/parse_frontmatter"; +import { notif } from "src/utils"; +import { + frontmatterFromFile, + getLinkedFrontmatter, + getProperties, +} from "src/utils/parse_frontmatter"; import merge from "ts-deepmerge"; /** @@ -22,24 +34,32 @@ import merge from "ts-deepmerge"; export function isInternalShared( frontmatter: FrontMatterCache | undefined | null, properties: MultiProperties, - file: TFile, + file: TFile ): boolean { const frontmatterSettings = properties.frontmatter.general; if (frontmatterSettings.unshared) { return true; } - if (properties.repository?.shareAll?.enable) - { + if (properties.repository?.shareAll?.enable) { const excludedFileName = properties.repository.shareAll.excludedFileName; - return !file.basename.startsWith(excludedFileName) && !isInDryRunFolder(properties.plugin.settings, properties.repository, file); + return ( + !file.basename.startsWith(excludedFileName) && + !isInDryRunFolder(properties.plugin.settings, properties.repository, file) + ); } if (!frontmatter) return false; - if (isExcludedPath(properties.plugin.settings, file, properties.repository)) return false; - const shareKey = properties.repository?.shareKey || properties.plugin.settings.plugin.shareKey; - if (frontmatter[shareKey] == null || frontmatter[shareKey] === undefined || ["false", "0", "no"].includes(frontmatter[shareKey].toString().toLowerCase())) return false; + if (isExcludedPath(properties.plugin.settings, file, properties.repository)) + return false; + const shareKey = + properties.repository?.shareKey || properties.plugin.settings.plugin.shareKey; + if ( + frontmatter[shareKey] == null || + frontmatter[shareKey] === undefined || + ["false", "0", "no"].includes(frontmatter[shareKey].toString().toLowerCase()) + ) + return false; return ["true", "1", "yes"].includes(frontmatter[shareKey].toString().toLowerCase()); - } /** * Retrieves the shared key for a repository based on the provided settings, app, frontmatter, and file. @@ -48,16 +68,21 @@ export function isInternalShared( * @param file - The TFile object representing the file being processed. * @returns The Repository object representing the repository with the shared key, or null if no repository is found. */ -export function getRepoSharedKey(plugin: GithubPublisher, frontmatter?: FrontMatterCache | null, file?: TFile): Repository | null{ - const {settings} = plugin; +export function getRepoSharedKey( + plugin: GithubPublisher, + frontmatter?: FrontMatterCache | null, + file?: TFile +): Repository | null { + const { settings } = plugin; const allOtherRepo = settings.github.otherRepo; if (settings.plugin.shareAll?.enable && !frontmatter) { return defaultRepo(settings); } else if (!frontmatter) return null; const linkedFrontmatter = getLinkedFrontmatter(frontmatter, file, plugin); frontmatter = linkedFrontmatter ? merge(linkedFrontmatter, frontmatter) : frontmatter; - return allOtherRepo.find(repo => frontmatter?.[repo.shareKey]) ?? defaultRepo(settings); - + return ( + allOtherRepo.find((repo) => frontmatter?.[repo.shareKey]) ?? defaultRepo(settings) + ); } /** @@ -75,31 +100,39 @@ export function isShared( meta: FrontMatterCache | undefined | null, settings: GitHubPublisherSettings, file: TFile, - otherRepo: Repository|null + otherRepo: Repository | null ): boolean { if (!file || file.extension !== "md") { return false; } - const otherRepoWithShareAll = settings.github.otherRepo.filter((repo) => repo.shareAll?.enable); + const otherRepoWithShareAll = settings.github.otherRepo.filter( + (repo) => repo.shareAll?.enable + ); if (!settings.plugin.shareAll?.enable && !otherRepoWithShareAll.length) { const shareKey = otherRepo ? otherRepo.shareKey : settings.plugin.shareKey; - if ( meta == null - || !meta[shareKey] - || meta[shareKey] == null - || isExcludedPath(settings, file, otherRepo) - || meta[shareKey] === undefined - || ["false", "0", "no"].includes(meta[shareKey].toString().toLowerCase())) { + if ( + meta == null || + !meta[shareKey] || + meta[shareKey] == null || + isExcludedPath(settings, file, otherRepo) || + meta[shareKey] === undefined || + ["false", "0", "no"].includes(meta[shareKey].toString().toLowerCase()) + ) { return false; } - const shareKeyInFrontmatter:string = meta[shareKey].toString().toLowerCase(); + const shareKeyInFrontmatter: string = meta[shareKey].toString().toLowerCase(); return ["true", "1", "yes"].includes(shareKeyInFrontmatter); } else if (settings.plugin.shareAll?.enable || otherRepoWithShareAll.length > 0) { - const allExcludedFileName = otherRepoWithShareAll.map((repo) => repo.shareAll!.excludedFileName); + const allExcludedFileName = otherRepoWithShareAll.map( + (repo) => repo.shareAll!.excludedFileName + ); allExcludedFileName.push(settings.plugin.shareAll!.excludedFileName); if ( - allExcludedFileName.some(prefix => prefix.trim().length > 0 - && !file.basename.startsWith(prefix) - || prefix.trim().length === 0) + allExcludedFileName.some( + (prefix) => + (prefix.trim().length > 0 && !file.basename.startsWith(prefix)) || + prefix.trim().length === 0 + ) ) { return !isExcludedPath(settings, file, otherRepo); } @@ -112,10 +145,16 @@ export function isShared( * @param file {TFile} * @returns boolean */ -export function isExcludedPath(settings: GitHubPublisherSettings, file: TFile | TFolder, repository: Repository | null):boolean { +export function isExcludedPath( + settings: GitHubPublisherSettings, + file: TFile | TFolder, + repository: Repository | null +): boolean { const excludedFolder = settings.plugin.excludedFolder; if (settings.plugin.shareAll?.enable || repository?.shareAll?.enable) { - const excludedFromShare = repository?.shareAll?.excludedFileName ?? settings.plugin.shareAll?.excludedFileName; + const excludedFromShare = + repository?.shareAll?.excludedFileName ?? + settings.plugin.shareAll?.excludedFileName; if (excludedFromShare && file.name.startsWith(excludedFromShare)) { return true; } @@ -123,14 +162,13 @@ export function isExcludedPath(settings: GitHubPublisherSettings, file: TFile | for (const folder of excludedFolder) { const isRegex = folder.match(FIND_REGEX); const regex = isRegex ? new RegExp(isRegex[1], isRegex[2]) : null; - if ((regex?.test(file.path)) || file.path.contains(folder.trim())) { + if (regex?.test(file.path) || file.path.contains(folder.trim())) { return true; } } return isInDryRunFolder(settings, repository, file); } - /** * Allow to get all sharedKey from one file to count them * @@ -139,11 +177,14 @@ export function isExcludedPath(settings: GitHubPublisherSettings, file: TFile | * @param {TFile | null} file - The file to get the shared keys from. * @returns {string[]} - An array of shared keys found in the file. */ -export function multipleSharedKey(frontmatter: FrontMatterCache | undefined | null, file: TFile | null, plugin: GithubPublisher): string[] { +export function multipleSharedKey( + frontmatter: FrontMatterCache | undefined | null, + file: TFile | null, + plugin: GithubPublisher +): string[] { const keysInFile: string[] = []; - const {settings} = plugin; - if (settings.plugin.shareAll?.enable) - keysInFile.push("share"); //add a key to count the shareAll + const { settings } = plugin; + if (settings.plugin.shareAll?.enable) keysInFile.push("share"); //add a key to count the shareAll const otherRepoWithShareAll = settings.github.otherRepo.filter((repo) => repo.shareAll); if (otherRepoWithShareAll.length > 0) { @@ -191,7 +232,10 @@ export function multipleSharedKey(frontmatter: FrontMatterCache | undefined | nu * @return {RegExpMatchArray} */ -export function isAttachment(filename: string, attachmentExtern?: string[]): RegExpMatchArray | null { +export function isAttachment( + filename: string, + attachmentExtern?: string[] +): RegExpMatchArray | null { if (filename.includes("excalidraw")) return filename.match(/excalidraw\.md$/i); if (attachmentExtern && attachmentExtern.length > 0) { for (const att of attachmentExtern) { @@ -207,7 +251,6 @@ export function isAttachment(filename: string, attachmentExtern?: string[]): Reg ); } - /** * Check if a target Repository === source Repository * @param {Properties | Properties[]} source @@ -261,31 +304,32 @@ export function checkIfRepoIsInAnother( * @param silent * @return {Promise} */ -export async function checkEmptyConfiguration(prop: Properties | Properties[], plugin: GithubPublisher, silent= false): Promise { - prop = Array.isArray(prop) - ? prop - : [prop]; +export async function checkEmptyConfiguration( + prop: Properties | Properties[], + plugin: GithubPublisher, + silent = false +): Promise { + prop = Array.isArray(prop) ? prop : [prop]; const isEmpty: boolean[] = []; const token = await plugin.loadToken(); if (token.length === 0) { isEmpty.push(true); - const whatIsEmpty = i18next.t("common.ghToken") ; - if (!silent) new Notice(i18next.t("error.isEmpty", {what: whatIsEmpty})); - } - else { + const whatIsEmpty = i18next.t("common.ghToken"); + if (!silent) new Notice(i18next.t("error.isEmpty", { what: whatIsEmpty })); + } else { for (const repo of prop) { if (repo.repo.length === 0) { isEmpty.push(true); - const whatIsEmpty = i18next.t("common.repository") ; - if (!silent) new Notice(i18next.t("error.isEmpty", {what: whatIsEmpty})); + const whatIsEmpty = i18next.t("common.repository"); + if (!silent) new Notice(i18next.t("error.isEmpty", { what: whatIsEmpty })); } else if (repo.owner.length === 0) { isEmpty.push(true); - const whatIsEmpty = i18next.t("error.whatEmpty.owner") ; - if (!silent) new Notice(i18next.t("error.isEmpty", {what: whatIsEmpty})); + const whatIsEmpty = i18next.t("error.whatEmpty.owner"); + if (!silent) new Notice(i18next.t("error.isEmpty", { what: whatIsEmpty })); } else if (repo.branch.length === 0) { isEmpty.push(true); - const whatIsEmpty = i18next.t("error.whatEmpty.branch") ; - if (!silent) new Notice(i18next.t("error.isEmpty", {what: whatIsEmpty})); + const whatIsEmpty = i18next.t("error.whatEmpty.branch"); + if (!silent) new Notice(i18next.t("error.isEmpty", { what: whatIsEmpty })); } else { isEmpty.push(false); } @@ -305,11 +349,13 @@ export function noTextConversion(conditionConvert: PropertiesConversion): boolea const imageSettings = conditionConvert.attachment; const embedSettings = conditionConvert.embed; const convertLinks = conditionConvert.links; - return !convertWikilink - && convertLinks - && imageSettings - && embedSettings - && !conditionConvert.removeEmbed; + return ( + !convertWikilink && + convertLinks && + imageSettings && + embedSettings && + !conditionConvert.removeEmbed + ); } /** @@ -325,19 +371,23 @@ export async function checkRepositoryValidity( PublisherManager: GithubBranch, repository: Repository | null = null, file: TFile | null, - silent: boolean=false): Promise { + silent: boolean = false +): Promise { const settings = PublisherManager.settings; try { const frontmatter = frontmatterFromFile(file, PublisherManager.plugin, repository); const prop = getProperties(PublisherManager.plugin, repository, frontmatter); - const isNotEmpty = await checkEmptyConfiguration(prop, PublisherManager.plugin, silent); + const isNotEmpty = await checkEmptyConfiguration( + prop, + PublisherManager.plugin, + silent + ); if (isNotEmpty) { await PublisherManager.checkRepository(prop, silent); return true; } - } - catch (e) { - notif({settings, e: true}, e); + } catch (e) { + notif({ settings, e: true }, e); return false; } return false; @@ -353,7 +403,7 @@ export async function checkRepositoryValidity( export async function checkRepositoryValidityWithProperties( PublisherManager: GithubBranch, prop: Properties, - numberOfFile: number=1 + numberOfFile: number = 1 ): Promise { const settings = PublisherManager.settings; if (settings.github.dryRun.enable) return true; @@ -365,13 +415,19 @@ export async function checkRepositoryValidityWithProperties( if (isNotEmpty) { await PublisherManager.checkRepository(prop, true); if (prop?.rateLimit === 0 || numberOfFile > 20) { - return await verifyRateLimitAPI(PublisherManager.octokit, settings, false, numberOfFile) > 0; + return ( + (await verifyRateLimitAPI( + PublisherManager.octokit, + settings, + false, + numberOfFile + )) > 0 + ); } return true; } - } - catch (e) { - notif({settings, e: true}, e); + } catch (e) { + notif({ settings, e: true }, e); return false; } return false; @@ -411,7 +467,7 @@ export function defaultRepo(settings: GitHubPublisherSettings): Repository { applyRegex: settings.plugin.copyLink.transform.applyRegex, }, }, - set: null + set: null, }; } @@ -441,13 +497,15 @@ export async function verifyRateLimitAPI( const time = date.toLocaleTimeString(); if (remaining <= numberOfFile) { - new Notice(i18next.t("commands.checkValidity.rateLimit.limited", { resetTime: time })); + new Notice( + i18next.t("commands.checkValidity.rateLimit.limited", { resetTime: time }) + ); return 0; } const message = i18next.t("commands.checkValidity.rateLimit.notLimited", { remaining, - resetTime: time + resetTime: time, }); if (commands) { @@ -458,14 +516,16 @@ export async function verifyRateLimitAPI( return remaining; } catch (error) { - //if the error is 404 and user use enterprise, it's normal - if ((error as any).status === 404 - && settings.github.api.tiersForApi === GithubTiersVersion.entreprise - && (error as any).response.data.message === "Rate limiting is not enabled." - && (error as any).name === "HttpError") return 5000; + //if the error is 404 and user use enterprise, it's normal + if ( + (error as any).status === 404 && + settings.github.api.tiersForApi === GithubTiersVersion.entreprise && + (error as any).response.data.message === "Rate limiting is not enabled." && + (error as any).name === "HttpError" + ) + return 5000; notif({ settings, e: true }, error); return 0; - } } @@ -476,16 +536,16 @@ export async function verifyRateLimitAPI( * @param settings - The GitHub Publisher settings. * @returns True if the attachment file needs to be force pushed, false otherwise. */ -export function forcePushAttachment(file: TFile, settings: GitHubPublisherSettings):boolean { +export function forcePushAttachment( + file: TFile, + settings: GitHubPublisherSettings +): boolean { const needToBeForPush = settings.embed.overrideAttachments.filter((path) => { const isRegex = path.path.match(FIND_REGEX); const regex = isRegex ? new RegExp(isRegex[1], isRegex[2]) : null; return ( - path.forcePush &&( - regex?.test(file.path) - || file.path === path.path - || path.path.contains("{{all}}") - ) + path.forcePush && + (regex?.test(file.path) || file.path === path.path || path.path.contains("{{all}}")) ); }); if (needToBeForPush.length === 0) return false; @@ -498,7 +558,7 @@ export function forcePushAttachment(file: TFile, settings: GitHubPublisherSettin * @returns True if the file is a folder note, false otherwise. */ export function isFolderNote(properties: MultiProperties): boolean { - const {filepath } = properties; + const { filepath } = properties; const { settings } = properties.plugin; const { enable, rename } = settings.upload.folderNote; @@ -518,8 +578,12 @@ export function isFolderNote(properties: MultiProperties): boolean { * @param file - The file or folder to check. * @returns True if the file or folder is located within the dry run folder, false otherwise. */ -export function isInDryRunFolder(settings: GitHubPublisherSettings, repo: Repository | null, file: TFile | TFolder) { - if (settings.github.dryRun.folderName.trim().length ===0) return false; +export function isInDryRunFolder( + settings: GitHubPublisherSettings, + repo: Repository | null, + file: TFile | TFolder +) { + if (settings.github.dryRun.folderName.trim().length === 0) return false; const variables = { owner: repo?.user ?? settings.github.user, repo: repo?.repo ?? settings.github.repo, @@ -530,4 +594,4 @@ export function isInDryRunFolder(settings: GitHubPublisherSettings, repo: Reposi .replace("{{repo}}", variables.repo) .replace("{{branch}}", variables.branch); return file.path.startsWith(normalizePath(folder)); -} \ No newline at end of file +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 1deef02b..a1fbd89a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,456 +1,494 @@ -import { - Deleted, - FIND_REGEX, - GitHubPublisherSettings, - ListEditedFiles, - MetadataExtractor, - MultiRepoProperties, - Properties, - TOKEN_PATH, - UploadedFiles} from "@interfaces"; -import { ERROR_ICONS, HOURGLASS_ICON, SUCCESS_ICON } from "@interfaces/icons"; -import i18next from "i18next"; -import {App, normalizePath, Notice, Platform, TFile} from "obsidian"; -import slugify from "slugify"; -import {getReceiptFolder} from "src/conversion/file_path"; -import { createRegexFromText } from "src/conversion/find_and_replace_text"; -import Publisher from "src/GitHub/upload"; -import GithubPublisher from "src/main"; -import { frontmatterFromFile } from "src/utils/parse_frontmatter"; - -type LogsParameters = { - settings: Partial, - e?: boolean, - logs?: boolean, -} - -/** - * Create a notice message for the log - * @param args {LogsParameters} the settings and the error type - * @param {unknown[]} messages the message to display - */ -//eslint-disable-next-line @typescript-eslint/no-explicit-any -export function notif(args: LogsParameters, ...messages: unknown[]) { - const {settings, e} = args; - if (settings.plugin?.noticeError) { - new Notice(messages.join(" ")); - return; - } - let stack:string = callFunction(); - if (stack.contains("logs")) { - stack = callFunction(true); - } - const date = new Date().toISOString().slice(11, 23); - const prefix = args.logs ? `DEV LOGS [${date}] ${stack}:\n` : `[GitHub Publisher](${stack}):\n`; - if (e) - console.error(prefix, ...messages); - else - console.log(prefix, ...messages); -} - -/** - * Notify the user with a message in the console and a message in the notification - * Only for mobile - * @param cls {"error"|"load"|"success"|"wait"} To adjust css style in function of the type of message - * @param icon {string} The icon to display - * @param message {string} The message to display - * @returns {Notice | undefined} - */ -export function noticeMobile(cls:"error"|"load"|"success"|"wait", icon: string, message: string): Notice | undefined { - if (!Platform.isMobile) { - return; - } - const noticeFrag = document.createDocumentFragment(); - noticeFrag.createEl("span", { text: message, cls: ["obsidian-publisher", cls, "icons"] }).innerHTML = icon; - noticeFrag.createEl("span", { cls: ["obsidian-publisher", cls, "notification"] }).innerHTML = message; - return new Notice(noticeFrag, 0); -} - -/** - * Detect the function that call the function - * @param type {boolean} if true, return the function that call the function that call the function - * @returns {string} - */ -function callFunction(type?: boolean):string { - const index = type ? 4 : 3; - let callFunction = new Error().stack?.split("\n")[index].trim(); - callFunction = callFunction?.substring(callFunction.indexOf("at ") + 3, callFunction.lastIndexOf(" (")); - callFunction = callFunction?.replace("Object.callback", ""); - callFunction = callFunction ? callFunction : "main"; - callFunction = callFunction === "eval" ? "main" : callFunction; - return callFunction; -} - - -/** - * Add a new option in settings "dev" - * Will make appear ALL the logs in the console - * Not just the logs for normal process - * For advanced users only - * @param args {LogsParameters} the settings and the error type - * @param messages {unknown[]} the message to display - */ -export function logs(args: LogsParameters, ...messages: unknown[]) { - const settings = args.settings as GitHubPublisherSettings; - args.logs = true; - if (args.e) { - notif(args, ...messages); - return; - } - if (settings.plugin?.dev) { - notif(args, ...messages); - } -} - -/** - * Monkey patch the console to log all the messages in a file in the plugin folder - * @param plugin {GithubPublisher} the plugin instance - * @returns {void} - */ -export function monkeyPatchConsole(plugin: GithubPublisher): void { - const stack = new Error().stack?.split("\n")?.[3]; - if (!stack?.includes("obsidian-mkdocs-publisher") || !plugin.settings.plugin.dev) { - return; - } - - const logFile = `${plugin.manifest.dir}/logs.txt`; - const logs: string[] = []; - //detect if console.debug, console.error, console.info, console.log or console.warn is called - const originalConsole = { - debug: console.debug, - error: console.error, - info: console.info, - log: console.log, - warn: console.warn, - }; - const logMessages = (prefix: string) => (...messages: unknown[]) => { - logs.push(`\n[${prefix}]`); - for (const message of messages) { - logs.push(String(message)); - } - plugin.app.vault.adapter.write(logFile, logs.join(" ")); - //also display the message in the console - switch (prefix) { - case "error": - originalConsole.error(...messages); - break; - case "info": - originalConsole.info(...messages); - break; - case "log": - originalConsole.log(...messages); - break; - case "warn": - originalConsole.warn(...messages); - break; - case "debug": - originalConsole.debug(...messages); - break; - } - }; - console.debug = logMessages("debug"); - console.error = logMessages("error"); - console.info = logMessages("info"); - console.log = logMessages("log"); - console.warn = logMessages("warn"); - -} - - -/** - * Create the differents list of the modals opened after the upload - * @param {UploadedFiles[]} listUploaded - * @param {Deleted} deleted - * @param {string[]} fileError - * @return {ListEditedFiles} - */ -export function createListEdited(listUploaded: UploadedFiles[], deleted: Deleted, fileError: string[]): ListEditedFiles { - const listEdited: ListEditedFiles = { - added: [], - edited: [], - deleted: [], - unpublished: [], - notDeleted: [], - }; - listUploaded.forEach((file) => { - if (file.isUpdated) { - listEdited.edited.push(file.file); - } else { - listEdited.added.push(file.file); - } - }); - listEdited.unpublished = fileError; - if (deleted) { - listEdited.deleted = deleted.deleted; - listEdited.notDeleted = deleted.undeleted; - } - return listEdited; -} - -/** - * Get the settings of the metadata extractor plugin - * Disable the plugin if it is not installed, the settings are not set or if plateform is mobile - * @param {App} app - * @param {GitHubPublisherSettings} settings - * @returns {Promise} - */ - -export async function getSettingsOfMetadataExtractor( - app: App, - settings: GitHubPublisherSettings -): Promise { - if ( - Platform.isMobile || - !app.plugins.enabledPlugins.has("metadata-extractor") || - settings.upload.metadataExtractorPath.length === 0 - ) - return null; - const metadataExtractor: MetadataExtractor = { - allExceptMdFile: null, - metadataFile: null, - tagsFile: null, - }; - - const path = `${app.vault.configDir}/plugins/metadata-extractor`; - const plugin = app.plugins.getPlugin("metadata-extractor"); - //@ts-ignore - if (plugin?.settings) { - //@ts-ignore - if (plugin.settings.allExceptMdFile.length > 0) { - //get file from plugins folder in .obsidian folder - //@ts-ignore - metadataExtractor.allExceptMdFile = path + "/" + plugin.settings.allExceptMdFile; - } - //@ts-ignore - if (plugin.settings["metadataFile"].length > 0) { - //@ts-ignore - metadataExtractor.metadataFile = path + "/" + plugin.settings.metadataFile; - } - //@ts-ignore - if (plugin.settings.tagFile.length > 0) { - //@ts-ignore - metadataExtractor.tagsFile = path + "/" + plugin.settings.tagFile; - } - return metadataExtractor; - } - return null; -} - - -/** - * Check if the link has a slash at the end and if not add it - * @returns {string} string with the link with a slash at the end - * @param link {string} the link to check - */ - -function checkSlash(link: string): string { - const slash = link.match(/\/*$/); - if (slash && slash[0].length != 1) { - return link.replace(/\/*$/, "") + "/"; - } - return link; -} - -/** - * Create the link for the file and add it to the clipboard - * The path is based with the receipt folder but part can be removed using settings. - * By default, use a github.io page for the link. - */ - -export async function createLink( - file: TFile, - multiRepo: MultiRepoProperties, - plugin: GithubPublisher, -): Promise { - const otherRepo = multiRepo.repository; - const settings = plugin.settings; - const repo = multiRepo.frontmatter; - const copyLink = otherRepo ? otherRepo.copyLink : settings.plugin.copyLink; - const github = otherRepo ? otherRepo : settings.github; - if (!settings.plugin.copyLink.enable) { - return; - } - let filepath = getReceiptFolder(file, otherRepo, plugin, multiRepo.frontmatter); - - let baseLink = copyLink.links; - if (baseLink.length === 0) { - baseLink = repo instanceof Array ? `https://${github.user}.github.io/${settings.github.repo}/` : `https://${repo.owner}.github.io/${repo.repo}/`; - } - const frontmatter = frontmatterFromFile(file, plugin, otherRepo); - let removePart = copyLink.removePart; - const smartKey = otherRepo?.smartKey ? `${otherRepo.smartKey}.` : ""; - if (frontmatter) { - if (frontmatter[`${smartKey}baselink`] != undefined) { - baseLink = frontmatter[`${smartKey}baselink`] as unknown as string; - removePart = []; - } else if (frontmatter[`${smartKey}copylink`] && typeof frontmatter[`${smartKey}.copylink`] === "object") { - baseLink = frontmatter[`${smartKey}copylink`].base as unknown as string; - removePart = frontmatter[`${smartKey}copylink`].remove as unknown as string[] ?? []; - } - if (frontmatter[`${smartKey}copylink.base`]) baseLink = frontmatter[`${smartKey}copylink.base`] as unknown as string; - if (frontmatter[`${smartKey}copylink.remove`]) removePart = frontmatter[`${smartKey}copylink.remove`] as unknown as string[] ?? []; - } - - baseLink = checkSlash(baseLink); - if (removePart.length > 0) { - for (const part of removePart) { - if (part.length > 0) { - filepath = filepath.replace(part.trim(), ""); - } - } - } - filepath = checkSlash(filepath); - let url = baseLink + filepath; - let transform = copyLink.transform; - if (!transform) transform = settings.plugin.copyLink.transform; - if (transform.toUri === undefined) transform.toUri = settings.plugin.copyLink.transform.toUri; - if (transform.slugify === undefined) transform.slugify = settings.plugin.copyLink.transform.slugify; - if (transform.applyRegex === undefined) transform.applyRegex = settings.plugin.copyLink.transform.applyRegex; - if (transform.toUri) { - url = encodeURI(url); - } - if (transform.slugify === "lower") { - url = url.toLowerCase(); - } else if (transform.slugify === "strict") { - url = slugify(url, { lower: true, strict: true }); - } - for (const apply of transform.applyRegex) { - //detect if text is encapsed by // - const {regex, replacement} = apply; - if (regex.match(FIND_REGEX)) { - const reg = createRegexFromText(regex); - url = url.replace(reg, replacement); - } else { - url = url.replace(new RegExp(regex, "g"), replacement); - } - } - //fix links like double slash - await navigator.clipboard.writeText(url.replace(/([^:]\/)\/+/g, "$1")); - return; -} - -/** - * Create a notice message for the sharing ; the message can be delayed if a workflow is used. - * Loop through the list of repo and create a message for each one. - * @param {Publisher} PublisherManager - * @param {TFile | string} file - * @param {GitHubPublisherSettings} settings - * @param {Properties | Properties[]} prop - */ - -export async function publisherNotification( - PublisherManager: Publisher, - file: TFile | string | undefined, - settings: GitHubPublisherSettings, - prop: Properties | Properties[] -) { - prop = Array.isArray(prop) ? prop : [prop]; - for (const repository of prop) { - await publisherNotificationOneRepo( - PublisherManager, - file, - settings, - repository - ); - } -} - -/** - * Notify the user with a message in the console and a message in the notification - * @param properties - */ -export function notifError(properties: Properties | Properties[]) { - const repo = Array.isArray(properties) ? properties : [properties]; - for (const repository of repo) { - const notif = document.createDocumentFragment(); - notif.createSpan({ cls: ["error", "obsidian-publisher", "icons", "notification"] }).innerHTML = ERROR_ICONS; - notif.createSpan({ cls: ["error", "obsidian-publisher", "notification"] }).innerHTML = i18next.t("error.errorPublish", { repo: repository }); - new Notice(notif); - } -} - -/** - * Create a notice message for the sharing ; the message can be delayed if a workflow is used. - * @param {Publisher} PublisherManager - * @param {TFile | string} file - * @param {GitHubPublisherSettings} settings - * @param {Properties} prop - * @return {Promise} - */ - -async function publisherNotificationOneRepo( - PublisherManager: Publisher, - file: TFile | string | undefined, - settings: GitHubPublisherSettings, - prop: Properties -): Promise { - const noticeValue = - file instanceof TFile ? `"${file.basename}"` : file; - const docSuccess = document.createDocumentFragment(); - const successMsg = file instanceof String ? - i18next.t("informations.successfulPublish", { nbNotes: noticeValue, repo: prop }) - : i18next.t("informations.successPublishOneNote", { file: noticeValue, repo: prop }); - docSuccess.createEl("span", { text: successMsg, cls: ["obsidian-publisher", "success", "icons"] }).innerHTML = SUCCESS_ICON; - docSuccess.createEl("span", { cls: ["obsidian-publisher", "success", "notification"] }).innerHTML = successMsg; - if (settings.github.workflow.name.length === 0) { - new Notice(docSuccess, 0); - return; - } - const workflowSuccess = document.createDocumentFragment(); - workflowSuccess.createEl("span", { text: i18next.t("informations.successfulPublish", { nbNotes: noticeValue, repo: prop }), cls: ["obsidian-publisher", "wait", "icons"] }).innerHTML = HOURGLASS_ICON; - const msg = `${i18next.t("informations.sendMessage", {nbNotes: noticeValue, repo: prop})}.
${i18next.t("informations.waitingWorkflow")}`; - workflowSuccess.createEl("span", { cls: ["obsidian-publisher", "wait", "notification"] }).innerHTML = msg; - new Notice(workflowSuccess); - const successWorkflow = await PublisherManager.workflowGestion(prop); - if (successWorkflow) { - new Notice(docSuccess, 0); - } - -} - -/** - * Trim the object to remove the empty value - * @param {{[p: string]: string}} obj - * @return {any} - */ - -export function trimObject(obj: { [p: string]: string }) { - const trimmed = JSON.stringify(obj, (key, value) => { - if (typeof value === "string") { - return value.trim().toLowerCase(); - } - return value; - }); - return JSON.parse(trimmed); -} - - - - -/** - * The REST API of Github have a rate limit of 5000 requests per hour. - * This function check if the user is over the limit, or will be over the limit after the next request. - * If the user is over the limit, the function will display a message to the user. - * It also calculate the time remaining before the limit is reset. - */ - - - -/** - * Convert the variable to their final value: - * - %configDir% -> The config directory of the vault - * - %pluginID% -> The ID of the plugin - * @param {GithubPublisher} plugin - The plugin - * @param {string} tokenPath - The path of the token as entered by the user - * @return {string} - The final path of the token - */ -export function createTokenPath(plugin: GithubPublisher, tokenPath?: string): string { - const vault = plugin.app.vault; - if (!tokenPath) { - tokenPath = TOKEN_PATH; - } - tokenPath = tokenPath.replace("%configDir%", vault.configDir); - tokenPath = tokenPath.replace("%pluginID%", plugin.manifest.id); - return normalizePath(tokenPath); -} - +import { + Deleted, + FIND_REGEX, + GitHubPublisherSettings, + ListEditedFiles, + MetadataExtractor, + MultiRepoProperties, + Properties, + TOKEN_PATH, + UploadedFiles, +} from "@interfaces"; +import { ERROR_ICONS, HOURGLASS_ICON, SUCCESS_ICON } from "@interfaces/icons"; +import i18next from "i18next"; +import { App, normalizePath, Notice, Platform, TFile } from "obsidian"; +import slugify from "slugify"; +import { getReceiptFolder } from "src/conversion/file_path"; +import { createRegexFromText } from "src/conversion/find_and_replace_text"; +import Publisher from "src/GitHub/upload"; +import GithubPublisher from "src/main"; +import { frontmatterFromFile } from "src/utils/parse_frontmatter"; + +type LogsParameters = { + settings: Partial; + e?: boolean; + logs?: boolean; +}; + +/** + * Create a notice message for the log + * @param args {LogsParameters} the settings and the error type + * @param {unknown[]} messages the message to display + */ +//eslint-disable-next-line @typescript-eslint/no-explicit-any +export function notif(args: LogsParameters, ...messages: unknown[]) { + const { settings, e } = args; + if (settings.plugin?.noticeError) { + new Notice(messages.join(" ")); + return; + } + let stack: string = callFunction(); + if (stack.contains("logs")) { + stack = callFunction(true); + } + const date = new Date().toISOString().slice(11, 23); + const prefix = args.logs + ? `DEV LOGS [${date}] ${stack}:\n` + : `[GitHub Publisher](${stack}):\n`; + if (e) console.error(prefix, ...messages); + else console.log(prefix, ...messages); +} + +/** + * Notify the user with a message in the console and a message in the notification + * Only for mobile + * @param cls {"error"|"load"|"success"|"wait"} To adjust css style in function of the type of message + * @param icon {string} The icon to display + * @param message {string} The message to display + * @returns {Notice | undefined} + */ +export function noticeMobile( + cls: "error" | "load" | "success" | "wait", + icon: string, + message: string +): Notice | undefined { + if (!Platform.isMobile) { + return; + } + const noticeFrag = document.createDocumentFragment(); + noticeFrag.createEl("span", { + text: message, + cls: ["obsidian-publisher", cls, "icons"], + }).innerHTML = icon; + noticeFrag.createEl("span", { + cls: ["obsidian-publisher", cls, "notification"], + }).innerHTML = message; + return new Notice(noticeFrag, 0); +} + +/** + * Detect the function that call the function + * @param type {boolean} if true, return the function that call the function that call the function + * @returns {string} + */ +function callFunction(type?: boolean): string { + const index = type ? 4 : 3; + let callFunction = new Error().stack?.split("\n")[index].trim(); + callFunction = callFunction?.substring( + callFunction.indexOf("at ") + 3, + callFunction.lastIndexOf(" (") + ); + callFunction = callFunction?.replace("Object.callback", ""); + callFunction = callFunction ? callFunction : "main"; + callFunction = callFunction === "eval" ? "main" : callFunction; + return callFunction; +} + +/** + * Add a new option in settings "dev" + * Will make appear ALL the logs in the console + * Not just the logs for normal process + * For advanced users only + * @param args {LogsParameters} the settings and the error type + * @param messages {unknown[]} the message to display + */ +export function logs(args: LogsParameters, ...messages: unknown[]) { + const settings = args.settings as GitHubPublisherSettings; + args.logs = true; + if (args.e) { + notif(args, ...messages); + return; + } + if (settings.plugin?.dev) { + notif(args, ...messages); + } +} + +/** + * Monkey patch the console to log all the messages in a file in the plugin folder + * @param plugin {GithubPublisher} the plugin instance + * @returns {void} + */ +export function monkeyPatchConsole(plugin: GithubPublisher): void { + const stack = new Error().stack?.split("\n")?.[3]; + if (!stack?.includes("obsidian-mkdocs-publisher") || !plugin.settings.plugin.dev) { + return; + } + + const logFile = `${plugin.manifest.dir}/logs.txt`; + const logs: string[] = []; + //detect if console.debug, console.error, console.info, console.log or console.warn is called + const originalConsole = { + debug: console.debug, + error: console.error, + info: console.info, + log: console.log, + warn: console.warn, + }; + const logMessages = + (prefix: string) => + (...messages: unknown[]) => { + logs.push(`\n[${prefix}]`); + for (const message of messages) { + logs.push(String(message)); + } + plugin.app.vault.adapter.write(logFile, logs.join(" ")); + //also display the message in the console + switch (prefix) { + case "error": + originalConsole.error(...messages); + break; + case "info": + originalConsole.info(...messages); + break; + case "log": + originalConsole.log(...messages); + break; + case "warn": + originalConsole.warn(...messages); + break; + case "debug": + originalConsole.debug(...messages); + break; + } + }; + console.debug = logMessages("debug"); + console.error = logMessages("error"); + console.info = logMessages("info"); + console.log = logMessages("log"); + console.warn = logMessages("warn"); +} + +/** + * Create the differents list of the modals opened after the upload + * @param {UploadedFiles[]} listUploaded + * @param {Deleted} deleted + * @param {string[]} fileError + * @return {ListEditedFiles} + */ +export function createListEdited( + listUploaded: UploadedFiles[], + deleted: Deleted, + fileError: string[] +): ListEditedFiles { + const listEdited: ListEditedFiles = { + added: [], + edited: [], + deleted: [], + unpublished: [], + notDeleted: [], + }; + listUploaded.forEach((file) => { + if (file.isUpdated) { + listEdited.edited.push(file.file); + } else { + listEdited.added.push(file.file); + } + }); + listEdited.unpublished = fileError; + if (deleted) { + listEdited.deleted = deleted.deleted; + listEdited.notDeleted = deleted.undeleted; + } + return listEdited; +} + +/** + * Get the settings of the metadata extractor plugin + * Disable the plugin if it is not installed, the settings are not set or if plateform is mobile + * @param {App} app + * @param {GitHubPublisherSettings} settings + * @returns {Promise} + */ + +export async function getSettingsOfMetadataExtractor( + app: App, + settings: GitHubPublisherSettings +): Promise { + if ( + Platform.isMobile || + !app.plugins.enabledPlugins.has("metadata-extractor") || + settings.upload.metadataExtractorPath.length === 0 + ) + return null; + const metadataExtractor: MetadataExtractor = { + allExceptMdFile: null, + metadataFile: null, + tagsFile: null, + }; + + const path = `${app.vault.configDir}/plugins/metadata-extractor`; + const plugin = app.plugins.getPlugin("metadata-extractor"); + //@ts-ignore + if (plugin?.settings) { + //@ts-ignore + if (plugin.settings.allExceptMdFile.length > 0) { + //get file from plugins folder in .obsidian folder + //@ts-ignore + metadataExtractor.allExceptMdFile = path + "/" + plugin.settings.allExceptMdFile; + } + //@ts-ignore + if (plugin.settings["metadataFile"].length > 0) { + //@ts-ignore + metadataExtractor.metadataFile = path + "/" + plugin.settings.metadataFile; + } + //@ts-ignore + if (plugin.settings.tagFile.length > 0) { + //@ts-ignore + metadataExtractor.tagsFile = path + "/" + plugin.settings.tagFile; + } + return metadataExtractor; + } + return null; +} + +/** + * Check if the link has a slash at the end and if not add it + * @returns {string} string with the link with a slash at the end + * @param link {string} the link to check + */ + +function checkSlash(link: string): string { + const slash = link.match(/\/*$/); + if (slash && slash[0].length != 1) { + return link.replace(/\/*$/, "") + "/"; + } + return link; +} + +/** + * Create the link for the file and add it to the clipboard + * The path is based with the receipt folder but part can be removed using settings. + * By default, use a github.io page for the link. + */ + +export async function createLink( + file: TFile, + multiRepo: MultiRepoProperties, + plugin: GithubPublisher +): Promise { + const otherRepo = multiRepo.repository; + const settings = plugin.settings; + const repo = multiRepo.frontmatter; + const copyLink = otherRepo ? otherRepo.copyLink : settings.plugin.copyLink; + const github = otherRepo ? otherRepo : settings.github; + if (!settings.plugin.copyLink.enable) { + return; + } + let filepath = getReceiptFolder(file, otherRepo, plugin, multiRepo.frontmatter); + + let baseLink = copyLink.links; + if (baseLink.length === 0) { + baseLink = + repo instanceof Array + ? `https://${github.user}.github.io/${settings.github.repo}/` + : `https://${repo.owner}.github.io/${repo.repo}/`; + } + const frontmatter = frontmatterFromFile(file, plugin, otherRepo); + let removePart = copyLink.removePart; + const smartKey = otherRepo?.smartKey ? `${otherRepo.smartKey}.` : ""; + if (frontmatter) { + if (frontmatter[`${smartKey}baselink`] != undefined) { + baseLink = frontmatter[`${smartKey}baselink`] as unknown as string; + removePart = []; + } else if ( + frontmatter[`${smartKey}copylink`] && + typeof frontmatter[`${smartKey}.copylink`] === "object" + ) { + baseLink = frontmatter[`${smartKey}copylink`].base as unknown as string; + removePart = + (frontmatter[`${smartKey}copylink`].remove as unknown as string[]) ?? []; + } + if (frontmatter[`${smartKey}copylink.base`]) + baseLink = frontmatter[`${smartKey}copylink.base`] as unknown as string; + if (frontmatter[`${smartKey}copylink.remove`]) + removePart = + (frontmatter[`${smartKey}copylink.remove`] as unknown as string[]) ?? []; + } + + baseLink = checkSlash(baseLink); + if (removePart.length > 0) { + for (const part of removePart) { + if (part.length > 0) { + filepath = filepath.replace(part.trim(), ""); + } + } + } + filepath = checkSlash(filepath); + let url = baseLink + filepath; + let transform = copyLink.transform; + if (!transform) transform = settings.plugin.copyLink.transform; + if (transform.toUri === undefined) + transform.toUri = settings.plugin.copyLink.transform.toUri; + if (transform.slugify === undefined) + transform.slugify = settings.plugin.copyLink.transform.slugify; + if (transform.applyRegex === undefined) + transform.applyRegex = settings.plugin.copyLink.transform.applyRegex; + if (transform.toUri) { + url = encodeURI(url); + } + if (transform.slugify === "lower") { + url = url.toLowerCase(); + } else if (transform.slugify === "strict") { + url = slugify(url, { lower: true, strict: true }); + } + for (const apply of transform.applyRegex) { + //detect if text is encapsed by // + const { regex, replacement } = apply; + if (regex.match(FIND_REGEX)) { + const reg = createRegexFromText(regex); + url = url.replace(reg, replacement); + } else { + url = url.replace(new RegExp(regex, "g"), replacement); + } + } + //fix links like double slash + await navigator.clipboard.writeText(url.replace(/([^:]\/)\/+/g, "$1")); + return; +} + +/** + * Create a notice message for the sharing ; the message can be delayed if a workflow is used. + * Loop through the list of repo and create a message for each one. + * @param {Publisher} PublisherManager + * @param {TFile | string} file + * @param {GitHubPublisherSettings} settings + * @param {Properties | Properties[]} prop + */ + +export async function publisherNotification( + PublisherManager: Publisher, + file: TFile | string | undefined, + settings: GitHubPublisherSettings, + prop: Properties | Properties[] +) { + prop = Array.isArray(prop) ? prop : [prop]; + for (const repository of prop) { + await publisherNotificationOneRepo(PublisherManager, file, settings, repository); + } +} + +/** + * Notify the user with a message in the console and a message in the notification + * @param properties + */ +export function notifError(properties: Properties | Properties[]) { + const repo = Array.isArray(properties) ? properties : [properties]; + for (const repository of repo) { + const notif = document.createDocumentFragment(); + notif.createSpan({ + cls: ["error", "obsidian-publisher", "icons", "notification"], + }).innerHTML = ERROR_ICONS; + notif.createSpan({ cls: ["error", "obsidian-publisher", "notification"] }).innerHTML = + i18next.t("error.errorPublish", { repo: repository }); + new Notice(notif); + } +} + +/** + * Create a notice message for the sharing ; the message can be delayed if a workflow is used. + * @param {Publisher} PublisherManager + * @param {TFile | string} file + * @param {GitHubPublisherSettings} settings + * @param {Properties} prop + * @return {Promise} + */ + +async function publisherNotificationOneRepo( + PublisherManager: Publisher, + file: TFile | string | undefined, + settings: GitHubPublisherSettings, + prop: Properties +): Promise { + const noticeValue = file instanceof TFile ? `"${file.basename}"` : file; + const docSuccess = document.createDocumentFragment(); + const successMsg = + file instanceof String + ? i18next.t("informations.successfulPublish", { nbNotes: noticeValue, repo: prop }) + : i18next.t("informations.successPublishOneNote", { + file: noticeValue, + repo: prop, + }); + docSuccess.createEl("span", { + text: successMsg, + cls: ["obsidian-publisher", "success", "icons"], + }).innerHTML = SUCCESS_ICON; + docSuccess.createEl("span", { + cls: ["obsidian-publisher", "success", "notification"], + }).innerHTML = successMsg; + if (settings.github.workflow.name.length === 0) { + new Notice(docSuccess, 0); + return; + } + const workflowSuccess = document.createDocumentFragment(); + workflowSuccess.createEl("span", { + text: i18next.t("informations.successfulPublish", { + nbNotes: noticeValue, + repo: prop, + }), + cls: ["obsidian-publisher", "wait", "icons"], + }).innerHTML = HOURGLASS_ICON; + const msg = `${i18next.t("informations.sendMessage", { + nbNotes: noticeValue, + repo: prop, + })}.
${i18next.t("informations.waitingWorkflow")}`; + workflowSuccess.createEl("span", { + cls: ["obsidian-publisher", "wait", "notification"], + }).innerHTML = msg; + new Notice(workflowSuccess); + const successWorkflow = await PublisherManager.workflowGestion(prop); + if (successWorkflow) { + new Notice(docSuccess, 0); + } +} + +/** + * Trim the object to remove the empty value + * @param {{[p: string]: string}} obj + * @return {any} + */ + +export function trimObject(obj: { [p: string]: string }) { + const trimmed = JSON.stringify(obj, (key, value) => { + if (typeof value === "string") { + return value.trim().toLowerCase(); + } + return value; + }); + return JSON.parse(trimmed); +} + +/** + * The REST API of Github have a rate limit of 5000 requests per hour. + * This function check if the user is over the limit, or will be over the limit after the next request. + * If the user is over the limit, the function will display a message to the user. + * It also calculate the time remaining before the limit is reset. + */ + +/** + * Convert the variable to their final value: + * - %configDir% -> The config directory of the vault + * - %pluginID% -> The ID of the plugin + * @param {GithubPublisher} plugin - The plugin + * @param {string} tokenPath - The path of the token as entered by the user + * @return {string} - The final path of the token + */ +export function createTokenPath(plugin: GithubPublisher, tokenPath?: string): string { + const vault = plugin.app.vault; + if (!tokenPath) { + tokenPath = TOKEN_PATH; + } + tokenPath = tokenPath.replace("%configDir%", vault.configDir); + tokenPath = tokenPath.replace("%pluginID%", plugin.manifest.id); + return normalizePath(tokenPath); +} diff --git a/src/utils/parse_frontmatter.ts b/src/utils/parse_frontmatter.ts index 2c1aae82..585570dc 100644 --- a/src/utils/parse_frontmatter.ts +++ b/src/utils/parse_frontmatter.ts @@ -3,12 +3,22 @@ * See docs for all the condition */ -import { FolderSettings, GitHubPublisherSettings, Path, Properties, PropertiesConversion, Repository } from "@interfaces"; +import { + FolderSettings, + GitHubPublisherSettings, + Path, + Properties, + PropertiesConversion, + Repository, +} from "@interfaces"; import { FrontMatterCache, normalizePath, TFile } from "obsidian"; import GithubPublisher from "src/main"; import merge from "ts-deepmerge"; -export function frontmatterSettingsRepository(plugin: GithubPublisher, repo: Repository | null) { +export function frontmatterSettingsRepository( + plugin: GithubPublisher, + repo: Repository | null +) { const defaultConvert = getFrontmatterSettings(null, plugin.settings, repo); if (!repo?.set || !plugin.repositoryFrontmatter[repo.smartKey]) return defaultConvert; return getFrontmatterSettings( @@ -18,13 +28,23 @@ export function frontmatterSettingsRepository(plugin: GithubPublisher, repo: Rep ); } -export function getDefaultProperties(repository: Repository | null, plugin: GithubPublisher) { +export function getDefaultProperties( + repository: Repository | null, + plugin: GithubPublisher +) { const defaultSettings = getProperties(plugin, repository); - if (!repository?.set || (repository && !plugin.repositoryFrontmatter[repository.smartKey])) return defaultSettings; - return getProperties(plugin, repository, plugin.repositoryFrontmatter[repository.smartKey]); + if ( + !repository?.set || + (repository && !plugin.repositoryFrontmatter[repository.smartKey]) + ) + return defaultSettings; + return getProperties( + plugin, + repository, + plugin.repositoryFrontmatter[repository.smartKey] + ); } - /** * Retrieves the frontmatter settings for a given file. * @@ -38,7 +58,6 @@ export function getFrontmatterSettings( settings: GitHubPublisherSettings, repo: Repository | null ) { - let settingsConversion: PropertiesConversion = { convertWiki: settings.conversion.links.wiki, attachment: settings.embed.attachments, @@ -50,7 +69,7 @@ export function getFrontmatterSettings( hardbreak: settings.conversion.hardbreak, unshared: settings.conversion.links.unshared, convertInternalLinks: settings.conversion.links.internal, - includeLinks: settings.embed.sendSimpleLinks + includeLinks: settings.embed.sendSimpleLinks, }; const shareAll = repo ? repo.shareAll?.enable : settings.plugin.shareAll?.enable; @@ -90,7 +109,6 @@ function booleanRemoveEmbed(removeEmbed: unknown) { } else return "keep"; } - /** * Retrieves the repository frontmatter based on the provided settings and repository information. * @@ -112,7 +130,11 @@ export function getProperties( const setFrontmatter = plugin.repositoryFrontmatter[repository.smartKey]; frontmatter = merge(setFrontmatter ?? {}, frontmatter ?? {}); } - if (frontmatter && typeof frontmatter.shortRepo === "string" && frontmatter.shortRepo !== "default") { + if ( + frontmatter && + typeof frontmatter.shortRepo === "string" && + frontmatter.shortRepo !== "default" + ) { const smartKey = frontmatter.shortRepo.toLowerCase(); const allOtherRepo = settings.github.otherRepo; const shortRepo = allOtherRepo.find((repo) => { @@ -132,13 +154,18 @@ export function getProperties( rateLimit: github.rateLimit, dryRun: { ...settings.github.dryRun, - autoclean: settings.upload.autoclean.enable && settings.github.dryRun.enable - } + autoclean: settings.upload.autoclean.enable && settings.github.dryRun.enable, + }, }; if (settings.upload.behavior === FolderSettings.fixed) { Properties.autoclean = false; } - if (!frontmatter || (frontmatter.multipleRepo === undefined && frontmatter.repo === undefined && frontmatter.shortRepo === undefined)) { + if ( + !frontmatter || + (frontmatter.multipleRepo === undefined && + frontmatter.repo === undefined && + frontmatter.shortRepo === undefined) + ) { return parsePath(plugin, repository, Properties, frontmatter); } let isFrontmatterAutoClean = null; @@ -166,7 +193,12 @@ export function getProperties( Properties = repositoryStringSlice(repo, Properties); } } else if (frontmatter.shortRepo instanceof Array) { - return multipleShortKeyRepo(frontmatter, settings.github.otherRepo, Properties, plugin); + return multipleShortKeyRepo( + frontmatter, + settings.github.otherRepo, + Properties, + plugin + ); } if (frontmatter.autoclean != undefined && isFrontmatterAutoClean === null) { Properties.autoclean = frontmatter.autoclean; @@ -191,15 +223,9 @@ export function getProperties( * @return {Properties[]} */ -function parseMultipleRepo( - frontmatter: FrontMatterCache, - Properties: Properties -) { +function parseMultipleRepo(frontmatter: FrontMatterCache, Properties: Properties) { const multipleRepo: Properties[] = []; - if ( - frontmatter.multipleRepo instanceof Array && - frontmatter.multipleRepo.length > 0 - ) { + if (frontmatter.multipleRepo instanceof Array && frontmatter.multipleRepo.length > 0) { for (const repo of frontmatter.multipleRepo) { if (typeof repo === "object") { const repository: Properties = structuredClone(Properties); @@ -220,9 +246,7 @@ function parseMultipleRepo( //is string const repoString = repo.split("/"); const repository: Properties = structuredClone(Properties); - multipleRepo.push( - repositoryStringSlice(repoString, repository) - ); + multipleRepo.push(repositoryStringSlice(repoString, repository)); } } } @@ -255,7 +279,12 @@ function removeDuplicateRepo(multipleRepo: Properties[]) { * @param {Repository[]} allRepo - The list of all repo from the settings * @param {Properties} properties - The default Properties (from the default settings) */ -function multipleShortKeyRepo(frontmatter: FrontMatterCache, allRepo: Repository[], properties: Properties, plugin: GithubPublisher) { +function multipleShortKeyRepo( + frontmatter: FrontMatterCache, + allRepo: Repository[], + properties: Properties, + plugin: GithubPublisher +) { if (frontmatter.shortRepo instanceof Array) { const multipleRepo: Properties[] = []; for (const repo of frontmatter.shortRepo) { @@ -275,7 +304,7 @@ function multipleShortKeyRepo(frontmatter: FrontMatterCache, allRepo: Repository automaticallyMergePR: shortRepo.automaticallyMergePR, workflowName: shortRepo.workflow.name, commitMsg: shortRepo.workflow.commitMessage, - dryRun: properties.dryRun + dryRun: properties.dryRun, } as Properties; const parsedPath = parsePath(plugin, shortRepo, repo); repo = Array.isArray(parsedPath) ? parsedPath[0] : parsedPath; @@ -333,9 +362,13 @@ function repositoryStringSlice(repo: string, properties: Properties): Properties export function getCategory( frontmatter: FrontMatterCache | null | undefined, settings: GitHubPublisherSettings, - paths: Path | undefined): string { + paths: Path | undefined +): string { const key = paths?.category?.key ?? settings.upload.yamlFolderKey; - const category = frontmatter && frontmatter[key] != undefined ? frontmatter[key] : paths?.defaultName ?? settings.upload.defaultName; + const category = + frontmatter && frontmatter[key] != undefined + ? frontmatter[key] + : paths?.defaultName ?? settings.upload.defaultName; if (category instanceof Array) { return category.join("/"); } @@ -350,7 +383,7 @@ export function parsePath( ): Properties[] | Properties { properties = properties instanceof Array ? properties : [properties]; const settings = plugin.settings; - const splitArrayPath = (path?: string[] | string):string|undefined => { + const splitArrayPath = (path?: string[] | string): string | undefined => { if (!path) return; if (path instanceof Array) { return path.join("/"); @@ -365,21 +398,33 @@ export function parsePath( }; for (const repo of properties) { const smartKey = repository ? repository.smartKey : "default"; - + const path: Path = { type: matchType(frontmatter?.behavior), - defaultName: frontmatter?.defaultName ?? frontmatter?.category?.value ?? frontmatter?.["category.value"] ?? settings.upload.defaultName, + defaultName: + frontmatter?.defaultName ?? + frontmatter?.category?.value ?? + frontmatter?.["category.value"] ?? + settings.upload.defaultName, rootFolder: frontmatter?.rootFolder ?? settings.upload.rootFolder, category: { - key: splitArrayPath(frontmatter?.category?.key ?? frontmatter?.["category.key"]) ?? settings.upload.yamlFolderKey, - value: getCategory(frontmatter, settings, undefined) + key: + splitArrayPath(frontmatter?.category?.key ?? frontmatter?.["category.key"]) ?? + settings.upload.yamlFolderKey, + value: getCategory(frontmatter, settings, undefined), }, override: splitArrayPath(frontmatter?.path), smartkey: smartKey, attachment: { - send: frontmatter?.attachment?.send ?? frontmatter?.["attachment.send"] ?? settings.embed.attachments, - folder: splitArrayPath(frontmatter?.attachment?.send ?? frontmatter?.["attachment.folder"]) ?? settings.embed.folder, - } + send: + frontmatter?.attachment?.send ?? + frontmatter?.["attachment.send"] ?? + settings.embed.attachments, + folder: + splitArrayPath( + frontmatter?.attachment?.send ?? frontmatter?.["attachment.folder"] + ) ?? settings.embed.folder, + }, }; /** List of alias for path generation */ const smartkeys = { @@ -400,15 +445,15 @@ export function parsePath( /** Direct with smartkey.defaultName */ direct: frontmatter?.[`${smartKey}.defaultName`], /** Is used in the category.value as literal string */ - asCategoryValue: frontmatter?.[`${smartKey}.category.value`] + asCategoryValue: frontmatter?.[`${smartKey}.category.value`], }, /** Overriding of the category key */ categoryKey: { /** Can be direct with smarkey. like smartkey.folder: category */ direct: frontmatter?.[`${smartKey}.${path.category}`], /** Can be a key in a literal string: smartkey.category.key: category will rename the category */ - asKey: frontmatter?.[`${smartKey}.category.key`] - } + asKey: frontmatter?.[`${smartKey}.category.key`], + }, }; if (smartkeys.path) { @@ -416,23 +461,30 @@ export function parsePath( continue; } if (smartkeys.categoryKey.direct) path.category.key = smartkeys.categoryKey.direct; - if (smartkeys.categoryKey.asKey) path.category.key = splitArrayPath(smartkeys.categoryKey.asKey) ?? path.category.key; + if (smartkeys.categoryKey.asKey) + path.category.key = + splitArrayPath(smartkeys.categoryKey.asKey) ?? path.category.key; if (smartkeys.rootFolder) { const rootFolder = splitArrayPath(smartkeys.rootFolder) as string; path.rootFolder = rootFolder; } - if (smartkeys.defaultName.direct) path.defaultName = splitArrayPath(smartkeys.defaultName.direct) as string; - - if (smartkeys.defaultName.asCategoryValue) path.defaultName = splitArrayPath(smartkeys.defaultName.asCategoryValue) ?? path.defaultName; - + if (smartkeys.defaultName.direct) + path.defaultName = splitArrayPath(smartkeys.defaultName.direct) as string; + + if (smartkeys.defaultName.asCategoryValue) + path.defaultName = + splitArrayPath(smartkeys.defaultName.asCategoryValue) ?? path.defaultName; + if (smartkeys.category) { if (typeof smartkeys.category === "object") { - if (smartkeys.category.value) path.defaultName = splitArrayPath(smartkeys.category.value) ?? path.defaultName; - if (smartkeys.category.key) path.category.key = splitArrayPath(smartkeys.category.key) ?? path.category.key; + if (smartkeys.category.value) + path.defaultName = splitArrayPath(smartkeys.category.value) ?? path.defaultName; + if (smartkeys.category.key) + path.category.key = splitArrayPath(smartkeys.category.key) ?? path.category.key; } else path.category.key = splitArrayPath(smartkeys.category) ?? path.category.key; } if (smartkeys.behavior) path.type = matchType(smartkeys.behavior.toLowerCase()); - + if (smartkeys.attachment) { if (typeof smartkeys.attachment === "object") { path.attachment = { @@ -440,22 +492,23 @@ export function parsePath( folder: splitArrayPath(smartkeys.attachment?.folder) ?? path.attachment!.folder, }; } else path.attachment!.send = smartkeys.attachment; - } - if (smartkeys.attachmentLinks) path.attachment!.folder = normalizePath(smartkeys.attachmentLinks - .toString() - .replace(/\/$/, "")); + if (smartkeys.attachmentLinks) + path.attachment!.folder = normalizePath( + smartkeys.attachmentLinks.toString().replace(/\/$/, "") + ); path.category.value = getCategory(frontmatter, settings, path); repo.path = path; } - + return properties; } function getFrontmatterSettingRepository( repository: Repository | null, frontmatter: FrontMatterCache | null | undefined, - frontConvert: PropertiesConversion) { + frontConvert: PropertiesConversion +) { if (!repository) return frontConvert; const smartKey = repository.smartKey; frontConvert = settingsLink(frontmatter, frontConvert, smartKey); @@ -471,15 +524,14 @@ function getFrontmatterSettingRepository( if (frontmatter?.[`${key}includeLinks`] != undefined) { frontConvert.includeLinks = frontmatter[`${smartKey}.includeLinks`]; } - - return frontConvert; + return frontConvert; } export function getLinkedFrontmatter( originalFrontmatter: FrontMatterCache | null | undefined, sourceFile: TFile | null | undefined, - plugin: GithubPublisher, + plugin: GithubPublisher ) { const { settings } = plugin; const { metadataCache, vault } = plugin.app; @@ -495,24 +547,42 @@ export function getLinkedFrontmatter( } }); if (!linkedFile) return undefined; - const linkedFrontmatterFile = metadataCache.getFirstLinkpathDest(linkedFile, sourceFile.path) ?? vault.getAbstractFileByPath(linkedFile); - if (!linkedFrontmatterFile || !(linkedFrontmatterFile instanceof TFile)) return undefined; + const linkedFrontmatterFile = + metadataCache.getFirstLinkpathDest(linkedFile, sourceFile.path) ?? + vault.getAbstractFileByPath(linkedFile); + if (!linkedFrontmatterFile || !(linkedFrontmatterFile instanceof TFile)) + return undefined; return metadataCache.getFileCache(linkedFrontmatterFile)?.frontmatter; } -export function frontmatterFromFile(file: TFile | null, plugin: GithubPublisher, repo: Repository | null) { +export function frontmatterFromFile( + file: TFile | null, + plugin: GithubPublisher, + repo: Repository | null +) { let frontmatter = null; - const setFrontmatter = repo?.set && plugin.repositoryFrontmatter[repo.smartKey] ? plugin.repositoryFrontmatter[repo.smartKey] : null; + const setFrontmatter = + repo?.set && plugin.repositoryFrontmatter[repo.smartKey] + ? plugin.repositoryFrontmatter[repo.smartKey] + : null; if (file) { frontmatter = plugin.app.metadataCache.getFileCache(file)?.frontmatter; const linkedFrontmatter = getLinkedFrontmatter(frontmatter, file, plugin); - frontmatter = merge(setFrontmatter ?? {}, linkedFrontmatter ?? {}, frontmatter ?? {}) as FrontMatterCache; + frontmatter = merge( + setFrontmatter ?? {}, + linkedFrontmatter ?? {}, + frontmatter ?? {} + ) as FrontMatterCache; } - + return frontmatter; } -function settingsLink(frontmatter: FrontMatterCache | null | undefined, settingsConversion: PropertiesConversion, smartKey?: string) { +function settingsLink( + frontmatter: FrontMatterCache | null | undefined, + settingsConversion: PropertiesConversion, + smartKey?: string +) { let key = "links"; if (smartKey) key = `${smartKey}.${key}`; if (!frontmatter) return settingsConversion; @@ -534,20 +604,33 @@ function settingsLink(frontmatter: FrontMatterCache | null | undefined, settings settingsConversion.links = frontmatter[key]; } } - if (frontmatter[`${key}.convert`] != undefined) settingsConversion.links = frontmatter[`${key}.convert`]; - if (frontmatter[`${key}.internals`] != undefined) settingsConversion.convertInternalLinks = frontmatter[`${key}.internals`]; - if (frontmatter[`${key}.mdlinks`] != undefined) settingsConversion.convertWiki = frontmatter[`${key}.mdlinks`]; - if (frontmatter[`${key}.nonShared`] != undefined) settingsConversion.unshared = frontmatter[`${key}.nonShared`]; - - - if (frontmatter[smartKey ? `${smartKey}.mdlinks` : "mdlinks"] != undefined) settingsConversion.convertWiki = frontmatter[smartKey ? `${smartKey}.mdlinks` : "mdlinks"]; - if (frontmatter[smartKey ? `${smartKey}.internals` : "internals"] != undefined) settingsConversion.convertInternalLinks = frontmatter[smartKey ? `${smartKey}.internals` : "internals"]; - if (frontmatter[smartKey ? `${smartKey}.nonShared` : "nonShared"] != undefined) settingsConversion.unshared = frontmatter[smartKey ? `${smartKey}.nonShared` : "nonShared"]; + if (frontmatter[`${key}.convert`] != undefined) + settingsConversion.links = frontmatter[`${key}.convert`]; + if (frontmatter[`${key}.internals`] != undefined) + settingsConversion.convertInternalLinks = frontmatter[`${key}.internals`]; + if (frontmatter[`${key}.mdlinks`] != undefined) + settingsConversion.convertWiki = frontmatter[`${key}.mdlinks`]; + if (frontmatter[`${key}.nonShared`] != undefined) + settingsConversion.unshared = frontmatter[`${key}.nonShared`]; + + if (frontmatter[smartKey ? `${smartKey}.mdlinks` : "mdlinks"] != undefined) + settingsConversion.convertWiki = + frontmatter[smartKey ? `${smartKey}.mdlinks` : "mdlinks"]; + if (frontmatter[smartKey ? `${smartKey}.internals` : "internals"] != undefined) + settingsConversion.convertInternalLinks = + frontmatter[smartKey ? `${smartKey}.internals` : "internals"]; + if (frontmatter[smartKey ? `${smartKey}.nonShared` : "nonShared"] != undefined) + settingsConversion.unshared = + frontmatter[smartKey ? `${smartKey}.nonShared` : "nonShared"]; return settingsConversion; } -function settingsEmbed(frontmatter: FrontMatterCache | null | undefined, settingsConversion: PropertiesConversion, smartkey?: string) { +function settingsEmbed( + frontmatter: FrontMatterCache | null | undefined, + settingsConversion: PropertiesConversion, + smartkey?: string +) { if (!frontmatter) return settingsConversion; const key = smartkey ? `${smartkey}.embed` : "embed"; if (frontmatter[key] != undefined) { @@ -565,16 +648,24 @@ function settingsEmbed(frontmatter: FrontMatterCache | null | undefined, setting settingsConversion.embed = frontmatter[key]; } } - if (frontmatter[`${key}.send`] != undefined) settingsConversion.embed = frontmatter[`${key}.send`]; - if (frontmatter[`${key}.remove`] != undefined) settingsConversion.removeEmbed = booleanRemoveEmbed(frontmatter[`${key}.remove`]); - if (frontmatter[`${key}.char`] != undefined) settingsConversion.charEmbedLinks = frontmatter[`${key}.char`]; + if (frontmatter[`${key}.send`] != undefined) + settingsConversion.embed = frontmatter[`${key}.send`]; + if (frontmatter[`${key}.remove`] != undefined) + settingsConversion.removeEmbed = booleanRemoveEmbed(frontmatter[`${key}.remove`]); + if (frontmatter[`${key}.char`] != undefined) + settingsConversion.charEmbedLinks = frontmatter[`${key}.char`]; const removeEmbedKey = smartkey ? `${smartkey}.removeEmbed` : "removeEmbed"; - if (frontmatter[removeEmbedKey] != undefined) settingsConversion.removeEmbed = booleanRemoveEmbed(frontmatter[removeEmbedKey]); + if (frontmatter[removeEmbedKey] != undefined) + settingsConversion.removeEmbed = booleanRemoveEmbed(frontmatter[removeEmbedKey]); return settingsConversion; } -function settingAttachment(frontmatter: FrontMatterCache | undefined | null, settingsConversion: PropertiesConversion, smartKey?: string) { +function settingAttachment( + frontmatter: FrontMatterCache | undefined | null, + settingsConversion: PropertiesConversion, + smartKey?: string +) { if (!frontmatter) return settingsConversion; let key = "attachment"; if (smartKey) key = `${smartKey}.${key}`; @@ -591,14 +682,15 @@ function settingAttachment(frontmatter: FrontMatterCache | undefined | null, set } } - if (frontmatter[`${key}.send`] != undefined) settingsConversion.attachment = frontmatter[`${key}.send`]; - if (frontmatter[`${key}.folder`] != undefined) settingsConversion.attachmentLinks = frontmatter[`${key}.folder`]; + if (frontmatter[`${key}.send`] != undefined) + settingsConversion.attachment = frontmatter[`${key}.send`]; + if (frontmatter[`${key}.folder`] != undefined) + settingsConversion.attachmentLinks = frontmatter[`${key}.folder`]; if (settingsConversion.attachmentLinks) { - settingsConversion.attachmentLinks = normalizePath(settingsConversion.attachmentLinks - .toString() - .replace(/\/$/, "")); + settingsConversion.attachmentLinks = normalizePath( + settingsConversion.attachmentLinks.toString().replace(/\/$/, "") + ); } return settingsConversion; } - diff --git a/src/utils/status_bar.ts b/src/utils/status_bar.ts index 5d1bfa80..65816557 100644 --- a/src/utils/status_bar.ts +++ b/src/utils/status_bar.ts @@ -1,122 +1,142 @@ -import { ERROR_ICONS, FOUND_ATTACHMENTS, HOURGLASS_ICON, Properties, SUCCESS_ICON } from "@interfaces"; -import i18next from "i18next"; -import { Notice } from "obsidian"; -import { noticeMobile } from "src/utils"; - -// Credit : https://github.com/oleeskild/obsidian-digital-garden/ @oleeskild - -/** - * This class adds a status bar to the bottom of the screen. - * Only work for the desktop app. - * @class StatusBar - */ - -export class ShareStatusBar { - statusBarItem: HTMLElement; - counter: number; - numberOfNotesToPublish: number; - attachment = false; - status: HTMLElement; - icon: HTMLElement; - noticeMobile: Notice | undefined; - - /** - * @param {HTMLElement} statusBarItem - * @param {number} numberOfNotesToPublish - * @param {boolean} attachment true if there are attachment to the note - */ - - constructor( - statusBarItem: HTMLElement, - numberOfNotesToPublish: number, - attachment: boolean = false - ) { - this.statusBarItem = statusBarItem; - this.counter = 0; - this.numberOfNotesToPublish = numberOfNotesToPublish; - this.attachment = attachment; - - const typeAttachment = this.attachment ? i18next.t("common.attachments") : i18next.t("common.files"); - const msg = i18next.t("statusBar.markedForSharing", { nb: this.numberOfNotesToPublish, type: typeAttachment }); - this.icon = this.statusBarItem.createEl("span", { cls: ["obsidian-publisher", "icons"]}); - this.statusBarItem.addClass("found-attachments"); - this.icon.innerHTML = FOUND_ATTACHMENTS; - this.status = this.statusBarItem.createEl("span", { text: `${msg}` }); - this.status.addClass("found-attachments"); - this.noticeMobile = noticeMobile("wait", FOUND_ATTACHMENTS, msg); - } - - /** - * increment the counter - */ - - increment() { - const typeAttachment = this.attachment ? i18next.t("common.attachments") : i18next.t("common.files"); - const msg = i18next.t("statusBar.sharing", { type: typeAttachment.toLowerCase() }); - this.icon.innerHTML = HOURGLASS_ICON; - this.status.setText( - i18next.t("statusBar.counter", { msg, counter: ++this.counter, nb: this.numberOfNotesToPublish }) - ); - this.statusBarItem.addClass("sharing"); - this.statusBarItem.removeClass("found-attachments"); - - if (!this.noticeMobile?.noticeEl?.children[0]?.classList?.contains("load")) { - setTimeout(() => { - this.noticeMobile?.hide(); - }, 4000); - this.noticeMobile = noticeMobile("load", HOURGLASS_ICON, msg); - } - - } - - /** - * finish the status bar - * @param {number} displayDurationMillisec - */ - - finish(displayDurationMillisec: number) { - const msg = this.attachment ? - i18next.t("statusBar.success", - { - action: i18next.t("common.shared"), - type: i18next.t("common.attachments") - }) - : i18next.t("statusBar.success", { - action: i18next.t("common.published"), - type: i18next.t("common.files") - }); - this.icon.innerHTML = SUCCESS_ICON; - this.status.setText( - i18next.t("statusBar.counter", { msg, counter: this.counter, nb: this.numberOfNotesToPublish }) - ); - this.statusBarItem.addClass("success"); - this.statusBarItem.removeClass("sharing"); - this.noticeMobile?.hide(); - setTimeout(() => { - this.statusBarItem.remove(); - }, displayDurationMillisec); - setTimeout(() => { - this.noticeMobile?.hide(); - }, displayDurationMillisec - 4000); - - } - - /** - * Remove the status bar if error occurs - */ - - error(prop: Properties) { - this.statusBarItem.addClass("error"); - this.statusBarItem.removeClass("sharing"); - this.statusBarItem.removeClass("found-attachments"); - this.icon.innerHTML = ERROR_ICONS; - this.status.innerHTML = i18next.t("error.errorPublish", {repo: prop}); - this.noticeMobile?.hide(); - setTimeout(() => { - this.statusBarItem.remove(); - }, 8000); - setTimeout(() => { - this.noticeMobile?.hide(); - }, 4000); - } -} \ No newline at end of file +import { + ERROR_ICONS, + FOUND_ATTACHMENTS, + HOURGLASS_ICON, + Properties, + SUCCESS_ICON, +} from "@interfaces"; +import i18next from "i18next"; +import { Notice } from "obsidian"; +import { noticeMobile } from "src/utils"; + +// Credit : https://github.com/oleeskild/obsidian-digital-garden/ @oleeskild + +/** + * This class adds a status bar to the bottom of the screen. + * Only work for the desktop app. + * @class StatusBar + */ + +export class ShareStatusBar { + statusBarItem: HTMLElement; + counter: number; + numberOfNotesToPublish: number; + attachment = false; + status: HTMLElement; + icon: HTMLElement; + noticeMobile: Notice | undefined; + + /** + * @param {HTMLElement} statusBarItem + * @param {number} numberOfNotesToPublish + * @param {boolean} attachment true if there are attachment to the note + */ + + constructor( + statusBarItem: HTMLElement, + numberOfNotesToPublish: number, + attachment: boolean = false + ) { + this.statusBarItem = statusBarItem; + this.counter = 0; + this.numberOfNotesToPublish = numberOfNotesToPublish; + this.attachment = attachment; + + const typeAttachment = this.attachment + ? i18next.t("common.attachments") + : i18next.t("common.files"); + const msg = i18next.t("statusBar.markedForSharing", { + nb: this.numberOfNotesToPublish, + type: typeAttachment, + }); + this.icon = this.statusBarItem.createEl("span", { + cls: ["obsidian-publisher", "icons"], + }); + this.statusBarItem.addClass("found-attachments"); + this.icon.innerHTML = FOUND_ATTACHMENTS; + this.status = this.statusBarItem.createEl("span", { text: `${msg}` }); + this.status.addClass("found-attachments"); + this.noticeMobile = noticeMobile("wait", FOUND_ATTACHMENTS, msg); + } + + /** + * increment the counter + */ + + increment() { + const typeAttachment = this.attachment + ? i18next.t("common.attachments") + : i18next.t("common.files"); + const msg = i18next.t("statusBar.sharing", { type: typeAttachment.toLowerCase() }); + this.icon.innerHTML = HOURGLASS_ICON; + this.status.setText( + i18next.t("statusBar.counter", { + msg, + counter: ++this.counter, + nb: this.numberOfNotesToPublish, + }) + ); + this.statusBarItem.addClass("sharing"); + this.statusBarItem.removeClass("found-attachments"); + + if (!this.noticeMobile?.noticeEl?.children[0]?.classList?.contains("load")) { + setTimeout(() => { + this.noticeMobile?.hide(); + }, 4000); + this.noticeMobile = noticeMobile("load", HOURGLASS_ICON, msg); + } + } + + /** + * finish the status bar + * @param {number} displayDurationMillisec + */ + + finish(displayDurationMillisec: number) { + const msg = this.attachment + ? i18next.t("statusBar.success", { + action: i18next.t("common.shared"), + type: i18next.t("common.attachments"), + }) + : i18next.t("statusBar.success", { + action: i18next.t("common.published"), + type: i18next.t("common.files"), + }); + this.icon.innerHTML = SUCCESS_ICON; + this.status.setText( + i18next.t("statusBar.counter", { + msg, + counter: this.counter, + nb: this.numberOfNotesToPublish, + }) + ); + this.statusBarItem.addClass("success"); + this.statusBarItem.removeClass("sharing"); + this.noticeMobile?.hide(); + setTimeout(() => { + this.statusBarItem.remove(); + }, displayDurationMillisec); + setTimeout(() => { + this.noticeMobile?.hide(); + }, displayDurationMillisec - 4000); + } + + /** + * Remove the status bar if error occurs + */ + + error(prop: Properties) { + this.statusBarItem.addClass("error"); + this.statusBarItem.removeClass("sharing"); + this.statusBarItem.removeClass("found-attachments"); + this.icon.innerHTML = ERROR_ICONS; + this.status.innerHTML = i18next.t("error.errorPublish", { repo: prop }); + this.noticeMobile?.hide(); + setTimeout(() => { + this.statusBarItem.remove(); + }, 8000); + setTimeout(() => { + this.noticeMobile?.hide(); + }, 4000); + } +} diff --git a/tsconfig.json b/tsconfig.json index 53b4fc0f..9c264a17 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ ], "obsidian-dataview": [ "node_modules/@obsidian_publisher/obsidian-dataview" - ], + ] }, "noEmit": true, "inlineSourceMap": true, @@ -40,5 +40,5 @@ }, "include": [ "**/*.ts" - ], + ] } \ No newline at end of file