1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Plugin Repo: Add link to plugin commit in commit message

This commit is contained in:
Laurent Cozic 2021-01-27 23:57:08 +00:00
parent 4156b13c32
commit 95253f70ea
18 changed files with 3694 additions and 439 deletions

View File

@ -1333,6 +1333,12 @@ packages/plugin-repo-cli/lib/errorsHaveChanged.js.map
packages/plugin-repo-cli/lib/errorsHaveChanged.test.d.ts packages/plugin-repo-cli/lib/errorsHaveChanged.test.d.ts
packages/plugin-repo-cli/lib/errorsHaveChanged.test.js packages/plugin-repo-cli/lib/errorsHaveChanged.test.js
packages/plugin-repo-cli/lib/errorsHaveChanged.test.js.map packages/plugin-repo-cli/lib/errorsHaveChanged.test.js.map
packages/plugin-repo-cli/lib/gitCompareUrl.d.ts
packages/plugin-repo-cli/lib/gitCompareUrl.js
packages/plugin-repo-cli/lib/gitCompareUrl.js.map
packages/plugin-repo-cli/lib/gitCompareUrl.test.d.ts
packages/plugin-repo-cli/lib/gitCompareUrl.test.js
packages/plugin-repo-cli/lib/gitCompareUrl.test.js.map
packages/plugin-repo-cli/lib/types.d.ts packages/plugin-repo-cli/lib/types.d.ts
packages/plugin-repo-cli/lib/types.js packages/plugin-repo-cli/lib/types.js
packages/plugin-repo-cli/lib/types.js.map packages/plugin-repo-cli/lib/types.js.map

6
.gitignore vendored
View File

@ -1320,6 +1320,12 @@ packages/plugin-repo-cli/lib/errorsHaveChanged.js.map
packages/plugin-repo-cli/lib/errorsHaveChanged.test.d.ts packages/plugin-repo-cli/lib/errorsHaveChanged.test.d.ts
packages/plugin-repo-cli/lib/errorsHaveChanged.test.js packages/plugin-repo-cli/lib/errorsHaveChanged.test.js
packages/plugin-repo-cli/lib/errorsHaveChanged.test.js.map packages/plugin-repo-cli/lib/errorsHaveChanged.test.js.map
packages/plugin-repo-cli/lib/gitCompareUrl.d.ts
packages/plugin-repo-cli/lib/gitCompareUrl.js
packages/plugin-repo-cli/lib/gitCompareUrl.js.map
packages/plugin-repo-cli/lib/gitCompareUrl.test.d.ts
packages/plugin-repo-cli/lib/gitCompareUrl.test.js
packages/plugin-repo-cli/lib/gitCompareUrl.test.js.map
packages/plugin-repo-cli/lib/types.d.ts packages/plugin-repo-cli/lib/types.d.ts
packages/plugin-repo-cli/lib/types.js packages/plugin-repo-cli/lib/types.js
packages/plugin-repo-cli/lib/types.js.map packages/plugin-repo-cli/lib/types.js.map

View File

