1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-09-05 20:56:22 +02:00

Compare commits

...

28 Commits

Author SHA1 Message Date
Laurent Cozic
fe7691d786 Plugin Generator release v1.6.10 2021-01-05 21:59:41 +00:00
Laurent Cozic
db4f35b936 Generator: Added "npm run update" command 2021-01-05 21:59:16 +00:00
Laurent Cozic
72aabf71f3 Plugin Generator release v1.6.9 2021-01-05 21:49:37 +00:00
Laurent Cozic
d145ce1876 Generator: Fixed package name conversion and added test units 2021-01-05 21:49:08 +00:00
Laurent Cozic
ab6f02a949 Plugin Generator release v1.6.8 2021-01-05 18:28:36 +00:00
Laurent Cozic
1339414443 Generator: Merge ignore files when updating plugin 2021-01-05 18:28:12 +00:00
Laurent Cozic
732ca53b70 Plugin Generator release v1.6.7 2021-01-05 18:20:00 +00:00
Laurent Cozic
fa749d6d2a Generator: Merge ignore files when updating plugin 2021-01-05 18:18:40 +00:00
Laurent Cozic
6e69343bb6 Plugin Generator release v1.6.6 2021-01-05 18:02:35 +00:00
Laurent Cozic
520454affb Generator: Do not overwrite README.md when updating 2021-01-05 17:57:42 +00:00
Laurent Cozic
734514b6d8 Tools: Add script to build plugin repository 2021-01-05 15:25:15 +00:00
Laurent Cozic
ceb252b9ad Add "plugins" sub-package to group official Joplin plugins 2021-01-05 13:38:38 +00:00
Laurent Cozic
f121245e40 Plugin Generator release v1.6.5 2021-01-05 13:27:41 +00:00
Laurent Cozic
215a1e0240 Generator: Cleaned up prompts 2021-01-05 12:09:43 +00:00
Laurent Cozic
8ed36d7a29 Plugin Generator release v1.6.4 2021-01-05 11:31:26 +00:00
Laurent Cozic
2b33df2955 Tools: Fixed git-changelog for server 2021-01-04 22:33:36 +00:00
Laurent Cozic
5c283e4508 Desktop release v1.6.2 2021-01-04 19:39:01 +00:00
Laurent Cozic
c94ee5d99a Plugin Generator release v1.6.3 2021-01-04 19:38:41 +00:00
Laurent Cozic
4a258a2427 Generator: Better handling of package.json keywords 2021-01-04 19:32:30 +00:00
Laurent Cozic
330ef6f7e6 Tools: Fixed plugin package.json 2021-01-04 19:18:29 +00:00
Laurent Cozic
4a579393f3 Plugins: Updated types 2021-01-04 18:46:43 +00:00
Laurent Cozic
1091795a3a Generator: Update plugin generator to handle requirements of coming plugin repository 2021-01-04 18:45:43 +00:00
Laurent Cozic
b5fc206202 Plugins: Added joplin.workspace.selectedFolder() 2021-01-04 16:49:59 +00:00
Laurent Cozic
794fb6a122 Plugins: Fixes #4285: Allow API paths that contain 4 elements 2021-01-04 16:17:06 +00:00
Laurent Cozic
ec7cccf573 Doc: Fixed server doc 2021-01-04 16:16:09 +00:00
Laurent Cozic
e94a1cac1c Server release v1.6.4 2021-01-04 15:03:36 +00:00
Laurent Cozic
76ff0b9c11 Server: Added script to create release 2021-01-04 15:03:17 +00:00
Laurent Cozic
fb01c64133 Server: Added script to create release 2021-01-04 15:01:45 +00:00
149 changed files with 12507 additions and 357 deletions

View File

