1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-01-08 00:14:28 +02:00

Compare commits

..

13 Commits

Author SHA1 Message Date
Laurent Cozic
dac3685a56 fix docker-compose 2021-01-18 08:15:14 +00:00
Laurent Cozic
bdf0e50b56 tags 2021-01-18 01:39:33 +00:00
Laurent Cozic
0b14d927ce Server release v1.7.1 2021-01-18 01:38:09 +00:00
Laurent Cozic
767041dddb release script 2021-01-18 01:33:35 +00:00
Laurent Cozic
df65b8e6de Clean up config 2021-01-17 19:39:27 +00:00
Laurent Cozic
58c2bb5916 Detect env 2021-01-17 18:41:17 +00:00
Laurent Cozic
dce2f2955f Merge branch 'docker_server_update' of github.com:laurent22/joplin into docker_server_update 2021-01-17 16:57:17 +00:00
Florian Jensen
fdbd790ce1 Server: fix Docker build (#4381) 2021-01-17 16:51:41 +00:00
Laurent Cozic
d979866f2f Update doc 2021-01-17 12:45:35 +00:00
Laurent Cozic
d3a53abe32 Various tweaks 2021-01-17 12:04:55 +00:00
Laurent Cozic
31ef1d675c update 2021-01-15 23:34:40 +00:00
Laurent Cozic
58b5f4830c Merge branch 'dev' into docker_server_update 2021-01-15 22:30:50 +00:00
Laurent Cozic
92095c5f34 update 2021-01-15 16:51:02 +00:00
78 changed files with 653 additions and 9017 deletions

View File

@@ -6,5 +6,4 @@ packages/app-desktop
packages/app-cli
packages/app-mobile
packages/app-clipper
packages/generator-joplin
packages/plugin-repo-cli
packages/generator-joplin

View File

@@ -111,9 +111,6 @@ packages/app-cli/tests/models_Note.js.map
packages/app-cli/tests/models_Setting.d.ts
packages/app-cli/tests/models_Setting.js
packages/app-cli/tests/models_Setting.js.map
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
packages/app-cli/tests/services/plugins/RepositoryApi.js
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
packages/app-cli/tests/services/plugins/api/JoplinSettings.d.ts
packages/app-cli/tests/services/plugins/api/JoplinSettings.js
packages/app-cli/tests/services/plugins/api/JoplinSettings.js.map
@@ -1374,12 +1371,6 @@ packages/lib/uuid.js.map
packages/lib/versionInfo.d.ts
packages/lib/versionInfo.js
packages/lib/versionInfo.js.map
packages/plugin-repo-cli/dummy.test.d.ts
packages/plugin-repo-cli/dummy.test.js
packages/plugin-repo-cli/dummy.test.js.map
packages/plugin-repo-cli/index.d.ts
packages/plugin-repo-cli/index.js
packages/plugin-repo-cli/index.js.map
packages/plugins/ToggleSidebars/api/index.d.ts
packages/plugins/ToggleSidebars/api/index.js
packages/plugins/ToggleSidebars/api/index.js.map
@@ -1470,6 +1461,21 @@ packages/renderer/utils.js.map
packages/server/src/app.d.ts
packages/server/src/app.js
packages/server/src/app.js.map
packages/server/src/config-base.d.ts
packages/server/src/config-base.js
packages/server/src/config-base.js.map
packages/server/src/config-buildTypes.d.ts
packages/server/src/config-buildTypes.js
packages/server/src/config-buildTypes.js.map
packages/server/src/config-dev.d.ts
packages/server/src/config-dev.js
packages/server/src/config-dev.js.map
packages/server/src/config-prod.d.ts
packages/server/src/config-prod.js
packages/server/src/config-prod.js.map
packages/server/src/config-tests.d.ts
packages/server/src/config-tests.js
packages/server/src/config-tests.js.map
packages/server/src/config.d.ts
packages/server/src/config.js
packages/server/src/config.js.map
@@ -1680,13 +1686,10 @@ packages/server/src/utils/urlUtils.js.map
packages/server/src/utils/uuidgen.d.ts
packages/server/src/utils/uuidgen.js
packages/server/src/utils/uuidgen.js.map
packages/tools/lerna-add.d.ts
packages/tools/lerna-add.js
packages/tools/lerna-add.js.map
packages/tools/build-plugin-repository.d.ts
packages/tools/build-plugin-repository.js
packages/tools/build-plugin-repository.js.map
packages/tools/release-server.d.ts
packages/tools/release-server.js
packages/tools/release-server.js.map
packages/tools/tool-utils.d.ts
packages/tools/tool-utils.js
packages/tools/tool-utils.js.map
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD

33
.gitignore vendored
View File

@@ -99,9 +99,6 @@ packages/app-cli/tests/models_Note.js.map
packages/app-cli/tests/models_Setting.d.ts
packages/app-cli/tests/models_Setting.js
packages/app-cli/tests/models_Setting.js.map
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
packages/app-cli/tests/services/plugins/RepositoryApi.js
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
packages/app-cli/tests/services/plugins/api/JoplinSettings.d.ts
packages/app-cli/tests/services/plugins/api/JoplinSettings.js
packages/app-cli/tests/services/plugins/api/JoplinSettings.js.map
@@ -1362,12 +1359,6 @@ packages/lib/uuid.js.map
packages/lib/versionInfo.d.ts
packages/lib/versionInfo.js
packages/lib/versionInfo.js.map
packages/plugin-repo-cli/dummy.test.d.ts
packages/plugin-repo-cli/dummy.test.js
packages/plugin-repo-cli/dummy.test.js.map
packages/plugin-repo-cli/index.d.ts
packages/plugin-repo-cli/index.js
packages/plugin-repo-cli/index.js.map
packages/plugins/ToggleSidebars/api/index.d.ts
packages/plugins/ToggleSidebars/api/index.js
packages/plugins/ToggleSidebars/api/index.js.map
@@ -1458,6 +1449,21 @@ packages/renderer/utils.js.map
packages/server/src/app.d.ts
packages/server/src/app.js
packages/server/src/app.js.map
packages/server/src/config-base.d.ts
packages/server/src/config-base.js
packages/server/src/config-base.js.map
packages/server/src/config-buildTypes.d.ts
packages/server/src/config-buildTypes.js
packages/server/src/config-buildTypes.js.map
packages/server/src/config-dev.d.ts
packages/server/src/config-dev.js
packages/server/src/config-dev.js.map
packages/server/src/config-prod.d.ts
packages/server/src/config-prod.js
packages/server/src/config-prod.js.map
packages/server/src/config-tests.d.ts
packages/server/src/config-tests.js
packages/server/src/config-tests.js.map
packages/server/src/config.d.ts
packages/server/src/config.js
packages/server/src/config.js.map
@@ -1668,13 +1674,10 @@ packages/server/src/utils/urlUtils.js.map
packages/server/src/utils/uuidgen.d.ts
packages/server/src/utils/uuidgen.js
packages/server/src/utils/uuidgen.js.map
packages/tools/lerna-add.d.ts
packages/tools/lerna-add.js
packages/tools/lerna-add.js.map
packages/tools/build-plugin-repository.d.ts
packages/tools/build-plugin-repository.js
packages/tools/build-plugin-repository.js.map
packages/tools/release-server.d.ts
packages/tools/release-server.js
packages/tools/release-server.js.map
packages/tools/tool-utils.d.ts
packages/tools/tool-utils.js
packages/tools/tool-utils.js.map
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD

View File

@@ -14,6 +14,7 @@
"buildApiDoc": "npm start --prefix=packages/app-cli -- apidoc ../../readme/api/references/rest_api.md",
"buildDoc": "./packages/tools/build-all.sh",
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out docs/api/references/plugin_api packages/lib/services/plugins/api/",
"buildPluginRepo": "node packages/tools/build-plugin-repository.js",
"buildTranslations": "npm run tsc && node packages/tools/build-translation.js",
"buildTranslationsNoTsc": "node packages/tools/build-translation.js",
"buildWebsite": "npm run buildApiDoc && node ./packages/tools/build-website.js && npm run buildPluginDoc",
@@ -25,7 +26,7 @@
"linter-precommit": "./node_modules/.bin/eslint --resolve-plugins-relative-to . --fix --ext .js --ext .jsx --ext .ts --ext .tsx",
"linter": "./node_modules/.bin/eslint --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
"postinstall": "npm run bootstrap --no-ci && npm run build",
"publishAll": "git pull && npm run build && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll",
"publishAll": "git pull && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll",
"releaseAndroidClean": "node packages/tools/release-android.js",
"releaseAndroid": "export PATH=\"/usr/local/opt/openjdk@11/bin:$PATH\" && node packages/tools/release-android.js",
"releaseCli": "node packages/tools/release-cli.js",

View File

@@ -1,8 +1,22 @@
const gulp = require('gulp');
const fs = require('fs-extra');
const utils = require('@joplin/tools/gulp/utils');
const { setPackagePrivateField } = require('@joplin/tools/tool-utils');
const tasks = {
// compileExtensions: {
// fn: require('../Tools/gulp/tasks/compileExtensions.js'),
// },
// copyLib: require('../Tools/gulp/tasks/copyLib'),
// tsc: require('../Tools/gulp/tasks/tsc'),
// updateIgnoredTypeScriptBuild: require('../Tools/gulp/tasks/updateIgnoredTypeScriptBuild'),
};
const tasks = {};
// async function makePackagePublic(filePath) {
// const text = await fs.readFile(filePath, 'utf8');
// const obj = JSON.parse(text);
// delete obj.private;
// await fs.writeFile(filePath, JSON.stringify(obj), 'utf8');
// }
tasks.prepareBuild = {
fn: async () => {
@@ -12,7 +26,8 @@ tasks.prepareBuild = {
});
await utils.copyFile(`${__dirname}/package.json`, `${buildDir}/package.json`);
await utils.setPackagePrivateField(`${buildDir}/package.json`, false);
// await makePackagePublic(`${buildDir}/package.json`);
await setPackagePrivateField(`${buildDir}/package.json`, false);
await utils.copyFile(`${__dirname}/package-lock.json`, `${buildDir}/package-lock.json`);
await utils.copyFile(`${__dirname}/gulpfile.js`, `${buildDir}/gulpfile.js`);
@@ -35,6 +50,14 @@ tasks.prepareTestBuild = {
],
});
// const rootDir = utils.rootDir();
// await utils.copyDir(`${rootDir}/packages/app-mobile/lib`, `${testBuildDir}/lib`, {
// excluded: [
// `${rootDir}/packages/renderer/node_modules`,
// ],
// });
// await utils.copyDir(`${rootDir}/packages/app-mobile/locales`, `${testBuildDir}/locales`);
await fs.mkdirp(`${testBuildDir}/data`);
},
};
@@ -44,4 +67,12 @@ utils.registerGulpTasks(gulp, tasks);
gulp.task('build', gulp.series([
'prepareBuild',
// 'compileExtensions',
// 'copyLib',
]));
// gulp.task('buildTests', gulp.series([
// // 'prepareTestBuild',
// // 'compileExtensions',
// // 'copyLib',
// ]));

View File

@@ -27,22 +27,17 @@ describe('InMemoryCache', function() {
await time.msleep(510);
expect(cache.value('test')).toBe(undefined);
// This test can sometimes fail in some cases, probably because it
// sleeps for more than 100ms (when the computer is slow). Changing this
// to use higher values would slow down the test unit too much, so let's
// disable it for now.
// Check that the TTL is reset every time setValue is called
// cache.setValue('test', 'something', 300);
// await time.msleep(100);
// cache.setValue('test', 'something', 300);
// await time.msleep(100);
// cache.setValue('test', 'something', 300);
// await time.msleep(100);
// cache.setValue('test', 'something', 300);
// await time.msleep(100);
cache.setValue('test', 'something', 300);
await time.msleep(100);
cache.setValue('test', 'something', 300);
await time.msleep(100);
cache.setValue('test', 'something', 300);
await time.msleep(100);
cache.setValue('test', 'something', 300);
await time.msleep(100);
// expect(cache.value('test')).toBe('something');
expect(cache.value('test')).toBe('something');
});
it('should delete old records', async () => {

View File

@@ -1,56 +0,0 @@
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
import shim from '@joplin/lib/shim';
import { setupDatabaseAndSynchronizer, switchClient, supportDir, createTempDir } from '../../test-utils';
async function newRepoApi(): Promise<RepositoryApi> {
const repo = new RepositoryApi(`${supportDir}/pluginRepo`, await createTempDir());
await repo.loadManifests();
return repo;
}
describe('services_plugins_RepositoryApi', function() {
beforeEach(async (done: Function) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should get the manifests', (async () => {
const api = await newRepoApi();
const manifests = await api.manifests();
expect(!!manifests.find(m => m.id === 'joplin.plugin.ambrt.backlinksToNote')).toBe(true);
expect(!!manifests.find(m => m.id === 'org.joplinapp.plugins.ToggleSidebars')).toBe(true);
}));
it('should search', (async () => {
const api = await newRepoApi();
{
const results = await api.search('to');
expect(results.length).toBe(2);
expect(!!results.find(m => m.id === 'joplin.plugin.ambrt.backlinksToNote')).toBe(true);
expect(!!results.find(m => m.id === 'org.joplinapp.plugins.ToggleSidebars')).toBe(true);
}
{
const results = await api.search('backlink');
expect(results.length).toBe(1);
expect(!!results.find(m => m.id === 'joplin.plugin.ambrt.backlinksToNote')).toBe(true);
}
}));
it('should download a plugin', (async () => {
const api = await newRepoApi();
const pluginPath = await api.downloadPlugin('org.joplinapp.plugins.ToggleSidebars');
expect(await shim.fsDriver().exists(pluginPath)).toBe(true);
}));
it('should tell if a plugin can be updated', (async () => {
const api = await newRepoApi();
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.0')).toBe(true);
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.2')).toBe(false);
expect(await api.pluginCanBeUpdated('does.not.exist', '1.0.0')).toBe(false);
}));
});

View File