@ -9,6 +9,7 @@ import checkIfPluginCanBeAdded from './lib/checkIfPluginCanBeAdded';
import updateReadme from './lib/updateReadme'; import updateReadme from './lib/updateReadme';
import { ImportErrors, NpmPackage } from './lib/types'; import { ImportErrors, NpmPackage } from './lib/types';
import errorsHaveChanged from './lib/errorsHaveChanged'; import errorsHaveChanged from './lib/errorsHaveChanged';
import gitCompareUrl from './lib/gitCompareUrl';
function stripOffPackageOrg(name: string): string { function stripOffPackageOrg(name: string): string {
const n = name.split('/'); const n = name.split('/');
@ -103,7 +104,7 @@ enum ProcessingActionType {
Update = 2, Update = 2,
} }
function commitMessage(actionType: ProcessingActionType, manifest: any, npmPackage: NpmPackage, error: any): string { function commitMessage(actionType: ProcessingActionType, manifest: any, previousManifest: any, npmPackage: NpmPackage, error: any): string {
const output: string[] = []; const output: string[] = [];
if (!error) { if (!error) {
@ -118,7 +119,9 @@ function commitMessage(actionType: ProcessingActionType, manifest: any, npmPacka
output.push(`Error: ${npmPackage.name}@${npmPackage.version}`); output.push(`Error: ${npmPackage.name}@${npmPackage.version}`);
} }
return output.join(': '); const compareUrl = gitCompareUrl(manifest, previousManifest);
return output.join(': ') + (compareUrl ? `\n\n${compareUrl}` : '');
} }
function pluginManifestsPath(repoDir: string): string { function pluginManifestsPath(repoDir: string): string {
@ -170,6 +173,7 @@ async function processNpmPackage(npmPackage: NpmPackage, repoDir: string) {
let manifests: any = {}; let manifests: any = {};
let manifest: any = {}; let manifest: any = {};
let error: any = null; let error: any = null;
let previousManifest: any = null;
try { try {
const destDir = `${repoDir}/plugins/`; const destDir = `${repoDir}/plugins/`;
@ -179,7 +183,10 @@ async function processNpmPackage(npmPackage: NpmPackage, repoDir: string) {
actionType = ProcessingActionType.Add; actionType = ProcessingActionType.Add;
} }
if (!obsoleteManifests[manifest.id]) manifests[manifest.id] = manifest; if (!obsoleteManifests[manifest.id]) {
previousManifest = { ...manifests[manifest.id] };
manifests[manifest.id] = manifest;
}
} catch (e) { } catch (e) {
console.error(e); console.error(e);
errors[npmPackage.name] = e.message || ''; errors[npmPackage.name] = e.message || '';
@ -213,7 +220,7 @@ async function processNpmPackage(npmPackage: NpmPackage, repoDir: string) {
if (!(await gitRepoClean())) { if (!(await gitRepoClean())) {
await execCommand2('git add -A', { showOutput: false }); await execCommand2('git add -A', { showOutput: false });
await execCommand2(['git', 'commit', '-m', commitMessage(actionType, manifest, npmPackage, error)], { showOutput: false }); await execCommand2(['git', 'commit', '-m', commitMessage(actionType, manifest, previousManifest, npmPackage, error)], { showOutput: false });
} else { } else {
console.info('Nothing to commit'); console.info('Nothing to commit');
} }

View File

@ -0,0 +1,42 @@
import gitCompareUrl from './gitCompareUrl';
describe('gitCompareUrl', () => {
test('should create a compare URL', () => {
const testCases = [
[
{
repository_url: 'https://github.com/JackGruber/joplin-plugin-copytags',
_publish_commit: 'master:9ec4a476a54440ac43422c34e179dcabfca1e5a0',
},
{
repository_url: 'https://github.com/JackGruber/joplin-plugin-copytags',
_publish_commit: 'master:b52b01f6d3b709a811ac214253636a7c207c87dd',
},
'https://github.com/JackGruber/joplin-plugin-copytags/compare/b52b01f6d3b709a811ac214253636a7c207c87dd..9ec4a476a54440ac43422c34e179dcabfca1e5a0',
],
[
{
repository_url: 'https://github.com/JackGruber/joplin-plugin-copytags',
_publish_commit: 'master:9ec4a476a54440ac43422c34e179dcabfca1e5a0',
},
null,
'https://github.com/JackGruber/joplin-plugin-copytags/tree/9ec4a476a54440ac43422c34e179dcabfca1e5a0',
],
[
{
_publish_commit: 'master:9ec4a476a54440ac43422c34e179dcabfca1e5a0',
},
null,
null,
],
];
for (const t of testCases) {
const [manifest, previousManifest, expected] = t;
const actual = gitCompareUrl(manifest, previousManifest);
expect(actual).toBe(expected);
}
});
});

View File

@ -0,0 +1,29 @@
import { rtrimSlashes } from '@joplin/lib/path-utils';
function removeBranch(commit: string): string {
if (!commit) return '';
if (commit.indexOf(':') >= 0) {
const s = commit.split(':');
return s[1];
}
return commit;
}
export default function(manifest: any, previousManifest: any = null): string {
// "repository_url": "https://github.com/JackGruber/joplin-plugin-copytags",
// "_publish_commit": "master:b52b01f6d3b709a811ac214253636a7c207c87dd",
// https://github.com/JackGruber/joplin-plugin-copytags/compare/9ec4a476a54440ac43422c34e179dcabfca1e5a0..b52b01f6d3b709a811ac214253636a7c207c87dd
const repoUrl: string = manifest.repository_url;
const commit: string = removeBranch(manifest._publish_commit);
const previousCommit: string = previousManifest ? removeBranch(previousManifest._publish_commit) : '';
if (!repoUrl) return null;
if (!commit && !previousCommit) return repoUrl;
if (commit && !previousCommit) return `${rtrimSlashes(repoUrl)}/tree/${commit}`;
return `${rtrimSlashes(repoUrl)}/compare/${previousCommit}..${commit}`;
}

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,8 @@ The main two files you will want to look at are:
- `/src/index.ts`, which contains the entry point for the plugin source code. - `/src/index.ts`, which contains the entry point for the plugin source code.
- `/src/manifest.json`, which is the plugin manifest. It contains information such as the plugin a name, version, etc. - `/src/manifest.json`, which is the plugin manifest. It contains information such as the plugin a name, version, etc.
The file `/plugin.config.json` could also be useful if you intend to use [external scripts](#external-script-files), such as content scripts or webview scripts.
## Building the plugin ## Building the plugin
The plugin is built using Webpack, which creates the compiled code in `/dist`. A JPL archive will also be created at the root, which can use to distribute the plugin. The plugin is built using Webpack, which creates the compiled code in `/dist`. A JPL archive will also be created at the root, which can use to distribute the plugin.
@ -49,44 +51,21 @@ In general all this is done automatically by the plugin generator, which will se
## Updating the plugin framework ## 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 ## External script files
A plugin that uses [content scripts](https://joplinapp.org/api/references/plugin_api/classes/joplinplugins.html#registercontentscript) must declare them under the `content_scripts` key of [manifest.json](https://joplinapp.org/api/references/plugin_manifest/). By default, the compiler (webpack) is going to compile `src/index.ts` only (as well as any file it imports), and any other file will simply be copied to the plugin package. In some cases this is sufficient, however if you have [content scripts](https://joplinapp.org/api/references/plugin_api/classes/joplincontentscripts.html) or [webview scripts](https://joplinapp.org/api/references/plugin_api/classes/joplinviewspanels.html#addscript) you might want to compile them too, in particular in these two cases:
Each entry must be a path **relative to /src**, and **without extension**. The extension should not be included because it might change once the script is compiled. Each of these scripts will then be compiled to JavaScript and packaged into the plugin file. The content script files can be TypeScript (.ts or .tsx) or JavaScript. - The script is a TypeScript file - in which case it has to be compiled to JavaScript.
For example, assuming these files: - The script requires modules you've added to package.json. In that case, the script, whether JS or TS, must be compiled so that the dependencies are bundled with the JPL file.
```bash To get such an external script file to compile, you need to add it to the `extraScripts` array in `plugin.config.json`. The path you add should be relative to /src. For example, if you have a file in "/src/webviews/index.ts", the path should be set to "webviews/index.ts". Once compiled, the file will always be named with a .js extension. So you will get "webviews/index.js" in the plugin package, and that's the path you should use to reference the file.
/src
index.ts # Main plugin script
myContentScript.js # One content script (JS)
otherContentScript.ts # Another content script (TypeScript)
vendor/
test.ts # Sub-directories are also supported
```
The `manifest.json` file would be:
```json
{
"manifest_version": 1,
"name": "Testing Content Scripts",
content_scripts: [
"myContentScript",
"otherContentScript",
"vendor/test"
]
}
```
Note in particular how the file path is relative to /src and the extensions removed.
## License ## License

View File

@ -7,6 +7,7 @@ import JoplinCommands from './JoplinCommands';
import JoplinViews from './JoplinViews'; import JoplinViews from './JoplinViews';
import JoplinInterop from './JoplinInterop'; import JoplinInterop from './JoplinInterop';
import JoplinSettings from './JoplinSettings'; import JoplinSettings from './JoplinSettings';
import JoplinContentScripts from './JoplinContentScripts';
/** /**
* This is the main entry point to the Joplin API. You can access various services using the provided accessors. * This is the main entry point to the Joplin API. You can access various services using the provided accessors.
* *
@ -31,10 +32,12 @@ export default class Joplin {
private views_; private views_;
private interop_; private interop_;
private settings_; private settings_;
private contentScripts_;
constructor(implementation: any, plugin: Plugin, store: any); constructor(implementation: any, plugin: Plugin, store: any);
get data(): JoplinData; get data(): JoplinData;
get plugins(): JoplinPlugins; get plugins(): JoplinPlugins;
get workspace(): JoplinWorkspace; get workspace(): JoplinWorkspace;
get contentScripts(): JoplinContentScripts;
/** /**
* @ignore * @ignore
* *

View File

@ -0,0 +1,40 @@
import Plugin from '../Plugin';
import { ContentScriptType } from './types';
export default class JoplinContentScripts {
private plugin;
constructor(plugin: Plugin);
/**
* Registers a new content script. Unlike regular plugin code, which runs in
* a separate process, content scripts run within the main process code and
* thus allow improved performances and more customisations in specific
* cases. It can be used for example to load a Markdown or editor plugin.
*
* Note that registering a content script in itself will do nothing - it
* will only be loaded in specific cases by the relevant app modules (eg.
* the Markdown renderer or the code editor). So it is not a way to inject
* and run arbitrary code in the app, which for safety and performance
* reasons is not supported.
*
* The plugin generator provides a way to build any content script you might
* want to package as well as its dependencies. See the [Plugin Generator
* doc](https://github.com/laurent22/joplin/blob/dev/packages/generator-joplin/README.md)
* for more information.
*
* * [View the renderer demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
* * [View the editor demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script)
*
* See also the [postMessage demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages)
*
* @param type Defines how the script will be used. See the type definition for more information about each supported type.
* @param id A unique ID for the content script.
* @param scriptPath Must be a path relative to the plugin main script. For example, if your file content_script.js is next to your index.ts file, you would set `scriptPath` to `"./content_script.js`.
*/
register(type: ContentScriptType, id: string, scriptPath: string): Promise<void>;
/**
* Listens to a messages sent from the content script using postMessage().
* See {@link ContentScriptType} for more information as well as the
* [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages)
*/
onMessage(contentScriptId: string, callback: any): Promise<void>;
}

View File

@ -20,28 +20,7 @@ export default class JoplinPlugins {
*/ */
register(script: Script): Promise<void>; register(script: Script): Promise<void>;
/** /**
* Registers a new content script. Unlike regular plugin code, which runs in * @deprecated Use joplin.contentScripts.register()
* a separate process, content scripts run within the main process code and
* thus allow improved performances and more customisations in specific
* cases. It can be used for example to load a Markdown or editor plugin.
*
* Note that registering a content script in itself will do nothing - it
* will only be loaded in specific cases by the relevant app modules (eg.
* the Markdown renderer or the code editor). So it is not a way to inject
* and run arbitrary code in the app, which for safety and performance
* reasons is not supported.
*
* The plugin generator provides a way to build any content script you might
* want to package as well as its dependencies. See the [Plugin Generator
* doc](https://github.com/laurent22/joplin/blob/dev/packages/generator-joplin/README.md)
* for more information.
*
* * [View the renderer demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
* * [View the editor demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script)
*
* @param type Defines how the script will be used. See the type definition for more information about each supported type.
* @param id A unique ID for the content script.
* @param scriptPath Must be a path relative to the plugin main script. For example, if your file content_script.js is next to your index.ts file, you would set `scriptPath` to `"./content_script.js`.
*/ */
registerContentScript(type: ContentScriptType, id: string, scriptPath: string): Promise<void>; registerContentScript(type: ContentScriptType, id: string, scriptPath: string): Promise<void>;
} }

View File

@ -1,5 +1,12 @@
import Plugin from '../Plugin'; import Plugin from '../Plugin';
import { SettingItem, SettingSection } from './types'; import { SettingItem, SettingSection } from './types';
export interface ChangeEvent {
/**
* Setting keys that have been changed
*/
keys: string[];
}
export declare type ChangeHandler = (event: ChangeEvent)=> void;
/** /**
* This API allows registering new settings and setting sections, as well as getting and setting settings. Once a setting has been registered it will appear in the config screen and be editable by the user. * This API allows registering new settings and setting sections, as well as getting and setting settings. Once a setting has been registered it will appear in the config screen and be editable by the user.
* *
@ -12,6 +19,7 @@ import { SettingItem, SettingSection } from './types';
export default class JoplinSettings { export default class JoplinSettings {
private plugin_; private plugin_;
constructor(plugin: Plugin); constructor(plugin: Plugin);
private get keyPrefix();
private namespacedKey; private namespacedKey;
/** /**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts. * Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
@ -40,4 +48,10 @@ export default class JoplinSettings {
* https://github.com/laurent22/joplin/blob/dev/packages/lib/models/Setting.ts#L142 * https://github.com/laurent22/joplin/blob/dev/packages/lib/models/Setting.ts#L142
*/ */
globalValue(key: string): Promise<any>; globalValue(key: string): Promise<any>;
/**
* Called when one or multiple settings of your plugin have been changed.
* - For performance reasons, this event is triggered with a delay.
* - You will only get events for your own plugin settings.
*/
onChange(handler: ChangeHandler): Promise<void>;
} }

View File

@ -28,6 +28,22 @@ export default class JoplinViewsPanels {
addScript(handle: ViewHandle, scriptPath: string): Promise<void>; addScript(handle: ViewHandle, scriptPath: string): Promise<void>;
/** /**
* Called when a message is sent from the webview (using postMessage). * Called when a message is sent from the webview (using postMessage).
*
* To post a message from the webview to the plugin use:
*
* ```javascript
* const response = await webviewApi.postMessage(message);
* ```
*
* - `message` can be any JavaScript object, string or number
* - `response` is whatever was returned by the `onMessage` handler
*
* Using this mechanism, you can have two-way communication between the
* plugin and webview.
*
* See the [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages) for more details.
*
*/ */
onMessage(handle: ViewHandle, callback: Function): Promise<void>; onMessage(handle: ViewHandle, callback: Function): Promise<void>;
/** /**

View File

@ -366,9 +366,31 @@ export interface SettingSection {
export type Path = string[]; export type Path = string[];
// ================================================================= // =================================================================
// Plugins type // Content Script types
// ================================================================= // =================================================================
export type PostMessageHandler = (id: string, message: any)=> Promise<any>;
/**
* When a content script is initialised, it receives a `context` object.
*/
export interface ContentScriptContext {
/**
* The plugin ID that registered this content script
*/
pluginId: string;
/**
* The content script ID, which may be necessary to post messages
*/
contentScriptId: string;
/**
* Can be used by CodeMirror content scripts to post a message to the plugin
*/
postMessage: PostMessageHandler;
}
export enum ContentScriptType { export enum ContentScriptType {
/** /**
* Registers a new Markdown-It plugin, which should follow the template * Registers a new Markdown-It plugin, which should follow the template
@ -394,43 +416,56 @@ export enum ContentScriptType {
* *
* ## Exported members * ## Exported members
* *
* - The `context` argument is currently unused but could be used later * - The `context` argument is currently unused but could be used later on
* on to provide access to your own plugin so that the content script * to provide access to your own plugin so that the content script and
* and plugin can communicate. * plugin can communicate.
* *
* - The **required** `plugin` key is the actual Markdown-It plugin - * - The **required** `plugin` key is the actual Markdown-It plugin - check
* check the [official * the [official doc](https://github.com/markdown-it/markdown-it) for more
* doc](https://github.com/markdown-it/markdown-it) for more
* information. The `options` parameter is of type * information. The `options` parameter is of type
* [RuleOptions](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml.ts), * [RuleOptions](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml.ts),
* which contains a number of options, mostly useful for Joplin's * which contains a number of options, mostly useful for Joplin's internal
* internal code. * code.
* *
* - Using the **optional** `assets` key you may specify assets such as * - Using the **optional** `assets` key you may specify assets such as JS
* JS or CSS that should be loaded in the rendered HTML document. * or CSS that should be loaded in the rendered HTML document. Check for
* Check for example the Joplin [Mermaid * example the Joplin [Mermaid
* plugin](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts) * plugin](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts)
* to see how the data should be structured. * to see how the data should be structured.
* *
* ## Passing messages from the content script to your plugin * ## Posting messages from the content script to your plugin
* *
* The application provides the following function to allow executing * The application provides the following function to allow executing
* commands from the rendered HTML code: * commands from the rendered HTML code:
* *
* `webviewApi.executeCommand(commandName, ...args)` * ```javascript
* const response = await webviewApi.postMessage(contentScriptId, message);
* ```
* *
* So you can use this mechanism to pass messages from the note viewer * - `contentScriptId` is the ID you've defined when you registered the
* to your own plugin. To do so you would define a command, using * content script. You can retrieve it from the
* `joplin.commands.register`, then you would call this command using * {@link ContentScriptContext | context}.
* the `webviewApi` object. See again [the * - `message` can be any basic JavaScript type (number, string, plain
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script) * object), but it cannot be a function or class instance.
* to see how this can be done. *
* When you post a message, the plugin can send back a `response` thus
* allowing two-way communication:
*
* ```javascript
* await joplin.contentScripts.onMessage(contentScriptId, (message) => {
* // Process message
* return response; // Can be any object, string or number
* });
* ```
*
* See {@link JoplinContentScripts.onMessage} for more details, as well as
* the [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages).
* *
* ## Registering an existing Markdown-it plugin * ## Registering an existing Markdown-it plugin
* *
* To include a regular Markdown-It plugin, that doesn't make use of * To include a regular Markdown-It plugin, that doesn't make use of any
* any Joplin-specific features, you would simply create a file such as * Joplin-specific features, you would simply create a file such as this:
* this:
* *
* ```javascript * ```javascript
* module.exports = { * module.exports = {
@ -443,6 +478,7 @@ export enum ContentScriptType {
* ``` * ```
*/ */
MarkdownItPlugin = 'markdownItPlugin', MarkdownItPlugin = 'markdownItPlugin',
/** /**
* Registers a new CodeMirror plugin, which should follow the template * Registers a new CodeMirror plugin, which should follow the template
* below. * below.
@ -466,42 +502,65 @@ export enum ContentScriptType {
* } * }
* ``` * ```
* *
* - The `context` argument is currently unused but could be used later * - The `context` argument is currently unused but could be used later on
* on to provide access to your own plugin so that the content script * to provide access to your own plugin so that the content script and
* and plugin can communicate. * plugin can communicate.
* *
* - The `plugin` key is your CodeMirror plugin. This is where you can * - The `plugin` key is your CodeMirror plugin. This is where you can
* register new commands with CodeMirror or interact with the * register new commands with CodeMirror or interact with the CodeMirror
* CodeMirror instance as needed. * instance as needed.
* *
* - The `codeMirrorResources` key is an array of CodeMirror resources * - The `codeMirrorResources` key is an array of CodeMirror resources that
* that will be loaded and attached to the CodeMirror module. These * will be loaded and attached to the CodeMirror module. These are made up
* are made up of addons, keymaps, and modes. For example, for a * of addons, keymaps, and modes. For example, for a plugin that want's to
* plugin that want's to enable clojure highlighting in code blocks. * enable clojure highlighting in code blocks. `codeMirrorResources` would
* `codeMirrorResources` would be set to `['mode/clojure/clojure']`. * be set to `['mode/clojure/clojure']`.
* *
* - The `codeMirrorOptions` key contains all the * - The `codeMirrorOptions` key contains all the
* [CodeMirror](https://codemirror.net/doc/manual.html#config) * [CodeMirror](https://codemirror.net/doc/manual.html#config) options
* options that will be set or changed by this plugin. New options * that will be set or changed by this plugin. New options can alse be
* can alse be declared via * declared via
* [`CodeMirror.defineOption`](https://codemirror.net/doc/manual.html#defineOption), * [`CodeMirror.defineOption`](https://codemirror.net/doc/manual.html#defineOption),
* and then have their value set here. For example, a plugin that * and then have their value set here. For example, a plugin that enables
* enables line numbers would set `codeMirrorOptions` to * line numbers would set `codeMirrorOptions` to `{'lineNumbers': true}`.
* `{'lineNumbers': true}`.
* *
* - Using the **optional** `assets` key you may specify **only** CSS * - Using the **optional** `assets` key you may specify **only** CSS assets
* assets that should be loaded in the rendered HTML document. Check * that should be loaded in the rendered HTML document. Check for example
* for example the Joplin [Mermaid * the Joplin [Mermaid
* plugin](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts) * plugin](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts)
* to see how the data should be structured. * to see how the data should be structured.
* *
* One of the `plugin`, `codeMirrorResources`, or `codeMirrorOptions` * One of the `plugin`, `codeMirrorResources`, or `codeMirrorOptions` keys
* keys must be provided for the plugin to be valid. Having multiple or * must be provided for the plugin to be valid. Having multiple or all
* all provided is also okay. * provided is also okay.
* *
* See the [demo * See also the [demo
* plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script) * plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script)
* for an example of all these keys being used in one plugin. * for an example of all these keys being used in one plugin.
*
* ## Posting messages from the content script to your plugin
*
* In order to post messages to the plugin, you can use the postMessage
* function passed to the {@link ContentScriptContext | context}.
*
* ```javascript
* const response = await context.postMessage('messageFromCodeMirrorContentScript');
* ```
*
* When you post a message, the plugin can send back a `response` thus
* allowing two-way communication:
*
* ```javascript
* await joplin.contentScripts.onMessage(contentScriptId, (message) => {
* // Process message
* return response; // Can be any object, string or number
* });
* ```
*
* See {@link JoplinContentScripts.onMessage} for more details, as well as
* the [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages).
*
*/ */
CodeMirrorPlugin = 'codeMirrorPlugin', CodeMirrorPlugin = 'codeMirrorPlugin',
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "joplin-plugin-toggle-side-bars", "name": "@joplin/joplin-plugin-toggle-sidebars",
"version": "1.0.0", "version": "1.0.3",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -1211,6 +1211,12 @@
"prr": "~1.0.1" "prr": "~1.0.1"
} }
}, },
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"dev": true
},
"escape-string-regexp": { "escape-string-regexp": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@ -4510,6 +4516,15 @@
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true "dev": true
}, },
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"has-flag": { "has-flag": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -4536,6 +4551,40 @@
"json5": "^1.0.1" "json5": "^1.0.1"
} }
}, },
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
"supports-color": { "supports-color": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
@ -4544,6 +4593,24 @@
"requires": { "requires": {
"has-flag": "^3.0.0" "has-flag": "^3.0.0"
} }
},
"yargs": {
"version": "13.3.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
"dev": true,
"requires": {
"cliui": "^5.0.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^13.1.2"
}
} }
} }
}, },
@ -4643,64 +4710,90 @@
"dev": true "dev": true
}, },
"yargs": { "yargs": {
"version": "13.3.2", "version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dev": true, "dev": true,
"requires": { "requires": {
"cliui": "^5.0.0", "cliui": "^7.0.2",
"find-up": "^3.0.0", "escalade": "^3.1.1",
"get-caller-file": "^2.0.1", "get-caller-file": "^2.0.5",
"require-directory": "^2.1.1", "require-directory": "^2.1.1",
"require-main-filename": "^2.0.0", "string-width": "^4.2.0",
"set-blocking": "^2.0.0", "y18n": "^5.0.5",
"string-width": "^3.0.0", "yargs-parser": "^20.2.2"
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^13.1.2"
}, },
"dependencies": { "dependencies": {
"find-up": { "ansi-regex": {
"version": "3.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true
},
"cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"locate-path": "^3.0.0" "string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
} }
}, },
"locate-path": { "emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"is-fullwidth-code-point": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"dev": true, "dev": true,
"requires": { "requires": {
"p-locate": "^3.0.0", "emoji-regex": "^8.0.0",
"path-exists": "^3.0.0" "is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
} }
}, },
"p-limit": { "strip-ansi": {
"version": "2.3.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true, "dev": true,
"requires": { "requires": {
"p-try": "^2.0.0" "ansi-regex": "^5.0.0"
} }
}, },
"p-locate": { "wrap-ansi": {
"version": "3.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"p-limit": "^2.0.0" "ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
} }
}, },
"path-exists": { "y18n": {
"version": "3.0.0", "version": "5.0.5",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==",
"dev": true
},
"yargs-parser": {
"version": "20.2.4",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
"integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
"dev": true "dev": true
} }
} }