@@ -1334,6 +1334,15 @@ packages/lib/uuid.js.map
packages/lib/versionInfo.d.ts
packages/lib/versionInfo.js
packages/lib/versionInfo.js.map
packages/plugins/ToggleSideBars/api/index.d.ts
packages/plugins/ToggleSideBars/api/index.js
packages/plugins/ToggleSideBars/api/index.js.map
packages/plugins/ToggleSideBars/api/types.d.ts
packages/plugins/ToggleSideBars/api/types.js
packages/plugins/ToggleSideBars/api/types.js.map
packages/plugins/ToggleSideBars/src/index.d.ts
packages/plugins/ToggleSideBars/src/index.js
packages/plugins/ToggleSideBars/src/index.js.map
packages/renderer/HtmlToHtml.d.ts
packages/renderer/HtmlToHtml.js
packages/renderer/HtmlToHtml.js.map
@@ -1646,4 +1655,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/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
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD

15
.gitignore vendored
View File

@@ -1323,6 +1323,15 @@ packages/lib/uuid.js.map
packages/lib/versionInfo.d.ts
packages/lib/versionInfo.js
packages/lib/versionInfo.js.map
packages/plugins/ToggleSideBars/api/index.d.ts
packages/plugins/ToggleSideBars/api/index.js
packages/plugins/ToggleSideBars/api/index.js.map
packages/plugins/ToggleSideBars/api/types.d.ts
packages/plugins/ToggleSideBars/api/types.js
packages/plugins/ToggleSideBars/api/types.js.map
packages/plugins/ToggleSideBars/src/index.d.ts
packages/plugins/ToggleSideBars/src/index.js
packages/plugins/ToggleSideBars/src/index.js.map
packages/renderer/HtmlToHtml.d.ts
packages/renderer/HtmlToHtml.js
packages/renderer/HtmlToHtml.js.map
@@ -1635,4 +1644,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/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
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD

View File