@@ -1,25 +0,0 @@
# Joplin Plugin Repository
This is the official Joplin Plugin Repository
## Installation
To install any of these plugins, open the desktop application, then go to the "Plugins" section in the Configuration screen. You can then search for any plugin and install it from there.
## Plugins
This repository contains the following plugins:
<!-- PLUGIN_LIST -->
&nbsp; | Name | Version | Description | Author
--- | --- | --- | --- | ---
[🏠](https://discourse.joplinapp.org/t/insert-referencing-notes-backlinks-plugin/13632) | Backlinks to note | 1.0.4 | Creates backlinks to opened note | a
[🏠](https://github.com/JackGruber/joplin-plugin-combine-notes) | Combine notes | 0.2.1 | Combine one or more notes | JackGruber
[🏠](https://github.com/JackGruber/joplin-plugin-copytags) | Copy Tags | 0.3.2 | Plugin to extend the Joplin tagging menu with a coppy all tags and tagging list with more control. | JackGruber
[🏠](https://discourse.joplinapp.org/t/go-to-note-tag-or-notebook-via-highlighting-text-in-editor/12731) | Create and go to #tags and @notebooks | 1.3.4 | Go to tag,notebook or note via links or via text | a
[🏠](https://github.com/benji300/joplin-favorites) | Favorites | 1.0.0 | Save any notebook, note, to-do, tag, or search as favorite in an extra panel view for quick access. (v1.0.0) | Benji300
[🏠](https://github.com/laurent22/joplin/tree/dev/packages/plugins/ToggleSidebars) | Note list and side bar toggle buttons | 1.0.2 | Adds buttons to toggle note list and sidebar | Laurent Cozic
[🏠](https://github.com/JackGruber/joplin-plugin-note-overview) | Note overview | 1.0.0 | A note overview is created based on the defined search and the specified fields | JackGruber
[🏠](https://github.com/benji300/joplin-note-tabs) | Note Tabs | 1.1.1 | Allows to open several notes at once in tabs and pin them. (v1.1.1) | Benji300
[🏠](https://github.com/JackGruber/joplin-plugin-backup) | Simple Backup | 0.3.0 | Plugin to create manual and automatic backups | JackGruber
<!-- PLUGIN_LIST -->

View File

@@ -1,29 +0,0 @@
{
"joplin.plugin.ambrt.backlinksToNote": {
"manifest_version": 1,
"id": "joplin.plugin.ambrt.backlinksToNote",
"app_min_version": "1.5",
"version": "1.0.4",
"name": "Backlinks to note",
"description": "Creates backlinks to opened note",
"author": "a",
"homepage_url": "https://discourse.joplinapp.org/t/insert-referencing-notes-backlinks-plugin/13632",
"_publish_hash": "sha256:ab9c9bc776e167a3b1a9ec40a4927d93da14514c0508f46dd6e5ea3f8a3a6c3b",
"_publish_commit": "master:5b74f93c687463572c46200292fa911e0ba96033",
"_npm_package_name": "joplin-plugin-backlinks"
},
"org.joplinapp.plugins.ToggleSidebars": {
"manifest_version": 1,
"id": "org.joplinapp.plugins.ToggleSidebars",
"app_min_version": "1.6",
"version": "1.0.2",
"name": "Note list and side bar toggle buttons",
"description": "Adds buttons to toggle note list and sidebar",
"author": "Laurent Cozic",
"homepage_url": "https://github.com/laurent22/joplin/tree/dev/packages/plugins/ToggleSidebars",
"repository_url": "https://github.com/laurent22/joplin/tree/dev/packages/plugins/ToggleSidebars",
"_publish_hash": "sha256:e0d833b7ef1bb8f02ee4cb861ef1989621358c45a5614911071302dc0527a3b4",
"_publish_commit": "dev:1b5b2342fc25717b77ad9f1627c1a334e5bbae54",
"_npm_package_name": "@joplin/joplin-plugin-toggle-sidebars"
}
}

View File

@@ -1,13 +0,0 @@
{
"manifest_version": 1,
"id": "joplin.plugin.ambrt.backlinksToNote",
"app_min_version": "1.5",
"version": "1.0.4",
"name": "Backlinks to note",
"description": "Creates backlinks to opened note",
"author": "a",
"homepage_url": "https://discourse.joplinapp.org/t/insert-referencing-notes-backlinks-plugin/13632",
"_publish_hash": "sha256:ab9c9bc776e167a3b1a9ec40a4927d93da14514c0508f46dd6e5ea3f8a3a6c3b",
"_publish_commit": "master:5b74f93c687463572c46200292fa911e0ba96033",
"_npm_package_name": "joplin-plugin-backlinks"
}

View File

@@ -1,14 +0,0 @@
{
"manifest_version": 1,
"id": "org.joplinapp.plugins.ToggleSidebars",
"app_min_version": "1.6",
"version": "1.0.2",
"name": "Note list and side bar toggle buttons",
"description": "Adds buttons to toggle note list and sidebar",
"author": "Laurent Cozic",
"homepage_url": "https://github.com/laurent22/joplin/tree/dev/packages/plugins/ToggleSidebars",
"repository_url": "https://github.com/laurent22/joplin/tree/dev/packages/plugins/ToggleSidebars",
"_publish_hash": "sha256:e0d833b7ef1bb8f02ee4cb861ef1989621358c45a5614911071302dc0527a3b4",
"_publish_commit": "dev:1b5b2342fc25717b77ad9f1627c1a334e5bbae54",
"_npm_package_name": "@joplin/joplin-plugin-toggle-sidebars"
}

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -4,7 +4,7 @@
"app_min_version": "1.4",
"name": "Register Command Test",
"description": "To test registering commands",
"version": "1.0.3",
"version": "1.0.0",
"author": "Laurent Cozic",
"homepage_url": "https://joplinapp.org"
}

View File

@@ -1,7 +0,0 @@
#!/bin/bash
# - Update src/manifest.json with the new version number
# - Run the below command
# - Then the file /manifests.json also needs to be updated with the new manifest file
npm run dist && cp publish/org.joplinapp.plugins.RegisterCommandDemo.jpl ~/src/joplin-plugins-test/plugins/org.joplinapp.plugins.RegisterCommandDemo/plugin.jpl && cp publish/org.joplinapp.plugins.RegisterCommandDemo.json ~/src/joplin-plugins-test/plugins/org.joplinapp.plugins.RegisterCommandDemo/plugin.json

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -233,7 +233,7 @@ function main(processArgv) {
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
buildMain: pluginConfig,
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
@@ -247,7 +247,7 @@ function main(processArgv) {
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
createArchive: createArchiveConfig,
};
// If we are running the first config step, we clean up and create the build
@@ -270,10 +270,4 @@ try {
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -104,7 +104,6 @@ FileApiDriverLocal.fsDriver_ = fsDriver;
const logDir = `${__dirname}/../tests/logs`;
const baseTempDir = `${__dirname}/../tests/tmp/${suiteName_}`;
const supportDir = `${__dirname}/support`;
// We add a space in the data directory path as that will help uncover
// various space-in-path issues.
@@ -181,7 +180,6 @@ BaseItem.loadClass('Revision', Revision);
Setting.setConstant('appId', 'net.cozic.joplintest-cli');
Setting.setConstant('appType', 'cli');
Setting.setConstant('tempDir', baseTempDir);
Setting.setConstant('cacheDir', baseTempDir);
Setting.setConstant('env', 'dev');
BaseService.logger_ = logger;
@@ -866,4 +864,4 @@ class TestApp extends BaseApplication {
}
}
export { supportDir, waitForFolderCount, afterAllCleanUp, exportDir, newPluginService, newPluginScript, synchronizerStart, afterEachCleanUp, syncTargetName, setSyncTargetName, syncDir, createTempDir, isNetworkSyncTarget, kvStore, expectThrow, logger, expectNotThrow, resourceService, resourceFetcher, tempFilePath, allSyncTargetItemsEncrypted, msleep, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, checkThrow, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp };
module.exports = { waitForFolderCount, afterAllCleanUp, exportDir, newPluginService, newPluginScript, synchronizerStart, afterEachCleanUp, syncTargetName, setSyncTargetName, syncDir, createTempDir, isNetworkSyncTarget, kvStore, expectThrow, logger, expectNotThrow, resourceService, resourceFetcher, tempFilePath, allSyncTargetItemsEncrypted, msleep, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, checkThrow, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp };

View File

@@ -78,7 +78,7 @@ const commands = [
require('./gui/NoteEditor/commands/showRevisions'),
require('./gui/NoteList/commands/focusElementNoteList'),
require('./gui/NoteListControls/commands/focusSearch'),
require('./gui/Sidebar/commands/focusElementSideBar'),
require('./gui/SideBar/commands/focusElementSideBar'),
];
// Commands that are not tied to any particular component.
@@ -527,7 +527,7 @@ class Application extends BaseApplication {
// time, however we only effectively uninstall the plugin the next
// time the app is started. What plugin should be uninstalled is
// stored in the settings.
const newSettings = service.clearUpdateState(await service.uninstallPlugins(pluginSettings));
const newSettings = await service.uninstallPlugins(pluginSettings);
Setting.setValue('plugins.states', newSettings);
try {

View File

@@ -7,7 +7,6 @@ export enum ButtonLevel {
Secondary = 'secondary',
Tertiary = 'tertiary',
SidebarSecondary = 'sidebarSecondary',
Recommended = 'recommended',
}
interface Props {
@@ -122,20 +121,6 @@ const StyledButtonTertiary = styled(StyledButtonBase)`
}
`;
const StyledButtonRecommended = styled(StyledButtonBase)`
border: 1px solid ${(props: any) => props.theme.borderColor4};
background-color: ${(props: any) => props.theme.warningBackgroundColor};
${StyledIcon} {
color: ${(props: any) => props.theme.color};
}
${StyledTitle} {
color: ${(props: any) => props.theme.color};
opacity: 0.9;
}
`;
const StyledButtonSidebarSecondary = styled(StyledButtonBase)`
background: none;
border-color: ${(props: any) => props.theme.color2};
@@ -182,11 +167,10 @@ function buttonClass(level: ButtonLevel) {
if (level === ButtonLevel.Primary) return StyledButtonPrimary;
if (level === ButtonLevel.Tertiary) return StyledButtonTertiary;
if (level === ButtonLevel.SidebarSecondary) return StyledButtonSidebarSecondary;
if (level === ButtonLevel.Recommended) return StyledButtonRecommended;
return StyledButtonSecondary;
}
function Button(props: Props) {
export default function Button(props: Props) {
const iconOnly = props.iconName && !props.title;
const StyledButton = buttonClass(props.level);
@@ -213,5 +197,3 @@ function Button(props: Props) {
</StyledButton>
);
}
export default styled(Button)`${space}`;

View File

@@ -148,7 +148,6 @@ class ConfigScreenComponent extends React.Component<any, any> {
const createSettingComponents = (advanced: boolean) => {
const output = [];
for (let i = 0; i < section.metadatas.length; i++) {
const md = section.metadatas[i];
if (!!md.advanced !== advanced) continue;
@@ -161,8 +160,8 @@ class ConfigScreenComponent extends React.Component<any, any> {
const settingComps = createSettingComponents(false);
const advancedSettingComps = createSettingComponents(true);
const sectionWidths: Record<string, any> = {
plugins: '100%',
const sectionWidths: Record<string, number> = {
plugins: 900,
};
const sectionStyle: any = {
@@ -306,7 +305,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
);
}
private renderHeader(themeId: number, label: string, style: any = null) {
private renderHeader(themeId: number, label: string) {
const theme = themeStyle(themeId);
const labelStyle = Object.assign({}, theme.textStyle, {
@@ -315,7 +314,6 @@ class ConfigScreenComponent extends React.Component<any, any> {
fontSize: theme.fontSize * 1.25,
fontWeight: 500,
marginBottom: theme.mainPadding,
...style,
});
return (
@@ -459,7 +457,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
// There's probably a better way to do this but can't figure it out.
return (
<div key={key + (`${value}`).toString()} style={rowStyle}>
<div key={key + value.toString()} style={rowStyle}>
<div style={{ ...controlStyle, backgroundColor: 'transparent', display: 'flex', alignItems: 'center' }}>
<input
id={`setting_checkbox_${key}`}
@@ -566,7 +564,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
value={cmd[1]}
spellCheck={false}
/>
<div style={{ width: inputStyle.width, minWidth: inputStyle.minWidth }}>
<div style={{ width: inputStyle.width }}>
{descriptionComp}
</div>
</div>
@@ -595,7 +593,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
}}
spellCheck={false}
/>
<div style={{ width: inputStyle.width, minWidth: inputStyle.minWidth }}>
<div style={{ width: inputStyle.width }}>
{descriptionComp}
</div>
</div>

View File

@@ -11,47 +11,34 @@ export enum InstallState {
Installed = 3,
}
export enum UpdateState {
Idle = 1,
CanUpdate = 2,
Updating = 3,
HasBeenUpdated = 4,
}
interface Props {
item?: PluginItem;
manifest?: PluginManifest;
installState?: InstallState;
updateState?: UpdateState;
themeId: number;
onToggle?: Function;
onDelete?: Function;
onInstall?: Function;
onUpdate?: Function;
}
function manifestToItem(manifest: PluginManifest): PluginItem {
return {
id: manifest.id,
name: manifest.name,
version: manifest.version,
description: manifest.description,
enabled: true,
deleted: false,
devMode: false,
hasBeenUpdated: false,
};
}
export interface PluginItem {
id: string;
name: string;
version: string;
description: string;
enabled: boolean;
deleted: boolean;
devMode: boolean;
hasBeenUpdated: boolean;
}
const CellRoot = styled.div`
@@ -63,7 +50,7 @@ const CellRoot = styled.div`
padding: 15px;
border: 1px solid ${props => props.theme.dividerColor};
border-radius: 6px;
width: 320px;
width: 250px;
margin-right: 20px;
margin-bottom: 20px;
box-shadow: 1px 1px 3px rgba(0,0,0,0.2);
@@ -103,12 +90,6 @@ const StyledName = styled.div`
flex: 1;
`;
const StyledVersion = styled.span`
margin-left: 5px;
color: ${props => props.theme.colorFaded};
font-size: ${props => props.theme.fontSize * 0.9}px;
`;
const StyledDescription = styled.div`
font-family: ${props => props.theme.fontFamily};
color: ${props => props.theme.colorFaded};
@@ -157,23 +138,6 @@ export default function(props: Props) {
/>;
}
function renderUpdateButton() {
if (!props.onUpdate) return null;
let title = _('Update');
if (props.updateState === UpdateState.Updating) title = _('Updating...');
if (props.updateState === UpdateState.Idle) title = _('Updated');
if (props.updateState === UpdateState.HasBeenUpdated) title = _('Updated');
return <Button
ml={1}
level={ButtonLevel.Recommended}
onClick={() => props.onUpdate({ item })}
title={title}
disabled={props.updateState === UpdateState.HasBeenUpdated}
/>;
}
function renderFooter() {
if (item.devMode) return null;
@@ -181,7 +145,6 @@ export default function(props: Props) {
<CellFooter>
{renderDeleteButton()}
{renderInstallButton()}
{renderUpdateButton()}
<div style={{ display: 'flex', flex: 1 }}/>
</CellFooter>
);
@@ -190,7 +153,7 @@ export default function(props: Props) {
return (
<CellRoot>
<CellTop>
<StyledName mb={'5px'}>{item.name} {item.deleted ? '(Deleted)' : ''} <StyledVersion>v{item.version}</StyledVersion></StyledName>
<StyledName mb={'5px'}>{item.name} {item.deleted ? '(Deleted)' : ''}</StyledName>
{renderToggleButton()}
</CellTop>
<CellContent>

View File

@@ -1,21 +1,18 @@
import * as React from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import PluginService, { defaultPluginSetting, Plugins, PluginSetting, PluginSettings } from '@joplin/lib/services/plugins/PluginService';
import { _ } from '@joplin/lib/locale';
import styled from 'styled-components';
import SearchPlugins from './SearchPlugins';
import PluginBox, { UpdateState } from './PluginBox';
import PluginBox from './PluginBox';
import Button, { ButtonLevel } from '../../../Button/Button';
import bridge from '../../../../services/bridge';
import produce from 'immer';
import { OnChangeEvent } from '../../../lib/SearchInput/SearchInput';
import { PluginItem } from './PluginBox';
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
import Setting from '@joplin/lib/models/Setting';
import useOnInstallHandler, { OnPluginSettingChangeEvent } from './useOnInstallHandler';
const { space } = require('styled-system');
const maxWidth: number = 320;
const maxWidth: number = 250;
const Root = styled.div`
display: flex;
@@ -29,7 +26,7 @@ const UserPluginsRoot = styled.div`
`;
const ToolsButton = styled(Button)`
margin-right: 6px;
margin-right: 2px;
`;
interface Props {
@@ -41,15 +38,6 @@ interface Props {
renderHeader: Function;
}
let repoApi_: RepositoryApi = null;
function repoApi(): RepositoryApi {
if (repoApi_) return repoApi_;
repoApi_ = new RepositoryApi('https://github.com/joplin/plugins', Setting.value('tempDir'));
// repoApi_ = new RepositoryApi('/Users/laurent/src/joplin-plugins-test', Setting.value('tempDir'));
return repoApi_;
}
function usePluginItems(plugins: Plugins, settings: PluginSettings): PluginItem[] {
return useMemo(() => {
const output: PluginItem[] = [];
@@ -65,12 +53,10 @@ function usePluginItems(plugins: Plugins, settings: PluginSettings): PluginItem[
output.push({
id: pluginId,
name: plugin.manifest.name,
version: plugin.manifest.version,
description: plugin.manifest.description,
enabled: setting.enabled,
deleted: setting.deleted,
devMode: plugin.devMode,
hasBeenUpdated: setting.hasBeenUpdated,
});
}
@@ -84,9 +70,6 @@ function usePluginItems(plugins: Plugins, settings: PluginSettings): PluginItem[
export default function(props: Props) {
const [searchQuery, setSearchQuery] = useState('');
const [manifestsLoaded, setManifestsLoaded] = useState<boolean>(false);
const [updatingPluginsIds, setUpdatingPluginIds] = useState<Record<string, boolean>>({});
const [canBeUpdatedPluginIds, setCanBeUpdatedPluginIds] = useState<Record<string, boolean>>({});
const pluginService = PluginService.instance();
@@ -94,43 +77,6 @@ export default function(props: Props) {
return pluginService.unserializePluginSettings(props.value);
}, [props.value]);
const pluginItems = usePluginItems(pluginService.plugins, pluginSettings);
useEffect(() => {
let cancelled = false;
async function fetchManifests() {
await repoApi().loadManifests();
if (cancelled) return;
setManifestsLoaded(true);
}
void fetchManifests();
return () => {
cancelled = true;
};
}, []);
useEffect(() => {
if (!manifestsLoaded) return () => {};
let cancelled = false;
async function fetchPluginIds() {
const pluginIds = await repoApi().canBeUpdatedPlugins(pluginItems as any);
if (cancelled) return;
const conv: Record<string, boolean> = {};
pluginIds.forEach(id => conv[id] = true);
setCanBeUpdatedPluginIds(conv);
}
void fetchPluginIds();
return () => {
cancelled = true;
};
}, [manifestsLoaded, pluginItems]);
const onDelete = useCallback(async (event: any) => {
const item: PluginItem = event.item;
const confirm = await bridge().showConfirmMessageBox(_('Delete plugin "%s"?', item.name));
@@ -172,12 +118,6 @@ export default function(props: Props) {
props.onChange({ value: pluginService.serializePluginSettings(newSettings) });
}, [pluginSettings, props.onChange]);
const onPluginSettingsChange = useCallback((event: OnPluginSettingChangeEvent) => {
props.onChange({ value: pluginService.serializePluginSettings(event.value) });
}, []);
const onUpdate = useOnInstallHandler(setUpdatingPluginIds, pluginSettings, repoApi, onPluginSettingsChange, true);
const onToolsClick = useCallback(async () => {
const template = [];
@@ -204,22 +144,12 @@ export default function(props: Props) {
for (const item of items) {
if (item.deleted) continue;
const isUpdating = updatingPluginsIds[item.id];
const onUpdateHandler = canBeUpdatedPluginIds[item.id] ? onUpdate : null;
let updateState = UpdateState.Idle;
if (onUpdateHandler) updateState = UpdateState.CanUpdate;
if (isUpdating) updateState = UpdateState.Updating;
if (item.hasBeenUpdated) updateState = UpdateState.HasBeenUpdated;
output.push(<PluginBox
key={item.id}
item={item}
themeId={props.themeId}
updateState={updateState}
onDelete={onDelete}
onToggle={onToggle}
onUpdate={onUpdateHandler}
/>);
}
@@ -244,11 +174,13 @@ export default function(props: Props) {
}
}
function renderSearchArea() {
return (
const pluginItems = usePluginItems(pluginService.plugins, pluginSettings);
return (
<Root>
<div style={{ marginBottom: 20 }}>
{props.renderHeader(props.themeId, _('Search for plugins'))}
<SearchPlugins
disabled={!manifestsLoaded}
maxWidth={maxWidth}
themeId={props.themeId}
searchQuery={searchQuery}
@@ -256,32 +188,18 @@ export default function(props: Props) {
onSearchQueryChange={onSearchQueryChange}
onPluginSettingsChange={onSearchPluginSettingsChange}
renderDescription={props.renderDescription}
repoApi={repoApi}
/>
</div>
);
}
function renderBottomArea() {
if (searchQuery) return null;
return (
<div>
<div style={{ display: 'flex', flexDirection: 'row', maxWidth }}>
<ToolsButton tooltip={_('Plugin tools')} iconName="fas fa-cog" level={ButtonLevel.Secondary} onClick={onToolsClick}/>
<div style={{ display: 'flex', flex: 1 }}>
{props.renderHeader(props.themeId, _('Manage your plugins'))}
</div>
<ToolsButton tooltip={_('Plugin tools')} iconName="fas fa-cog" level={ButtonLevel.Primary} onClick={onToolsClick}/>
</div>
{renderUserPlugins(pluginItems)}
</div>
);
}
return (
<Root>
{renderSearchArea()}
{renderBottomArea()}
</Root>
);
}

View File

@@ -6,6 +6,7 @@ import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
import { PluginManifest } from '@joplin/lib/services/plugins/utils/types';
import PluginBox, { InstallState } from './PluginBox';
import Setting from '@joplin/lib/models/Setting';
import { PluginSettings } from '@joplin/lib/services/plugins/PluginService';
import { _ } from '@joplin/lib/locale';
import useOnInstallHandler from './useOnInstallHandler';
@@ -26,18 +27,24 @@ interface Props {
onPluginSettingsChange(event: any): void;
renderDescription: Function;
maxWidth: number;
repoApi(): RepositoryApi;
disabled: boolean;
}
let repoApi_: RepositoryApi = null;
function repoApi(): RepositoryApi {
if (repoApi_) return repoApi_;
repoApi_ = new RepositoryApi('https://github.com/joplin/plugins', Setting.value('tempDir'));
return repoApi_;
}
export default function(props: Props) {
const [searchStarted, setSearchStarted] = useState(false);
const [manifests, setManifests] = useState<PluginManifest[]>([]);
const asyncSearchQueue = useRef(new AsyncActionQueue(10));
const asyncSearchQueue = useRef(new AsyncActionQueue(200));
const [installingPluginsIds, setInstallingPluginIds] = useState<Record<string, boolean>>({});
const [searchResultCount, setSearchResultCount] = useState(null);
const onInstall = useOnInstallHandler(setInstallingPluginIds, props.pluginSettings, props.repoApi, props.onPluginSettingsChange, false);
const onInstall = useOnInstallHandler(setInstallingPluginIds, props.pluginSettings, repoApi, props.onPluginSettingsChange);
useEffect(() => {
setSearchResultCount(null);
@@ -46,7 +53,7 @@ export default function(props: Props) {
setManifests([]);
setSearchResultCount(null);
} else {
const r = await props.repoApi().search(props.searchQuery);
const r = await repoApi().search(props.searchQuery);
setManifests(r);
setSearchResultCount(r.length);
}
@@ -100,8 +107,6 @@ export default function(props: Props) {
onChange={onChange}
onSearchButtonClick={onSearchButtonClick}
searchStarted={searchStarted}
placeholder={_('Search for plugins...')}
disabled={props.disabled}
/>
</div>

View File

@@ -6,13 +6,7 @@ import Logger from '@joplin/lib/Logger';
const logger = Logger.create('useOnInstallHandler');
export interface OnPluginSettingChangeEvent {
value: PluginSettings;
}
type OnPluginSettingChangeHandler = (event: OnPluginSettingChangeEvent)=> void;
export default function(setInstallingPluginIds: Function, pluginSettings: PluginSettings, repoApi: Function, onPluginSettingsChange: OnPluginSettingChangeHandler, isUpdate: boolean) {
export default function(setInstallingPluginIds: Function, pluginSettings: PluginSettings, repoApi: Function, onPluginSettingsChange: Function) {
return useCallback(async (event: any) => {
const pluginId = event.item.id;
@@ -25,11 +19,7 @@ export default function(setInstallingPluginIds: Function, pluginSettings: Plugin
let installError = null;
try {
if (isUpdate) {
await PluginService.instance().updatePluginFromRepo(repoApi(), pluginId);
} else {
await PluginService.instance().installPluginFromRepo(repoApi(), pluginId);
}
await PluginService.instance().installPluginFromRepo(repoApi(), pluginId);
} catch (error) {
installError = error;
logger.error(error);
@@ -38,7 +28,6 @@ export default function(setInstallingPluginIds: Function, pluginSettings: Plugin
if (!installError) {
const newSettings = produce(pluginSettings, (draft: PluginSettings) => {
draft[pluginId] = defaultPluginSetting();
if (isUpdate) draft[pluginId].hasBeenUpdated = true;
});
onPluginSettingsChange({ value: newSettings });

View File

@@ -41,8 +41,6 @@ interface Props {
onKeyDown?: Function;
onSearchButtonClick: Function;
searchStarted: boolean;
placeholder?: string;
disabled?: boolean;
}
export interface OnChangeEvent {
@@ -62,13 +60,12 @@ export default function(props: Props) {
ref={props.inputRef}
value={props.value}
type="text"
placeholder={props.placeholder || _('Search...')}
placeholder={_('Search...')}
onChange={onChange}
onFocus={props.onFocus}
onBlur={props.onBlur}
onKeyDown={props.onKeyDown}
spellCheck={false}
disabled={props.disabled}
/>
<SearchButton onClick={props.onSearchButtonClick}>
<SearchButtonIcon className={iconName}/>

View File

@@ -1,5 +1,6 @@
const gulp = require('gulp');
const utils = require('@joplin/tools/gulp/utils');
const fs = require('fs-extra');
const tasks = {
compileScripts: {
@@ -17,18 +18,55 @@ const tasks = {
electronRebuild: {
fn: require('./tools/electronRebuild.js'),
},
// compileExtensions: {
// fn: require('@joplin/tools/gulp/tasks/compileExtensions.js'),
// },
// copyLib: require('@joplin/tools/gulp/tasks/copyLib'),
tsc: require('@joplin/tools/gulp/tasks/tsc'),
updateIgnoredTypeScriptBuild: require('@joplin/tools/gulp/tasks/updateIgnoredTypeScriptBuild'),
linkReact: {
fn: async () => {
// React is a dependency of both the lib and app-desktop
// packages, which cause a duplicate React issue. To go around
// this, one way is to manually link the package.
//
// Note that React must also be unlinked in preinstall step
// otherwise there will be permission errors when running
// `lerna bootstrap`
//
// https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react
process.chdir(__dirname);
await fs.remove('./node_modules/react');
await fs.remove('./node_modules/react-dom');
await utils.execCommand('npm link ../lib/node_modules/react');
await utils.execCommand('npm link ../lib/node_modules/react-dom');
},
},
};
utils.registerGulpTasks(gulp, tasks);
// const buildSeries = [
// // 'compileExtensions',
// // 'copyLib',
// ];
// On Windows also run tsc because `npm run watch` locks some folders
// which makes the copyPluginAssets command fail. For that reason,
// it's not possible to run watch on Windows while testing the desktop app.
// if (require('os').platform() === 'win32') {
// buildSeries.push('tsc');
// }
const buildParallel = [
// gulp.series(...buildSeries),
'compileScripts',
'compilePackageInfo',
'copyPluginAssets',
'copyTinyMceLangs',
'updateIgnoredTypeScriptBuild',
// 'linkReact',
];
gulp.task('build', gulp.parallel(...buildParallel));

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "1.7.2",
"version": "1.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "1.7.2",
"version": "1.7.0",
"description": "Joplin for Desktop",
"main": "main.js",
"private": true,

View File

@@ -635,8 +635,8 @@ class NoteScreenComponent extends BaseScreenComponent {
let resource = Resource.new();
resource.id = uuid.create();
resource.mime = mimeType;
resource.title = pickerResponse.name ? pickerResponse.name : '';
resource.file_extension = safeFileExtension(fileExtension(pickerResponse.name ? pickerResponse.name : localFilePath));
resource.title = pickerResponse.fileName ? pickerResponse.fileName : '';
resource.file_extension = safeFileExtension(fileExtension(pickerResponse.fileName ? pickerResponse.fileName : localFilePath));
if (!resource.mime) resource.mime = 'application/octet-stream';

View File

@@ -1,4 +1,5 @@
const gulp = require('gulp');
const fs = require('fs-extra');
const utils = require('@joplin/tools/gulp/utils');
const tasks = {
@@ -11,6 +12,23 @@ const tasks = {
podInstall: {
fn: require('./tools/podInstall'),
},
prepareRelease: {
fn: require('./tools/prepareRelease'),
},
// clean: {
// fn: require('./tools/clean'),
// },
linkReact: {
fn: async () => {
// React is a dependency of both the lib and app-desktop
// packages, which cause a duplicate React issue. To go around
// this, one way is to manually link the package.
// https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react
process.chdir(__dirname);
await fs.remove('./node_modules/react');
await utils.execCommand('npm link ../lib/node_modules/react');
},
},
};
utils.registerGulpTasks(gulp, tasks);
@@ -18,5 +36,6 @@ utils.registerGulpTasks(gulp, tasks);
gulp.task('build', gulp.series(
'buildReactNativeInjectedJs',
'encodeAssets',
// 'linkReact',
'podInstall'
));

View File

@@ -0,0 +1,32 @@
// This is to replace the symlinks inside node_modules with the actual packages
// as I assumed it was needed to build the final release. However it seems
// Android `assembleRelease` handles symlinks properly so maybe this is not
// needed after all ¯\_(ツ)_/¯
const { copyDir } = require('@joplin/tools/gulp/utils');
const { rootDir, deleteLink, toSystemSlashes } = require('@joplin/tools/tool-utils');
const mobileDir = `${rootDir}/packages/app-mobile`;
module.exports = async function() {
const dirsToCopy = [
'fork-htmlparser2',
'fork-sax',
'lib',
'renderer',
];
const destDir = `${mobileDir}/node_modules/@joplin`;
for (const dir of dirsToCopy) {
const destPath = toSystemSlashes(`${destDir}/${dir}`);
const sourcePath = toSystemSlashes(`${rootDir}/packages/${dir}`);
console.info(`Copying ${sourcePath} => ${destPath}`);
// TODO: copy symlink so that it can be restored
await deleteLink(destPath);
await copyDir(sourcePath, destPath, {
excluded: ['node_modules'],
});
}
};

View File

@@ -0,0 +1,31 @@
// const fs = require('fs');
// const { execSync } = require("child_process");
// const isWindows = process.platform === "win32";
// function toSystemSlashes(path) {
// const os = process.platform;
// if (os === 'win32') return path.replace(/\//g, '\\');
// return path.replace(/\\/g, '/');
// }
// const nodeModulesPath = `${__dirname}/../node_modules`;
// function deleteLink(path) {
// if (isWindows) {
// try {
// execSync(`rmdir "${toSystemSlashes(path)}"`, { stdio : 'pipe' });
// } catch (error) {
// // console.info('Error: ' + error.message);
// }
// } else {
// try {
// fs.unlinkSync(toSystemSlashes(path));
// } catch (error) {
// }
// }
// }
// deleteLink(nodeModulesPath + '/react');
// deleteLink(nodeModulesPath + '/react-dom');

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/fork-htmlparser2",
"version": "4.1.19",
"version": "4.1.15",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,7 +1,7 @@
{
"name": "@joplin/fork-htmlparser2",
"description": "Fast & forgiving HTML/XML/RSS parser",
"version": "4.1.19",
"version": "4.1.15",
"author": "Felix Boehm <me@feedic.com>",
"publishConfig": {
"access": "public"
@@ -52,7 +52,7 @@
"coveralls": "^3.0.1",
"eslint": "^6.0.0",
"eslint-config-prettier": "^6.0.0",
"jest": "^26.6.3",
"jest": "^24.8.0",
"prettier": "^1.18.2",
"ts-jest": "^24.0.2",
"typescript": "^3.5.3"

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/fork-sax",
"version": "1.2.23",
"version": "1.2.19",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -2,7 +2,7 @@
"name": "@joplin/fork-sax",
"description": "An evented streaming XML parser in JavaScript",
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
"version": "1.2.23",
"version": "1.2.19",
"main": "lib/sax.js",
"publishConfig": {
"access": "public"

View File

@@ -34,4 +34,4 @@
"repository": "https://github.com/laurent22/generator-joplin",
"license": "MIT",
"private": true
}
}

View File

@@ -667,7 +667,6 @@ export default class BaseApplication {
const resourceDirName = 'resources';
const resourceDir = `${profileDir}/${resourceDirName}`;
const tempDir = `${profileDir}/tmp`;
const cacheDir = `${profileDir}/cache`;
Setting.setConstant('env', initArgs.env);
Setting.setConstant('profileDir', profileDir);
@@ -675,7 +674,6 @@ export default class BaseApplication {
Setting.setConstant('resourceDirName', resourceDirName);
Setting.setConstant('resourceDir', resourceDir);
Setting.setConstant('tempDir', tempDir);
Setting.setConstant('cacheDir', cacheDir);
Setting.setConstant('pluginDir', `${profileDir}/plugins`);
SyncTargetRegistry.addClass(SyncTargetFilesystem);
@@ -697,7 +695,6 @@ export default class BaseApplication {
await fs.mkdirp(profileDir, 0o755);
await fs.mkdirp(resourceDir, 0o755);
await fs.mkdirp(tempDir, 0o755);
await fs.mkdirp(cacheDir, 0o755);
// Clean up any remaining watched files (they start with "edit-")
await shim.fsDriver().removeAllThatStartWith(profileDir, 'edit-');

View File

@@ -1659,7 +1659,6 @@ Setting.constants_ = {
profileDir: '',
templateDir: '',
tempDir: '',
cacheDir: '',
pluginDir: '',
flagOpenDevTools: false,
syncVersion: 2,

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/lib",
"version": "1.7.2",
"version": "1.0.16",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/lib",
"version": "1.7.2",
"version": "1.0.16",
"description": "Joplin Core library",
"author": "Laurent Cozic",
"homepage": "",
@@ -22,11 +22,11 @@
"typescript": "^4.0.5"
},
"dependencies": {
"@joplin/fork-htmlparser2": "^4.1.19",
"@joplin/fork-sax": "^1.2.23",
"@joplin/renderer": "^1.7.2",
"@joplin/turndown": "^4.0.41",
"@joplin/turndown-plugin-gfm": "^1.0.23",
"@joplin/fork-htmlparser2": "^4.1.15",
"@joplin/fork-sax": "^1.2.19",
"@joplin/renderer": "^1.0.24",
"@joplin/turndown": "^4.0.37",
"@joplin/turndown-plugin-gfm": "^1.0.19",
"async-mutex": "^0.1.3",
"aws-sdk": "^2.588.0",
"base-64": "^0.1.0",

View File

@@ -8,7 +8,6 @@ import { filename, dirname, rtrimSlashes } from '../../path-utils';
import Setting from '../../models/Setting';
import Logger from '../../Logger';
import RepositoryApi from './RepositoryApi';
import produce from 'immer';
const compareVersions = require('compare-versions');
const uslug = require('uslug');
const md5File = require('md5-file/promise');
@@ -32,19 +31,12 @@ export interface Plugins {
export interface PluginSetting {
enabled: boolean;
deleted: boolean;
// After a plugin has been updated, the user needs to restart the app before
// loading the new version. In the meantime, we set this property to `true`
// so that we know the plugin has been updated. It is used for example to
// disable the Update button.
hasBeenUpdated: boolean;
}
export function defaultPluginSetting(): PluginSetting {
return {
enabled: true,
deleted: false,
hasBeenUpdated: false,
};
}
@@ -100,10 +92,6 @@ export default class PluginService extends BaseService {
delete this.plugins_[pluginId];
}
private async deletePluginFiles(plugin: Plugin) {
await shim.fsDriver().remove(plugin.baseDir);
}
public pluginById(id: string): Plugin {
if (!this.plugins_[id]) throw new Error(`Plugin not found: ${id}`);
@@ -185,7 +173,7 @@ export default class PluginService extends BaseService {
const fname = filename(path);
const hash = await md5File(path);
const unpackDir = `${Setting.value('cacheDir')}/${fname}`;
const unpackDir = `${Setting.value('tempDir')}/${fname}`;
const manifestFilePath = `${unpackDir}/manifest.json`;
let manifest: any = await this.loadManifestToObject(manifestFilePath);
@@ -206,7 +194,7 @@ export default class PluginService extends BaseService {
manifest._package_hash = hash;
await shim.fsDriver().writeFile(manifestFilePath, JSON.stringify(manifest, null, '\t'), 'utf8');
await shim.fsDriver().writeFile(manifestFilePath, JSON.stringify(manifest), 'utf8');
}
return this.loadPluginFromPath(unpackDir);
@@ -357,10 +345,6 @@ export default class PluginService extends BaseService {
return plugin;
}
public async updatePluginFromRepo(repoApi: RepositoryApi, pluginId: string): Promise<Plugin> {
return this.installPluginFromRepo(repoApi, pluginId);
}
public async installPlugin(jplPath: string): Promise<Plugin> {
logger.info(`Installing plugin: "${jplPath}"`);
@@ -368,7 +352,6 @@ export default class PluginService extends BaseService {
// from where it is now to check that it is valid and to retrieve
// the plugin ID.
const preloadedPlugin = await this.loadPluginFromPath(jplPath);
await this.deletePluginFiles(preloadedPlugin);
const destPath = `${Setting.value('pluginDir')}/${preloadedPlugin.id}.jpl`;
await shim.fsDriver().copy(jplPath, destPath);
@@ -419,16 +402,6 @@ export default class PluginService extends BaseService {
return newSettings;
}
// On startup the "hasBeenUpdated" prop can be cleared since the new version
// of the plugin has now been loaded.
public clearUpdateState(settings: PluginSettings): PluginSettings {
return produce(settings, (draft: PluginSettings) => {
for (const pluginId in draft) {
if (draft[pluginId].hasBeenUpdated) draft[pluginId].hasBeenUpdated = false;
}
});
}
public async destroy() {
await this.runner_.waitForSandboxCalls();
}

View File

@@ -1,15 +1,11 @@
import shim from '../../shim';
import { PluginManifest } from './utils/types';
const md5 = require('md5');
const compareVersions = require('compare-versions');
export default class RepositoryApi {
// As a base URL, this class can support either a remote repository or a
// local one (a directory path), which is useful for testing.
//
// For now, if the baseUrl is an actual URL it's assumed it's a GitHub repo
// URL, such as https://github.com/joplin/plugins
// For now, it's assumed that the baseUrl is a GitHub repo URL, such as
// https://github.com/joplin/plugins
//
// Later on, other repo types could be supported.
private baseUrl_: string;
@@ -21,29 +17,8 @@ export default class RepositoryApi {
this.tempDir_ = tempDir;
}
public async loadManifests() {
const manifestsText = await this.fetchText('manifests.json');
try {
const manifests = JSON.parse(manifestsText);
if (!manifests) throw new Error('Invalid or missing JSON');
this.manifests_ = Object.keys(manifests).map(id => {
return manifests[id];
});
} catch (error) {
throw new Error(`Could not parse JSON: ${error.message}`);
}
}
private get isLocalRepo(): boolean {
return this.baseUrl_.indexOf('http') !== 0;
}
private get contentBaseUrl(): string {
if (this.isLocalRepo) {
return this.baseUrl_;
} else {
return `${this.baseUrl_.replace(/github\.com/, 'raw.githubusercontent.com')}/master`;
}
return `${this.baseUrl_.replace(/github\.com/, 'raw.githubusercontent.com')}/master`;
}
private fileUrl(relativePath: string): string {
@@ -51,11 +26,7 @@ export default class RepositoryApi {
}
private async fetchText(path: string): Promise<string> {
if (this.isLocalRepo) {
return shim.fsDriver().readFile(this.fileUrl(path), 'utf8');
} else {
return shim.fetchText(this.fileUrl(path));
}
return shim.fetchText(this.fileUrl(path));
}
public async search(query: string): Promise<PluginManifest[]> {
@@ -89,40 +60,27 @@ export default class RepositoryApi {
const fileUrl = this.fileUrl(`plugins/${manifest.id}/plugin.jpl`);
const hash = md5(Date.now() + Math.random());
const targetPath = `${this.tempDir_}/${hash}_${manifest.id}.jpl`;
const response = await shim.fetchBlob(fileUrl, {
path: targetPath,
});
if (this.isLocalRepo) {
await shim.fsDriver().copy(fileUrl, targetPath);
} else {
const response = await shim.fetchBlob(fileUrl, {
path: targetPath,
});
if (!response.ok) throw new Error(`Could not download plugin "${pluginId}" from "${fileUrl}"`);
}
if (!response.ok) throw new Error(`Could not download plugin "${pluginId}" from "${fileUrl}"`);
return targetPath;
}
public async manifests(): Promise<PluginManifest[]> {
if (!this.manifests_) throw new Error('Manifests have no been loaded!');
return this.manifests_;
}
public async canBeUpdatedPlugins(installedManifests: PluginManifest[]): Promise<string[]> {
const output = [];
for (const manifest of installedManifests) {
const canBe = await this.pluginCanBeUpdated(manifest.id, manifest.version);
if (canBe) output.push(manifest.id);
private async manifests(): Promise<PluginManifest[]> {
if (this.manifests_) return this.manifests_;
const manifestsText = await this.fetchText('manifests.json');
try {
const manifests = JSON.parse(manifestsText);
if (!manifests) throw new Error('Invalid or missing JSON');
this.manifests_ = Object.keys(manifests).map(id => {
return manifests[id];
});
return this.manifests_;
} catch (error) {
throw new Error(`Could not parse JSON: ${error.message}`);
}
return output;
}
public async pluginCanBeUpdated(pluginId: string, installedVersion: string): Promise<boolean> {
const manifest = (await this.manifests()).find(m => m.id === pluginId);
if (!manifest) return false;
return compareVersions(installedVersion, manifest.version) < 0;
}
}

View File

@@ -1,9 +0,0 @@
// Dummy test because the Jest setup is done but there's for now no test.
describe('dummy', () => {
it('should pass', () => {
expect(1).toBe(1);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +0,0 @@
{
"name": "@joplin/plugin-repo-cli",
"version": "1.7.4",
"description": "",
"main": "index.js",
"bin": {
"plugin-repo-cli": "./index.js"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"tsc": "tsc --project tsconfig.json",
"watch": "tsc --watch --project tsconfig.json",
"test": "jest",
"test-ci": "npm run test"
},
"author": "",
"license": "MIT",
"dependencies": {
"@joplin/lib": "^1.7.2",
"@joplin/tools": "^1.7.2",
"fs-extra": "^9.0.1",
"yargs": "^16.0.3"
},
"devDependencies": {
"@types/fs-extra": "^9.0.6",
"@types/jest": "^26.0.15",
"@types/node": "^14.14.6",
"jest": "^26.6.3",
"typescript": "^4.1.3"
}
}

View File

@@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"**/node_modules",
],
}

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/renderer",
"version": "1.7.2",
"version": "1.0.24",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/renderer",
"version": "1.7.2",
"version": "1.0.24",
"description": "The Joplin note renderer, used the mobile and desktop application",
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/renderer",
"main": "index.js",
@@ -24,7 +24,7 @@
"typescript": "^4.0.5"
},
"dependencies": {
"@joplin/fork-htmlparser2": "^4.1.19",
"@joplin/fork-htmlparser2": "^4.1.15",
"font-awesome-filetypes": "^2.1.0",
"fs-extra": "^8.1.0",
"highlight.js": "^10.2.1",

View File

@@ -1,11 +1,9 @@
#!/usr/bin/env node
import * as fs from 'fs-extra';
import * as path from 'path';
import * as process from 'process';
import validatePluginId from '@joplin/lib/services/plugins/utils/validatePluginId';
import markdownUtils, { MarkdownTableHeader, MarkdownTableRow } from '@joplin/lib/markdownUtils';
import { execCommand2, resolveRelativePathWithinDir, gitPullTry, gitRepoCleanTry, gitRepoClean } from '@joplin/tools/tool-utils.js';
const { execCommand, execCommandVerbose, rootDir, resolveRelativePathWithinDir, gitPullTry } = require('./tool-utils.js');
interface NpmPackage {
name: string;
@@ -47,7 +45,6 @@ async function checkPluginRepository(dirPath: string) {
const previousDir = process.cwd();
process.chdir(dirPath);
await gitRepoCleanTry();
await gitPullTry();
process.chdir(previousDir);
}
@@ -73,13 +70,13 @@ async function extractPluginFilesFromPackage(existingManifests: any, workDir: st
const previousDir = process.cwd();
process.chdir(workDir);
await execCommand2(`npm install ${packageName} --save --ignore-scripts`, { showOutput: false });
await execCommandVerbose('npm', ['install', packageName, '--save', '--ignore-scripts']);
const pluginDir = resolveRelativePathWithinDir(workDir, 'node_modules', packageName, 'publish');
const files = await fs.readdir(pluginDir);
const manifestFilePath = path.resolve(pluginDir, files.find((f: any) => path.extname(f) === '.json'));
const pluginFilePath = path.resolve(pluginDir, files.find((f: any) => path.extname(f) === '.jpl'));
const manifestFilePath = path.resolve(pluginDir, files.find(f => path.extname(f) === '.json'));
const pluginFilePath = path.resolve(pluginDir, files.find(f => path.extname(f) === '.jpl'));
if (!(await fs.pathExists(manifestFilePath))) throw new Error(`Could not find manifest file at ${manifestFilePath}`);
if (!(await fs.pathExists(pluginFilePath))) throw new Error(`Could not find plugin file at ${pluginFilePath}`);
@@ -158,41 +155,23 @@ async function updateReadme(readmePath: string, manifests: any) {
const tableRegex = /<!-- PLUGIN_LIST -->([^]*)<!-- PLUGIN_LIST -->/;
const content = await fs.pathExists(readmePath) ? await fs.readFile(readmePath, 'utf8') : '<!-- PLUGIN_LIST -->\n<!-- PLUGIN_LIST -->';
const content = await fs.readFile(readmePath, 'utf8');
const newContent = content.replace(tableRegex, `<!-- PLUGIN_LIST -->\n${mdTable}\n<!-- PLUGIN_LIST -->`);
await fs.writeFile(readmePath, newContent, 'utf8');
}
interface CommandBuildArgs {
pluginRepoDir: string;
}
enum ProcessingActionType {
Add = 1,
Update = 2,
}
function commitMessage(actionType: ProcessingActionType, npmPackage: NpmPackage): string {
const output: string[] = [];
if (actionType === ProcessingActionType.Add) {
output.push('New');
} else {
output.push('Update');
}
output.push(`${npmPackage.name}@${npmPackage.version}`);
return output.join(': ');
}
async function processNpmPackage(npmPackage: NpmPackage, repoDir: string) {
async function main() {
// We assume that the repository is located in a directory next to the main
// Joplin monorepo.
const repoDir = path.resolve(path.dirname(rootDir), 'joplin-plugins');
const tempDir = `${repoDir}/temp`;
const pluginManifestsPath = path.resolve(repoDir, 'manifests.json');
const obsoleteManifestsPath = path.resolve(repoDir, 'obsoletes.json');
const errorsPath = path.resolve(repoDir, 'errors.json');
await checkPluginRepository(repoDir);
await fs.mkdirp(tempDir);
const originalPluginManifests = await readJsonFile(pluginManifestsPath, {});
@@ -202,31 +181,29 @@ async function processNpmPackage(npmPackage: NpmPackage, repoDir: string) {
...obsoleteManifests,
};
const searchResults = (await execCommand('npm search joplin-plugin --searchlimit 5000 --json')).trim();
const npmPackages = pluginInfoFromSearchResults(JSON.parse(searchResults));
const packageTempDir = `${tempDir}/packages`;
await fs.mkdirp(packageTempDir);
const previousDir = process.cwd();
process.chdir(packageTempDir);
await execCommand2('npm init --yes --loglevel silent', { quiet: true });
await execCommand('npm init --yes --loglevel silent');
const errors: any = await readJsonFile(errorsPath, {});
delete errors[npmPackage.name];
const errors: any[] = [];
let actionType: ProcessingActionType = ProcessingActionType.Update;
let manifests: any = {};
try {
const destDir = `${repoDir}/plugins/`;
const manifest = await extractPluginFilesFromPackage(existingManifests, packageTempDir, npmPackage.name, destDir);
if (!existingManifests[manifest.id]) {
actionType = ProcessingActionType.Add;
for (const npmPackage of npmPackages) {
try {
const packageName = npmPackage.name;
const destDir = `${repoDir}/plugins/`;
const manifest = await extractPluginFilesFromPackage(existingManifests, packageTempDir, packageName, destDir);
if (!obsoleteManifests[manifest.id]) manifests[manifest.id] = manifest;
} catch (error) {
console.error(error);
errors.push(error);
}
if (!obsoleteManifests[manifest.id]) manifests[manifest.id] = manifest;
} catch (error) {
console.error(error);
errors[npmPackage.name] = error.message || '';
}
// We preserve the original manifests so that if a plugin has been removed
@@ -240,91 +217,20 @@ async function processNpmPackage(npmPackage: NpmPackage, repoDir: string) {
await fs.writeFile(pluginManifestsPath, JSON.stringify(manifests, null, '\t'), 'utf8');
if (Object.keys(errors).length) {
await fs.writeFile(errorsPath, JSON.stringify(errors, null, '\t'), 'utf8');
if (errors.length) {
const toWrite = errors.map((e: any) => {
return {
message: e.message || '',
};
});
await fs.writeFile(errorsPath, JSON.stringify(toWrite, null, '\t'), 'utf8');
} else {
await fs.remove(errorsPath);
}
await updateReadme(`${repoDir}/README.md`, manifests);
process.chdir(previousDir);
await fs.remove(tempDir);
process.chdir(repoDir);
if (!(await gitRepoClean())) {
await execCommand2('git add -A', { showOutput: false });
await execCommand2(['git', 'commit', '-m', commitMessage(actionType, npmPackage)], { showOutput: false });
} else {
console.info('Nothing to commit');
}
}
async function commandBuild(args: CommandBuildArgs) {
console.info(new Date(), 'Building repository...');
const repoDir = args.pluginRepoDir;
await checkPluginRepository(repoDir);
const searchResults = (await execCommand2('npm search joplin-plugin --searchlimit 5000 --json', { showOutput: false })).trim();
const npmPackages = pluginInfoFromSearchResults(JSON.parse(searchResults));
for (const npmPackage of npmPackages) {
await processNpmPackage(npmPackage, repoDir);
}
await execCommand2('git push');
}
async function commandVersion() {
const p = await readJsonFile(path.resolve(__dirname, 'package.json'));
console.info(`Version ${p.version}`);
}
async function main() {
const scriptName: string = 'plugin-repo-cli';
const commands: Record<string, Function> = {
build: commandBuild,
version: commandVersion,
};
let selectedCommand: string = '';
let selectedCommandArgs: string = '';
function setSelectedCommand(name: string, args: any) {
selectedCommand = name;
selectedCommandArgs = args;
}
require('yargs')
.scriptName(scriptName)
.usage('$0 <cmd> [args]')
.command('build <plugin-repo-dir>', 'Build the plugin repository', (yargs: any) => {
yargs.positional('plugin-repo-dir', {
type: 'string',
describe: 'Directory where the plugin repository is located',
});
}, (args: any) => setSelectedCommand('build', args))
.command('version', 'Gives version info', () => {}, (args: any) => setSelectedCommand('version', args))
.help()
.argv;
if (!selectedCommand) {
console.error(`Please provide a command name or type \`${scriptName} --help\` for help`);
process.exit(1);
}
if (!commands[selectedCommand]) {
console.error(`No such command: ${selectedCommand}`);
process.exit(1);
}
await commands[selectedCommand](selectedCommandArgs);
}
main().catch((error) => {

View File

@@ -155,15 +155,4 @@ utils.registerGulpTasks = function(gulp, tasks) {
}
};
utils.setPackagePrivateField = async function(filePath, value) {
const text = await fs.readFile(filePath, 'utf8');
const obj = JSON.parse(text);
if (!value) {
delete obj.private;
} else {
obj.private = true;
}
await fs.writeFile(filePath, JSON.stringify(obj, null, 2), 'utf8');
};
module.exports = utils;

View File

@@ -1,40 +0,0 @@
// // npx lerna add --no-bootstrap --scope @joplin/plugin-repo-builder -D jest && npx lerna bootstrap --no-ci --include-dependents --include-dependencies --scope @joplin/plugin-repo-builder
// import { execCommand2, rootDir, gitPullTry } from './tool-utils.js';
// async function main() {
// const argv = require('yargs').argv;
// console.info(process.argv);
// const args = [];
// if (argv.D) args.push('-D');
// await execCommand2('npx lerna add --no-bootstrap --scope @joplin/plugin-repo-builder -D jest');
// //npx lerna add --no-bootstrap --scope @joplin/plugin-repo-builder -D jest && npx lerna bootstrap --no-ci --include-dependents --include-dependencies --scope @joplin/plugin-repo-builder
// // process.chdir(rootDir);
// // const version = (await execCommand2('npm version patch')).trim();
// // const versionShort = version.substr(1);
// // const tagName = `server-${version}`;
// // process.chdir(rootDir);
// // console.info(`Running from: ${process.cwd()}`);
// // await execCommand2(`docker build -t "joplin/server:${versionShort}" -f Dockerfile.server .`);
// // await execCommand2(`docker tag "joplin/server:${versionShort}" "joplin/server:latest"`);
// // await execCommand2(`docker push joplin/server:${versionShort}`);
// // await execCommand2('docker push joplin/server:latest');
// // await execCommand2('git add -A');
// // await execCommand2(`git commit -m 'Server release ${version}'`);
// // await execCommand2(`git tag ${tagName}`);
// // await execCommand2('git push');
// // await execCommand2('git push --tags');
// }
// main().catch((error) => {
// console.error('Fatal error');
// console.error(error);
// process.exit(1);
// });

View File

@@ -14,9 +14,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 2.4.2\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"X-Generator: Poedit 2.3.1\n"
#: packages/app-desktop/bridge.js:106 packages/app-desktop/bridge.js:110
#: packages/app-desktop/bridge.js:126 packages/app-desktop/bridge.js:134
@@ -63,17 +61,16 @@ msgstr "Batal"
msgid ""
"The app is now going to close. Please relaunch it to complete the process."
msgstr ""
"Aplikasi akan ditutup. Silakan luncurkan ulang untuk menyelesaikan prosesnya."
#: packages/app-desktop/plugins/GotoAnything.js:431
#, fuzzy
msgid ""
"Type a note title or part of its content to jump to it. Or type # followed "
"by a tag name, or @ followed by a notebook name. Or type : to search for "
"commands."
msgstr ""
"Ketik judul catatan atau bagian di isinya untuk temukan catatan yang dicari. "
"Atau ketik # diikuti dengan nama label, atau @ diikuti dengan nama buku "
"catatan. Atau ketik : untuk mencari perintah."
"Ketik judul catatan atau bagian dari isinya untuk melompat ke sana. Atau "
"ketik # diikuti dengan nama label, atau @ diikuti dengan nama buku catatan."
#: packages/app-desktop/plugins/GotoAnything.js:456
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:20
@@ -119,8 +116,9 @@ msgid "New version: %s"
msgstr "Versi baru: %s"
#: packages/app-desktop/checkForUpdates.js:154
#, fuzzy
msgid "Download"
msgstr "Unduh"
msgstr "Terunduh"
#: packages/app-desktop/checkForUpdates.js:154
msgid "Full Release Notes"
@@ -279,6 +277,7 @@ msgid "strong text"
msgstr "teks yang ditekankan lebih kuat (strong text)"
#: packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.js:179
#, fuzzy
msgid "emphasised text"
msgstr "teks yang ditekankan (emphasized text)"
@@ -325,6 +324,7 @@ msgid "Paste"
msgstr "Tempel"
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/plugins/lists.js:2151
#, fuzzy
msgid "Checkbox list"
msgstr "Kotak centang"
@@ -355,7 +355,7 @@ msgstr "Masukkan Waktu dan Tanggal"
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js:1003
msgid "Drop notes or files here"
msgstr "Taruh catatan atau file di sini"
msgstr ""
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js:1003
#, fuzzy
@@ -368,8 +368,6 @@ msgid ""
"Please wait for all attachments to be downloaded and decrypted. You may also "
"switch to %s to edit the note."
msgstr ""
"Silakan tunggu sampai semua lampiran sudah diunduh dan didekripsi. Anda juga "
"bisa beralih ke %s untuk mengedit catatan."
#: packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.js:73
msgid "There was an error downloading this attachment:"
@@ -390,7 +388,7 @@ msgstr "Simpan sebagai..."
#: packages/app-desktop/gui/NoteEditor/utils/contextMenu.js:74
msgid "Reveal file in folder"
msgstr "Buka file di berkas"
msgstr ""
#: packages/app-desktop/gui/NoteEditor/utils/contextMenu.js:82
msgid "Copy path to clipboard"
@@ -418,26 +416,24 @@ msgid ""
"This Rich Text editor has a number of limitations and it is recommended to "
"be aware of them before using it."
msgstr ""
"Editor Rich Text ini punya beberapa keterbatasan dan Anda dianjurkan untuk "
"tahu keterbatasannya sebelum menggunakan editor ini."
#: packages/app-desktop/gui/NoteEditor/NoteEditor.js:340
msgid "Read more about it"
msgstr "Baca lebih lanjut"
msgstr ""
#: packages/app-desktop/gui/NoteEditor/NoteEditor.js:345
msgid "Dismiss"
msgstr "Abaikan"
msgstr ""
#: packages/app-desktop/gui/NoteEditor/NoteEditor.js:381
msgid "The following attachments are being watched for changes:"
msgstr "Lampiran ini sedang diawasi untuk perubahan:"
msgstr ""
#: packages/app-desktop/gui/NoteEditor/NoteEditor.js:384
msgid ""
"The attachments will no longer be watched when you switch to a different "
"note."
msgstr "Lampiran tidak akan diawasi ketika Anda berganti ke catatan lain."
msgstr ""
#: packages/app-desktop/gui/NoteEditor/NoteEditor.js:389
#, javascript-format
@@ -485,32 +481,34 @@ msgid "Horizontal Rule"
msgstr "Garis Horisontal"
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:88
#, fuzzy
msgid "Delete line"
msgstr "Hapus catatan"
msgstr "Hapus catatan?"
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:92
msgid "Undo"
msgstr "Urungkan"
msgstr ""
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:96
msgid "Redo"
msgstr "Ulangi"
msgstr ""
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:100
msgid "Indent less"
msgstr "Kurangi indentasi"
msgstr ""
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:104
msgid "Indent more"
msgstr "Tambah indentasi"
msgstr ""
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:108
#, fuzzy
msgid "Toggle comment"
msgstr "Lihat/sembunyikan komentar"
msgstr "Alihkan daftar catatan"
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:112
msgid "Sort selected lines"
msgstr "Sortir baris terpilih"
msgstr ""
#: packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.js:116
msgid "Swap line up"
@@ -582,9 +580,9 @@ msgstr "Properti catatan"
#: packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js:63
#: packages/lib/services/KeymapService.js:176
#, javascript-format
#, fuzzy, javascript-format
msgid "Error: %s"
msgstr "Terjadi kesalahan: %s"
msgstr "Kesalahan"
#: packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js:110
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:443
@@ -601,11 +599,12 @@ msgstr "Impor"
#: packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js:126
msgid "Command"
msgstr "Perintah"
msgstr ""
#: packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js:127
#, fuzzy
msgid "Keyboard Shortcut"
msgstr "Pintasan Papan Ketik"
msgstr "Mode Papan Ketik"
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:14
#: packages/app-desktop/gui/MenuBar.js:156 packages/app-desktop/app.js:338
@@ -628,8 +627,9 @@ msgid "Website and documentation"
msgstr "Situs web dan dokumentasi"
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:24
#, fuzzy
msgid "Hide Joplin"
msgstr "Sembunyikan Joplin"
msgstr "Tentang Joplin"
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:26
#: packages/app-desktop/gui/MenuBar.js:408
@@ -637,8 +637,9 @@ msgid "Close Window"
msgstr "Tutup jendela"
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:30
#, fuzzy
msgid "Preferences"
msgstr "Preferensi"
msgstr "Preferensi..."
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:30
#: packages/app-desktop/gui/MenuBar.js:284 packages/app-desktop/gui/Root.js:100
@@ -646,20 +647,19 @@ msgid "Options"
msgstr "Pilihan"
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.js:37
#, fuzzy
msgid "Invalid"
msgstr "Jawaban tidak valid"
msgstr "Jawaban tidak valid: %s"
#: packages/app-desktop/gui/KeymapConfig/ShortcutRecorder.js:49
msgid "Press the shortcut"
msgstr "Klik pintasan"
msgstr ""
#: packages/app-desktop/gui/KeymapConfig/ShortcutRecorder.js:49
msgid ""
"Press the shortcut and then press ENTER. Or, press BACKSPACE to clear the "
"shortcut."
msgstr ""
"Klik pintasan kemudian tekan ENTER atau tekan BACKSPACE untuk menghapus "
"pintasan."
#: packages/app-desktop/gui/KeymapConfig/ShortcutRecorder.js:50
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:96
@@ -673,13 +673,11 @@ msgid ""
"may take a few minutes to complete and the app needs to be restarted. To "
"proceed please click on the link."
msgstr ""
"Target sinkronisasi harus diperbarui sebelum Joplin dapat sinkronisasi. "
"Operasinya akan berjalan beberapa menit dan aplikasi perlu dimulai ulang. "
"Untuk melanjutkan, silakan klik linknya."
#: packages/app-desktop/gui/MainScreen/MainScreen.js:405
#, fuzzy
msgid "Restart and upgrade"
msgstr "Mulai ulang dan upgrade"
msgstr "Kunci master yang perlu upgrade"
#: packages/app-desktop/gui/MainScreen/MainScreen.js:409
msgid "Some items cannot be synchronised."
@@ -721,8 +719,6 @@ msgstr "Info lain"
#: packages/app-desktop/gui/MainScreen/MainScreen.js:528
msgid "Use the arrows to move the layout items. Press \"Escape\" to exit."
msgstr ""
"Gunakan tanda panah untuk memindahkan item tata letak. Tekan \"Escape\" "
"untuk keluar."
#: packages/app-desktop/gui/MainScreen/commands/showNoteContentProperties.js:18
msgid "Statistics..."
@@ -786,12 +782,13 @@ msgid "Toggle editor layout"
msgstr "Alihkan tata letak editor"
#: packages/app-desktop/gui/MainScreen/commands/toggleEditors.js:18
#, fuzzy
msgid "Toggle editors"
msgstr "Alihkan editor"
msgstr "Alihkan tata letak editor"
#: packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js:16
msgid "Change application layout"
msgstr "Ubah tata letak aplikasi"
msgstr ""
#: packages/app-desktop/gui/MainScreen/commands/renameTag.js:30
msgid "Rename tag:"
@@ -808,7 +805,7 @@ msgstr "Judul buku catatan:"
#: packages/app-desktop/gui/MainScreen/commands/showSpellCheckerMenu.js:19
#: packages/lib/services/spellChecker/SpellCheckerService.js:180
msgid "Spell checker"
msgstr "Pemeriksa ejaan"
msgstr ""
#: packages/app-desktop/gui/MainScreen/commands/showShareNoteDialog.js:16
msgid "Share note..."
@@ -1002,8 +999,9 @@ msgstr "Templat"
#: packages/app-desktop/gui/MenuBar.js:377
#: packages/app-desktop/gui/MenuBar.js:423
#, fuzzy
msgid "Export all"
msgstr "Ekspor semuanya"
msgstr "Ekspor"
#: packages/app-desktop/gui/MenuBar.js:389
#, javascript-format
@@ -1238,23 +1236,27 @@ msgstr ""
"Penting: Anda hanya perlu menjalankan ini SEKALI pada satu perangkat."
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:198
#, fuzzy
msgid "Re-encryption"
msgstr "Enkripsi ulang"
msgstr "Enkripsi"
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:213
msgid "Ignore"
msgstr "Abaikan"
msgstr ""
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:244
#: packages/app-mobile/components/screens/encryption-config.js:215
#, fuzzy
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
"continue?"
msgstr ""
"Menonaktifkan enkripsi berarti *semua* catatan dan lampiran Anda akan "
"disinkronkan ulang dan dikirim dalam keadaan tidak terenkripsi ke target "
"sinkronisasi. Apa Anda mau lanjut?"
"Mengaktifkan enkripsi berarti *semua* catatan dan lampiran Anda akan "
"disinkronkan ulang dan dikirim dalam keadaan terenkripsi ke target "
"sinkronisasi. Jangan sampai kehilangan kata sandinya karena, untuk alasan "
"keamanan, ini akan menjadi *hanya* satu-satunya cara untuk mendekripsi data! "
"Untuk mengaktifkan enkripsi, silakan masukkan kata sandi Anda di bawah ini."
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:246
#: packages/app-mobile/components/screens/encryption-config.js:150
@@ -1267,8 +1269,8 @@ msgstr ""
"Mengaktifkan enkripsi berarti *semua* catatan dan lampiran Anda akan "
"disinkronkan ulang dan dikirim dalam keadaan terenkripsi ke target "
"sinkronisasi. Jangan sampai kehilangan kata sandinya karena, untuk alasan "
"keamanan, ini *hanya* satu-satunya cara untuk mendekripsi data! Untuk "
"mengaktifkan enkripsi, silakan masukkan kata sandi Anda di bawah ini."
"keamanan, ini akan menjadi *hanya* satu-satunya cara untuk mendekripsi data! "
"Untuk mengaktifkan enkripsi, silakan masukkan kata sandi Anda di bawah ini."
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:275
#: packages/app-mobile/components/screens/encryption-config.js:258
@@ -1281,24 +1283,28 @@ msgid "Enable encryption"
msgstr "Aktifkan enkripsi"
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:290
#, fuzzy
msgid "Master Keys"
msgstr "Kunci Master"
msgstr "Kunci Master %s"
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:304
#, fuzzy
msgid "Active"
msgstr "Aktif"
msgstr "Aksi"
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:314
msgid "Source"
msgstr "Sumber"
msgstr ""
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:329
#, fuzzy
msgid "Password"
msgstr "Kata sandi"
msgstr "Kata sandi:"
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:334
#, fuzzy
msgid "Password OK"
msgstr "Kata sandi OK"
msgstr "Kata sandi:"
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:343
msgid ""
@@ -1306,10 +1312,6 @@ msgid ""
"as \"active\"). Any of the keys might be used for decryption, depending on "
"how the notes or notebooks were originally encrypted."
msgstr ""
"Peringatan: Hanya satu kunci master yang akan digunakan untuk enkripsi "
"(kunci yang ditandai \"aktif\"). Kunci yang lain mungkin digunakan untuk "
"dekripsi, tergantung bagaimana buku catatan atau catatannya awalnya "
"dienkripsi."
#: packages/app-desktop/gui/EncryptionConfigScreen.min.js:371
#: packages/app-mobile/components/screens/encryption-config.js:248
@@ -1420,7 +1422,7 @@ msgstr "Telusuri..."
#: packages/app-desktop/gui/ConfigScreen/ConfigScreen.js:456
msgid "The application must be restarted for these changes to take effect."
msgstr "Aplikasi harus dimulai ulang agar perubahan ini diterapkan."
msgstr ""
#: packages/app-desktop/gui/ConfigScreen/ConfigScreen.js:468
#: packages/app-desktop/gui/NoteList/NoteList.js:163
@@ -1429,24 +1431,25 @@ msgstr "Lakukan sekarang"
#: packages/app-desktop/gui/ConfigScreen/ConfigScreen.js:468
msgid "Later"
msgstr "Nanti"
msgstr ""
#: packages/app-desktop/gui/ConfigScreen/ConfigScreen.js:521
#, fuzzy
msgid "Restart now"
msgstr "Mulai ulang sekarang"
msgstr "Dapatkan sekarang:"
#: packages/app-desktop/gui/ConfigScreen/ButtonBar.js:27
msgid "Apply"
msgstr "Terapkan"
#: packages/app-desktop/gui/ConfigScreen/controls/PluginsStates.js:137
#, javascript-format
#, fuzzy, javascript-format
msgid "Delete plugin \"%s\"?"
msgstr "Hapus plugin \"%s\"?"
msgstr "Hapus catatan \"%s\"?"
#: packages/app-desktop/gui/ConfigScreen/controls/PluginsStates.js:181
msgid "Install plugin"
msgstr "Pasang plugin"
msgstr ""
#: packages/app-desktop/gui/PromptDialog.min.js:249
msgid "Clear"
@@ -1483,11 +1486,12 @@ msgstr "Viewer"
#: packages/app-desktop/gui/NoteContentPropertiesDialog.js:105
#, javascript-format
msgid "Read time: %s min"
msgstr "Waktu baca: %s menit"
msgstr ""
#: packages/app-desktop/gui/NoteContentPropertiesDialog.js:108
#, fuzzy
msgid "Statistics"
msgstr "Statistik"
msgstr "Statistik..."
#: packages/app-desktop/gui/NoteContentPropertiesDialog.js:113
#: packages/app-desktop/gui/ShareNoteDialog.js:175
@@ -1615,16 +1619,18 @@ msgid_plural "Copy Shareable Links"
msgstr[0] "Salin Tautan yang Dapat Dibagikan"
#: packages/app-desktop/commands/toggleExternalEditing.js:18
#, fuzzy
msgid "Toggle external editing"
msgstr "Ubah pengeditan eksternal"
msgstr "Hentikan pengeditan eksternal"
#: packages/app-desktop/commands/toggleExternalEditing.js:37
msgid "Stop"
msgstr ""
#: packages/app-desktop/commands/copyDevCommand.js:18
#, fuzzy
msgid "Copy dev mode command to clipboard"
msgstr "Salin perintah dev mode ke papan klip"
msgstr "Salin jalur ke papan klip"
#: packages/app-desktop/commands/stopExternalEditing.js:18
msgid "Stop external editing"
@@ -1641,8 +1647,9 @@ msgid "Error opening note in editor: %s"
msgstr "Terjadi kesalahan saat membuka catatan di editor: %s"
#: packages/app-desktop/commands/openProfileDirectory.js:18
#, fuzzy
msgid "Open profile directory"
msgstr "Buka direktori profil"
msgstr "Buka direktori templat"
#: packages/app-desktop/app.js:336
#, javascript-format
@@ -1670,8 +1677,9 @@ msgid "Save alarm"
msgstr "Simpan alarm"
#: packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:28
#, fuzzy
msgid "Open"
msgstr "Buka"
msgstr "Buka..."
#: packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:29
#: packages/app-mobile/components/screens/Note.js:796
@@ -1742,9 +1750,8 @@ msgid "Refresh"
msgstr "Segarkan"
#: packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js:42
#, fuzzy
msgid "Sync Target Upgrade"
msgstr "Sinkronisasi Target Peningkatan"
msgstr ""
#: packages/app-mobile/components/screens/notes.js:154
#: packages/app-cli/app/command-rmbook.js:26
@@ -1960,14 +1967,11 @@ msgid ""
"\n"
"You may turn off this option at any time in the Configuration screen."
msgstr ""
"Untuk menghubungkan geolokasi dengan catatan, aplikasi perlu izin Anda untuk "
"mengakses lokasi Anda.\n"
"\n"
"Anda dapat mematikan fungsi ini kapan saja di layar Konfigurasi."
#: packages/app-mobile/components/screens/Note.js:343
#, fuzzy
msgid "Permission needed"
msgstr "Izin diperlukan"
msgstr "Izin penggunaan kamera"
#: packages/app-mobile/components/screens/Note.js:449
#: packages/app-cli/app/command-rmnote.js:27
@@ -2048,12 +2052,11 @@ msgid ""
"The default admin password is insecure and has not been changed! [Change it "
"now](%s)"
msgstr ""
"Kata sandi default admin tidak aman dan belum diubah. [Ubah sekarang](%s)"
#: packages/lib/onedrive-api-node-utils.js:46
#, javascript-format
msgid "All potential ports are in use - please report the issue at %s"
msgstr "Semua port potensial sedang digunakan - silakan laporkan isu di %s"
msgstr ""
#: packages/lib/onedrive-api-node-utils.js:86
msgid ""
@@ -2089,8 +2092,9 @@ msgid "Dropbox"
msgstr "Dropbox"
#: packages/lib/SyncTargetJoplinServer.js:30
#, fuzzy
msgid "Joplin Server"
msgstr "Server Joplin"
msgstr "Situs web Joplin"
#: packages/lib/shim-init-node.js:201
#, javascript-format
@@ -2235,19 +2239,21 @@ msgstr ""
#: packages/lib/models/Setting.js:259
msgid "Joplin Server URL"
msgstr "URL Server Joplin"
msgstr ""
#: packages/lib/models/Setting.js:273
#, fuzzy
msgid "Joplin Server Directory"
msgstr "Direktori Ekspor Joplin"
#: packages/lib/models/Setting.js:283
msgid "Joplin Server username"
msgstr "Nama pengguna Server Joplin"
msgstr ""
#: packages/lib/models/Setting.js:293
#, fuzzy
msgid "Joplin Server password"
msgstr "Kata sandi Server Joplin"
msgstr "Masukkan kata sandi master:"
#: packages/lib/models/Setting.js:305
msgid "Attachment download behaviour"
@@ -2380,8 +2386,9 @@ msgid "Enable typographer support"
msgstr "Aktifkan dukungan typographer"
#: packages/lib/models/Setting.js:562
#, fuzzy
msgid "Enable Linkify"
msgstr "Aktifkan Linkify"
msgstr "Aktifkan riwayat catatan"
#: packages/lib/models/Setting.js:563
msgid "Enable math expressions"
@@ -2396,16 +2403,17 @@ msgid "Enable Mermaid diagrams support"
msgstr "Aktifkan dukungan diagram Mermaid"
#: packages/lib/models/Setting.js:566
#, fuzzy
msgid "Enable audio player"
msgstr "Aktifkan pemutar audio"
msgstr "Aktifkan emoji markdown"
#: packages/lib/models/Setting.js:567
msgid "Enable video player"
msgstr "Aktifkan pemutar video"
msgstr ""
#: packages/lib/models/Setting.js:568
msgid "Enable PDF viewer"
msgstr "Aktifkan penampil PDF"
msgstr ""
#: packages/lib/models/Setting.js:569
msgid "Enable ==mark== syntax"
@@ -2482,18 +2490,20 @@ msgid "Editor font family"
msgstr "Keluarga font pada editor"
#: packages/lib/models/Setting.js:641
#, fuzzy
msgid ""
"This should be a *monospace* font or some elements will render incorrectly. "
"If the font is incorrect or empty, it will default to a generic monospace "
"font."
msgstr ""
"Ini harus font *monospace* atau beberapa elemen tidak akan berfungsi "
"sebagaimana mestinya. Jika font tidak benar atau kosong, font akan disetel "
"secara otomatis ke font monospace generik."
"Ini harus font *monospace* atau ia tidak akan berfungsi sebagaimana "
"mestinya. Jika font tidak benar atau kosong, font akan disetel secara "
"otomatis ke font monospace generik."
#: packages/lib/models/Setting.js:662
#, fuzzy
msgid "Custom stylesheet for rendered Markdown"
msgstr "Lembar gaya untuk rendering Markdown"
msgstr "Lembar gaya khusus untuk gaya keseluruhan aplikasi Joplin"
#: packages/lib/models/Setting.js:678
msgid "Custom stylesheet for Joplin-wide app styles"
@@ -2724,8 +2734,9 @@ msgid "Web Clipper"
msgstr "Web Clipper"
#: packages/lib/models/Setting.js:1389
#, fuzzy
msgid "Keyboard Shortcuts"
msgstr "Pintasan Papan Ketik"
msgstr "Mode Papan Ketik"
#: packages/lib/models/Setting.js:1396
msgid ""
@@ -2763,20 +2774,17 @@ msgid "Downloaded"
msgstr "Terunduh"
#: packages/lib/models/Resource.js:382
#, javascript-format
#, fuzzy, javascript-format
msgid "Attachment conflict: \"%s\""
msgstr "Konflik lampiran: \"%s\""
msgstr "Lampiran"
#: packages/lib/models/Resource.js:383
#, javascript-format
#, fuzzy, javascript-format
msgid ""
"There was a [conflict](%s) on the attachment below.\n"
"\n"
"%s"
msgstr ""
"Terjadi konflik (%s) saat mengunduh lampiran ini:\n"
"\n"
"%s"
msgstr "Terjadi kesalahan saat mengunduh lampiran ini:"
#: packages/lib/models/Tag.js:202
#, javascript-format
@@ -2970,7 +2978,6 @@ msgstr "Sedang berlangsung"
msgid ""
"Unknown item type downloaded - please upgrade Joplin to the latest version"
msgstr ""
"Tipe item tidak dikenal diunduh - silakan perbarui Joplin ke versi terbaru"
#: packages/lib/JoplinServerApi.js:71
#, javascript-format
@@ -3000,7 +3007,7 @@ msgstr "Sistem berkas"
#: packages/lib/commands/historyForward.js:17
msgid "Forward"
msgstr "Maju"
msgstr ""
#: packages/lib/versionInfo.js:10
#, javascript-format
@@ -3030,7 +3037,7 @@ msgstr "Versi Profil: %s"
#: packages/lib/versionInfo.js:25
#, javascript-format
msgid "Keychain Supported: %s"
msgstr "Keychain Didukung: %s"
msgstr ""
#: packages/lib/services/RevisionService.js:221
msgid "Restored Notes"
@@ -3079,14 +3086,14 @@ msgid "HTML Directory"
msgstr "Direktori HTML"
#: packages/lib/services/interop/InteropService.js:127
#, javascript-format
#, fuzzy, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\" and output \"%s\""
msgstr "Tidak dapat memuat modul \"%s\" untuk format \"%s\" dan output \"%s\""
msgstr "Tidak dapat memuat modul \"%s\" untuk format \"%s\""
#: packages/lib/services/interop/InteropService.js:150
#, javascript-format
#, fuzzy, javascript-format
msgid "Cannot load \"%s\" module for format \"%s\" and target \"%s\""
msgstr "Tidak dapat memuat modul \"%s\" untuk format \"%s\" dan target \"%s\""
msgstr "Tidak dapat memuat modul \"%s\" untuk format \"%s\""
#: packages/lib/services/interop/InteropService.js:194
#: packages/lib/services/interop/InteropService.js:206
@@ -3124,23 +3131,23 @@ msgstr "Silakan tentukan format impor untuk %s"
#: packages/lib/services/KeymapService.js:273
msgid "command"
msgstr "perintah"
msgstr ""
#: packages/lib/services/KeymapService.js:273
#: packages/lib/services/KeymapService.js:278
#, javascript-format
msgid "\"%s\" is missing the required \"%s\" property."
msgstr "\"%s\" tidak menemukan properti \"%s\"."
msgstr ""
#: packages/lib/services/KeymapService.js:278
#: packages/lib/services/KeymapService.js:285
msgid "accelerator"
msgstr "akselerator"
msgstr ""
#: packages/lib/services/KeymapService.js:285
#, javascript-format
#, fuzzy, javascript-format
msgid "Invalid %s: %s."
msgstr "%s tidak valid: %s."
msgstr "Jawaban tidak valid: %s"
#: packages/lib/services/KeymapService.js:303
#, javascript-format
@@ -3148,13 +3155,11 @@ msgid ""
"Accelerator \"%s\" is used for \"%s\" and \"%s\" commands. This may lead to "
"unexpected behaviour."
msgstr ""
"Akselerator \"%s\" digunakan untuk perintah \"%s\" dan \"%s\". Ini bisa "
"memberikan dampak tidak terduga."
#: packages/lib/services/KeymapService.js:328
#, javascript-format
msgid "Accelerator \"%s\" is not valid."
msgstr "Akselerator \"%s\" tidak valid."
msgstr ""
#: packages/lib/services/report.js:121
msgid "Items that cannot be synchronised"
@@ -3218,8 +3223,9 @@ msgid "Downloaded and encrypted"
msgstr "Terunduh dan terenkripsi"
#: packages/lib/services/report.js:193
#, fuzzy
msgid "Created locally"
msgstr "Item lokal yang dibuat"
msgstr "Item lokal yang dibuat: %d."
#: packages/lib/services/report.js:206
msgid "Attachments that could not be downloaded"
@@ -3274,19 +3280,20 @@ msgstr "Di %s: %s"
#: packages/lib/services/spellChecker/SpellCheckerService.js:107
msgid "No suggestions"
msgstr "Tidak ada saran"
msgstr ""
#: packages/lib/services/spellChecker/SpellCheckerService.js:114
msgid "Add to dictionary"
msgstr "Tambahkan ke kamus"
msgstr ""
#: packages/lib/services/spellChecker/SpellCheckerService.js:153
msgid "Use spell checker"
msgstr "Gunakan pemeriksa ejaan"
msgstr ""
#: packages/lib/services/spellChecker/SpellCheckerService.js:173
#, fuzzy
msgid "Change language"
msgstr "Ubah bahasa"
msgstr "Bahasa"
#: packages/app-cli/app/command-cp.js:13
msgid ""
@@ -3361,9 +3368,9 @@ msgid "Do not ask for confirmation."
msgstr "Jangan meminta konfirmasi."
#: packages/app-cli/app/command-import.js:27
#, javascript-format
#, fuzzy, javascript-format
msgid "Output format: %s"
msgstr "Format output: %s"
msgstr "Format sumber: %s"
#: packages/app-cli/app/command-import.js:65
msgid "Importing notes..."
@@ -3517,7 +3524,7 @@ msgstr ""
#: packages/app-cli/app/command-sync.js:35
msgid "Upgrade the sync target to the latest version."
msgstr "Perbarui target sinkronisasi ke versi terakhir."
msgstr ""
#: packages/app-cli/app/command-sync.js:105
#, javascript-format
@@ -3547,6 +3554,7 @@ msgid "Synchronisation target: %s (%s)"
msgstr "Target sinkronisasi: %s (%s)"
#: packages/app-cli/app/command-sync.js:177
#, fuzzy
msgid "Cannot initialise synchroniser."
msgstr "Tidak dapat menginisiasi penyinkron."
@@ -3561,7 +3569,7 @@ msgstr "Mengunduh sumber daya..."
#: packages/app-cli/app/command-sync.js:242
#, javascript-format
msgid "Sync target must be upgraded! Run `%s` to proceed."
msgstr "Target sinkronisasi harus diperbarui. Jalankan `%s` untuk melanjutkan."
msgstr ""
#: packages/app-cli/app/command-sync.js:260
msgid "Cancelling... Please wait."
@@ -3620,11 +3628,12 @@ msgstr "Catatan tidak ada: \"%s\". Buat?"
#: packages/app-cli/app/command-edit.js:75
msgid "Starting to edit note. Close the editor to get back to the prompt."
msgstr "Mulai mengedit catatan. Tutup editor untuk kembali ke prompt."
msgstr ""
#: packages/app-cli/app/command-edit.js:97
#, fuzzy
msgid "Note has been saved."
msgstr "Catatan sudah disimpan."
msgstr "Belum ada buku catatan yang ditentukan."
#: packages/app-cli/app/app-gui.js:452
msgid "To delete a tag, untag the associated notes."

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/tools",
"version": "1.7.2",
"version": "1.0.16",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/tools",
"version": "1.7.2",
"version": "1.0.16",
"description": "Various tools for Joplin",
"main": "index.js",
"author": "Laurent Cozic",
@@ -18,7 +18,7 @@
},
"license": "MIT",
"dependencies": {
"@joplin/lib": "^1.7.2",
"@joplin/lib": "^1.0.16",
"execa": "^4.1.0",
"fs-extra": "^4.0.3",
"gettext-parser": "^1.3.0",

View File

@@ -1,40 +1,37 @@
'use strict';
const __awaiter = (this && this.__awaiter) || function(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); }
return new (P || (P = Promise))(function(resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator['throw'](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, '__esModule', { value: true });
const tool_utils_1 = require('./tool-utils');
const appDir = `${tool_utils_1.rootDir}/packages/app-desktop`;
function main() {
return __awaiter(this, void 0, void 0, function* () {
yield tool_utils_1.gitPullTry(false);
const argv = require('yargs').argv;
process.chdir(appDir);
console.info(`Running from: ${process.cwd()}`);
const version = (yield tool_utils_1.execCommand2('npm version patch')).trim();
const tagName = version;
console.info(`New version number: ${version}`);
console.info(yield tool_utils_1.execCommand2('git add -A'));
console.info(yield tool_utils_1.execCommand2(`git commit -m "Desktop release ${version}"`));
console.info(yield tool_utils_1.execCommand2(`git tag ${tagName}`));
console.info(yield tool_utils_1.execCommand2('git push && git push --tags'));
const releaseOptions = { isDraft: true, isPreRelease: !!argv.beta };
console.info('Release options: ', releaseOptions);
const release = yield tool_utils_1.githubRelease('joplin', tagName, releaseOptions);
console.info(`Created GitHub release: ${release.html_url}`);
console.info('GitHub release page: https://github.com/laurent22/joplin/releases');
console.info(`To create changelog: node packages/tools/git-changelog.js ${version}`);
});
const { execCommand, githubRelease, rootDir } = require('./tool-utils.js');
const appDir = `${rootDir}/packages/app-desktop`;
async function main() {
const argv = require('yargs').argv;
process.chdir(appDir);
console.info(`Running from: ${process.cwd()}`);
const version = (await execCommand('npm version patch')).trim();
const tagName = version;
console.info(`New version number: ${version}`);
console.info(await execCommand('git add -A'));
console.info(await execCommand(`git commit -m "Desktop release ${version}"`));
console.info(await execCommand(`git tag ${tagName}`));
console.info(await execCommand('git push && git push --tags'));
const releaseOptions = { isDraft: true, isPreRelease: !!argv.beta };
console.info('Release options: ', releaseOptions);
const release = await githubRelease('joplin', tagName, releaseOptions);
console.info(`Created GitHub release: ${release.html_url}`);
console.info('GitHub release page: https://github.com/laurent22/joplin/releases');
console.info(`To create changelog: node packages/tools/git-changelog.js ${version}`);
}
main().catch((error) => {
console.error('Fatal error');
console.error(error);
process.exit(1);
});
// # sourceMappingURL=release-electron.js.map

View File

@@ -1,39 +0,0 @@
import { execCommand2, githubRelease, gitPullTry, rootDir } from './tool-utils';
const appDir = `${rootDir}/packages/app-desktop`;
async function main() {
await gitPullTry(false);
const argv = require('yargs').argv;
process.chdir(appDir);
console.info(`Running from: ${process.cwd()}`);
const version = (await execCommand2('npm version patch')).trim();
const tagName = version;
console.info(`New version number: ${version}`);
console.info(await execCommand2('git add -A'));
console.info(await execCommand2(`git commit -m "Desktop release ${version}"`));
console.info(await execCommand2(`git tag ${tagName}`));
console.info(await execCommand2('git push && git push --tags'));
const releaseOptions = { isDraft: true, isPreRelease: !!argv.beta };
console.info('Release options: ', releaseOptions);
const release = await githubRelease('joplin', tagName, releaseOptions);
console.info(`Created GitHub release: ${release.html_url}`);
console.info('GitHub release page: https://github.com/laurent22/joplin/releases');
console.info(`To create changelog: node packages/tools/git-changelog.js ${version}`);
}
main().catch((error) => {
console.error('Fatal error');
console.error(error);
process.exit(1);
});

View File

@@ -1,4 +1,4 @@
import { execCommand2, rootDir, gitPullTry } from './tool-utils';
const { execCommand2, rootDir, gitPullTry } = require('./tool-utils.js');
const serverDir = `${rootDir}/packages/server`;

View File

@@ -80,10 +80,6 @@ async function main() {
await updatePackageVersion(`${rootDir}/packages/app-cli/package.json`, majorMinorVersion);
await updatePackageVersion(`${rootDir}/packages/generator-joplin/package.json`, majorMinorVersion);
await updatePackageVersion(`${rootDir}/packages/server/package.json`, majorMinorVersion);
await updatePackageVersion(`${rootDir}/packages/plugin-repo-cli/package.json`, majorMinorVersion);
await updatePackageVersion(`${rootDir}/packages/lib/package.json`, majorMinorVersion);
await updatePackageVersion(`${rootDir}/packages/renderer/package.json`, majorMinorVersion);
await updatePackageVersion(`${rootDir}/packages/tools/package.json`, majorMinorVersion);
await updateGradleVersion(`${rootDir}/packages/app-mobile/android/app/build.gradle`, majorMinorVersion);
await updateCodeProjVersion(`${rootDir}/packages/app-mobile/ios/Joplin.xcodeproj/project.pbxproj`, iosVersionHack(majorMinorVersion));
await updateClipperManifestVersion(`${rootDir}/packages/app-clipper/manifest.json`, majorMinorVersion);

View File

@@ -1,50 +1,16 @@
import * as fs from 'fs-extra';
const fetch = require('node-fetch');
const fs = require('fs-extra');
const execa = require('execa');
const { execSync } = require('child_process');
const { splitCommandString } = require('@joplin/lib/string-utils');
function quotePath(path: string) {
if (!path) return '';
if (path.indexOf('"') < 0 && path.indexOf(' ') < 0) return path;
path = path.replace(/"/, '\\"');
return `"${path}"`;
}
const toolUtils = {};
function commandToString(commandName: string, args: string[] = []) {
const output = [quotePath(commandName)];
for (const arg of args) {
output.push(quotePath(arg));
}
return output.join(' ');
}
async function loadGitHubUsernameCache() {
const path = `${__dirname}/github_username_cache.json`;
if (await fs.pathExists(path)) {
const jsonString = await fs.readFile(path, 'utf8');
return JSON.parse(jsonString);
}
return {};
}
async function saveGitHubUsernameCache(cache: any) {
const path = `${__dirname}/github_username_cache.json`;
await fs.writeFile(path, JSON.stringify(cache));
}
// Returns the project root dir
export const rootDir = require('path').dirname(require('path').dirname(__dirname));
export function execCommand(command: string) {
toolUtils.execCommand = function(command) {
const exec = require('child_process').exec;
return new Promise((resolve, reject) => {
exec(command, (error: any, stdout: any, stderr: any) => {
exec(command, (error, stdout, stderr) => {
if (error) {
if (error.signal == 'SIGTERM') {
resolve('Process was killed');
@@ -56,28 +22,39 @@ export function execCommand(command: string) {
}
});
});
};
function quotePath(path) {
if (!path) return '';
if (path.indexOf('"') < 0 && path.indexOf(' ') < 0) return path;
path = path.replace(/"/, '\\"');
return `"${path}"`;
}
export function resolveRelativePathWithinDir(baseDir: string, ...relativePath: string[]) {
function commandToString(commandName, args = []) {
const output = [quotePath(commandName)];
for (const arg of args) {
output.push(quotePath(arg));
}
return output.join(' ');
}
toolUtils.resolveRelativePathWithinDir = function(baseDir, ...relativePath) {
const path = require('path');
const resolvedBaseDir = path.resolve(baseDir);
const resolvedPath = path.resolve(baseDir, ...relativePath);
if (resolvedPath.indexOf(resolvedBaseDir) !== 0) throw new Error(`Resolved path for relative path "${JSON.stringify(relativePath)}" is not within base directory "${baseDir}" (Was resolved to ${resolvedPath})`);
return resolvedPath;
}
};
export function execCommandVerbose(commandName: string, args: string[] = []) {
toolUtils.execCommandVerbose = function(commandName, args = []) {
console.info(`> ${commandToString(commandName, args)}`);
const promise = execa(commandName, args);
promise.stdout.pipe(process.stdout);
return promise;
}
interface ExecCommandOptions {
showInput?: boolean;
showOutput?: boolean;
quiet?: boolean;
}
};
// There's lot of execCommandXXX functions, but eventually all scripts should
// use the one below, which supports:
@@ -85,63 +62,66 @@ interface ExecCommandOptions {
// - Printing the command being executed
// - Printing the output in real time (piping to stdout)
// - Returning the command result as string
export async function execCommand2(command: string | string[], options: ExecCommandOptions = null): Promise<string> {
toolUtils.execCommand2 = async function(command, options = null) {
options = {
showInput: true,
showOutput: true,
quiet: false,
...options,
};
if (options.quiet) {
options.showInput = false;
options.showOutput = false;
}
if (options.showInput) {
if (typeof command === 'string') {
console.info(`> ${command}`);
} else {
console.info(`> ${commandToString(command[0], command.slice(1))}`);
}
}
const args: string[] = typeof command === 'string' ? splitCommandString(command) : command as string[];
if (options.showInput) console.info(`> ${command}`);
const args = splitCommandString(command);
const executableName = args[0];
args.splice(0, 1);
const promise = execa(executableName, args);
if (options.showOutput) promise.stdout.pipe(process.stdout);
const result = await promise;
return result.stdout.trim();
}
return result.stdout;
};
export function execCommandWithPipes(executable: string, args: string[]) {
toolUtils.execCommandWithPipes = function(executable, args) {
const spawn = require('child_process').spawn;
return new Promise((resolve, reject) => {
const child = spawn(executable, args, { stdio: 'inherit' });
child.on('error', (error: any) => {
child.on('error', (error) => {
reject(error);
});
child.on('close', (code: any) => {
child.on('close', (code) => {
if (code !== 0) {
reject(`Ended with code ${code}`);
} else {
resolve(null);
resolve();
}
});
});
}
};
export function toSystemSlashes(path: string) {
toolUtils.toSystemSlashes = function(path) {
const os = process.platform;
if (os === 'win32') return path.replace(/\//g, '\\');
return path.replace(/\\/g, '/');
}
};
export async function setPackagePrivateField(filePath: string, value: any) {
toolUtils.deleteLink = async function(path) {
if (toolUtils.isWindows()) {
try {
execSync(`rmdir "${toolUtils.toSystemSlashes(path)}"`, { stdio: 'pipe' });
} catch (error) {
// console.info('Error: ' + error.message);
}
} else {
try {
fs.unlinkSync(toolUtils.toSystemSlashes(path));
} catch (error) {
// ignore
}
}
};
toolUtils.setPackagePrivateField = async function(filePath, value) {
const text = await fs.readFile(filePath, 'utf8');
const obj = JSON.parse(text);
if (!value) {
@@ -150,9 +130,9 @@ export async function setPackagePrivateField(filePath: string, value: any) {
obj.private = true;
}
await fs.writeFile(filePath, JSON.stringify(obj, null, 2), 'utf8');
}
};
export async function credentialDir() {
toolUtils.credentialDir = async function() {
const username = require('os').userInfo().username;
const toTry = [
@@ -163,45 +143,48 @@ export async function credentialDir() {
];
for (const dirPath of toTry) {
if (await fs.pathExists(dirPath)) return dirPath;
if (await fs.exists(dirPath)) return dirPath;
}
throw new Error(`Could not find credential directory in any of these paths: ${JSON.stringify(toTry)}`);
}
};
export async function credentialFile(filename: string) {
const rootDir = await credentialDir();
// Returns the project root dir
toolUtils.rootDir = require('path').dirname(require('path').dirname(__dirname));
toolUtils.credentialFile = async function(filename) {
const rootDir = await toolUtils.credentialDir();
const output = `${rootDir}/${filename}`;
if (!(await fs.pathExists(output))) throw new Error(`No such file: ${output}`);
if (!(await fs.exists(output))) throw new Error(`No such file: ${output}`);
return output;
}
};
export async function readCredentialFile(filename: string) {
const filePath = await credentialFile(filename);
toolUtils.readCredentialFile = async function(filename) {
const filePath = await toolUtils.credentialFile(filename);
const r = await fs.readFile(filePath);
return r.toString();
}
};
export async function downloadFile(url: string, targetPath: string) {
toolUtils.downloadFile = function(url, targetPath) {
const https = require('https');
const fs = require('fs');
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(targetPath);
https.get(url, function(response: any) {
https.get(url, function(response) {
if (response.statusCode !== 200) reject(new Error(`HTTP error ${response.statusCode}`));
response.pipe(file);
file.on('finish', function() {
// file.close();
resolve(null);
resolve();
});
}).on('error', (error: any) => {
}).on('error', (error) => {
reject(error);
});
});
}
};
export function fileSha256(filePath: string) {
toolUtils.fileSha256 = function(filePath) {
return new Promise((resolve, reject) => {
const crypto = require('crypto');
const fs = require('fs');
@@ -209,18 +192,18 @@ export function fileSha256(filePath: string) {
const shasum = crypto.createHash(algo);
const s = fs.ReadStream(filePath);
s.on('data', function(d: any) { shasum.update(d); });
s.on('data', function(d) { shasum.update(d); });
s.on('end', function() {
const d = shasum.digest('hex');
resolve(d);
});
s.on('error', function(error: any) {
s.on('error', function(error) {
reject(error);
});
});
}
};
export async function unlinkForce(filePath: string) {
toolUtils.unlinkForce = async function(filePath) {
const fs = require('fs-extra');
try {
@@ -229,13 +212,13 @@ export async function unlinkForce(filePath: string) {
if (error.code === 'ENOENT') return;
throw error;
}
}
};
export function fileExists(filePath: string) {
toolUtils.fileExists = async function(filePath) {
const fs = require('fs-extra');
return new Promise((resolve, reject) => {
fs.stat(filePath, function(err: any) {
fs.stat(filePath, function(err) {
if (err == null) {
resolve(true);
} else if (err.code == 'ENOENT') {
@@ -245,39 +228,44 @@ export function fileExists(filePath: string) {
}
});
});
};
async function loadGitHubUsernameCache() {
const path = `${__dirname}/github_username_cache.json`;
if (await fs.exists(path)) {
const jsonString = await fs.readFile(path);
return JSON.parse(jsonString);
}
return {};
}
export async function gitRepoClean(): Promise<boolean> {
const output = await execCommand2('git status --porcelain', { quiet: true });
return !output.trim();
async function saveGitHubUsernameCache(cache) {
const path = `${__dirname}/github_username_cache.json`;
await fs.writeFile(path, JSON.stringify(cache));
}
export async function gitRepoCleanTry() {
if (!(await gitRepoClean())) throw new Error(`There are pending changes in the repository: ${process.cwd()}`);
}
export async function gitPullTry(ignoreIfNotBranch = true) {
toolUtils.gitPullTry = async function() {
try {
await execCommand('git pull');
await toolUtils.execCommand('git pull');
} catch (error) {
if (ignoreIfNotBranch && error.message.includes('no tracking information for the current branch')) {
if (error.message.includes('no tracking information for the current branch')) {
console.info('Skipping git pull because no tracking information on current branch');
} else {
throw error;
}
}
}
};
export async function githubUsername(email: string, name: string) {
toolUtils.githubUsername = async function(email, name) {
const cache = await loadGitHubUsernameCache();
const cacheKey = `${email}:${name}`;
if (cacheKey in cache) return cache[cacheKey];
let output = null;
const oauthToken = await githubOauthToken();
const oauthToken = await toolUtils.githubOauthToken();
const urlsToTry = [
`https://api.github.com/search/users?q=${encodeURI(email)}+in:email`,
@@ -308,23 +296,23 @@ export async function githubUsername(email: string, name: string) {
await saveGitHubUsernameCache(cache);
return output;
}
};
export function patreonOauthToken() {
return readCredentialFile('patreon_oauth_token.txt');
}
toolUtils.patreonOauthToken = async function() {
return toolUtils.readCredentialFile('patreon_oauth_token.txt');
};
export function githubOauthToken() {
return readCredentialFile('github_oauth_token.txt');
}
toolUtils.githubOauthToken = async function() {
return toolUtils.readCredentialFile('github_oauth_token.txt');
};
export async function githubRelease(project: string, tagName: string, options: any = null) {
toolUtils.githubRelease = async function(project, tagName, options = null) {
options = Object.assign({}, {
isDraft: false,
isPreRelease: false,
}, options);
const oauthToken = await githubOauthToken();
const oauthToken = await toolUtils.githubOauthToken();
const response = await fetch(`https://api.github.com/repos/laurent22/${project}/releases`, {
method: 'POST',
@@ -348,9 +336,9 @@ export async function githubRelease(project: string, tagName: string, options: a
if (!responseJson.url) throw new Error(`No URL for release: ${responseText}`);
return responseJson;
}
};
export function readline(question: string) {
toolUtils.readline = question => {
return new Promise((resolve) => {
const readline = require('readline');
@@ -359,61 +347,63 @@ export function readline(question: string) {
output: process.stdout,
});
rl.question(`${question} `, (answer: string) => {
rl.question(`${question} `, answer => {
resolve(answer);
rl.close();
});
});
}
};
export function isLinux() {
toolUtils.isLinux = () => {
return process && process.platform === 'linux';
}
};
export function isWindows() {
toolUtils.isWindows = () => {
return process && process.platform === 'win32';
}
};
export function isMac() {
toolUtils.isMac = () => {
return process && process.platform === 'darwin';
}
};
export async function insertContentIntoFile(filePath: string, markerOpen: string, markerClose: string, contentToInsert: string) {
toolUtils.insertContentIntoFile = async function(filePath, markerOpen, markerClose, contentToInsert) {
const fs = require('fs-extra');
let content = await fs.readFile(filePath, 'utf-8');
// [^]* matches any character including new lines
const regex = new RegExp(`${markerOpen}[^]*?${markerClose}`);
content = content.replace(regex, markerOpen + contentToInsert + markerClose);
await fs.writeFile(filePath, content);
}
};
export function dirname(path: string) {
toolUtils.dirname = (path) => {
if (!path) throw new Error('Path is empty');
const s = path.split(/\/|\\/);
s.pop();
return s.join('/');
}
};
export function basename(path: string) {
toolUtils.basename = (path) => {
if (!path) throw new Error('Path is empty');
const s = path.split(/\/|\\/);
return s[s.length - 1];
}
};
export function filename(path: string, includeDir = false) {
toolUtils.filename = (path, includeDir = false) => {
if (!path) throw new Error('Path is empty');
const output = includeDir ? path : basename(path);
const output = includeDir ? path : toolUtils.basename(path);
if (output.indexOf('.') < 0) return output;
const splitted = output.split('.');
splitted.pop();
return splitted.join('.');
}
};
export function fileExtension(path: string) {
toolUtils.fileExtension = (path) => {
if (!path) throw new Error('Path is empty');
const output = path.split('.');
if (output.length <= 1) return '';
return output[output.length - 1];
}
};
module.exports = toolUtils;

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/turndown-plugin-gfm",
"version": "1.0.23",
"version": "1.0.19",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -4,7 +4,7 @@
"publishConfig": {
"access": "public"
},
"version": "1.0.23",
"version": "1.0.19",
"author": "Dom Christie",
"main": "lib/turndown-plugin-gfm.cjs.js",
"module": "lib/turndown-plugin-gfm.es.js",

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/turndown",
"version": "4.0.41",
"version": "4.0.37",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,7 +1,7 @@
{
"name": "@joplin/turndown",
"description": "A library that converts HTML to Markdown",
"version": "4.0.41",
"version": "4.0.37",
"author": "Dom Christie",
"main": "lib/turndown.cjs.js",
"module": "lib/turndown.es.js",