View File

@ -1,9 +1,10 @@
{ {
"name": "@joplin/joplin-plugin-toggle-sidebars", "name": "@joplin/joplin-plugin-toggle-sidebars",
"version": "1.0.2", "version": "1.0.3",
"scripts": { "scripts": {
"dist": "webpack", "dist": "webpack --joplin-plugin-config buildMain && webpack --joplin-plugin-config buildExtraScripts && webpack --joplin-plugin-config createArchive",
"prepare": "npm run dist" "prepare": "npm run dist",
"update": "npm install -g generator-joplin && yo joplin --update"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
@ -23,6 +24,7 @@
"ts-loader": "^7.0.5", "ts-loader": "^7.0.5",
"typescript": "^3.9.3", "typescript": "^3.9.3",
"webpack": "^4.43.0", "webpack": "^4.43.0",
"webpack-cli": "^3.3.11" "webpack-cli": "^3.3.11",
"yargs": "^16.2.0"
} }
} }

View File

@ -0,0 +1,3 @@
{
"extraScripts": []
}

View File

@ -2,8 +2,8 @@
"manifest_version": 1, "manifest_version": 1,
"id": "org.joplinapp.plugins.ToggleSidebars", "id": "org.joplinapp.plugins.ToggleSidebars",
"app_min_version": "1.6", "app_min_version": "1.6",
"version": "1.0.2", "version": "1.0.3",
"name": "Note list and side bar toggle buttons", "name": "Note list and sidebar toggle buttons",
"description": "Adds buttons to toggle note list and sidebar", "description": "Adds buttons to toggle note list and sidebar",
"author": "Laurent Cozic", "author": "Laurent Cozic",
"homepage_url": "https://github.com/laurent22/joplin/tree/dev/packages/plugins/ToggleSidebars", "homepage_url": "https://github.com/laurent22/joplin/tree/dev/packages/plugins/ToggleSidebars",

