1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-04-18 19:42:23 +02:00

Compare commits

...

193 Commits

Author SHA1 Message Date
Laurent Cozic 4693c924c3 Merge branch 'dev' into plugin_system 2020-10-09 18:33:38 +01:00
Laurent Cozic 6e7015e8f9 Fixed mobile build 2020-10-09 17:55:14 +01:00
Laurent Cozic 1f1cd9f11b Updated TOC plugin doc 2020-10-09 17:22:30 +01:00
Laurent Cozic ac959b2882 Updated TOC plugin doc 2020-10-09 16:56:51 +01:00
Laurent Cozic 4609634259 Update website 2020-10-09 15:52:45 +01:00
Laurent Cozic f06b64f3f9 Update TOC 2020-10-09 15:52:33 +01:00
Laurent Cozic 80503ad0c1 Updated links 2020-10-09 15:45:21 +01:00
Laurent Cozic 05dfaf7347 Replace links 2020-10-09 15:22:57 +01:00
Laurent Cozic 59be83d958 go back to absolute URLs as it is easier to grep 2020-10-09 15:15:44 +01:00
Laurent Cozic e7676a148f Merge branch 'dev' into plugin_system 2020-10-09 15:09:55 +01:00
Laurent Cozic cb28021a27 Provide more info when a revision cannot be created 2020-10-09 12:00:55 +01:00
Laurent Cozic d8ab03f3bc Updating doc 2020-10-08 17:56:03 +01:00
Laurent Cozic 48b6b83950 Updating doc 2020-10-08 17:55:32 +01:00
Laurent Cozic 3e3bad0947 Fixed test plugin 2020-10-08 17:11:54 +01:00
Laurent Cozic e527f590c5 Fixed plugin 2020-10-08 16:53:37 +01:00
Laurent Cozic 88f968aac5 Updated types 2020-10-08 16:52:22 +01:00
Laurent Cozic bbd01c3a38 More API clean up 2020-10-08 16:39:20 +01:00
Laurent Cozic e8d22a4177 Add command documentation 2020-10-08 15:52:41 +01:00
Laurent Cozic 6caec4df39 Refactored markdownUtils 2020-10-08 15:22:05 +01:00
Laurent Cozic eea56fcf94 Added more plugin doc 2020-10-07 22:43:35 +01:00
Laurent Cozic a5a278eced Version num 2020-10-07 21:08:02 +01:00
Laurent Cozic a978ae4cfa Merge branch 'dev' into plugin_system 2020-10-07 21:05:39 +01:00
Laurent Cozic dd1c1dded4 Update plugin types 2020-10-07 15:03:42 +01:00
Laurent Cozic 5407481976 fix 2020-10-07 12:33:42 +01:00
Laurent Cozic 5a1f6b13cf Fixed plugin update 2020-10-07 12:29:56 +01:00
Laurent Cozic a904431782 Added way to update plugins 2020-10-07 12:28:25 +01:00
Laurent Cozic b23ec936fd Deleted file 2020-10-07 10:15:51 +01:00
Laurent Cozic 73a4517902 More clean up on plugin doc 2020-10-07 10:14:52 +01:00
Laurent Cozic a966b32583 Cleaned up plugin API to avoid depending on internal classes 2020-10-07 09:48:38 +01:00
Laurent Cozic 91a3428124 Rebuilt plugin doc 2020-10-06 21:30:33 +01:00
Laurent Cozic 93a5ba9490 Improved plugin doc 2020-10-06 21:30:05 +01:00
Laurent Cozic e570f7a226 Disable filters for now 2020-10-06 18:23:15 +01:00
Laurent Cozic 9b28a8995f Open plugin dev tool window 2020-10-06 18:14:46 +01:00
Laurent Cozic 197f509f5d Increase version to 1.3 2020-10-06 14:04:21 +01:00
Laurent Cozic 4c83d7aa75 Fixed sidebar resize issue 2020-10-06 12:03:15 +01:00
Laurent Cozic 17e053b358 Handle module change event 2020-10-06 11:33:13 +01:00
Laurent Cozic a29c93634d Converted locale file to TS 2020-10-06 10:27:43 +01:00
Laurent Cozic a5f5bfcfec clean up 2020-10-06 10:16:18 +01:00
Laurent Cozic b509329383 Convert interopservicehelper to TS 2020-10-06 10:14:54 +01:00
Laurent Cozic 91c7abed42 Added back support for keymap on command service 2020-10-06 09:55:23 +01:00
Laurent Cozic 54fda6ac97 Refactored menu bar 2020-10-05 19:29:53 +01:00
Laurent Cozic 341eefebaf Converted BaseApplication to TypeScripy 2020-10-05 14:44:56 +01:00
Laurent Cozic af714cbdac Optimize command state handling 2020-10-05 13:20:23 +01:00
Laurent Cozic 6a0bf15c28 Clean up 2020-10-03 18:24:44 +01:00
Laurent Cozic 5a2bdad348 Cleaned up toolbar code to improve rendering 2020-10-03 18:22:39 +01:00
Laurent Cozic fd08bfdd86 Better way to create buttonToolbarInfos 2020-10-03 17:49:28 +01:00
Laurent Cozic 1a5cead659 Added @typescript-eslint/explicit-member-accessibility rule 2020-10-03 15:45:32 +01:00
Laurent Cozic 85df133386 Added command service tests and a way to get multiple toolbar buttons 2020-10-03 15:36:23 +01:00
Laurent Cozic 869d11e113 Added way to parse when-clauses for commands 2020-10-03 14:40:12 +01:00
Laurent Cozic d7a01d3965 Fixed issue with state that was accidentally mutated 2020-10-02 20:06:52 +01:00
Laurent Cozic 989036241d Fixed toolbar button label 2020-10-02 20:03:14 +01:00
Laurent Cozic 433cef1827 Fixed encryption config screen password save logic 2020-10-02 19:59:36 +01:00
Laurent Cozic 433d34f62b Resize app content when banner is added on top 2020-10-02 19:40:12 +01:00
Laurent Cozic e7ec33bbf9 Added event tests 2020-10-02 18:48:25 +01:00
Laurent Cozic a29f42d78d Converted synchronizer to TypeScript 2020-10-02 18:03:29 +01:00
Laurent Cozic 74ed234ab9 Added support for onAlarmTrigger event 2020-10-02 17:41:18 +01:00
Laurent Cozic 86b76ccb77 Fixed setting plugin 2020-10-02 16:49:39 +01:00
Laurent Cozic 29aedf8480 Fixed toc demo 2020-10-02 16:21:32 +01:00
Laurent Cozic 9486f5e0d2 Simplify plugin 2020-10-02 16:07:04 +01:00
Laurent Cozic 9a458431b8 Fixed sandbox proxy namespace logic 2020-10-02 15:56:34 +01:00
Laurent Cozic 2c9db9d18c Fixed dialog plugin API 2020-10-02 15:21:58 +01:00
Laurent Cozic ae9c535842 Improved setting API 2020-10-02 13:02:39 +01:00
Laurent Cozic a518da587e Fixed demo 2020-10-02 12:52:05 +01:00
Laurent Cozic 92e6e00d9e Fixed commands 2020-10-02 12:49:40 +01:00
Laurent Cozic b64c9cf18a Finished refactoring interop service 2020-10-02 12:42:25 +01:00
Laurent Cozic 275d6e230d Converted shim to TypeScript 2020-10-02 11:38:04 +01:00
Laurent Cozic ab8ce15c33 Fixed command api 2020-10-02 10:42:03 +01:00
Laurent Cozic 3fb6179587 lonter 2020-10-02 01:03:39 +01:00
Laurent Cozic 28cc38064c Fixed interop plugin API to work with multi-process arch and cleaned up interop service 2020-10-02 00:44:42 +01:00
Laurent Cozic 1fa5ab2b96 Refactoring and got ipc messages to work 2020-10-01 23:42:49 +01:00
Laurent Cozic 15f4e635d9 Minor tweaks 2020-10-01 15:10:59 +01:00
Laurent Cozic 7a79d7ef7e Refactoring and got ipc messages to work 2020-10-01 14:47:38 +01:00
Laurent Cozic 776e7dfe3f renaming logger 2020-10-01 10:31:01 +01:00
Laurent Cozic 790b622cbc Updated doc 2020-09-30 23:38:28 +01:00
Laurent Cozic 89117b4ee9 Renamed 2020-09-30 23:36:27 +01:00
Laurent Cozic c245517e0f Renamed to PlatformImplementation 2020-09-30 23:31:27 +01:00
Laurent Cozic 31db95d0c4 Cleaned up plugin API names 2020-09-30 23:17:18 +01:00
Laurent Cozic 2cd50de065 Moved plugin classes under /plugins namespace 2020-09-30 23:07:46 +01:00
Laurent Cozic 6dcb7e2605 Added doc 2020-09-30 17:19:34 +01:00
Laurent Cozic 9c9bd4ba51 Changed plugin system architecture to allow using one process per plugin 2020-09-30 16:56:41 +01:00
Laurent Cozic 71e6964945 Added proxy class 2020-09-30 10:24:15 +01:00
Laurent Cozic 97dc2fa6bd Fixed tests 2020-09-29 16:42:32 +01:00
Laurent Cozic 161e112e7b Added support for getting selected notes and adding a context menu item 2020-09-29 16:28:25 +01:00
Laurent Cozic c93a6f86cd Added way to add a custom Markdown plugin 2020-09-29 15:29:11 +01:00
Laurent Cozic 9bb178b9d9 Fixed broken merge 2020-09-29 15:28:12 +01:00
Laurent Cozic 8087bc5280 Merge branch 'dev' into plugin_system 2020-09-29 14:30:33 +01:00
Laurent Cozic 100aaab58d Refactor reducer to TypeScript and using immerjs 2020-09-29 11:16:01 +01:00
Laurent Cozic 126c7b5a75 Merge branch 'dev' into reducer_refactor 2020-09-29 11:00:31 +01:00
Laurent Cozic d448ea3a02 Merge branch 'dev' into plugin_system 2020-09-29 10:59:55 +01:00
Laurent Cozic 555727e5fe Refactor reducer 2020-09-29 10:56:33 +01:00
Laurent Cozic 77eceea426 Ensure plugin settings are within their own namespaces 2020-09-28 18:11:53 +01:00
Laurent Cozic a09c6f152c Added support for plugin dialogs 2020-09-28 17:26:22 +01:00
Laurent Cozic 3830195752 Allow creating new section from plugin 2020-09-27 13:45:08 +01:00
Laurent Cozic c9594065c3 Refactor Settings 2020-09-27 13:31:45 +01:00
Laurent Cozic 360d3bbff9 Allow creating new settings from plugin 2020-09-27 13:03:37 +01:00
Laurent Cozic 6fde8dfe52 Refactored Settings into TypeScript 2020-09-26 18:14:00 +01:00
Laurent Cozic de2ccfbfe0 Added explanation for shim timers and implemented RN side 2020-09-26 12:42:44 +01:00
Laurent Cozic 31f53b12fd Move timers to shim 2020-09-26 12:19:13 +01:00
Laurent Cozic d45b280ada Done text selection plugin 2020-09-25 11:18:06 +01:00
Laurent Cozic b5a137545e Add support getting and replacing editor selection 2020-09-24 18:35:20 +01:00
Laurent Cozic 4e80dbb7a7 Allow tagging desktop-only feature in plugin doc 2020-09-24 15:53:37 +01:00
Laurent Cozic 90b0c99806 Merge branch 'release-1.2' into plugin_system 2020-09-24 14:50:20 +01:00
Laurent Cozic c8c0c3af46 Changed to node-slug 2020-09-23 17:34:58 +01:00
Laurent Cozic 110de50bf9 Allow setting custom shortcuts and saving/loading them 2020-09-23 17:26:30 +01:00
Laurent Cozic b7e110d888 Converted eventManager to TypeScript 2020-09-23 16:25:00 +01:00
Laurent Cozic f1c557cb43 Converted app to TypeScript 2020-09-23 15:51:31 +01:00
Laurent Cozic 43e9fc92c9 Adding support for custom menu items 2020-09-23 15:24:42 +01:00
Laurent Cozic 10fa331731 Define plugin state types 2020-09-23 15:18:45 +01:00
Laurent Cozic cc8e122de3 Added support for custom commands and toolbar buttons 2020-09-23 15:10:31 +01:00
Laurent Cozic bca5fd8db9 gitignore 2020-09-23 14:10:02 +01:00
Laurent Cozic 13101f7d9b Merge branch 'dev' into plugin_system 2020-09-23 14:09:21 +01:00
Laurent Cozic 4f2884a0f7 Started adding support for registering commands 2020-09-21 11:38:09 +01:00
Laurent Cozic 4872f1419c Also add placeholder for JSON import 2020-09-20 16:57:17 +01:00
Laurent Cozic fdcaab3caf Got JSON export working as a plugin 2020-09-20 16:19:37 +01:00
Laurent Cozic 9f2c4b938f Added complete lib to plugin doc 2020-09-20 15:36:30 +01:00
Laurent Cozic 8be65acf7d Testing interop plugin 2020-09-20 12:09:32 +01:00
Laurent Cozic e05aa433e4 Allow creating custom exporters 2020-09-19 15:50:46 +01:00
Laurent Cozic ccfb0b43d6 Refactor 2020-09-19 14:56:49 +01:00
Laurent Cozic 4211e962c6 Merge branch 'release-1.2' into plugin_system 2020-09-19 14:22:24 +01:00
Laurent Cozic 6b54c4244b Refactored 2020-09-19 14:19:09 +01:00
Laurent Cozic 1d5a4bd381 Simplified interop interface 2020-09-19 12:20:01 +01:00
Laurent Cozic 86a1103a64 Remove JSON exporter 2020-09-19 11:13:49 +01:00
Laurent Cozic 45edaaa96f Converted interop service to TypeScript 2020-09-19 11:04:29 +01:00
Laurent Cozic b7b158aa94 Fixed doc title 2020-09-18 15:53:25 +01:00
Laurent Cozic e6bc55b6f8 Update plugin doc 2020-09-18 15:46:25 +01:00
Laurent Cozic 111092791c Updated plugin doc 2020-09-18 13:10:47 +01:00
Laurent Cozic 4e91e01ddc Updated plugin doc 2020-09-18 13:08:01 +01:00
Laurent Cozic 44dce6c6c3 Fixed frozen state issue 2020-09-18 10:10:26 +01:00
Laurent Cozic b25ad50a1b Add more doc 2020-09-18 09:59:22 +01:00
Laurent Cozic e2e3be66ee doc 2020-09-17 18:02:57 +01:00
Laurent Cozic 4b3119ebba Finished tutorial 2020-09-17 17:39:15 +01:00
Laurent Cozic 1712a67e2e Doc draft 2020-09-17 16:19:25 +01:00
Laurent Cozic cd563b050c Bottom border on user views 2020-09-17 15:41:05 +01:00
Laurent Cozic 4426f2dbec Make sure plugin views can be resized 2020-09-17 15:08:43 +01:00
Laurent Cozic 0fbbf2eee0 Remember plugin view sizes 2020-09-17 14:51:50 +01:00
Laurent Cozic 2fdad2fe80 Make sure user views have a stable ID 2020-09-17 09:22:03 +01:00
Laurent Cozic 0249ff48ca Removed obsolete handlers 2020-09-17 08:51:12 +01:00
Laurent Cozic 142fc02f9c Simplified UserWebview component 2020-09-16 17:57:30 +01:00
Laurent Cozic 842b123285 Fixed slub 2020-09-16 17:30:39 +01:00
Laurent Cozic a4aca68b85 Support for theme in webview 2020-09-16 17:27:13 +01:00
Laurent Cozic 1397af0692 User webviews 2020-09-16 16:19:33 +01:00
Laurent Cozic 5da760dd0c Added a way to load a plugin for development 2020-09-15 17:49:43 +01:00
Laurent Cozic 511d58d3af Various improvements 2020-09-15 17:02:02 +01:00
Laurent Cozic a1c9e675ad Improve plugin build 2020-09-15 16:56:48 +01:00
Laurent Cozic 0349ad0b80 Merge branch 'release-1.2' into plugin_system 2020-09-15 14:41:47 +01:00
Laurent Cozic 9d8411a25e Add TOC plugin demo 2020-09-15 13:47:47 +01:00
Laurent Cozic 754ba2267a Merge branch 'dev' into plugin_system 2020-09-15 13:41:57 +01:00
Laurent Cozic 4cdac308d1 Wraps console calls 2020-09-15 10:36:32 +01:00
Laurent Cozic c0d5c7c282 Merge branch 'dev' into plugin_system 2020-09-14 14:06:21 +01:00
Laurent f9296992bf Merge branch 'dev' into plugin_system 2020-08-01 18:58:08 +01:00
Laurent Cozic 5b06ebde1e Updated 2020-08-01 18:38:43 +01:00
Laurent Cozic d309c75974 Merge branch 'dev' into plugin_system 2020-08-01 18:35:05 +01:00
Laurent Cozic 807c89a42f Added way to get dev mode command line 2020-07-05 18:55:21 +01:00
Laurent Cozic 0b8350e9ea Adding plugin doc 2020-07-05 17:58:24 +01:00
Laurent Cozic 49e7be8924 Ignored files 2020-07-05 17:24:20 +01:00
Laurent Cozic 810c63e3b2 Update doc 2020-07-04 18:38:12 +01:00
Laurent Cozic d917ca5551 Added plugin doc auto-generation 2020-07-04 18:27:47 +01:00
Laurent Cozic ec642300ab Moved sandbox to sub-folder 2020-07-04 17:56:21 +01:00
Laurent Cozic 9034d646bf Moved sandbox to a class 2020-07-04 17:02:32 +01:00
Laurent Cozic 96f6bbb035 Allow executing commands from plugin 2020-07-04 16:05:02 +01:00
Laurent Cozic e14ed655d2 Freeze state in dev mode 2020-07-04 15:32:24 +01:00
Laurent Cozic 54e5323f54 Fixed another issue due to modifying state data 2020-07-04 15:24:53 +01:00
Laurent Cozic ef58affed1 Fix tests 2020-07-04 15:09:14 +01:00
Laurent Cozic 76fa87d878 Fixed test issue due modification of immutable state 2020-07-04 14:52:55 +01:00
Laurent Cozic 2db2225788 Merge branch 'master' into plugin_system 2020-07-04 14:31:54 +01:00
Laurent Cozic 425c1763f4 Desktop: Fixed GotoAnything rendering issue with HTML notes 2020-07-04 12:57:19 +01:00
Laurent Cozic 044f2eac6a Improved view support 2020-06-28 17:10:34 +01:00
Laurent Cozic 281f135125 Improved view support 2020-06-28 17:08:38 +01:00
Laurent Cozic 6c88f8b8ae Merge branch 'plugin_system' of github.com:laurent22/joplin into plugin_system 2020-06-28 16:57:28 +01:00
Laurent Cozic 832abcddf0 Add support for state watch 2020-06-27 11:54:42 +01:00
Laurent Cozic e6c481f45c Fixed tests 2020-06-27 01:05:40 +01:00
Laurent Cozic 11fecfb83a Cleaned up naming and types 2020-06-26 23:03:20 +01:00
Laurent Cozic 2facc9868e Renamed control to view 2020-06-26 22:52:40 +01:00
Laurent Cozic 7b9ad9d1c3 Moved listener to view itself 2020-06-26 22:46:45 +01:00
Laurent Cozic 3b96a99f3e Allow sending messages from webview script to plugin 2020-06-26 18:13:00 +01:00
Laurent Cozic 575ce5128b Added reducer 2020-06-26 16:14:59 +01:00
Laurent Cozic dece28da35 Better integration of custom webviews 2020-06-25 22:15:31 +01:00
Laurent Cozic 2ffc429971 Merge branch 'master' into plugin_system 2020-06-25 20:27:38 +01:00
Laurent Cozic 8baa125842 Refactored to save plugin state in Redux store 2020-06-25 20:25:50 +01:00
Laurent Cozic 6b6092ffc7 Merge branch 'master' into plugin_system 2020-06-24 23:14:16 +01:00
Laurent Cozic 714c912aff Added support for plugin webview 2020-06-24 23:10:19 +01:00
Laurent Cozic ebf8b744e1 Tools: Fixed build issues 2020-06-24 16:33:35 +01:00
Laurent Cozic 1039e98396 Only load plugins if dir present 2020-06-23 17:25:35 +01:00
Laurent Cozic 8f00538cb1 Merge branch 'master' into plugin_system 2020-06-22 23:07:12 +01:00
Laurent Cozic 2b530a3ff9 Merge branch 'master' into plugin_system 2020-06-21 21:37:58 +00:00
Laurent Cozic d089511aa6 Added support filters and custom views 2020-06-21 21:30:07 +00:00
Laurent Cozic 450c8dbf22 Added support for setting and getting runtime preferences and done CodeMirror plugin test 2020-06-18 00:49:32 +01:00
Laurent Cozic 94d87285dc Merge branch 'master' into plugin_system 2020-06-17 23:50:12 +01:00
Laurent Cozic ffeb7d0ecd Add support for TypeScript in plugins 2020-05-25 18:04:15 +01:00
Laurent Cozic aba92857cb Load plugins from directory 2020-05-25 17:28:32 +01:00
Laurent Cozic 5918c81dbb Merge branch 'master' into plugin_system 2020-05-25 16:56:04 +01:00
Laurent Cozic 819d9fd977 Added more tests 2020-05-23 10:40:10 +01:00
Laurent Cozic ad80e074de Started plugin system 2020-05-21 23:51:51 +00:00
804 changed files with 95622 additions and 5307 deletions
+91 -2
View File
@@ -59,15 +59,25 @@ Tools/node_modules
Tools/PortableAppsLauncher
Modules/TinyMCE/IconPack/postinstall.js
Modules/TinyMCE/langs/
CliClient/build/
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
CliClient/app/LinkSelector.js
CliClient/build/LinkSelector.js
CliClient/app/services/plugins/PluginRunner.js
CliClient/tests/models_Setting.js
CliClient/tests/services_CommandService.js
CliClient/tests/services_InteropService.js
CliClient/tests/services_PluginService.js
CliClient/tests/services/plugins/sandboxProxy.js
CliClient/tests/synchronizer_LockHandler.js
CliClient/tests/synchronizer_MigrationHandler.js
ElectronClient/app.js
ElectronClient/bridge.js
ElectronClient/commands/copyDevCommand.js
ElectronClient/commands/focusElement.js
ElectronClient/commands/startExternalEditing.js
ElectronClient/commands/stopExternalEditing.js
ElectronClient/ElectronAppWrapper.js
ElectronClient/global.d.js
ElectronClient/gui/Button/Button.js
ElectronClient/gui/ConfigScreen/ButtonBar.js
@@ -103,6 +113,7 @@ ElectronClient/gui/MainScreen/commands/toggleNoteList.js
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
ElectronClient/gui/MainScreen/MainScreen.js
ElectronClient/gui/MenuBar.js
ElectronClient/gui/MultiNoteActions.js
ElectronClient/gui/NoteContentPropertiesDialog.js
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
@@ -138,7 +149,7 @@ ElectronClient/gui/NoteEditor/utils/useFormNote.js
ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js
ElectronClient/gui/NoteEditor/utils/useMessageHandler.js
ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js
ElectronClient/gui/NoteEditor/utils/useNoteToolbarButtons.js
ElectronClient/gui/NoteEditor/utils/usePluginServiceRegistration.js
ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js
ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js
ElectronClient/gui/NoteList/commands/focusElementNoteList.js
@@ -154,6 +165,7 @@ ElectronClient/gui/ResizableLayout/hooks/useWindowResizeEvent.js
ElectronClient/gui/ResizableLayout/ResizableLayout.js
ElectronClient/gui/ResourceScreen.js
ElectronClient/gui/Root_UpgradeSyncTarget.js
ElectronClient/gui/Root.js
ElectronClient/gui/SearchBar/hooks/useSearch.js
ElectronClient/gui/SearchBar/SearchBar.js
ElectronClient/gui/SearchBar/styles/index.js
@@ -169,12 +181,25 @@ ElectronClient/gui/ToggleEditorsButton/ToggleEditorsButton.js
ElectronClient/gui/ToolbarBase.js
ElectronClient/gui/ToolbarButton/styles/index.js
ElectronClient/gui/ToolbarButton/ToolbarButton.js
ElectronClient/gui/utils/NoteListUtils.js
ElectronClient/InteropServiceHelper.js
ElectronClient/services/bridge.js
ElectronClient/services/plugins/hooks/useThemeCss.js
ElectronClient/services/plugins/hooks/useViewIsReady.js
ElectronClient/services/plugins/PlatformImplementation.js
ElectronClient/services/plugins/PluginRunner.js
ElectronClient/services/plugins/UserWebview.js
ElectronClient/services/plugins/UserWebviewDialog.js
ElectronClient/services/plugins/UserWebviewDialogButtonBar.js
ReactNativeClient/lib/AsyncActionQueue.js
ReactNativeClient/lib/BaseApplication.js
ReactNativeClient/lib/checkPermissions.js
ReactNativeClient/lib/commands/historyBackward.js
ReactNativeClient/lib/commands/historyForward.js
ReactNativeClient/lib/commands/synchronize.js
ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
ReactNativeClient/lib/errorUtils.js
ReactNativeClient/lib/eventManager.js
ReactNativeClient/lib/hooks/useEffectDebugger.js
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
ReactNativeClient/lib/hooks/usePrevious.js
@@ -184,18 +209,79 @@ ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
ReactNativeClient/lib/JoplinServerApi.js
ReactNativeClient/lib/locale.js
ReactNativeClient/lib/Logger.js
ReactNativeClient/lib/markdownUtils.js
ReactNativeClient/lib/models/Alarm.js
ReactNativeClient/lib/models/Setting.js
ReactNativeClient/lib/ntpDate.js
ReactNativeClient/lib/reducer.js
ReactNativeClient/lib/services/AlarmService.js
ReactNativeClient/lib/services/AlarmServiceDriver.android.js
ReactNativeClient/lib/services/AlarmServiceDriver.ios.js
ReactNativeClient/lib/services/AlarmServiceDriverNode.js
ReactNativeClient/lib/services/BaseService.js
ReactNativeClient/lib/services/BooleanExpression.js
ReactNativeClient/lib/services/commands/MenuUtils.js
ReactNativeClient/lib/services/commands/propsHaveChanged.js
ReactNativeClient/lib/services/commands/ToolbarButtonUtils.js
ReactNativeClient/lib/services/CommandService.js
ReactNativeClient/lib/services/contextkey/contextkey.js
ReactNativeClient/lib/services/debug/populateDatabase.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Base.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Custom.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Html.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Jex.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Md.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Raw.js
ReactNativeClient/lib/services/interop/InteropService_Importer_Base.js
ReactNativeClient/lib/services/interop/InteropService_Importer_Custom.js
ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToHtml.js
ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToMd.js
ReactNativeClient/lib/services/interop/InteropService_Importer_Jex.js
ReactNativeClient/lib/services/interop/InteropService_Importer_Md.js
ReactNativeClient/lib/services/interop/InteropService_Importer_Raw.js
ReactNativeClient/lib/services/interop/InteropService.js
ReactNativeClient/lib/services/interop/types.js
ReactNativeClient/lib/services/keychain/KeychainService.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.node.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriverBase.js
ReactNativeClient/lib/services/KeymapService.js
ReactNativeClient/lib/services/plugins/api/Global.js
ReactNativeClient/lib/services/plugins/api/Joplin.js
ReactNativeClient/lib/services/plugins/api/JoplinCommands.js
ReactNativeClient/lib/services/plugins/api/JoplinData.js
ReactNativeClient/lib/services/plugins/api/JoplinFilters.js
ReactNativeClient/lib/services/plugins/api/JoplinInterop.js
ReactNativeClient/lib/services/plugins/api/JoplinPlugins.js
ReactNativeClient/lib/services/plugins/api/JoplinSettings.js
ReactNativeClient/lib/services/plugins/api/JoplinViews.js
ReactNativeClient/lib/services/plugins/api/JoplinViewsDialogs.js
ReactNativeClient/lib/services/plugins/api/JoplinViewsMenuItems.js
ReactNativeClient/lib/services/plugins/api/JoplinViewsPanels.js
ReactNativeClient/lib/services/plugins/api/JoplinViewsToolbarButtons.js
ReactNativeClient/lib/services/plugins/api/JoplinWorkspace.js
ReactNativeClient/lib/services/plugins/api/types.js
ReactNativeClient/lib/services/plugins/BasePluginRunner.js
ReactNativeClient/lib/services/plugins/MenuItemController.js
ReactNativeClient/lib/services/plugins/Plugin.js
ReactNativeClient/lib/services/plugins/PluginService.js
ReactNativeClient/lib/services/plugins/reducer.js
ReactNativeClient/lib/services/plugins/sandboxProxy.js
ReactNativeClient/lib/services/plugins/ToolbarButtonController.js
ReactNativeClient/lib/services/plugins/utils/createViewHandle.js
ReactNativeClient/lib/services/plugins/utils/executeSandboxCall.js
ReactNativeClient/lib/services/plugins/utils/manifestFromObject.js
ReactNativeClient/lib/services/plugins/utils/mapEventHandlersToIds.js
ReactNativeClient/lib/services/plugins/utils/types.js
ReactNativeClient/lib/services/plugins/ViewController.js
ReactNativeClient/lib/services/plugins/WebviewController.js
ReactNativeClient/lib/services/ResourceEditWatcher/index.js
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
ReactNativeClient/lib/services/rest/actionApi.desktop.js
ReactNativeClient/lib/services/rest/Api.js
ReactNativeClient/lib/services/rest/errors.js
ReactNativeClient/lib/services/searchengine/filterParser.js
ReactNativeClient/lib/services/searchengine/queryBuilder.js
@@ -209,6 +295,8 @@ ReactNativeClient/lib/services/synchronizer/utils/types.js
ReactNativeClient/lib/services/UndoRedoService.js
ReactNativeClient/lib/ShareExtension.js
ReactNativeClient/lib/shareHandler.js
ReactNativeClient/lib/shim.js
ReactNativeClient/lib/Synchronizer.js
ReactNativeClient/lib/theme.js
ReactNativeClient/lib/themes/aritimDark.js
ReactNativeClient/lib/themes/dark.js
@@ -219,6 +307,7 @@ ReactNativeClient/lib/themes/oledDark.js
ReactNativeClient/lib/themes/solarizedDark.js
ReactNativeClient/lib/themes/solarizedLight.js
ReactNativeClient/lib/themes/type.js
ReactNativeClient/lib/uuid.js
ReactNativeClient/lib/versionInfo.js
ReactNativeClient/PluginAssetsLoader.js
ReactNativeClient/setUpQuickActions.js
+14 -2
View File
@@ -48,8 +48,9 @@ module.exports = {
// -------------------------------
'react/jsx-uses-react': 'error',
'react/jsx-uses-vars': 'error',
'no-unused-vars': 'error',
'@typescript-eslint/no-unused-vars': 'error',
'no-unused-vars': ['error', { 'argsIgnorePattern': '^_' }],
'@typescript-eslint/no-unused-vars': ['error', { 'argsIgnorePattern': '^_' }],
'@typescript-eslint/explicit-member-accessibility': 'off',
'no-constant-condition': 0,
'no-prototype-builtins': 0,
// This error is always a false positive so far since it detects
@@ -121,4 +122,15 @@ module.exports = {
'react-hooks',
'import',
],
'overrides': [
{
// enable the rule specifically for TypeScript files
'files': ['*.ts', '*.tsx'],
'rules': {
// Warn only because it would make it difficult to convert JS classes to TypeScript, unless we
// make everything public which is not great. New code however should specify member accessibility.
'@typescript-eslint/explicit-member-accessibility': ['warn'],
},
},
],
};
+92 -2
View File
@@ -52,15 +52,26 @@ Tools/commit_hook.txt
*.map
ReactNativeClient/lib/sql-extensions/spellfix.so
ReactNativeClient/lib/sql-extensions/spellfix.dylib
CliClient/build/
plugin_types/
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
CliClient/app/LinkSelector.js
CliClient/build/LinkSelector.js
CliClient/app/services/plugins/PluginRunner.js
CliClient/tests/models_Setting.js
CliClient/tests/services_CommandService.js
CliClient/tests/services_InteropService.js
CliClient/tests/services_PluginService.js
CliClient/tests/services/plugins/sandboxProxy.js
CliClient/tests/synchronizer_LockHandler.js
CliClient/tests/synchronizer_MigrationHandler.js
ElectronClient/app.js
ElectronClient/bridge.js
ElectronClient/commands/copyDevCommand.js
ElectronClient/commands/focusElement.js
ElectronClient/commands/startExternalEditing.js
ElectronClient/commands/stopExternalEditing.js
ElectronClient/ElectronAppWrapper.js
ElectronClient/global.d.js
ElectronClient/gui/Button/Button.js
ElectronClient/gui/ConfigScreen/ButtonBar.js
@@ -96,6 +107,7 @@ ElectronClient/gui/MainScreen/commands/toggleNoteList.js
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
ElectronClient/gui/MainScreen/MainScreen.js
ElectronClient/gui/MenuBar.js
ElectronClient/gui/MultiNoteActions.js
ElectronClient/gui/NoteContentPropertiesDialog.js
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
@@ -131,7 +143,7 @@ ElectronClient/gui/NoteEditor/utils/useFormNote.js
ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js
ElectronClient/gui/NoteEditor/utils/useMessageHandler.js
ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js
ElectronClient/gui/NoteEditor/utils/useNoteToolbarButtons.js
ElectronClient/gui/NoteEditor/utils/usePluginServiceRegistration.js
ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js
ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js
ElectronClient/gui/NoteList/commands/focusElementNoteList.js
@@ -147,6 +159,7 @@ ElectronClient/gui/ResizableLayout/hooks/useWindowResizeEvent.js
ElectronClient/gui/ResizableLayout/ResizableLayout.js
ElectronClient/gui/ResourceScreen.js
ElectronClient/gui/Root_UpgradeSyncTarget.js
ElectronClient/gui/Root.js
ElectronClient/gui/SearchBar/hooks/useSearch.js
ElectronClient/gui/SearchBar/SearchBar.js
ElectronClient/gui/SearchBar/styles/index.js
@@ -162,12 +175,25 @@ ElectronClient/gui/ToggleEditorsButton/ToggleEditorsButton.js
ElectronClient/gui/ToolbarBase.js
ElectronClient/gui/ToolbarButton/styles/index.js
ElectronClient/gui/ToolbarButton/ToolbarButton.js
ElectronClient/gui/utils/NoteListUtils.js
ElectronClient/InteropServiceHelper.js
ElectronClient/services/bridge.js
ElectronClient/services/plugins/hooks/useThemeCss.js
ElectronClient/services/plugins/hooks/useViewIsReady.js
ElectronClient/services/plugins/PlatformImplementation.js
ElectronClient/services/plugins/PluginRunner.js
ElectronClient/services/plugins/UserWebview.js
ElectronClient/services/plugins/UserWebviewDialog.js
ElectronClient/services/plugins/UserWebviewDialogButtonBar.js
ReactNativeClient/lib/AsyncActionQueue.js
ReactNativeClient/lib/BaseApplication.js
ReactNativeClient/lib/checkPermissions.js
ReactNativeClient/lib/commands/historyBackward.js
ReactNativeClient/lib/commands/historyForward.js
ReactNativeClient/lib/commands/synchronize.js
ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
ReactNativeClient/lib/errorUtils.js
ReactNativeClient/lib/eventManager.js
ReactNativeClient/lib/hooks/useEffectDebugger.js
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
ReactNativeClient/lib/hooks/usePrevious.js
@@ -177,18 +203,79 @@ ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
ReactNativeClient/lib/JoplinServerApi.js
ReactNativeClient/lib/locale.js
ReactNativeClient/lib/Logger.js
ReactNativeClient/lib/markdownUtils.js
ReactNativeClient/lib/models/Alarm.js
ReactNativeClient/lib/models/Setting.js
ReactNativeClient/lib/ntpDate.js
ReactNativeClient/lib/reducer.js
ReactNativeClient/lib/services/AlarmService.js
ReactNativeClient/lib/services/AlarmServiceDriver.android.js
ReactNativeClient/lib/services/AlarmServiceDriver.ios.js
ReactNativeClient/lib/services/AlarmServiceDriverNode.js
ReactNativeClient/lib/services/BaseService.js
ReactNativeClient/lib/services/BooleanExpression.js
ReactNativeClient/lib/services/commands/MenuUtils.js
ReactNativeClient/lib/services/commands/propsHaveChanged.js
ReactNativeClient/lib/services/commands/ToolbarButtonUtils.js
ReactNativeClient/lib/services/CommandService.js
ReactNativeClient/lib/services/contextkey/contextkey.js
ReactNativeClient/lib/services/debug/populateDatabase.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Base.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Custom.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Html.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Jex.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Md.js
ReactNativeClient/lib/services/interop/InteropService_Exporter_Raw.js
ReactNativeClient/lib/services/interop/InteropService_Importer_Base.js
ReactNativeClient/lib/services/interop/InteropService_Importer_Custom.js
ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToHtml.js
ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToMd.js
ReactNativeClient/lib/services/interop/InteropService_Importer_Jex.js
ReactNativeClient/lib/services/interop/InteropService_Importer_Md.js
ReactNativeClient/lib/services/interop/InteropService_Importer_Raw.js
ReactNativeClient/lib/services/interop/InteropService.js
ReactNativeClient/lib/services/interop/types.js
ReactNativeClient/lib/services/keychain/KeychainService.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.node.js
ReactNativeClient/lib/services/keychain/KeychainServiceDriverBase.js
ReactNativeClient/lib/services/KeymapService.js
ReactNativeClient/lib/services/plugins/api/Global.js
ReactNativeClient/lib/services/plugins/api/Joplin.js
ReactNativeClient/lib/services/plugins/api/JoplinCommands.js
ReactNativeClient/lib/services/plugins/api/JoplinData.js
ReactNativeClient/lib/services/plugins/api/JoplinFilters.js
ReactNativeClient/lib/services/plugins/api/JoplinInterop.js
ReactNativeClient/lib/services/plugins/api/JoplinPlugins.js
ReactNativeClient/lib/services/plugins/api/JoplinSettings.js
ReactNativeClient/lib/services/plugins/api/JoplinViews.js
ReactNativeClient/lib/services/plugins/api/JoplinViewsDialogs.js
ReactNativeClient/lib/services/plugins/api/JoplinViewsMenuItems.js
ReactNativeClient/lib/services/plugins/api/JoplinViewsPanels.js
ReactNativeClient/lib/services/plugins/api/JoplinViewsToolbarButtons.js
ReactNativeClient/lib/services/plugins/api/JoplinWorkspace.js
ReactNativeClient/lib/services/plugins/api/types.js
ReactNativeClient/lib/services/plugins/BasePluginRunner.js
ReactNativeClient/lib/services/plugins/MenuItemController.js
ReactNativeClient/lib/services/plugins/Plugin.js
ReactNativeClient/lib/services/plugins/PluginService.js
ReactNativeClient/lib/services/plugins/reducer.js
ReactNativeClient/lib/services/plugins/sandboxProxy.js
ReactNativeClient/lib/services/plugins/ToolbarButtonController.js
ReactNativeClient/lib/services/plugins/utils/createViewHandle.js
ReactNativeClient/lib/services/plugins/utils/executeSandboxCall.js
ReactNativeClient/lib/services/plugins/utils/manifestFromObject.js
ReactNativeClient/lib/services/plugins/utils/mapEventHandlersToIds.js
ReactNativeClient/lib/services/plugins/utils/types.js
ReactNativeClient/lib/services/plugins/ViewController.js
ReactNativeClient/lib/services/plugins/WebviewController.js
ReactNativeClient/lib/services/ResourceEditWatcher/index.js
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
ReactNativeClient/lib/services/rest/actionApi.desktop.js
ReactNativeClient/lib/services/rest/Api.js
ReactNativeClient/lib/services/rest/errors.js
ReactNativeClient/lib/services/searchengine/filterParser.js
ReactNativeClient/lib/services/searchengine/queryBuilder.js
@@ -202,6 +289,8 @@ ReactNativeClient/lib/services/synchronizer/utils/types.js
ReactNativeClient/lib/services/UndoRedoService.js
ReactNativeClient/lib/ShareExtension.js
ReactNativeClient/lib/shareHandler.js
ReactNativeClient/lib/shim.js
ReactNativeClient/lib/Synchronizer.js
ReactNativeClient/lib/theme.js
ReactNativeClient/lib/themes/aritimDark.js
ReactNativeClient/lib/themes/dark.js
@@ -212,6 +301,7 @@ ReactNativeClient/lib/themes/oledDark.js
ReactNativeClient/lib/themes/solarizedDark.js
ReactNativeClient/lib/themes/solarizedLight.js
ReactNativeClient/lib/themes/type.js
ReactNativeClient/lib/uuid.js
ReactNativeClient/lib/versionInfo.js
ReactNativeClient/PluginAssetsLoader.js
ReactNativeClient/setUpQuickActions.js
+1 -1
View File
@@ -96,4 +96,4 @@ The Markdown renderer is located under ReactNativeClient/lib/joplin-renderer. Wh
# Troubleshooting
Please read for the [Build Troubleshooting Document](https://github.com/laurent22/joplin/blob/master/readme/build_troubleshooting.md) for various tips on how to get the build working.
Please read for the [Build Troubleshooting Document](https://github.com/laurent22/joplin/blob/dev/readme/build_troubleshooting.md) for various tips on how to get the build working.
+1 -1
View File
@@ -1,4 +1,4 @@
const { Logger } = require('lib/logger.js');
const Logger = require('lib/Logger').default;
const { netUtils } = require('lib/net-utils.js');
const http = require('http');
+7 -5
View File
@@ -1,15 +1,17 @@
const { Logger } = require('lib/logger.js');
const Logger = require('lib/Logger').default;
const Folder = require('lib/models/Folder.js');
const BaseItem = require('lib/models/BaseItem.js');
const Tag = require('lib/models/Tag.js');
const BaseModel = require('lib/BaseModel.js');
const Note = require('lib/models/Note.js');
const Resource = require('lib/models/Resource.js');
const Setting = require('lib/models/Setting.js');
const { reducer, defaultState } = require('lib/reducer.js');
const Setting = require('lib/models/Setting').default;
const reducer = require('lib/reducer').default;
const { defaultState } = require('lib/reducer');
const { splitCommandString } = require('lib/string-utils.js');
const { reg } = require('lib/registry.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const shim = require('lib/shim').default;
const Entities = require('html-entities').AllHtmlEntities;
const htmlentities = new Entities().encode;
@@ -477,7 +479,7 @@ class AppGui {
this.linkSelector_.noteX + cursorOffsetX,
this.linkSelector_.noteY + cursorOffsetY
);
setTimeout(() => this.term_.term().inverse(this.linkSelector_.link), 50);
shim.setTimeout(() => this.term_.term().inverse(this.linkSelector_.link), 50);
}
} else if (cmd === 'open_link') {
if (this.widget('noteText').hasFocus) {
+5 -4
View File
@@ -1,4 +1,4 @@
const { BaseApplication } = require('lib/BaseApplication');
const BaseApplication = require('lib/BaseApplication').default;
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
const ResourceService = require('lib/services/ResourceService');
const BaseModel = require('lib/BaseModel.js');
@@ -6,14 +6,15 @@ const Folder = require('lib/models/Folder.js');
const BaseItem = require('lib/models/BaseItem.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const { reg } = require('lib/registry.js');
const { fileExtension } = require('lib/path-utils.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const fs = require('fs-extra');
const { cliUtils } = require('./cli-utils.js');
const Cache = require('lib/Cache');
const RevisionService = require('lib/services/RevisionService');
const shim = require('lib/shim').default;
class Application extends BaseApplication {
constructor() {
@@ -161,7 +162,7 @@ class Application extends BaseApplication {
};
// Give it a few seconds to cancel otherwise exit anyway
setTimeout(async () => {
shim.setTimeout(async () => {
await doExit();
}, 5000);
+1 -1
View File
@@ -1,4 +1,4 @@
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const { reg } = require('lib/registry.js');
class BaseCommand {
+1 -1
View File
@@ -1,7 +1,7 @@
const fs = require('fs-extra');
const { fileExtension, dirname } = require('lib/path-utils.js');
const wrap_ = require('word-wrap');
const { languageCode } = require('lib/locale.js');
const { languageCode } = require('lib/locale');
const rootDir = dirname(dirname(__dirname));
const MAX_WIDTH = 78;
+2 -2
View File
@@ -1,14 +1,14 @@
'use strict';
const fs = require('fs-extra');
const { Logger } = require('lib/logger.js');
const Logger = require('lib/Logger').default;
const { dirname } = require('lib/path-utils.js');
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
const { JoplinDatabase } = require('lib/joplin-database.js');
const BaseModel = require('lib/BaseModel.js');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const { sprintf } = require('sprintf-js');
const exec = require('child_process').exec;
+2 -2
View File
@@ -1,8 +1,8 @@
const yargParser = require('yargs-parser');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const { time } = require('lib/time-utils.js');
const stringPadding = require('string-padding');
const { Logger } = require('lib/logger.js');
const Logger = require('lib/Logger').default;
const cliUtils = {};
+3 -3
View File
@@ -3,7 +3,7 @@ const BaseItem = require('lib/models/BaseItem');
const BaseModel = require('lib/BaseModel');
const { toTitleCase } = require('lib/string-utils.js');
const { reg } = require('lib/registry.js');
const markdownUtils = require('lib/markdownUtils');
const markdownUtils = require('lib/markdownUtils').default;
const { Database } = require('lib/database.js');
class Command extends BaseCommand {
@@ -53,9 +53,9 @@ class Command extends BaseCommand {
const lines = [];
lines.push('# Joplin API');
lines.push('# Joplin Data API');
lines.push('');
lines.push('This API is available when the clipper server is running. It provides access to the notes, notebooks, tags and other Joplin object via a REST API. Plugins can also access this API even when the clipper server is not running.');
lines.push('');
lines.push('In order to use it, you\'ll first need to find on which port the service is running. To do so, open the Web Clipper Options in Joplin and if the service is running it should tell you on which port. Normally it runs on port **41184**. If you want to find it programmatically, you may follow this kind of algorithm:');
lines.push('');
+2 -2
View File
@@ -1,8 +1,8 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
const { shim } = require('lib/shim.js');
const shim = require('lib/shim').default;
class Command extends BaseCommand {
usage() {
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
const BaseItem = require('lib/models/BaseItem.js');
const Note = require('lib/models/Note.js');
+2 -2
View File
@@ -1,8 +1,8 @@
const { BaseCommand } = require('./base-command.js');
const { _, setLocale } = require('lib/locale.js');
const { _, setLocale } = require('lib/locale');
const { app } = require('./app.js');
const fs = require('fs-extra');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
class Command extends BaseCommand {
usage() {
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
const Note = require('lib/models/Note.js');
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
const Note = require('lib/models/Note.js');
const { time } = require('lib/time-utils.js');
+4 -4
View File
@@ -1,10 +1,10 @@
const { BaseCommand } = require('./base-command.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const EncryptionService = require('lib/services/EncryptionService');
const DecryptionWorker = require('lib/services/DecryptionWorker');
const BaseItem = require('lib/models/BaseItem');
const Setting = require('lib/models/Setting.js');
const { shim } = require('lib/shim');
const Setting = require('lib/models/Setting').default;
const shim = require('lib/shim').default;
const pathUtils = require('lib/path-utils.js');
const imageType = require('image-type');
const readChunk = require('read-chunk');
@@ -38,7 +38,7 @@ class Command extends BaseCommand {
this.stdout(_('Operation cancelled'));
return false;
}
Setting.setObjectKey('encryption.passwordCache', masterKeyId, password);
Setting.setObjectValue('encryption.passwordCache', masterKeyId, password);
await EncryptionService.instance().loadMasterKeysFromSettings();
return true;
};
+3 -3
View File
@@ -1,11 +1,11 @@
const fs = require('fs-extra');
const { BaseCommand } = require('./base-command.js');
const { splitCommandString } = require('lib/string-utils.js');
const { uuid } = require('lib/uuid.js');
const uuid = require('lib/uuid').default;
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const Note = require('lib/models/Note.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const BaseModel = require('lib/BaseModel.js');
class Command extends BaseCommand {
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
class Command extends BaseCommand {
usage() {
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const { ReportService } = require('lib/services/report.js');
const fs = require('fs-extra');
+4 -4
View File
@@ -1,8 +1,8 @@
const { BaseCommand } = require('./base-command.js');
const InteropService = require('lib/services/InteropService.js');
const InteropService = require('lib/services/interop/InteropService').default;
const BaseModel = require('lib/BaseModel.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
class Command extends BaseCommand {
usage() {
@@ -14,7 +14,7 @@ class Command extends BaseCommand {
}
options() {
const service = new InteropService();
const service = InteropService.instance();
const formats = service
.modules()
.filter(m => m.type === 'exporter' && m.format !== 'html')
@@ -41,7 +41,7 @@ class Command extends BaseCommand {
exportOptions.sourceFolderIds = folders.map(n => n.id);
}
const service = new InteropService();
const service = InteropService.instance();
const result = await service.export(exportOptions);
result.warnings.map(w => this.stdout(w));
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
const Note = require('lib/models/Note.js');
+1 -1
View File
@@ -1,7 +1,7 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { renderCommandHelp } = require('./help-utils.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const { cliUtils } = require('./cli-utils.js');
class Command extends BaseCommand {
+4 -4
View File
@@ -1,9 +1,9 @@
const { BaseCommand } = require('./base-command.js');
const InteropService = require('lib/services/InteropService.js');
const InteropService = require('lib/services/interop/InteropService').default;
const BaseModel = require('lib/BaseModel.js');
const { cliUtils } = require('./cli-utils.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
class Command extends BaseCommand {
usage() {
@@ -15,7 +15,7 @@ class Command extends BaseCommand {
}
options() {
const service = new InteropService();
const service = InteropService.instance();
const formats = service
.modules()
.filter(m => m.type === 'importer')
@@ -63,7 +63,7 @@ class Command extends BaseCommand {
app().gui().showConsole();
this.stdout(_('Importing notes...'));
const service = new InteropService();
const service = InteropService.instance();
const result = await service.import(importOptions);
result.warnings.map(w => this.stdout(w));
cliUtils.redrawDone();
+2 -2
View File
@@ -1,9 +1,9 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
const Folder = require('lib/models/Folder.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const Note = require('lib/models/Note.js');
const { sprintf } = require('sprintf-js');
const { time } = require('lib/time-utils.js');
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const Folder = require('lib/models/Folder.js');
class Command extends BaseCommand {
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const Note = require('lib/models/Note.js');
class Command extends BaseCommand {
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const Note = require('lib/models/Note.js');
class Command extends BaseCommand {
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const Folder = require('lib/models/Folder.js');
const BaseModel = require('lib/BaseModel.js');
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const Note = require('lib/models/Note.js');
const BaseModel = require('lib/BaseModel.js');
+2 -2
View File
@@ -1,8 +1,8 @@
const { BaseCommand } = require('./base-command.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
const Folder = require('lib/models/Folder.js');
const { uuid } = require('lib/uuid.js');
const uuid = require('lib/uuid').default;
class Command extends BaseCommand {
usage() {
+4 -4
View File
@@ -1,8 +1,8 @@
const { BaseCommand } = require('./base-command.js');
const { _ } = require('lib/locale.js');
const Setting = require('lib/models/Setting.js');
const { Logger } = require('lib/logger.js');
const { shim } = require('lib/shim');
const { _ } = require('lib/locale');
const Setting = require('lib/models/Setting').default;
const Logger = require('lib/Logger').default;
const shim = require('lib/shim').default;
class Command extends BaseCommand {
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
const { Database } = require('lib/database.js');
const Note = require('lib/models/Note.js');
+2 -2
View File
@@ -1,7 +1,7 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const Setting = require('lib/models/Setting.js');
const { _ } = require('lib/locale.js');
const Setting = require('lib/models/Setting').default;
const { _ } = require('lib/locale');
const { ReportService } = require('lib/services/report.js');
class Command extends BaseCommand {
+3 -3
View File
@@ -1,10 +1,10 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const { OneDriveApiNodeUtils } = require('lib/onedrive-api-node-utils.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const ResourceFetcher = require('lib/services/ResourceFetcher');
const { Synchronizer } = require('lib/synchronizer.js');
const Synchronizer = require('lib/Synchronizer').default;
const { reg } = require('lib/registry.js');
const { cliUtils } = require('./cli-utils.js');
const md5 = require('md5');
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const Tag = require('lib/models/Tag.js');
const BaseModel = require('lib/BaseModel.js');
const { time } = require('lib/time-utils.js');
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
const Note = require('lib/models/Note.js');
const { time } = require('lib/time-utils.js');
+1 -1
View File
@@ -1,5 +1,5 @@
const { BaseCommand } = require('./base-command.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const CommandDone = require('./command-done.js');
+1 -1
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
class Command extends BaseCommand {
+2 -2
View File
@@ -1,6 +1,6 @@
const { BaseCommand } = require('./base-command.js');
const Setting = require('lib/models/Setting.js');
const { _ } = require('lib/locale.js');
const Setting = require('lib/models/Setting').default;
const { _ } = require('lib/locale');
class Command extends BaseCommand {
usage() {
+1 -1
View File
@@ -1,7 +1,7 @@
'use strict';
const { time } = require('lib/time-utils.js');
const { Logger } = require('lib/logger.js');
const Logger = require('lib/Logger').default;
const Resource = require('lib/models/Resource.js');
const { dirname } = require('lib/path-utils.js');
const { FsDriverNode } = require('./fs-driver-node.js');
+1 -1
View File
@@ -2,7 +2,7 @@ const Folder = require('lib/models/Folder.js');
const Tag = require('lib/models/Tag.js');
const BaseModel = require('lib/BaseModel.js');
const ListWidget = require('tkwidgets/ListWidget.js');
const _ = require('lib/locale.js')._;
const _ = require('lib/locale')._;
class FolderListWidget extends ListWidget {
constructor() {
+1 -1
View File
@@ -1,6 +1,6 @@
const Note = require('lib/models/Note.js');
const TextWidget = require('tkwidgets/TextWidget.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
class NoteWidget extends TextWidget {
constructor() {
+2 -2
View File
@@ -1,6 +1,6 @@
const { wrap } = require('lib/string-utils.js');
const Setting = require('lib/models/Setting.js');
const { _ } = require('lib/locale.js');
const Setting = require('lib/models/Setting').default;
const { _ } = require('lib/locale');
const MAX_WIDTH = 78;
const INDENT = ' ';
+3 -3
View File
@@ -21,12 +21,12 @@ const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
const NoteTag = require('lib/models/NoteTag.js');
const MasterKey = require('lib/models/MasterKey');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const Revision = require('lib/models/Revision.js');
const { Logger } = require('lib/logger.js');
const Logger = require('lib/Logger').default;
const { FsDriverNode } = require('lib/fs-driver-node.js');
const { shimInit } = require('lib/shim-init-node.js');
const { _ } = require('lib/locale.js');
const { _ } = require('lib/locale');
const { FileApiDriverLocal } = require('lib/file-api-driver-local.js');
const EncryptionService = require('lib/services/EncryptionService');
const envFromArgs = require('lib/envFromArgs');
@@ -0,0 +1,68 @@
import * as vm from 'vm';
import Plugin from 'lib/services/plugins/Plugin';
import sandboxProxy from 'lib/services/plugins/sandboxProxy';
import BasePluginRunner from 'lib/services/plugins/BasePluginRunner';
import executeSandboxCall from 'lib/services/plugins/utils/executeSandboxCall';
import Global from 'lib/services/plugins/api/Global';
import mapEventHandlersToIds, { EventHandlers } from 'lib/services/plugins/utils/mapEventHandlersToIds';
function createConsoleWrapper(pluginId:string) {
const wrapper:any = {};
for (const n in console) {
if (!console.hasOwnProperty(n)) continue;
wrapper[n] = (...args:any[]) => {
const newArgs = args.slice();
newArgs.splice(0, 0, `Plugin "${pluginId}":`);
return (console as any)[n](...newArgs);
};
}
return wrapper;
}
// The CLI plugin runner is more complex than it needs to be because it more or less emulates
// how it would work in a multi-process architecture, as in the desktop app (and probably how
// it would work in the mobile app too). This is mainly to allow doing integration testing.
//
// For example, all plugin calls go through a proxy, however they could made directly since
// the plugin script is running within the same process as the main app.
export default class PluginRunner extends BasePluginRunner {
private eventHandlers_:EventHandlers = {};
constructor() {
super();
this.eventHandler = this.eventHandler.bind(this);
}
private async eventHandler(eventHandlerId:string, args:any[]) {
const cb = this.eventHandlers_[eventHandlerId];
return cb(...args);
}
private newSandboxProxy(pluginId:string, sandbox:Global) {
const target = async (path:string, args:any[]) => {
return executeSandboxCall(pluginId, sandbox, `joplin.${path}`, mapEventHandlersToIds(args, this.eventHandlers_), this.eventHandler);
};
return {
joplin: sandboxProxy(target),
console: createConsoleWrapper(pluginId),
};
}
async run(plugin:Plugin, sandbox:Global) {
const vmSandbox = vm.createContext(this.newSandboxProxy(plugin.id, sandbox));
try {
vm.runInContext(plugin.scriptText, vmSandbox);
} catch (error) {
this.logger().error(`In plugin ${plugin.id}:`, error);
return;
}
}
}
+8 -2
View File
@@ -46,8 +46,14 @@ tasks.prepareTestBuild = {
],
});
await utils.copyDir(`${__dirname}/../ReactNativeClient/lib`, `${testBuildDir}/lib`);
await utils.copyDir(`${__dirname}/../ReactNativeClient/locales`, `${testBuildDir}/locales`);
const rootDir = utils.rootDir();
await utils.copyDir(`${rootDir}/ReactNativeClient/lib`, `${testBuildDir}/lib`, {
excluded: [
`${rootDir}/ReactNativeClient/lib/joplin-renderer/node_modules`,
],
});
await utils.copyDir(`${rootDir}/ReactNativeClient/locales`, `${testBuildDir}/locales`);
await fs.mkdirp(`${testBuildDir}/data`);
},
};
+21 -1
View File
@@ -1,6 +1,6 @@
{
"name": "joplin",
"version": "1.2.3",
"version": "1.3.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -606,6 +606,11 @@
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"builtin-modules": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
"integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw=="
},
"cache-base": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
@@ -4344,6 +4349,11 @@
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
},
"nanoid": {
"version": "3.1.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz",
"integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A=="
},
"nanomatch": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -5250,6 +5260,11 @@
}
}
},
"re-reselect": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/re-reselect/-/re-reselect-4.0.0.tgz",
"integrity": "sha512-wuygyq8TXUlSdVXv2kigXxQNOgdb9m7LbIjwfTNGSpaY1riLd5e+VeQjlQMyUtrk0oiyhi1AqIVynworl3qxHA=="
},
"read-chunk": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-2.1.0.tgz",
@@ -5610,6 +5625,11 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"reselect": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz",
"integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA=="
},
"resolve": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
+6 -2
View File
@@ -28,7 +28,7 @@
],
"owner": "Laurent Cozic"
},
"version": "1.2.3",
"version": "1.3.0",
"bin": {
"joplin": "./main.js"
},
@@ -41,6 +41,7 @@
"aws-sdk": "^2.588.0",
"base-64": "^0.1.0",
"base64-stream": "^1.0.0",
"builtin-modules": "^3.1.0",
"clean-html": "^1.5.0",
"compare-version": "^0.1.2",
"diacritics": "^1.3.0",
@@ -83,6 +84,7 @@
"mime": "^2.0.3",
"moment": "^2.24.0",
"multiparty": "^4.2.1",
"nanoid": "^3.1.12",
"node-emoji": "^1.8.1",
"node-fetch": "^1.7.1",
"node-persist": "^2.1.0",
@@ -91,13 +93,16 @@
"promise": "^7.1.1",
"proper-lockfile": "^2.0.1",
"query-string": "4.3.4",
"re-reselect": "^4.0.0",
"read-chunk": "^2.1.0",
"redux": "^3.7.2",
"relative": "^3.0.2",
"request": "^2.88.0",
"reselect": "^4.0.0",
"sax": "^1.2.4",
"server-destroy": "^1.0.1",
"sharp": "^0.23.2",
"slug": "^3.3.4",
"sprintf-js": "^1.1.1",
"sqlite3": "^4.1.1",
"string-padding": "^1.0.2",
@@ -109,7 +114,6 @@
"terminal-kit": "^1.30.0",
"tkwidgets": "^0.5.26",
"url-parse": "^1.4.7",
"slug": "^3.3.4",
"uuid": "^3.0.1",
"valid-url": "^1.0.9",
"word-wrap": "^1.2.3",
+1 -1
View File
@@ -1,7 +1,7 @@
require('app-module-path').addPath(__dirname);
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
const { enexXmlToHtml } = require('lib/import-enex-html-gen.js');
process.on('unhandledRejection', (reason, p) => {
+1 -1
View File
@@ -9,7 +9,7 @@ const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const BaseModel = require('lib/BaseModel.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
process.on('unhandledRejection', (reason, p) => {
+1 -1
View File
@@ -9,7 +9,7 @@ const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const BaseModel = require('lib/BaseModel.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
const HtmlToHtml = require('lib/joplin-renderer/HtmlToHtml');
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
+1 -1
View File
@@ -9,7 +9,7 @@ const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const BaseModel = require('lib/BaseModel.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
const HtmlToMd = require('lib/HtmlToMd');
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
+1 -1
View File
@@ -9,7 +9,7 @@ const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const BaseModel = require('lib/BaseModel.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
const MdToHtml = require('lib/joplin-renderer/MdToHtml');
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
const { themeStyle } = require('lib/theme');
+1 -1
View File
@@ -1,4 +1,4 @@
const mdImporterService = require('lib/services/InteropService_Importer_Md');
const mdImporterService = require('lib/services/interop/InteropService_Importer_Md').default;
const Note = require('lib/models/Note.js');
const { setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
+2 -2
View File
@@ -6,10 +6,10 @@ const { time } = require('lib/time-utils.js');
const { sortedIds, createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const BaseModel = require('lib/BaseModel.js');
const ArrayUtils = require('lib/ArrayUtils.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+121
View File
@@ -0,0 +1,121 @@
'use strict';
require('app-module-path').addPath(__dirname);
const { asyncTest,checkThrow } = require('test-utils.js');
const eventManager = require('lib/eventManager').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
describe('eventManager', function() {
beforeEach(async (done) => {
eventManager.reset();
done();
});
afterEach(async (done) => {
eventManager.reset();
done();
});
it('should watch state props', asyncTest(async () => {
let localStateName = '';
let callCount = 0;
function nameWatch(event) {
callCount++;
localStateName = event.value;
}
const globalState = {
name: 'john',
};
eventManager.appStateOn('name', nameWatch);
eventManager.appStateEmit(globalState);
expect(localStateName).toBe('john');
globalState.name = 'paul';
eventManager.appStateEmit(globalState);
expect(localStateName).toBe('paul');
expect(callCount).toBe(2);
eventManager.appStateEmit(globalState);
expect(callCount).toBe(2);
}));
it('should unwatch state props', asyncTest(async () => {
let localStateName = '';
function nameWatch(event) {
localStateName = event.value;
}
const globalState = {
name: 'john',
};
eventManager.appStateOn('name', nameWatch);
eventManager.appStateOff('name', nameWatch);
eventManager.appStateEmit(globalState);
expect(localStateName).toBe('');
}));
it('should watch nested props', asyncTest(async () => {
let localStateName = '';
function nameWatch(event) {
localStateName = event.value;
}
const globalState = {
user: {
name: 'john',
},
};
eventManager.appStateOn('user.name', nameWatch);
eventManager.appStateEmit(globalState);
expect(localStateName).toBe('john');
globalState.user.name = 'paul';
eventManager.appStateEmit(globalState);
expect(localStateName).toBe('paul');
}));
it('should not be possible to modify state props', asyncTest(async () => {
let localUser = {};
function userWatch(event) {
// Normally, the user should not keep a reference to the whole object
// but make a copy. However, if they do keep a reference and try to
// modify it, it should throw an exception as that would be an attempt
// to directly modify the Redux state.
localUser = event.value;
}
const globalState = {
user: {
name: 'john',
},
};
eventManager.appStateOn('user', userWatch);
eventManager.appStateEmit(globalState);
expect(checkThrow(() => localUser.name = 'paul')).toBe(true);
}));
});
+1 -1
View File
@@ -1,7 +1,7 @@
require('app-module-path').addPath(__dirname);
const { asyncTest, id, ids, createNTestFolders, sortedIds, createNTestNotes, TestApp } = require('test-utils.js');
const BaseModel = require('lib/BaseModel.js');
const { uuid } = require('lib/uuid.js');
const uuid = require('lib/uuid').default;
const Note = require('lib/models/Note.js');
const Folder = require('lib/models/Folder.js');
+1 -1
View File
@@ -1,7 +1,7 @@
/* eslint-disable no-unused-vars */
require('app-module-path').addPath(__dirname);
const { setupDatabaseAndSynchronizer, switchClient, asyncTest, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('test-utils.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
+1 -1
View File
@@ -1,7 +1,7 @@
/* eslint-disable no-unused-vars */
require('app-module-path').addPath(__dirname);
const { setupDatabaseAndSynchronizer, switchClient, asyncTest, id, ids, sortedIds, at, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('test-utils.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
+1 -1
View File
@@ -1,7 +1,7 @@
/* eslint-disable no-unused-vars */
require('app-module-path').addPath(__dirname);
const { setupDatabaseAndSynchronizer, switchClient, asyncTest, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('test-utils.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
+3 -3
View File
@@ -2,12 +2,12 @@
require('app-module-path').addPath(__dirname);
const { uuid } = require('lib/uuid.js');
const uuid = require('lib/uuid').default;
const { time } = require('lib/time-utils.js');
const { asyncTest, sleep, fileApi, fileContentEqual, checkThrowAsync } = require('test-utils.js');
const { shim } = require('lib/shim.js');
const shim = require('lib/shim').default;
const fs = require('fs-extra');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+1 -1
View File
@@ -3,7 +3,7 @@
require('app-module-path').addPath(__dirname);
const { asyncTest } = require('test-utils.js');
const markdownUtils = require('lib/markdownUtils.js');
const markdownUtils = require('lib/markdownUtils').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+1 -1
View File
@@ -9,7 +9,7 @@ const Note = require('lib/models/Note.js');
const BaseItem = require('lib/models/BaseItem.js');
const Resource = require('lib/models/Resource.js');
const BaseModel = require('lib/BaseModel.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+1 -1
View File
@@ -7,7 +7,7 @@ const { createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatab
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const BaseModel = require('lib/BaseModel.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+1 -1
View File
@@ -8,7 +8,7 @@ const SearchEngine = require('lib/services/searchengine/SearchEngine');
const ResourceService = require('lib/services/ResourceService');
const ItemChangeUtils = require('lib/services/ItemChangeUtils');
const Note = require('lib/models/Note');
const Setting = require('lib/models/Setting');
const Setting = require('lib/models/Setting').default;
const ItemChange = require('lib/models/ItemChange');
process.on('unhandledRejection', (reason, p) => {
+2 -2
View File
@@ -6,10 +6,10 @@ const { time } = require('lib/time-utils.js');
const { sortedIds, createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const BaseModel = require('lib/BaseModel.js');
const ArrayUtils = require('lib/ArrayUtils.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
@@ -6,10 +6,10 @@ const { time } = require('lib/time-utils.js');
const { sortedIds, createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const BaseModel = require('lib/BaseModel.js');
const ArrayUtils = require('lib/ArrayUtils.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+1 -1
View File
@@ -8,7 +8,7 @@ const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Resource = require('lib/models/Resource.js');
const BaseModel = require('lib/BaseModel.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+1 -1
View File
@@ -10,7 +10,7 @@ const NoteTag = require('lib/models/NoteTag.js');
const Tag = require('lib/models/Tag.js');
const Revision = require('lib/models/Revision.js');
const BaseModel = require('lib/BaseModel.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
-37
View File
@@ -1,37 +0,0 @@
/* eslint-disable no-unused-vars */
require('app-module-path').addPath(__dirname);
const { time } = require('lib/time-utils.js');
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const { shim } = require('lib/shim');
const Setting = require('lib/models/Setting.js');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
describe('models_Setting', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should return only sub-values', asyncTest(async () => {
const settings = {
'sync.5.path': 'http://example.com',
'sync.5.username': 'testing',
};
let output = Setting.subValues('sync.5', settings);
expect(output['path']).toBe('http://example.com');
expect(output['username']).toBe('testing');
output = Setting.subValues('sync.4', settings);
expect('path' in output).toBe(false);
expect('username' in output).toBe(false);
}));
});
+135
View File
@@ -0,0 +1,135 @@
import Setting from 'lib/models/Setting';
require('app-module-path').addPath(__dirname);
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('test-utils.js');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
describe('models_Setting', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should return only sub-values', asyncTest(async () => {
const settings = {
'sync.5.path': 'http://example.com',
'sync.5.username': 'testing',
};
let output = Setting.subValues('sync.5', settings);
expect(output['path']).toBe('http://example.com');
expect(output['username']).toBe('testing');
output = Setting.subValues('sync.4', settings);
expect('path' in output).toBe(false);
expect('username' in output).toBe(false);
}));
it('should allow registering new settings dynamically', asyncTest(async () => {
await expectThrow(async () => Setting.setValue('myCustom', '123'));
await Setting.registerSetting('myCustom', {
public: true,
value: 'default',
type: Setting.TYPE_STRING,
});
await expectNotThrow(async () => Setting.setValue('myCustom', '123'));
expect(Setting.value('myCustom')).toBe('123');
}));
it('should not clear old custom settings', asyncTest(async () => {
// In general the following should work:
//
// - Plugin register a new setting
// - User set new value for setting
// - Settings are saved
// - => App restart
// - Plugin does not register setting again
// - Settings are loaded
// - Settings are saved
// - Plugin register setting again
// - Previous value set by user should still be there.
//
// In other words, once a custom setting has been set we don't clear it
// even if registration doesn't happen immediately. That allows for example
// to delay setting registration without a risk for the custom setting to be deleted.
await Setting.registerSetting('myCustom', {
public: true,
value: 'default',
type: Setting.TYPE_STRING,
});
Setting.setValue('myCustom', '123');
await Setting.saveAll();
await Setting.reset();
await Setting.load();
await Setting.registerSetting('myCustom', {
public: true,
value: 'default',
type: Setting.TYPE_STRING,
});
await Setting.saveAll();
expect(Setting.value('myCustom')).toBe('123');
}));
it('should return values with correct type for custom settings', asyncTest(async () => {
await Setting.registerSetting('myCustom', {
public: true,
value: 123,
type: Setting.TYPE_INT,
});
Setting.setValue('myCustom', 456);
await Setting.saveAll();
await Setting.reset();
await Setting.load();
await Setting.registerSetting('myCustom', {
public: true,
value: 123,
type: Setting.TYPE_INT,
});
expect(typeof Setting.value('myCustom')).toBe('number');
expect(Setting.value('myCustom')).toBe(456);
}));
it('should validate registered keys', asyncTest(async () => {
const md = {
public: true,
value: 'default',
type: Setting.TYPE_STRING,
};
await expectThrow(async () => await Setting.registerSetting('', md));
await expectThrow(async () => await Setting.registerSetting('no spaces', md));
await expectThrow(async () => await Setting.registerSetting('nospecialcharacters!!!', md));
await expectThrow(async () => await Setting.registerSetting('Robert\'); DROP TABLE Students;--', md));
await expectNotThrow(async () => await Setting.registerSetting('numbersareok123', md));
await expectNotThrow(async () => await Setting.registerSetting('so-ARE-dashes_123', md));
}));
it('should register new sections', asyncTest(async () => {
await Setting.registerSection('mySection', {
label: 'My section',
});
expect(Setting.sectionNameToLabel('mySection')).toBe('My section');
}));
});
+1 -1
View File
@@ -9,7 +9,7 @@ const Note = require('lib/models/Note.js');
const NoteTag = require('lib/models/NoteTag.js');
const Tag = require('lib/models/Tag.js');
const BaseModel = require('lib/BaseModel.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+3 -2
View File
@@ -5,7 +5,8 @@ const { setupDatabaseAndSynchronizer, switchClient, asyncTest, createNTestNotes,
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
const { reducer, defaultState, stateUtils, MAX_HISTORY } = require('lib/reducer.js');
const reducer = require('lib/reducer').default;
const { defaultState, stateUtils, MAX_HISTORY } = require('lib/reducer');
function initTestState(folders, selectedFolderIndex, notes, selectedNoteIndexes, tags = null, selectedTagIndex = null) {
let state = defaultState;
@@ -87,7 +88,7 @@ function getIds(items, indexes = null) {
let insideBeforeEach = false;
describe('Reducer', function() {
describe('reducer', function() {
beforeEach(async (done) => {
insideBeforeEach = true;
@@ -0,0 +1,58 @@
import sandboxProxy, { Target } from 'lib/services/plugins/sandboxProxy';
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
describe('services_plugins_sandboxProxy', function() {
beforeEach(async (done:Function) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should create a new sandbox proxy', asyncTest(async () => {
interface Result {
path: string,
args: any[],
}
const results:Result[] = [];
const target:Target = (path:string, args:any[]) => {
results.push({ path, args });
};
const proxy = sandboxProxy(target);
proxy.testing.bla.test('hello', '123');
proxy.testing.test2();
expect(results[0].path).toBe('testing.bla.test');
expect(results[0].args.join('_')).toBe('hello_123');
expect(results[1].path).toBe('testing.test2');
expect(results[1].args.join('_')).toBe('');
}));
it('should allow importing a namespace', asyncTest(async () => {
interface Result {
path: string,
args: any[],
}
const results:Result[] = [];
const target:Target = (path:string, args:any[]) => {
results.push({ path, args });
};
const proxy = sandboxProxy(target);
const ns = proxy.testing.blabla;
ns.test();
ns.test2();
expect(results[0].path).toBe('testing.blabla.test');
expect(results[1].path).toBe('testing.blabla.test2');
}));
});
+278
View File
@@ -0,0 +1,278 @@
import MenuUtils from 'lib/services/commands/MenuUtils';
import ToolbarButtonUtils from 'lib/services/commands/ToolbarButtonUtils';
import CommandService, { CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
interface TestCommand {
declaration: CommandDeclaration,
runtime: CommandRuntime,
}
function newService():CommandService {
const service = new CommandService();
service.initialize({});
return service;
}
function createCommand(name:string, options:any):TestCommand {
const declaration:CommandDeclaration = {
name: name,
};
const runtime:CommandRuntime = {
execute: options.execute,
};
if (options.mapStateToProps) runtime.mapStateToProps = options.mapStateToProps;
if (options.isEnabled) runtime.isEnabled = options.isEnabled;
return { declaration, runtime };
}
function registerCommand(service:CommandService, cmd:TestCommand) {
service.registerDeclaration(cmd.declaration);
service.registerRuntime(cmd.declaration.name, cmd.runtime);
}
describe('services_CommandService', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should create toolbar button infos from commands', asyncTest(async () => {
const service = newService();
const toolbarButtonUtils = new ToolbarButtonUtils(service);
const executedCommands:string[] = [];
registerCommand(service, createCommand('test1', {
execute: () => {
executedCommands.push('test1');
},
}));
registerCommand(service, createCommand('test2', {
execute: () => {
executedCommands.push('test2');
},
}));
const toolbarInfos = toolbarButtonUtils.commandsToToolbarButtons({}, ['test1', 'test2']);
await toolbarInfos[0].onClick();
await toolbarInfos[1].onClick();
expect(executedCommands.join('_')).toBe('test1_test2');
expect(toolbarInfos[0].enabled).toBe(true);
expect(toolbarInfos[1].enabled).toBe(true);
}));
it('should enable and disable toolbar buttons depending on state', asyncTest(async () => {
const service = newService();
const toolbarButtonUtils = new ToolbarButtonUtils(service);
registerCommand(service, createCommand('test1', {
execute: () => {},
mapStateToProps: (state:any) => {
return {
selectedNoteId: state.selectedNoteId,
selectedFolderId: state.selectedFolderId,
};
},
isEnabled: (props:any) => {
return props.selectedNoteId === 'abc';
},
}));
registerCommand(service, createCommand('test2', {
execute: () => {},
mapStateToProps: (state:any) => {
return {
selectedNoteId: state.selectedNoteId,
selectedFolderId: state.selectedFolderId,
};
},
isEnabled: (props:any) => {
return props.selectedNoteId === '123';
},
}));
const toolbarInfos = toolbarButtonUtils.commandsToToolbarButtons({
selectedNoteId: '123',
selectedFolderId: 'aaa',
}, ['test1', 'test2']);
expect(toolbarInfos[0].enabled).toBe(false);
expect(toolbarInfos[1].enabled).toBe(true);
}));
it('should return the same toolbarButtons array if nothing has changed', asyncTest(async () => {
const service = newService();
const toolbarButtonUtils = new ToolbarButtonUtils(service);
registerCommand(service, createCommand('test1', {
execute: () => {},
mapStateToProps: (state:any) => {
return {
selectedNoteId: state.selectedNoteId,
};
},
isEnabled: (props:any) => {
return props.selectedNoteId === 'ok';
},
}));
registerCommand(service, createCommand('test2', {
execute: () => {},
mapStateToProps: (state:any) => {
return {
selectedFolderId: state.selectedFolderId,
};
},
isEnabled: (props:any) => {
return props.selectedFolderId === 'ok';
},
}));
const toolbarInfos1 = toolbarButtonUtils.commandsToToolbarButtons({
selectedNoteId: 'ok',
selectedFolderId: 'notok',
}, ['test1', 'test2']);
const toolbarInfos2 = toolbarButtonUtils.commandsToToolbarButtons({
selectedNoteId: 'ok',
selectedFolderId: 'notok',
}, ['test1', 'test2']);
expect(toolbarInfos1).toBe(toolbarInfos2);
expect(toolbarInfos1[0] === toolbarInfos2[0]).toBe(true);
expect(toolbarInfos1[1] === toolbarInfos2[1]).toBe(true);
const toolbarInfos3 = toolbarButtonUtils.commandsToToolbarButtons({
selectedNoteId: 'ok',
selectedFolderId: 'ok',
}, ['test1', 'test2']);
expect(toolbarInfos2 === toolbarInfos3).toBe(false);
expect(toolbarInfos2[0] === toolbarInfos3[0]).toBe(true);
expect(toolbarInfos2[1] === toolbarInfos3[1]).toBe(false);
{
expect(toolbarButtonUtils.commandsToToolbarButtons({
selectedNoteId: 'ok',
selectedFolderId: 'notok',
}, ['test1', '-', 'test2'])).toBe(toolbarButtonUtils.commandsToToolbarButtons({
selectedNoteId: 'ok',
selectedFolderId: 'notok',
}, ['test1', '-', 'test2']));
}
}));
it('should create menu items from commands', asyncTest(async () => {
const service = newService();
const utils = new MenuUtils(service);
registerCommand(service, createCommand('test1', {
execute: () => {},
}));
registerCommand(service, createCommand('test2', {
execute: () => {},
}));
const clickedCommands:string[] = [];
const onClick = (commandName:string) => {
clickedCommands.push(commandName);
};
const menuItems = utils.commandsToMenuItems(['test1', 'test2'], onClick);
menuItems.test1.click();
menuItems.test2.click();
expect(clickedCommands.join('_')).toBe('test1_test2');
// Also check that the same commands always return strictly the same menu
expect(utils.commandsToMenuItems(['test1', 'test2'], onClick)).toBe(utils.commandsToMenuItems(['test1', 'test2'], onClick));
}));
it('should give menu item props from state', asyncTest(async () => {
const service = newService();
const utils = new MenuUtils(service);
registerCommand(service, createCommand('test1', {
mapStateToProps: (state:any) => {
return {
isOk: state.test1 === 'ok',
};
},
execute: () => {},
}));
registerCommand(service, createCommand('test2', {
mapStateToProps: (state:any) => {
return {
isOk: state.test2 === 'ok',
};
},
execute: () => {},
}));
{
const menuItemProps = utils.commandsToMenuItemProps({
test1: 'ok',
test2: 'notok',
}, ['test1', 'test2']);
expect(menuItemProps.test1.isOk).toBe(true);
expect(menuItemProps.test2.isOk).toBe(false);
}
{
const menuItemProps = utils.commandsToMenuItemProps({
test1: 'ok',
test2: 'ok',
}, ['test1', 'test2']);
expect(menuItemProps.test1.isOk).toBe(true);
expect(menuItemProps.test2.isOk).toBe(true);
}
expect(utils.commandsToMenuItemProps({
test1: 'ok',
test2: 'ok',
}, ['test1', 'test2'])).toBe(utils.commandsToMenuItemProps({
test1: 'ok',
test2: 'ok',
}, ['test1', 'test2']));
}));
it('should create stateful menu items', asyncTest(async () => {
const service = newService();
const utils = new MenuUtils(service);
let propValue = null;
registerCommand(service, createCommand('test1', {
mapStateToProps: (state:any) => {
return {
isOk: state.test1 === 'ok',
};
},
execute: (props:any) => {
propValue = props.isOk;
},
}));
const menuItem = utils.commandToStatefulMenuItem('test1', { isOk: 'hello' });
menuItem.click();
expect(propValue).toBe('hello');
}));
});
+1 -32
View File
@@ -8,7 +8,7 @@ const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
const { Database } = require('lib/database.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const BaseItem = require('lib/models/BaseItem.js');
const BaseModel = require('lib/BaseModel.js');
const MasterKey = require('lib/models/MasterKey');
@@ -289,35 +289,4 @@ describe('services_EncryptionService', function() {
const plainText = await service.decryptString(cipherText);
expect(plainText).toBe('🐶🐶🐶'.substr(0,5));
}));
// it('should upgrade master key encryption mode', asyncTest(async () => {
// let masterKey = await service.generateMasterKey('123456', {
// encryptionMethod: EncryptionService.METHOD_SJCL_2,
// });
// masterKey = await MasterKey.save(masterKey);
// Setting.setObjectKey('encryption.passwordCache', masterKey.id, '123456');
// Setting.setValue('encryption.activeMasterKeyId', masterKey.id);
// await sleep(0.01);
// await service.loadMasterKeysFromSettings();
// masterKeyNew = await MasterKey.load(masterKey.id);
// // Check that the master key has been upgraded
// expect(masterKeyNew.created_time).toBe(masterKey.created_time);
// expect(masterKeyNew.updated_time === masterKey.updated_time).toBe(false);
// expect(masterKeyNew.content === masterKey.content).toBe(false);
// expect(masterKeyNew.encryption_method === masterKey.encryption_method).toBe(false);
// expect(masterKeyNew.checksum === masterKey.checksum).toBe(false);
// expect(masterKeyNew.encryption_method).toBe(service.defaultMasterKeyEncryptionMethod_);
// // Check that encryption still works
// const cipherText = await service.encryptString('some secret');
// const plainText = await service.decryptString(cipherText);
// expect(plainText).toBe('some secret');
// }));
});
@@ -1,19 +1,16 @@
/* eslint-disable no-unused-vars */
import InteropService from 'lib/services/interop/InteropService';
import { CustomExportContext, CustomImportContext, Module, ModuleType } from 'lib/services/interop/types';
import shim from 'lib/shim';
require('app-module-path').addPath(__dirname);
const { time } = require('lib/time-utils.js');
const { asyncTest, fileContentEqual, expectNotThrow, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const InteropService = require('lib/services/InteropService.js');
const { asyncTest, fileContentEqual, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('test-utils.js');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
const NoteTag = require('lib/models/NoteTag.js');
const Resource = require('lib/models/Resource.js');
const fs = require('fs-extra');
const ArrayUtils = require('lib/ArrayUtils');
const ObjectUtils = require('lib/ObjectUtils');
const { shim } = require('lib/shim.js');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
@@ -23,7 +20,7 @@ function exportDir() {
return `${__dirname}/export`;
}
function fieldsEqual(model1, model2, fieldNames) {
function fieldsEqual(model1:any, model2:any, fieldNames:string[]) {
for (let i = 0; i < fieldNames.length; i++) {
const f = fieldNames[i];
expect(model1[f]).toBe(model2[f], `For key ${f}`);
@@ -43,7 +40,7 @@ describe('services_InteropService', function() {
});
it('should export and import folders', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
let folder1 = await Folder.save({ title: 'folder1' });
folder1 = await Folder.load(folder1.id);
const filePath = `${exportDir()}/test.jex`;
@@ -78,7 +75,7 @@ describe('services_InteropService', function() {
}));
it('should import folders and de-duplicate titles when needed', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const folder1 = await Folder.save({ title: 'folder' });
const folder2 = await Folder.save({ title: 'folder' });
const filePath = `${exportDir()}/test.jex`;
@@ -90,15 +87,15 @@ describe('services_InteropService', function() {
await service.import({ path: filePath });
const allFolders = await Folder.all();
expect(allFolders.map(f => f.title).sort().join(' - ')).toBe('folder - folder (1)');
expect(allFolders.map((f:any) => f.title).sort().join(' - ')).toBe('folder - folder (1)');
}));
it('should import folders, and only de-duplicate titles when needed', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const folder1 = await Folder.save({ title: 'folder1' });
const folder2 = await Folder.save({ title: 'folder2' });
const sub1 = await Folder.save({ title: 'Sub', parent_id: folder1.id });
const sub2 = await Folder.save({ title: 'Sub', parent_id: folder2.id });
await Folder.save({ title: 'Sub', parent_id: folder1.id });
await Folder.save({ title: 'Sub', parent_id: folder2.id });
const filePath = `${exportDir()}/test.jex`;
await service.export({ path: filePath });
@@ -117,7 +114,7 @@ describe('services_InteropService', function() {
}));
it('should export and import folders and notes', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
note1 = await Note.load(note1.id);
@@ -156,7 +153,7 @@ describe('services_InteropService', function() {
}));
it('should export and import notes to specific folder', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
note1 = await Note.load(note1.id);
@@ -175,7 +172,7 @@ describe('services_InteropService', function() {
}));
it('should export and import tags', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const filePath = `${exportDir()}/test.jex`;
const folder1 = await Folder.save({ title: 'folder1' });
const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
@@ -215,7 +212,7 @@ describe('services_InteropService', function() {
}));
it('should export and import resources', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const filePath = `${exportDir()}/test.jex`;
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
@@ -251,7 +248,7 @@ describe('services_InteropService', function() {
}));
it('should export and import single notes', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const filePath = `${exportDir()}/test.jex`;
const folder1 = await Folder.save({ title: 'folder1' });
const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
@@ -271,7 +268,7 @@ describe('services_InteropService', function() {
}));
it('should export and import single folders', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const filePath = `${exportDir()}/test.jex`;
const folder1 = await Folder.save({ title: 'folder1' });
const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
@@ -292,7 +289,7 @@ describe('services_InteropService', function() {
it('should export and import folder and its sub-folders', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const filePath = `${exportDir()}/test.jex`;
const folder1 = await Folder.save({ title: 'folder1' });
const folder2 = await Folder.save({ title: 'folder2', parent_id: folder1.id });
@@ -326,7 +323,7 @@ describe('services_InteropService', function() {
}));
it('should export and import links to notes', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const filePath = `${exportDir()}/test.jex`;
const folder1 = await Folder.save({ title: 'folder1' });
const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
@@ -349,43 +346,21 @@ describe('services_InteropService', function() {
expect(note2_2.body.indexOf(note1_2.id) >= 0).toBe(true);
}));
it('should export into json format', asyncTest(async () => {
const service = new InteropService();
const folder1 = await Folder.save({ title: 'folder1' });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
note1 = await Note.load(note1.id);
const filePath = exportDir();
await service.export({ path: filePath, format: 'json' });
// verify that the json files exist and can be parsed
const items = [folder1, note1];
for (let i = 0; i < items.length; i++) {
const jsonFile = `${filePath}/${items[i].id}.json`;
const json = await fs.readFile(jsonFile, 'utf-8');
const obj = JSON.parse(json);
expect(obj.id).toBe(items[i].id);
expect(obj.type_).toBe(items[i].type_);
expect(obj.title).toBe(items[i].title);
expect(obj.body).toBe(items[i].body);
}
}));
it('should export selected notes in md format', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const folder1 = await Folder.save({ title: 'folder1' });
let note11 = await Note.save({ title: 'title note11', parent_id: folder1.id });
note11 = await Note.load(note11.id);
let note12 = await Note.save({ title: 'title note12', parent_id: folder1.id });
note12 = await Note.load(note12.id);
const note12 = await Note.save({ title: 'title note12', parent_id: folder1.id });
await Note.load(note12.id);
let folder2 = await Folder.save({ title: 'folder2', parent_id: folder1.id });
folder2 = await Folder.load(folder2.id);
let note21 = await Note.save({ title: 'title note21', parent_id: folder2.id });
note21 = await Note.load(note21.id);
let folder3 = await Folder.save({ title: 'folder3', parent_id: folder1.id });
folder3 = await Folder.load(folder2.id);
await Folder.save({ title: 'folder3', parent_id: folder1.id });
await Folder.load(folder2.id);
const outDir = exportDir();
@@ -401,16 +376,16 @@ describe('services_InteropService', function() {
}));
it('should export MD with unicode filenames', asyncTest(async () => {
const service = new InteropService();
const service = InteropService.instance();
const folder1 = await Folder.save({ title: 'folder1' });
const folder2 = await Folder.save({ title: 'ジョプリン' });
const note1 = await Note.save({ title: '生活', parent_id: folder1.id });
const note2 = await Note.save({ title: '生活', parent_id: folder1.id });
const note2b = await Note.save({ title: '生活', parent_id: folder1.id });
const note3 = await Note.save({ title: '', parent_id: folder1.id });
const note4 = await Note.save({ title: '', parent_id: folder1.id });
const note5 = await Note.save({ title: 'salut, ça roule ?', parent_id: folder1.id });
const note6 = await Note.save({ title: 'ジョプリン', parent_id: folder2.id });
await Note.save({ title: '生活', parent_id: folder1.id });
await Note.save({ title: '生活', parent_id: folder1.id });
await Note.save({ title: '生活', parent_id: folder1.id });
await Note.save({ title: '', parent_id: folder1.id });
await Note.save({ title: '', parent_id: folder1.id });
await Note.save({ title: 'salut, ça roule ?', parent_id: folder1.id });
await Note.save({ title: 'ジョプリン', parent_id: folder2.id });
const outDir = exportDir();
@@ -430,7 +405,7 @@ describe('services_InteropService', function() {
await Note.save({ title: 'textexportnote1', parent_id: folder1.id });
await Note.save({ title: 'textexportnote2', parent_id: folder1.id });
const service = new InteropService();
const service = InteropService.instance();
await service.export({
path: exportDir(),
@@ -454,7 +429,7 @@ describe('services_InteropService', function() {
await Folder.save({ title: 'orphan', parent_id: '0c5bbd8a1b5a48189484a412a7e534cc' });
const service = new InteropService();
const service = InteropService.instance();
const result = await service.export({
path: exportDir(),
@@ -464,4 +439,99 @@ describe('services_InteropService', function() {
expect(result.warnings.length).toBe(0);
}));
it('should allow registering new import modules', asyncTest(async () => {
const testImportFilePath = `${exportDir()}/testImport${Math.random()}.test`;
await shim.fsDriver().writeFile(testImportFilePath, 'test', 'utf8');
const result = {
hasBeenExecuted: false,
sourcePath: '',
};
const module:Module = {
type: ModuleType.Importer,
description: 'Test Import Module',
format: 'testing',
fileExtensions: ['test'],
isCustom: true,
onExec: async (context:CustomImportContext) => {
result.hasBeenExecuted = true;
result.sourcePath = context.sourcePath;
},
};
const service = InteropService.instance();
service.registerModule(module);
await service.import({
format: 'testing',
path: testImportFilePath,
});
expect(result.hasBeenExecuted).toBe(true);
expect(result.sourcePath).toBe(testImportFilePath);
}));
it('should allow registering new export modules', asyncTest(async () => {
const folder1 = await Folder.save({ title: 'folder1' });
const note1 = await Note.save({ title: 'note1', parent_id: folder1.id });
await Note.save({ title: 'note2', parent_id: folder1.id });
await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
const filePath = `${exportDir()}/example.test`;
const result:any = {
destPath: '',
itemTypes: [],
items: [],
resources: [],
filePaths: [],
closeCalled: false,
};
const module:Module = {
type: ModuleType.Exporter,
description: 'Test Export Module',
format: 'testing',
fileExtensions: ['test'],
isCustom: true,
onInit: async (context:CustomExportContext) => {
result.destPath = context.destPath;
},
onProcessItem: async (_context:CustomExportContext, itemType:number, item:any) => {
result.itemTypes.push(itemType);
result.items.push(item);
},
onProcessResource: async (_context:CustomExportContext, resource:any, filePath:string) => {
result.resources.push(resource);
result.filePaths.push(filePath);
},
onClose: async (_context:CustomExportContext) => {
result.closeCalled = true;
},
};
const service = InteropService.instance();
service.registerModule(module);
await service.export({
format: 'testing',
path: filePath,
});
expect(result.destPath).toBe(filePath);
expect(result.itemTypes.sort().join('_')).toBe('1_1_2_4');
expect(result.items.length).toBe(4);
expect(result.items.map((o:any) => o.title).sort().join('_')).toBe('folder1_note1_note2_photo.jpg');
expect(result.resources.length).toBe(1);
expect(result.resources[0].title).toBe('photo.jpg');
expect(result.filePaths.length).toBe(1);
expect(!!result.filePaths[0]).toBe(true);
expect(result.closeCalled).toBe(true);
}));
});
@@ -4,12 +4,12 @@ require('app-module-path').addPath(__dirname);
const fs = require('fs-extra');
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
const InteropService_Exporter_Md = require('lib/services/InteropService_Exporter_Md.js');
const InteropService_Exporter_Md = require('lib/services/interop/InteropService_Exporter_Md').default;
const BaseModel = require('lib/BaseModel.js');
const Folder = require('lib/models/Folder.js');
const Resource = require('lib/models/Resource.js');
const Note = require('lib/models/Note.js');
const { shim } = require('lib/shim.js');
const shim = require('lib/shim').default;
const exportDir = `${__dirname}/export`;
@@ -67,8 +67,8 @@ describe('services_InteropService_Exporter_Md', function() {
expect(!exporter.context() && !(exporter.context().notePaths || Object.keys(exporter.context().notePaths).length)).toBe(false, 'Context should be empty before processing.');
await exporter.processItem(Folder, folder1);
await exporter.processItem(Folder, folder2);
await exporter.processItem(Folder.modelType(), folder1);
await exporter.processItem(Folder.modelType(), folder2);
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
expect(Object.keys(exporter.context().notePaths).length).toBe(3, 'There should be 3 note paths in the context.');
@@ -96,7 +96,7 @@ describe('services_InteropService_Exporter_Md', function() {
queueExportItem(BaseModel.TYPE_NOTE, note1);
queueExportItem(BaseModel.TYPE_NOTE, note1_2);
await exporter.processItem(Folder, folder1);
await exporter.processItem(Folder.modelType(), folder1);
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
expect(Object.keys(exporter.context().notePaths).length).toBe(2, 'There should be 2 note paths in the context.');
@@ -121,7 +121,7 @@ describe('services_InteropService_Exporter_Md', function() {
queueExportItem(BaseModel.TYPE_FOLDER, folder1.id);
queueExportItem(BaseModel.TYPE_NOTE, note1);
await exporter.processItem(Folder, folder1);
await exporter.processItem(Folder.modelType(), folder1);
// Create a file with the path of note1 before processing note1
await shim.fsDriver().writeFile(`${exportDir}/folder1/note1.md`, 'Note content', 'utf-8');
@@ -189,10 +189,10 @@ describe('services_InteropService_Exporter_Md', function() {
const folder3 = await Folder.save({ title: 'folder3', parent_id: folder1.id });
queueExportItem(BaseModel.TYPE_FOLDER, folder3.id);
await exporter.processItem(Folder, folder2);
await exporter.processItem(Folder, folder3);
await exporter.processItem(Folder.modelType(), folder2);
await exporter.processItem(Folder.modelType(), folder3);
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
await exporter.processItem(Note, note2);
await exporter.processItem(Note.modelType(), note2);
expect(await shim.fsDriver().exists(`${exportDir}/folder1`)).toBe(true, 'Folder should be created in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir}/folder1/folder2`)).toBe(true, 'Folder should be created in filesystem.');
@@ -227,9 +227,9 @@ describe('services_InteropService_Exporter_Md', function() {
queueExportItem(BaseModel.TYPE_NOTE, note3);
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
await exporter.processItem(Note, note1);
await exporter.processItem(Note, note2);
await exporter.processItem(Note, note3);
await exporter.processItem(Note.modelType(), note1);
await exporter.processItem(Note.modelType(), note2);
await exporter.processItem(Note.modelType(), note3);
expect(await shim.fsDriver().exists(`${exportDir}/${exporter.context().notePaths[note1.id]}`)).toBe(true, 'File should be saved in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir}/${exporter.context().notePaths[note2.id]}`)).toBe(true, 'File should be saved in filesystem.');
@@ -262,8 +262,8 @@ describe('services_InteropService_Exporter_Md', function() {
queueExportItem(BaseModel.TYPE_NOTE, note2);
const resource2 = await Resource.load((await Note.linkedResourceIds(note2.body))[0]);
await exporter.processItem(Folder, folder1);
await exporter.processItem(Folder, folder2);
await exporter.processItem(Folder.modelType(), folder1);
await exporter.processItem(Folder.modelType(), folder2);
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
const context = {
resourcePaths: {},
@@ -271,8 +271,8 @@ describe('services_InteropService_Exporter_Md', function() {
context.resourcePaths[resource1.id] = 'resource1.jpg';
context.resourcePaths[resource2.id] = 'resource2.jpg';
exporter.updateContext(context);
await exporter.processItem(Note, note1);
await exporter.processItem(Note, note2);
await exporter.processItem(Note.modelType(), note1);
await exporter.processItem(Note.modelType(), note2);
const note1_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note1.id]}`);
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
@@ -315,13 +315,13 @@ describe('services_InteropService_Exporter_Md', function() {
queueExportItem(BaseModel.TYPE_NOTE, note2);
queueExportItem(BaseModel.TYPE_NOTE, note3);
await exporter.processItem(Folder, folder1);
await exporter.processItem(Folder, folder2);
await exporter.processItem(Folder, folder3);
await exporter.processItem(Folder.modelType(), folder1);
await exporter.processItem(Folder.modelType(), folder2);
await exporter.processItem(Folder.modelType(), folder3);
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
await exporter.processItem(Note, note1);
await exporter.processItem(Note, note2);
await exporter.processItem(Note, note3);
await exporter.processItem(Note.modelType(), note1);
await exporter.processItem(Note.modelType(), note2);
await exporter.processItem(Note.modelType(), note3);
const note1_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note1.id]}`);
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
@@ -351,10 +351,10 @@ describe('services_InteropService_Exporter_Md', function() {
queueExportItem(BaseModel.TYPE_NOTE, note1);
queueExportItem(BaseModel.TYPE_NOTE, note2);
await exporter.processItem(Folder, folder1);
await exporter.processItem(Folder.modelType(), folder1);
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
await exporter.processItem(Note, note1);
await exporter.processItem(Note, note2);
await exporter.processItem(Note.modelType(), note1);
await exporter.processItem(Note.modelType(), note2);
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
expect(note2_body).toContain('[link](../folder%20with%20space1/note1%20name%20with%20space.md)', 'Whitespace in URL should be encoded');
+34 -8
View File
@@ -1,5 +1,6 @@
require('app-module-path').addPath(__dirname);
const { tempFilePath } = require('test-utils.js');
const KeymapService = require('lib/services/KeymapService').default;
const keymapService = KeymapService.instance();
@@ -76,6 +77,31 @@ describe('services_KeymapService', () => {
});
});
describe('registerCommandAccelerator', () => {
beforeEach(() => keymapService.initialize());
it('should allow registering new commands', async () => {
keymapService.initialize('linux');
keymapService.registerCommandAccelerator('myCustomCommand', 'Ctrl+Shift+Alt+B');
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Ctrl+Shift+Alt+B');
// Check that macOS key conversion is working
keymapService.initialize('darwin');
keymapService.registerCommandAccelerator('myCustomCommand', 'CmdOrCtrl+Shift+Alt+B');
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+B');
keymapService.setAccelerator('myCustomCommand', 'Cmd+Shift+Option+X');
// Check that the new custom shortcut is being saved and loaded
const keymapFilePath = tempFilePath('json');
await keymapService.saveCustomKeymap(keymapFilePath);
keymapService.initialize('darwin');
await keymapService.loadCustomKeymap(keymapFilePath);
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+X');
});
});
describe('getAccelerator', () => {
beforeEach(() => keymapService.initialize());
@@ -254,15 +280,15 @@ describe('services_KeymapService', () => {
expect(() => keymapService.overrideKeymap(customKeymapItems)).toThrow();
});
it('should throw when the provided commands are invalid', () => {
const customKeymapItems = [
{ command: 'totallyInvalidCommand', accelerator: 'Ctrl+Shift+G' },
{ command: 'print', accelerator: 'Alt+P' },
{ command: 'focusElementNoteTitle', accelerator: 'Ctrl+Alt+Shift+J' },
];
// it('should throw when the provided commands are invalid', () => {
// const customKeymapItems = [
// { command: 'totallyInvalidCommand', accelerator: 'Ctrl+Shift+G' },
// { command: 'print', accelerator: 'Alt+P' },
// { command: 'focusElementNoteTitle', accelerator: 'Ctrl+Alt+Shift+J' },
// ];
expect(() => keymapService.overrideKeymap(customKeymapItems)).toThrow();
});
// expect(() => keymapService.overrideKeymap(customKeymapItems)).toThrow();
// });
it('should throw when duplicate accelerators are provided', () => {
const customKeymaps_Darwin = [
+81
View File
@@ -0,0 +1,81 @@
import PluginRunner from '../app/services/plugins/PluginRunner';
import PluginService from 'lib/services/plugins/PluginService';
require('app-module-path').addPath(__dirname);
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
const Note = require('lib/models/Note');
const Folder = require('lib/models/Folder');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
const testPluginDir = `${__dirname}/../tests/support/plugins`;
function newPluginService() {
const runner = new PluginRunner();
const service = new PluginService();
service.initialize(
{
joplin: {
workspace: {},
},
},
runner,
{
dispatch: () => {},
getState: () => {},
}
);
return service;
}
describe('services_PluginService', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should load and run a simple plugin', asyncTest(async () => {
const service = newPluginService();
const plugin = await service.loadPlugin(`${testPluginDir}/simple`);
await service.runPlugin(plugin);
const allFolders = await Folder.all();
expect(allFolders.length).toBe(1);
expect(allFolders[0].title).toBe('my plugin folder');
const allNotes = await Note.all();
expect(allNotes.length).toBe(1);
expect(allNotes[0].title).toBe('testing plugin!');
expect(allNotes[0].parent_id).toBe(allFolders[0].id);
}));
it('should load and run a plugin that uses external packages', asyncTest(async () => {
const service = newPluginService();
const plugin = await service.loadPlugin(`${testPluginDir}/withExternalModules`);
expect(plugin.id).toBe('withexternalmodules');
await service.runPlugin(plugin);
const allFolders = await Folder.all();
expect(allFolders.length).toBe(1);
expect(allFolders[0].title).toBe(' foo');
}));
it('should load multiple plugins from a directory', asyncTest(async () => {
const service = newPluginService();
await service.loadAndRunPlugins(`${testPluginDir}/multi_plugins`);
const plugin1 = service.pluginById('simple1');
const plugin2 = service.pluginById('simple2');
expect(!!plugin1).toBe(true);
expect(!!plugin2).toBe(true);
const allFolders = await Folder.all();
expect(allFolders.length).toBe(2);
expect(allFolders.map((f:any) => f.title).sort().join(', ')).toBe('multi - simple1, multi - simple2');
}));
});
+2 -2
View File
@@ -4,7 +4,7 @@ require('app-module-path').addPath(__dirname);
const { time } = require('lib/time-utils.js');
const { asyncTest, resourceService, decryptionWorker, encryptionService, loadEncryptionMasterKey, allSyncTargetItemsEncrypted, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const InteropService = require('lib/services/InteropService.js');
const InteropService = require('lib/services/interop/InteropService').default;
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
@@ -16,7 +16,7 @@ const ResourceService = require('lib/services/ResourceService.js');
const fs = require('fs-extra');
const ArrayUtils = require('lib/ArrayUtils');
const ObjectUtils = require('lib/ObjectUtils');
const { shim } = require('lib/shim.js');
const shim = require('lib/shim').default;
const SearchEngine = require('lib/services/searchengine/SearchEngine');
process.on('unhandledRejection', (reason, p) => {
+2 -2
View File
@@ -5,7 +5,7 @@ require('app-module-path').addPath(__dirname);
const { time } = require('lib/time-utils.js');
const { asyncTest, fileContentEqual, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const Folder = require('lib/models/Folder.js');
const Setting = require('lib/models/Setting.js');
const Setting = require('lib/models/Setting').default;
const Note = require('lib/models/Note.js');
const NoteTag = require('lib/models/NoteTag.js');
const ItemChange = require('lib/models/ItemChange.js');
@@ -13,7 +13,7 @@ const Tag = require('lib/models/Tag.js');
const Revision = require('lib/models/Revision.js');
const BaseModel = require('lib/BaseModel.js');
const RevisionService = require('lib/services/RevisionService.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+1 -1
View File
@@ -8,7 +8,7 @@ const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, asyncTest
const SearchEngine = require('lib/services/searchengine/SearchEngine');
const Note = require('lib/models/Note');
const ItemChange = require('lib/models/ItemChange');
const Setting = require('lib/models/Setting');
const Setting = require('lib/models/Setting').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+2 -2
View File
@@ -10,9 +10,9 @@ const Note = require('lib/models/Note');
const Folder = require('lib/models/Folder');
const Tag = require('lib/models/Tag');
const ItemChange = require('lib/models/ItemChange');
const Setting = require('lib/models/Setting');
const Setting = require('lib/models/Setting').default;
const Resource = require('lib/models/Resource.js');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
const ResourceService = require('lib/services/ResourceService.js');
+1 -2
View File
@@ -12,10 +12,9 @@
// const ItemChange = require('lib/models/ItemChange');
// const Setting = require('lib/models/Setting');
// const Resource = require('lib/models/Resource.js');
// const { shim } = require('lib/shim');
// const shim = require('lib/shim').default;
// const ResourceService = require('lib/services/ResourceService.js');
// process.on('unhandledRejection', (reason, p) => {
// console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
// });
@@ -1,84 +0,0 @@
// /* eslint-disable no-unused-vars */
// require('app-module-path').addPath(__dirname);
// const { asyncTest, fileContentEqual, setupDatabase, checkThrow, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
// const KvStore = require('lib/services/KvStore.js');
// const UndoRedoService = require('lib/services/UndoRedoService.js').default;
// process.on('unhandledRejection', (reason, p) => {
// console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
// });
// describe('services_UndoRedoService', function() {
// beforeEach(async (done) => {
// await setupDatabaseAndSynchronizer(1);
// await switchClient(1);
// done();
// });
// it('should undo and redo', asyncTest(async () => {
// const service = new UndoRedoService();
// expect(service.canUndo).toBe(false);
// expect(service.canRedo).toBe(false);
// service.push('test');
// expect(service.canUndo).toBe(true);
// expect(service.canRedo).toBe(false);
// service.push('test 2');
// service.push('test 3');
// expect(service.undo()).toBe('test 3');
// expect(service.canRedo).toBe(true);
// expect(service.undo()).toBe('test 2');
// expect(service.undo()).toBe('test');
// expect(checkThrow(() => service.undo())).toBe(true);
// expect(service.canUndo).toBe(false);
// expect(service.canRedo).toBe(true);
// expect(service.redo()).toBe('test');
// expect(service.canUndo).toBe(true);
// expect(service.redo()).toBe('test 2');
// expect(service.redo()).toBe('test 3');
// expect(service.canRedo).toBe(false);
// expect(checkThrow(() => service.redo())).toBe(true);
// }));
// it('should clear the redo stack when undoing', asyncTest(async () => {
// const service = new UndoRedoService();
// service.push('test');
// service.push('test 2');
// service.push('test 3');
// service.undo();
// expect(service.canRedo).toBe(true);
// service.push('test 4');
// expect(service.canRedo).toBe(false);
// expect(service.undo()).toBe('test 4');
// expect(service.undo()).toBe('test 2');
// }));
// it('should limit the size of the undo stack', asyncTest(async () => {
// const service = new UndoRedoService();
// for (let i = 0; i < 30; i++) {
// service.push(`test${i}`);
// }
// for (let i = 0; i < 20; i++) {
// service.undo();
// }
// expect(service.canUndo).toBe(false);
// }));
// });
+3 -3
View File
@@ -3,8 +3,8 @@
require('app-module-path').addPath(__dirname);
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
const { shim } = require('lib/shim');
const Setting = require('lib/models/Setting');
const shim = require('lib/shim').default;
const Setting = require('lib/models/Setting').default;
const KeychainService = require('lib/services/keychain/KeychainService').default;
process.on('unhandledRejection', (reason, p) => {
@@ -50,7 +50,7 @@ describeIfCompatible('services_KeychainService', function() {
}));
it('should save and load secure settings', asyncTest(async () => {
Setting.setObjectKey('encryption.passwordCache', 'testing', '123456');
Setting.setObjectValue('encryption.passwordCache', 'testing', '123456');
await Setting.saveAll();
await Setting.load();
const passwords = Setting.value('encryption.passwordCache');
+2 -2
View File
@@ -3,13 +3,13 @@
require('app-module-path').addPath(__dirname);
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('test-utils.js');
const Api = require('lib/services/rest/Api');
const Api = require('lib/services/rest/Api').default;
const Folder = require('lib/models/Folder');
const Resource = require('lib/models/Resource');
const Note = require('lib/models/Note');
const Tag = require('lib/models/Tag');
const NoteTag = require('lib/models/NoteTag');
const { shim } = require('lib/shim');
const shim = require('lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+1
View File
@@ -2,6 +2,7 @@
"spec_dir": "tests-build",
"spec_files": [
"*.js",
"services/plugins/*.js",
"!test-utils.js"
],
"stopSpecOnExpectationFailure": false,
@@ -0,0 +1 @@
_temp
@@ -0,0 +1,2 @@
dist/*
node_modules/
@@ -0,0 +1,14 @@
# Joplin Plugin
This is a template to create a new Joplin plugin.
The main two files you will want to look at are:
- `/src/index.ts`, which contains the entry point for the plugin source code.
- `/src/manifest.json`, which is the plugin manifest. It contains information such as the plugin a name, version, etc.
The plugin is built using webpack, which create the compiled code in `/dist`. The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Building the plugin
To build the plugin, simply run `npm run dist`.
+15
View File
@@ -0,0 +1,15 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
import Logger from 'lib/Logger';
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(logger: Logger, implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}
+38
View File
@@ -0,0 +1,38 @@
import Plugin from '../Plugin';
import JoplinData from './JoplinData';
import JoplinPlugins from './JoplinPlugins';
import JoplinWorkspace from './JoplinWorkspace';
import JoplinFilters from './JoplinFilters';
import JoplinCommands from './JoplinCommands';
import JoplinViews from './JoplinViews';
import JoplinInterop from './JoplinInterop';
import JoplinSettings from './JoplinSettings';
import Logger from 'lib/Logger';
/**
* This is the main entry point to the Joplin API. You can access various services using the provided accessors.
*/
export default class Joplin {
private data_;
private plugins_;
private workspace_;
private filters_;
private commands_;
private views_;
private interop_;
private settings_;
constructor(logger: Logger, implementation: any, plugin: Plugin, store: any);
get data(): JoplinData;
get plugins(): JoplinPlugins;
get workspace(): JoplinWorkspace;
/**
* @ignore
*
* Not sure if it's the best way to hook into the app
* so for now disable filters.
*/
get filters(): JoplinFilters;
get commands(): JoplinCommands;
get views(): JoplinViews;
get interop(): JoplinInterop;
get settings(): JoplinSettings;
}
@@ -0,0 +1,51 @@
import { Command } from './types';
/**
* This class allows executing or registering new Joplin commands. Commands can be executed or associated with
* {@link JoplinViewsToolbarButtons | toolbar buttons} or {@link JoplinViewsMenuItems | menu items}.
*
* [View the demo plugin](https://github.com/laurent22/joplin/CliClient/tests/support/plugins/register_command)
*
* ## Executing Joplin's internal commands
*
* It is also possible to execute internal Joplin's commands which, as of now, are not well documented.
* You can find the list directly on GitHub though at the following locations:
*
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/gui/MainScreen/commands
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/commands
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.ts
*
* To view what arguments are supported, you can open any of these files and look at the `execute()` command.
*/
export default class JoplinCommands {
/**
* <span class="platform-desktop">desktop</span> Executes the given command.
* The `props` are the arguments passed to the command, and they vary based on the command
*
* ```typescript
* // Create a new note in the current notebook:
* await joplin.commands.execute('newNote');
*
* // Create a new sub-notebook under the provided notebook
* // Note: internally, notebooks are called "folders".
* await joplin.commands.execute('newFolder', { parent_id: "SOME_FOLDER_ID" });
* ```
*/
execute(commandName: string, props?: any): Promise<any>;
/**
* <span class="platform-desktop">desktop</span> Registers a new command.
*
* ```typescript
* // Register a new commmand called "testCommand1"
*
* await joplin.commands.register({
* name: 'testCommand1',
* label: 'My Test Command 1',
* iconName: 'fas fa-music',
* execute: () => {
* alert('Testing plugin command 1');
* },
* });
* ```
*/
register(command: Command): Promise<void>;
}
@@ -0,0 +1,47 @@
import { Path } from './types';
/**
* This module provides access to the Joplin data API: https://joplinapp.org/api/
* This is the main way to retrieve data, such as notes, notebooks, tags, etc.
* or to update them or delete them.
*
* This is also what you would use to search notes, via the `search` endpoint.
*
* [View the demo plugin](https://github.com/laurent22/joplin/CliClient/tests/support/plugins/simple)
*
* In general you would use the methods in this class as if you were using a REST API. There are four methods that map to GET, POST, PUT and DELETE calls.
* And each method takes these parameters:
*
* * `path`: This is an array that represents the path to the resource in the form `["resouceName", "resourceId", "resourceLink"]` (eg. ["tags", ":id", "notes"]). The "resources" segment is the name of the resources you want to access (eg. "notes", "folders", etc.). If not followed by anything, it will refer to all the resources in that collection. The optional "resourceId" points to a particular resources within the collection. Finally, an optional "link" can be present, which links the resource to a collection of resources. This can be used in the API for example to retrieve all the notes associated with a tag.
* * `query`: (Optional) The query parameters. In a URL, this is the part after the question mark "?". In this case, it should be an object with key/value pairs.
* * `data`: (Optional) Applies to PUT and POST calls only. The request body contains the data you want to create or modify, for example the content of a note or folder.
* * `files`: (Optional) Used to create new resources and associate them with files.
*
* Please refer to the [Joplin API documentation](https://joplinapp.org/api/) for complete details about each call. As the plugin runs within the Joplin application **you do not need an authorisation token** to use this API.
*
* For example:
*
* ```typescript
* // Get a note ID, title and body
* const noteId = 'some_note_id';
* const note = await joplin.data.get(['notes', noteId], { fields: ['id', 'title', 'body'] });
*
* // Get all folders
* const folders = await joplin.data.get(['folders']);
*
* // Set the note body
* await joplin.data.put(['notes', noteId], null, { body: "New note body" });
*
* // Create a new note under one of the folders
* await joplin.data.post(['notes'], null, { body: "my new note", title: "some title", parent_id: folders[0].id });
* ```
*/
export default class JoplinData {
private api_;
private pathSegmentRegex_;
private serializeApiBody;
private pathToString;
get(path: Path, query?: any): Promise<any>;
post(path: Path, query?: any, body?: any, files?: any[]): Promise<any>;
put(path: Path, query?: any, body?: any, files?: any[]): Promise<any>;
delete(path: Path, query?: any): Promise<any>;
}

Some files were not shown because too many files have changed in this diff Show More