@@ -7,35 +7,30 @@
},
"license": "MIT",
"scripts": {
"addPackageCli": "lerna add --scope joplin",
"addPackageCliD": "lerna add --scope joplin -D",
"addPackageDesktop": "lerna add --scope @joplin/app-desktop",
"addPackageDesktopD": "lerna add --scope @joplin/app-desktop -D",
"addPackageMobile": "lerna add --scope @joplin/app-mobile",
"addPackageMobileD": "lerna add --scope @joplin/app-mobile -D",
"bootstrap": "lerna bootstrap --no-ci",
"bootstrapIgnoreScripts": "lerna bootstrap --ignore-scripts --no-ci",
"build": "lerna run build && npm run tsc",
"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/",
"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",
"clean": "lerna clean -y && lerna run clean",
"circularDependencyCheck": "npx madge --warning --circular --extensions js ./",
"clean": "lerna clean -y && lerna run clean",
"generateDatabaseTypes": "node packages/tools/generate-database-types",
"linkChecker": "linkchecker https://joplinapp.org",
"linter-ci": "./node_modules/.bin/eslint --resolve-plugins-relative-to . --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
"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",
"bootstrap": "lerna bootstrap --no-ci",
"bootstrapIgnoreScripts": "lerna bootstrap --ignore-scripts --no-ci",
"postinstall": "npm run bootstrap --no-ci && npm run build",
"build": "lerna run build && npm run tsc",
"publishAll": "git pull && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll",
"releaseAndroid": "node packages/tools/release-android.js",
"releaseCli": "node packages/tools/release-cli.js",
"releaseClipper": "node packages/tools/release-clipper.js",
"releaseDesktop": "node packages/tools/release-electron.js",
"releasePluginGenerator": "node packages/tools/release-plugin-generator.js",
"releaseServer": "node packages/tools/release-server.js",
"setupNewRelease": "node ./packages/tools/setupNewRelease",
"test-ci": "lerna run test-ci --stream",
"test": "lerna run test --stream",
@@ -43,8 +38,7 @@
"updateIgnored": "gulp updateIgnoredTypeScriptBuild",
"updatePluginTypes": "./packages/generator-joplin/updateTypes.sh",
"watch": "lerna run watch --stream --parallel",
"i": "lerna add --no-bootstrap --scope",
"server-start-dev": "docker-compose --file docker-compose.server-dev.yml up"
"i": "lerna add --no-bootstrap --scope"
},
"husky": {
"hooks": {

View File

@@ -1,3 +1,4 @@
import Setting from '@joplin/lib/models/Setting';
import PluginService from '@joplin/lib/services/plugins/PluginService';
const { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('../../../test-utils');
@@ -49,4 +50,26 @@ describe('JoplinWorkspace', () => {
await service.destroy();
});
test('should return the selected folder', async () => {
const service = new newPluginService() as PluginService;
const pluginScript = newPluginScript(`
joplin.plugins.register({
onStart: async function() {
const folder = await joplin.workspace.selectedFolder();
await joplin.data.put(['folders', folder.id], null, { title: "changedtitle" });
},
});
`);
const folder = await Folder.save({ title: 'folder' });
Setting.setValue('activeFolderId', folder.id);
const plugin = await service.loadPluginFromJsBundle('', pluginScript);
await service.runPlugin(plugin);
const modFolder = await Folder.load(folder.id);
expect(modFolder.title).toBe('changedtitle');
});
});

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,6 +18,7 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
}
}

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,7 +18,8 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
},
"dependencies": {
"left-pad": "^1.3.0"

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,6 +18,7 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
}
}

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,6 +18,7 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
}
}

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,6 +18,7 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
}
}

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,6 +18,7 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
}
}

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,6 +18,7 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
}
}

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,6 +18,7 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
}
}

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,6 +18,7 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
}
}

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,19 +0,0 @@
{
"name": "test_plugin",
"version": "1.0.0",
"description": "",
"scripts": {
"dist": "webpack"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
"copy-webpack-plugin": "^6.0.3",
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
}
}

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,6 +18,7 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
}
}

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,6 +18,7 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
}
}

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,6 +18,7 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
}
}

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,7 +18,8 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
},
"dependencies": {
"uslug": "^1.0.4"

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,6 +37,16 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

View File

@@ -4,9 +4,9 @@
"description": "",
"scripts": {
"dist": "webpack",
"postinstall": "npm run dist"
"prepare": "npm run dist"
},
"keywords": {},
"keywords": ["joplin-plugin"],
"license": "MIT",
"devDependencies": {
"@types/node": "^14.0.14",
@@ -18,7 +18,8 @@
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
"webpack-cli": "^3.3.11",
"chalk": "^4.1.0"
},
"dependencies": {
"left-pad": "^1.3.0"

View File

@@ -1,9 +1,61 @@
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
@@ -19,7 +71,7 @@ function createPluginArchive(sourceDir, destPath) {
if (!distFiles.length) {
// Usually means there's an error, which is going to be printed by
// webpack
console.info('Plugin archive was not created because the "dist" directory is empty');
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
@@ -36,21 +88,27 @@ function createPluginArchive(sourceDir, destPath) {
distFiles
);
console.info(`Plugin archive has been created in ${destPath}`);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const rootDir = path.resolve(__dirname);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const manifestPath = `${srcDir}/manifest.json`;
const manifest = readManifest(manifestPath);
const archiveFilePath = path.resolve(__dirname, `${manifest.id}.jpl`);
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
fs.removeSync(distDir);
function onBuildCompleted() {
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
@@ -93,7 +151,7 @@ const lastStepConfig = {
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// but theyr should be declared in manifest.json,
// but they should be declared in manifest.json,
// and then they are also compiled and copied to
// /dist. So wse also don't need to copy JS files.
'**/*.js',
@@ -102,9 +160,7 @@ const lastStepConfig = {
},
],
}),
new WebpackOnBuildPlugin(function() {
createPluginArchive(distDir, archiveFilePath);
}),
new WebpackOnBuildPlugin(onBuildCompleted),
],
};

View File

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

View File

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

View File

@@ -37,13 +37,23 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`
To update the plugin framework, run `npm run update`.
Keep in mind that doing so will overwrite all the framework-related files **outside of the "src/" directory** (your source code will not be touched). So if you have modified any of the framework-related files, such as package.json or .gitignore, make sure your code is under version control so that you can check the diff and re-apply your changes.
In general this command tries to do the right thing - in particular it's going to merge the changes in package.json and .gitignore instead of overwriting. It will also leave "/src" as well as README.md untouched.
For that reason, it's generally best not to change any of the framework files or to do so in a way that minimises the number of changes. For example, if you want to modify the Webpack config, create a new separate JavaScript file and include it in webpack.config.js. That way, when you update, you only have to restore the line that include your file.
The file that may cause problem is "webpack.config.js" because it's going to be overwritten. For that reason, if you want to change it, consider creating a separate JavaScript file and include it in webpack.config.js. That way, when you update, you only have to restore the line that include your file.
## Content scripts

View File

@@ -3,29 +3,7 @@
const Generator = require('yeoman-generator');
const chalk = require('chalk');
const yosay = require('yosay');
function mergePackageKey(parentKey, source, dest) {
const output = Object.assign({}, dest);
for (const k in source) {
if (!(k in output)) {
// If the key doesn't exist in the destination, add it
output[k] = source[k];
} else if (parentKey === 'devDependencies') {
// If we are dealing with the dependencies, overwrite with the
// version from source.
output[k] = source[k];
} else if (typeof source[k] === 'object' && !Array.isArray(source[k]) && source[k] !== null) {
// If it's an object, recursively process it
output[k] = mergePackageKey(k, source[k], output[k]);
} else {
// Otherwise, the default is to preserve the destination key
output[k] = dest[k];
}
}
return output;
}
const { mergePackageKey, mergeIgnoreFile, packageNameFromPluginName } = require('./utils');
module.exports = class extends Generator {
@@ -42,7 +20,7 @@ module.exports = class extends Generator {
}
async prompting() {
this.log(yosay(`Welcome to the fine ${chalk.red('generator-joplin')} generator!`));
this.log(yosay(`Welcome to the fine ${chalk.red('Joplin Plugin')} generator!`));
if (this.options.update && !this.options.silent) {
const answers = await this.prompt([
@@ -50,10 +28,10 @@ module.exports = class extends Generator {
type: 'confirm',
name: 'proceed',
message: [
'Updating will overwrite all the generator files **except for the',
'src/ directory**. So if you have made any changes outside of src/',
'make sure your code is under version control so that you can inspect',
'the diff and re-apply your changes if needed. Do you want to proceed?',
'Updating will overwrite the config-related files. It will not change the',
' content of /src or README.md. If you have made any changes to some of the',
' config files make sure your code is under version control so that you can',
' inspect the diff and re-apply your changes if needed. Do you want to proceed?',
].join('\n'),
},
]);
@@ -69,12 +47,12 @@ module.exports = class extends Generator {
{
type: 'input',
name: 'pluginId',
message: 'Plugin ID [Must be a globally unique ID such as "com.example.MyPlugin" or a UUID]',
message: 'Plugin ID\n Must be a globally unique ID such as "com.example.MyPlugin" or a UUID:',
},
{
type: 'input',
name: 'pluginName',
message: 'Plugin name [User-friendly string which will be displayed in UI]',
message: 'Plugin name\n User-friendly string which will be displayed in UI:',
},
{
type: 'input',
@@ -86,6 +64,11 @@ module.exports = class extends Generator {
name: 'pluginAuthor',
message: 'Author:',
},
{
type: 'input',
name: 'pluginRepositoryUrl',
message: 'Repository URL:',
},
{
type: 'input',
name: 'pluginHomepageUrl',
@@ -98,11 +81,25 @@ module.exports = class extends Generator {
for (const prompt of prompts) {
props[prompt.name] = '';
}
props.packageName = '';
this.props = props;
} else {
return this.prompt(prompts).then(props => {
this.props = props;
});
const initialProps = await this.prompt(prompts);
const defaultPackageName = packageNameFromPluginName(initialProps.pluginName);
const derivedProps = await this.prompt([
{
type: 'input',
name: 'packageName',
message: `The npm package will be named: "${defaultPackageName}"\n Press ENTER to keep this default, or type a name to change it:`,
},
]);
if (!derivedProps.packageName) derivedProps.packageName = defaultPackageName;
this.props = Object.assign({}, initialProps, derivedProps);
}
}
@@ -114,9 +111,9 @@ module.exports = class extends Generator {
const files = [
'.gitignore_TEMPLATE',
'package_TEMPLATE.json',
'README.md',
'.npmignore_TEMPLATE',
'GENERATOR_DOC.md',
'package_TEMPLATE.json',
'tsconfig.json',
'webpack.config.js',
];
@@ -124,6 +121,7 @@ module.exports = class extends Generator {
const noUpdateFiles = [
'src/index.ts',
'src/manifest.json',
'README.md',
];
const allFiles = files.concat(noUpdateFiles);
@@ -147,6 +145,17 @@ module.exports = class extends Generator {
},
}
);
} else if (this.options.update && (destFile === '.gitignore' || destFile === '.npmignore') && this.fs.exists(destFilePath)) {
const destContent = this.fs.read(destFilePath);
this.fs.copy(
this.templatePath(file),
destFilePath, {
process: (sourceBuffer) => {
return mergeIgnoreFile(sourceBuffer.toString(), destContent);
},
}
);
} else {
this.fs.copyTpl(
this.templatePath(file),

View File

@@ -1,3 +1,3 @@
dist/*
dist/
node_modules/
*.jpl
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -37,13 +37,23 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`
To update the plugin framework, run `npm run update`.
Keep in mind that doing so will overwrite all the framework-related files **outside of the "src/" directory** (your source code will not be touched). So if you have modified any of the framework-related files, such as package.json or .gitignore, make sure your code is under version control so that you can check the diff and re-apply your changes.
In general this command tries to do the right thing - in particular it's going to merge the changes in package.json and .gitignore instead of overwriting. It will also leave "/src" as well as README.md untouched.
For that reason, it's generally best not to change any of the framework files or to do so in a way that minimises the number of changes. For example, if you want to modify the Webpack config, create a new separate JavaScript file and include it in webpack.config.js. That way, when you update, you only have to restore the line that include your file.
The file that may cause problem is "webpack.config.js" because it's going to be overwritten. For that reason, if you want to change it, consider creating a separate JavaScript file and include it in webpack.config.js. That way, when you update, you only have to restore the line that include your file.
## Content scripts

View File

@@ -17,8 +17,8 @@ The project is setup to use TypeScript, although you can change the configuratio
## Updating the plugin framework
To update the plugin framework, run `yo joplin --update`
To update the plugin framework, run `npm run update`.
Keep in mind that doing so will overwrite all the framework-related files **outside of the "src/" directory** (your source code will not be touched). So if you have modified any of the framework-related files, such as package.json or .gitignore, make sure your code is under version control so that you can check the diff and re-apply your changes.
In general this command tries to do the right thing - in particular it's going to merge the changes in package.json and .gitignore instead of overwriting. It will also leave "/src" as well as README.md untouched.
For that reason, it's generally best not to change any of the framework files or to do so in a way that minimises the number of changes. For example, if you want to modify the Webpack config, create a new separate JavaScript file and include it in webpack.config.js. That way, when you update, you only have to restore the line that include your file.
The file that may cause problem is "webpack.config.js" because it's going to be overwritten. For that reason, if you want to change it, consider creating a separate JavaScript file and include it in webpack.config.js. That way, when you update, you only have to restore the line that include your file.

View File

@@ -1,3 +1,4 @@
import { FolderEntity } from '../../database/types';
import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
@@ -53,6 +54,13 @@ export default class JoplinWorkspace {
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/

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