View File

@ -1,3 +1,11 @@
// -----------------------------------------------------------------------------
// This file is used to build the plugin file (.jpl) and plugin info (.json). It
// is recommended not to edit this file as it would be overwritten when updating
// the plugin framework. If you do make some changes, consider using an external
// JS file and requiring it here to minimize the changes. That way when you
// update, you can easily restore the functionality you've added.
// -----------------------------------------------------------------------------
const path = require('path'); const path = require('path');
const crypto = require('crypto'); const crypto = require('crypto');
const fs = require('fs-extra'); const fs = require('fs-extra');
@ -9,20 +17,22 @@ const glob = require('glob');
const execSync = require('child_process').execSync; const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname); const rootDir = path.resolve(__dirname);
const userConfigFilename = './plugin.config.json';
const userConfigPath = path.resolve(rootDir, userConfigFilename);
const distDir = path.resolve(rootDir, 'dist'); const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src'); const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish'); const publishDir = path.resolve(rootDir, 'publish');
const userConfig = Object.assign({}, {
extraScripts: [],
}, fs.pathExistsSync(userConfigPath) ? require(userConfigFilename) : {});
const manifestPath = `${srcDir}/manifest.json`; const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`; const packageJsonPath = `${rootDir}/package.json`;
const manifest = readManifest(manifestPath); const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`); const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`); const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
function validatePackageJson() { function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) { if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
@ -68,13 +78,7 @@ function createPluginArchive(sourceDir, destPath) {
const distFiles = glob.sync(`${sourceDir}/**/*`, { nodir: true }) const distFiles = glob.sync(`${sourceDir}/**/*`, { nodir: true })
.map(f => f.substr(sourceDir.length + 1)); .map(f => f.substr(sourceDir.length + 1));
if (!distFiles.length) { if (!distFiles.length) throw new Error('Plugin archive was not created because the "dist" directory is empty');
// Usually means there's an error, which is going to be printed by
// webpack
console.warn(chalk.yellow('Plugin archive was not created because the "dist" directory is empty'));
return;
}
fs.removeSync(destPath); fs.removeSync(destPath);
tar.create( tar.create(
@ -100,9 +104,14 @@ function createPluginInfo(manifestPath, destPath, jplFilePath) {
} }
function onBuildCompleted() { function onBuildCompleted() {
try {
fs.removeSync(path.resolve(publishDir, 'index.js'));
createPluginArchive(distDir, pluginArchiveFilePath); createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath); createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson(); validatePackageJson();
} catch (error) {
console.error(chalk.red(error.message));
}
} }
const baseConfig = { const baseConfig = {
@ -132,9 +141,6 @@ const pluginConfig = Object.assign({}, baseConfig, {
filename: 'index.js', filename: 'index.js',
path: distDir, path: distDir,
}, },
});
const lastStepConfig = {
plugins: [ plugins: [
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
@ -148,23 +154,15 @@ const lastStepConfig = {
// already copied into /dist so we don't copy them. // already copied into /dist so we don't copy them.
'**/*.ts', '**/*.ts',
'**/*.tsx', '**/*.tsx',
// Currently we don't support JS files for the main
// plugin script. We support it for content scripts,
// 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',
], ],
}, },
}, },
], ],
}), }),
new WebpackOnBuildPlugin(onBuildCompleted),
], ],
}; });
const contentScriptConfig = Object.assign({}, baseConfig, { const extraScriptConfig = Object.assign({}, baseConfig, {
resolve: { resolve: {
alias: { alias: {
api: path.resolve(__dirname, 'api'), api: path.resolve(__dirname, 'api'),
@ -173,42 +171,46 @@ const contentScriptConfig = Object.assign({}, baseConfig, {
}, },
}); });
function resolveContentScriptPaths(name) { const createArchiveConfig = {
if (['.js', '.ts', '.tsx'].includes(path.extname(name).toLowerCase())) { stats: 'errors-only',
throw new Error(`Content script path must not include file extension: ${name}`); entry: './dist/index.js',
}
const pathsToTry = [
`./src/${name}.ts`,
`${'./src/' + '/'}${name}.js`,
];
for (const pathToTry of pathsToTry) {
if (fs.pathExistsSync(`${rootDir}/${pathToTry}`)) {
return {
entry: pathToTry,
output: { output: {
filename: `${name}.js`, filename: 'index.js',
path: publishDir,
},
plugins: [new WebpackOnBuildPlugin(onBuildCompleted)],
};
function resolveExtraScriptPath(name) {
const relativePath = `./src/${name}`;
const fullPath = path.resolve(`${rootDir}/${relativePath}`);
if (!fs.pathExistsSync(fullPath)) throw new Error(`Could not find extra script: "${name}" at "${fullPath}"`);
const s = name.split('.');
s.pop();
const nameNoExt = s.join('.');
return {
entry: relativePath,
output: {
filename: `${nameNoExt}.js`,
path: distDir, path: distDir,
library: 'default', library: 'default',
libraryTarget: 'commonjs', libraryTarget: 'commonjs',
libraryExport: 'default', libraryExport: 'default',
}, },
}; };
}
}
throw new Error(`Could not find content script "${name}" at locations ${JSON.stringify(pathsToTry)}`);
} }
function createContentScriptConfigs() { function buildExtraScriptConfigs(userConfig) {
if (!manifest.content_scripts) return []; if (!userConfig.extraScripts.length) return [];
const output = []; const output = [];
for (const contentScriptName of manifest.content_scripts) { for (const scriptName of userConfig.extraScripts) {
const scriptPaths = resolveContentScriptPaths(contentScriptName); const scriptPaths = resolveExtraScriptPath(scriptName);
output.push(Object.assign({}, contentScriptConfig, { output.push(Object.assign({}, extraScriptConfig, {
entry: scriptPaths.entry, entry: scriptPaths.entry,
output: scriptPaths.output, output: scriptPaths.output,
})); }));
@ -217,8 +219,61 @@ function createContentScriptConfigs() {
return output; return output;
} }
const exportedConfigs = [pluginConfig].concat(createContentScriptConfigs()); function main(processArgv) {
const yargs = require('yargs/yargs');
const argv = yargs(processArgv).argv;
exportedConfigs[exportedConfigs.length - 1] = Object.assign({}, exportedConfigs[exportedConfigs.length - 1], lastStepConfig); const configName = argv['joplin-plugin-config'];
if (!configName) throw new Error('A config file must be specified via the --joplin-plugin-config flag');
// Webpack configurations run in parallel, while we need them to run in
// sequence, and to do that it seems the only way is to run webpack multiple
// times, with different config each time.
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
// overwritten here by the compiled version. This is by design. The
// result is that JS files that don't need compilation, are simply
// copied to /dist, while those that do need it are correctly compiled.
buildExtraScripts: buildExtraScriptConfigs(userConfig),
// Ths config is for creating the .jpl, which is done via the plugin, so
// it doesn't actually need an entry and output, however webpack won't
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
};
// If we are running the first config step, we clean up and create the build
// directories.
if (configName === 'buildMain') {
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
}
return configs[configName];
}
let exportedConfigs = [];
try {
exportedConfigs = main(process.argv);
} catch (error) {
console.error(chalk.red(error.message));
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs; module.exports = exportedConfigs;