diff --git a/__tests__/__snapshots__/bin.js.snap b/__tests__/__snapshots__/bin.js.snap
index e89925da4..70fda8853 100644
--- a/__tests__/__snapshots__/bin.js.snap
+++ b/__tests__/__snapshots__/bin.js.snap
@@ -2024,6 +2024,277 @@ f5 comment
 
 exports[`lint command generates lint output 1`] = `""`;
 
+exports[`load a plugin 1`] = `
+Array [
+  Object {
+    "augments": Array [],
+    "context": Object {
+      "file": "[path]",
+      "loc": Object {
+        "end": Object {
+          "column": 2,
+          "line": 8,
+        },
+        "start": Object {
+          "column": 0,
+          "line": 5,
+        },
+      },
+    },
+    "description": Object {
+      "children": Array [
+        Object {
+          "children": Array [
+            Object {
+              "type": "text",
+              "value": "This function returns the number one.",
+            },
+          ],
+          "type": "paragraph",
+        },
+      ],
+      "type": "root",
+    },
+    "examples": Array [],
+    "implements": Array [],
+    "kind": "function",
+    "loc": Object {
+      "end": Object {
+        "column": 3,
+        "line": 4,
+      },
+      "start": Object {
+        "column": 0,
+        "line": 1,
+      },
+    },
+    "members": Object {
+      "events": Array [],
+      "global": Array [],
+      "inner": Array [],
+      "instance": Array [],
+      "static": Array [],
+    },
+    "name": "simple.input",
+    "namespace": "simple.input",
+    "params": Array [],
+    "path": Array [
+      Object {
+        "kind": "function",
+        "name": "simple.input",
+      },
+    ],
+    "properties": Array [],
+    "returns": Array [
+      Object {
+        "description": Object {
+          "children": Array [
+            Object {
+              "children": Array [
+                Object {
+                  "type": "text",
+                  "value": "numberone",
+                },
+              ],
+              "type": "paragraph",
+            },
+          ],
+          "type": "root",
+        },
+        "title": "returns",
+        "type": Object {
+          "name": "number",
+          "type": "NameExpression",
+        },
+      },
+    ],
+    "sees": Array [],
+    "tags": Array [
+      Object {
+        "description": "numberone",
+        "lineNumber": 2,
+        "title": "returns",
+        "type": Object {
+          "name": "number",
+          "type": "NameExpression",
+        },
+      },
+    ],
+    "throws": Array [],
+    "todos": Array [],
+    "yields": Array [],
+  },
+  Object {
+    "augments": Array [],
+    "context": Object {
+      "file": "[path]",
+      "loc": Object {
+        "end": Object {
+          "column": 4,
+          "line": 5,
+        },
+        "start": Object {
+          "column": 1,
+          "line": 5,
+        },
+      },
+    },
+    "description": "",
+    "examples": Array [],
+    "implements": Array [],
+    "kind": "function",
+    "members": Object {
+      "events": Array [],
+      "global": Array [],
+      "inner": Array [],
+      "instance": Array [],
+      "static": Array [],
+    },
+    "name": "dummy",
+    "namespace": "dummy",
+    "params": Array [],
+    "path": Array [
+      Object {
+        "kind": "function",
+        "name": "dummy",
+      },
+    ],
+    "properties": Array [],
+    "returns": Array [],
+    "sees": Array [],
+    "tags": Array [
+      Object {
+        "description": null,
+        "lineNumber": 1,
+        "name": "dummy",
+        "title": "method",
+      },
+    ],
+    "throws": Array [],
+    "todos": Array [],
+    "yields": Array [],
+  },
+  Object {
+    "augments": Array [],
+    "context": Object {
+      "file": "[path]",
+      "kind": "method",
+      "loc": Object {
+        "end": Object {
+          "column": 4,
+          "line": 5,
+        },
+        "start": Object {
+          "column": 1,
+          "line": 5,
+        },
+      },
+      "name": "dummy_method",
+    },
+    "description": "",
+    "examples": Array [],
+    "implements": Array [],
+    "kind": "method",
+    "members": Object {
+      "events": Array [],
+      "global": Array [],
+      "inner": Array [],
+      "instance": Array [],
+      "static": Array [],
+    },
+    "name": "dummy_method",
+    "namespace": "dummy_method",
+    "params": Array [
+      Object {
+        "lineNumber": 1,
+        "name": "dummy_param",
+        "title": "param",
+        "type": Object {
+          "name": "number",
+          "type": "NameExpression",
+        },
+      },
+    ],
+    "path": Array [
+      Object {
+        "kind": "method",
+        "name": "dummy_method",
+      },
+    ],
+    "properties": Array [],
+    "returns": Array [],
+    "sees": Array [],
+    "tags": Array [
+      Object {
+        "description": null,
+        "lineNumber": 1,
+        "name": "dummy_param",
+        "title": "param",
+        "type": Object {
+          "name": "number",
+          "type": "NameExpression",
+        },
+      },
+    ],
+    "throws": Array [],
+    "todos": Array [],
+    "yields": Array [],
+  },
+  Object {
+    "augments": Array [],
+    "context": Object {
+      "file": "[path]",
+      "kind": "SHOULD_NOT_APPEAR_IN_THE_RESULT",
+      "loc": Object {
+        "end": Object {
+          "column": 4,
+          "line": 5,
+        },
+        "start": Object {
+          "column": 1,
+          "line": 5,
+        },
+      },
+      "name": "SHOULD_NOT_APPEAR_IN_THE_RESULT",
+    },
+    "description": "",
+    "examples": Array [],
+    "implements": Array [],
+    "kind": "function",
+    "members": Object {
+      "events": Array [],
+      "global": Array [],
+      "inner": Array [],
+      "instance": Array [],
+      "static": Array [],
+    },
+    "name": "not_so_dummy",
+    "namespace": "not_so_dummy",
+    "params": Array [],
+    "path": Array [
+      Object {
+        "kind": "function",
+        "name": "not_so_dummy",
+      },
+    ],
+    "properties": Array [],
+    "returns": Array [],
+    "sees": Array [],
+    "tags": Array [
+      Object {
+        "description": null,
+        "lineNumber": 1,
+        "name": "not_so_dummy",
+        "title": "method",
+      },
+    ],
+    "throws": Array [],
+    "todos": Array [],
+    "yields": Array [],
+  },
+]
+`;
+
 exports[`should use browser resolve 1`] = `
 Array [
   Object {
diff --git a/__tests__/__snapshots__/test.js.snap b/__tests__/__snapshots__/test.js.snap
index e8a0a2294..ff595d1ed 100644
--- a/__tests__/__snapshots__/test.js.snap
+++ b/__tests__/__snapshots__/test.js.snap
@@ -287,6 +287,325 @@ Array [
 ]
 `;
 
+exports[`Check that plugins are loaded and used 1`] = `
+Array [
+  Object {
+    "augments": Array [],
+    "context": Object {
+      "loc": SourceLocation {
+        "end": Position {
+          "column": 1,
+          "line": 13,
+        },
+        "filename": undefined,
+        "identifierName": undefined,
+        "start": Position {
+          "column": 0,
+          "line": 10,
+        },
+      },
+    },
+    "description": Object {
+      "children": Array [
+        Object {
+          "children": Array [
+            Object {
+              "type": "text",
+              "value": "This function returns the number plus two.",
+            },
+          ],
+          "type": "paragraph",
+        },
+      ],
+      "type": "root",
+    },
+    "errors": Array [],
+    "examples": Array [
+      Object {
+        "description": "var result = returnTwo(4);
+// result is 6",
+      },
+    ],
+    "implements": Array [],
+    "kind": "function",
+    "loc": SourceLocation {
+      "end": Position {
+        "column": 3,
+        "line": 9,
+      },
+      "filename": undefined,
+      "identifierName": undefined,
+      "start": Position {
+        "column": 0,
+        "line": 1,
+      },
+    },
+    "members": Object {
+      "events": Array [],
+      "global": Array [],
+      "inner": Array [],
+      "instance": Array [],
+      "static": Array [],
+    },
+    "name": "returnTwo",
+    "namespace": "returnTwo",
+    "params": Array [
+      Object {
+        "description": Object {
+          "children": Array [
+            Object {
+              "children": Array [
+                Object {
+                  "type": "text",
+                  "value": "the number",
+                },
+              ],
+              "type": "paragraph",
+            },
+          ],
+          "type": "root",
+        },
+        "lineNumber": 3,
+        "name": "a",
+        "title": "param",
+        "type": Object {
+          "name": "Number",
+          "type": "NameExpression",
+        },
+      },
+    ],
+    "path": Array [
+      Object {
+        "kind": "function",
+        "name": "returnTwo",
+      },
+    ],
+    "properties": Array [],
+    "returns": Array [
+      Object {
+        "description": Object {
+          "children": Array [
+            Object {
+              "children": Array [
+                Object {
+                  "type": "text",
+                  "value": "numbertwo",
+                },
+              ],
+              "type": "paragraph",
+            },
+          ],
+          "type": "root",
+        },
+        "title": "returns",
+        "type": Object {
+          "name": "Number",
+          "type": "NameExpression",
+        },
+      },
+    ],
+    "sees": Array [],
+    "tags": Array [
+      Object {
+        "description": "the number",
+        "lineNumber": 3,
+        "name": "a",
+        "title": "param",
+        "type": Object {
+          "name": "Number",
+          "type": "NameExpression",
+        },
+      },
+      Object {
+        "description": "numbertwo",
+        "lineNumber": 4,
+        "title": "returns",
+        "type": Object {
+          "name": "Number",
+          "type": "NameExpression",
+        },
+      },
+      Object {
+        "description": "var result = returnTwo(4);
+// result is 6",
+        "lineNumber": 5,
+        "title": "example",
+      },
+    ],
+    "throws": Array [],
+    "todos": Array [],
+    "yields": Array [],
+  },
+  Object {
+    "augments": Array [],
+    "context": Object {
+      "loc": Object {
+        "end": Object {
+          "column": 4,
+          "line": 5,
+        },
+        "start": Object {
+          "column": 1,
+          "line": 5,
+        },
+      },
+    },
+    "description": "",
+    "errors": Array [],
+    "examples": Array [],
+    "implements": Array [],
+    "kind": "function",
+    "loc": undefined,
+    "members": Object {
+      "events": Array [],
+      "global": Array [],
+      "inner": Array [],
+      "instance": Array [],
+      "static": Array [],
+    },
+    "name": "dummy",
+    "namespace": "dummy",
+    "params": Array [],
+    "path": Array [
+      Object {
+        "kind": "function",
+        "name": "dummy",
+      },
+    ],
+    "properties": Array [],
+    "returns": Array [],
+    "sees": Array [],
+    "tags": Array [
+      Object {
+        "description": null,
+        "lineNumber": 1,
+        "name": "dummy",
+        "title": "method",
+      },
+    ],
+    "throws": Array [],
+    "todos": Array [],
+    "yields": Array [],
+  },
+  Object {
+    "augments": Array [],
+    "context": Object {
+      "loc": Object {
+        "end": Object {
+          "column": 4,
+          "line": 5,
+        },
+        "start": Object {
+          "column": 1,
+          "line": 5,
+        },
+      },
+    },
+    "description": "",
+    "errors": Array [],
+    "examples": Array [],
+    "implements": Array [],
+    "kind": "method",
+    "loc": undefined,
+    "members": Object {
+      "events": Array [],
+      "global": Array [],
+      "inner": Array [],
+      "instance": Array [],
+      "static": Array [],
+    },
+    "name": "dummy_method",
+    "namespace": "dummy_method",
+    "params": Array [
+      Object {
+        "lineNumber": 1,
+        "name": "dummy_param",
+        "title": "param",
+        "type": Object {
+          "name": "number",
+          "type": "NameExpression",
+        },
+      },
+    ],
+    "path": Array [
+      Object {
+        "kind": "method",
+        "name": "dummy_method",
+      },
+    ],
+    "properties": Array [],
+    "returns": Array [],
+    "sees": Array [],
+    "tags": Array [
+      Object {
+        "description": null,
+        "lineNumber": 1,
+        "name": "dummy_param",
+        "title": "param",
+        "type": Object {
+          "name": "number",
+          "type": "NameExpression",
+        },
+      },
+    ],
+    "throws": Array [],
+    "todos": Array [],
+    "yields": Array [],
+  },
+  Object {
+    "augments": Array [],
+    "context": Object {
+      "loc": Object {
+        "end": Object {
+          "column": 4,
+          "line": 5,
+        },
+        "start": Object {
+          "column": 1,
+          "line": 5,
+        },
+      },
+    },
+    "description": "",
+    "errors": Array [],
+    "examples": Array [],
+    "implements": Array [],
+    "kind": "function",
+    "loc": undefined,
+    "members": Object {
+      "events": Array [],
+      "global": Array [],
+      "inner": Array [],
+      "instance": Array [],
+      "static": Array [],
+    },
+    "name": "not_so_dummy",
+    "namespace": "not_so_dummy",
+    "params": Array [],
+    "path": Array [
+      Object {
+        "kind": "function",
+        "name": "not_so_dummy",
+      },
+    ],
+    "properties": Array [],
+    "returns": Array [],
+    "sees": Array [],
+    "tags": Array [
+      Object {
+        "description": null,
+        "lineNumber": 1,
+        "name": "not_so_dummy",
+        "title": "method",
+      },
+    ],
+    "throws": Array [],
+    "todos": Array [],
+    "yields": Array [],
+  },
+]
+`;
+
 exports[`Use Source attribute only 1`] = `
 Array [
   Object {
diff --git a/__tests__/bin.js b/__tests__/bin.js
index 7f46bca1e..e668ad26f 100644
--- a/__tests__/bin.js
+++ b/__tests__/bin.js
@@ -3,7 +3,6 @@
 import path from 'path';
 import os from 'os';
 import { exec } from 'child_process';
-import tmp from 'tmp';
 import fs from 'fs-extra';
 import { fileURLToPath } from 'url';
 
@@ -60,6 +59,13 @@ test.skip('defaults to parsing package.json main', async function () {
   expect(data.length).toBeTruthy();
 });
 
+test('load a plugin', async function () {
+  const data = await documentation([
+    'build fixture/simple.input.js fixture/plugin.txt --plugin=../src/mock_plugin.js'
+  ]);
+  expect(normalize(data)).toMatchSnapshot();
+});
+
 test('accepts config file', async function () {
   const data = await documentation([
     'build fixture/sorting/input.js -c fixture/config.json'
diff --git a/__tests__/fixture/plugin.txt b/__tests__/fixture/plugin.txt
new file mode 100644
index 000000000..2244c29af
--- /dev/null
+++ b/__tests__/fixture/plugin.txt
@@ -0,0 +1,6 @@
+/**
+ * @method test
+ */
+
+test
+
diff --git a/__tests__/lib/__snapshots__/sort.js.snap b/__tests__/lib/__snapshots__/sort.js.snap
index dee9fc0b0..6a9936c66 100644
--- a/__tests__/lib/__snapshots__/sort.js.snap
+++ b/__tests__/lib/__snapshots__/sort.js.snap
@@ -1,5 +1,33 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`sort by custom order 1`] = `
+Array [
+  Object {
+    "context": Object {
+      "sortKey": "b",
+    },
+    "memberof": "classB",
+    "name": "carrot",
+  },
+  Object {
+    "context": Object {
+      "sortKey": "c",
+    },
+    "kind": "typedef",
+    "memberof": "classA",
+    "name": "bananas",
+  },
+  Object {
+    "context": Object {
+      "sortKey": "a",
+    },
+    "kind": "method",
+    "memberof": "classB",
+    "name": "apples",
+  },
+]
+`;
+
 exports[`sort toc with files 1`] = `
 Array [
   Object {
@@ -139,7 +167,7 @@ Array [
       "sortKey": "c",
     },
     "kind": "function",
-    "memberof": "classB",
+    "memberof": "classA",
     "name": "bananas",
   },
   Object {
@@ -154,6 +182,13 @@ Array [
 
 exports[`sort toc with files absolute path 3`] = `
 Array [
+  Object {
+    "context": Object {
+      "sortKey": "b",
+    },
+    "memberof": "classB",
+    "name": "carrot",
+  },
   Object {
     "context": Object {
       "sortKey": "a",
@@ -167,7 +202,20 @@ Array [
       "sortKey": "c",
     },
     "kind": "function",
-    "memberof": "classB",
+    "memberof": "classA",
+    "name": "bananas",
+  },
+]
+`;
+
+exports[`sort toc with files absolute path 4`] = `
+Array [
+  Object {
+    "context": Object {
+      "sortKey": "c",
+    },
+    "kind": "function",
+    "memberof": "classA",
     "name": "bananas",
   },
   Object {
@@ -177,5 +225,13 @@ Array [
     "memberof": "classB",
     "name": "carrot",
   },
+  Object {
+    "context": Object {
+      "sortKey": "a",
+    },
+    "kind": "function",
+    "memberof": "classB",
+    "name": "apples",
+  },
 ]
 `;
diff --git a/__tests__/lib/sort.js b/__tests__/lib/sort.js
index 76ce57f3c..4e0063b72 100644
--- a/__tests__/lib/sort.js
+++ b/__tests__/lib/sort.js
@@ -141,7 +141,7 @@ test('sort toc with files absolute path', function () {
     context: { sortKey: 'c' },
     name: 'bananas',
     kind: 'function',
-    memberof: 'classB'
+    memberof: 'classA'
   };
 
   const snowflake = {
@@ -159,4 +159,36 @@ test('sort toc with files absolute path', function () {
       sortOrder: ['kind', 'alpha']
     })
   ).toMatchSnapshot();
+
+  expect(
+    sort([carrot, apples, bananas], {
+      sortOrder: ['memberof', 'kind', 'alpha']
+    })
+  ).toMatchSnapshot();
+});
+
+test('sort by custom order', function () {
+  const apples = {
+    context: { sortKey: 'a' },
+    name: 'apples',
+    kind: 'method',
+    memberof: 'classB'
+  };
+  const carrot = {
+    context: { sortKey: 'b' },
+    name: 'carrot',
+    memberof: 'classB'
+  };
+  const bananas = {
+    context: { sortKey: 'c' },
+    name: 'bananas',
+    kind: 'typedef',
+    memberof: 'classA'
+  };
+
+  expect(
+    sort([carrot, apples, bananas], {
+      sortOrder: [{ kind: ['typedef', 'method'] }, 'alpha']
+    })
+  ).toMatchSnapshot();
 });
diff --git a/__tests__/test.js b/__tests__/test.js
index aa9037c0c..76538e1f2 100644
--- a/__tests__/test.js
+++ b/__tests__/test.js
@@ -13,6 +13,7 @@ import _ from 'lodash';
 import chdir from 'chdir';
 import config from '../src/config';
 import { fileURLToPath } from 'url';
+import { jest } from '@jest/globals';
 
 const UPDATE = !!process.env.UPDATE;
 const __filename = fileURLToPath(import.meta.url);
@@ -71,6 +72,41 @@ test('Check that external modules could parse as input', async function () {
   expect(result).toMatchSnapshot();
 });
 
+test('Check that plugins are loaded and used', async function () {
+  const initCb = jest.fn();
+  const parseCb = jest.fn();
+  const mockPlugin = await import('../src/mock_plugin.js');
+  mockPlugin.mockInit(initCb, parseCb);
+
+  const dir = path.join(__dirname, 'fixture');
+  const result = await documentation.build(
+    [path.join(dir, 'simple-two.input.js'), path.join(dir, 'plugin.txt')],
+    { plugin: ['./mock_plugin.js'], order: 'test' }
+  );
+  normalize(result);
+  expect(result).toMatchSnapshot();
+
+  // name from JSDoc tag
+  expect(result[1].name).toBe('dummy');
+
+  // name parsed by the plugin
+  expect(result[2].name).toBe('dummy_method');
+
+  // name from plugin parsing overridden by JSDoc tag
+  expect(result[3].name).toBe('not_so_dummy');
+
+  expect(initCb.mock.calls.length).toBe(1);
+  expect(initCb.mock.calls[0][0].order).toBe('test');
+
+  expect(parseCb.mock.calls.length).toBe(2);
+  expect(
+    parseCb.mock.calls[0][0].file.includes('fixture/simple-two.input.js')
+  ).toBeTruthy();
+  expect(
+    parseCb.mock.calls[1][0].file.includes('fixture/plugin.txt')
+  ).toBeTruthy();
+});
+
 test('bad input', function () {
   glob
     .sync(path.join(__dirname, 'fixture/bad', '*.input.js'))
diff --git a/docs/CONFIG.md b/docs/CONFIG.md
index 6dab84d58..1c6da18f2 100644
--- a/docs/CONFIG.md
+++ b/docs/CONFIG.md
@@ -72,3 +72,31 @@ toc:
       - shortestPath
       - salesman
 ```
+
+## Sorting
+
+Sorting options can be specified in the configuration file. Example:
+
+```yml
+sortOrder:
+  - memberof
+  - alpha
+```
+
+Additionally, a custom sort order can be given, which is not possible when using the CLI option. Example:
+
+```yml
+sortOrder:
+  - kind:
+    - namespace
+    - class
+    - interface
+    - typedef
+    - enum
+    - constant
+    - function
+    - property
+    - member
+  - memberof
+  - alpha
+```
diff --git a/docs/POLYGLOT.md b/docs/POLYGLOT.md
deleted file mode 100644
index f0005ae0b..000000000
--- a/docs/POLYGLOT.md
+++ /dev/null
@@ -1,2 +0,0 @@
-🚨 Polyglot mode is now deprecated. It will be replaced by a pluggable
-input system in future versions. 🚨
diff --git a/docs/USAGE.md b/docs/USAGE.md
index 5fdc6bf03..f801e2568 100644
--- a/docs/USAGE.md
+++ b/docs/USAGE.md
@@ -58,7 +58,7 @@ Options:
                                                       [boolean] [default: false]
   --sort-order               The order to sort the documentation, may be
                              specified multiple times
-                                [choices: "source", "alpha", "kind"]
+                                [choices: "source", "alpha", "kind", "memberof"]
                                                              [default: "source"]
   --output, -o               output location. omit for stdout, otherwise is a
                              filename for single-file outputs and a directory
diff --git a/src/commands/shared_options.js b/src/commands/shared_options.js
index 0379debb8..c826ae519 100644
--- a/src/commands/shared_options.js
+++ b/src/commands/shared_options.js
@@ -45,6 +45,10 @@ export const sharedInputOptions = {
     type: 'array',
     alias: 'pe'
   },
+  plugin: {
+    type: 'array',
+    describe: 'load a plugin'
+  },
   access: {
     describe:
       'Include only comments with a given access level, out of private, ' +
@@ -75,7 +79,7 @@ export const sharedInputOptions = {
   'sort-order': {
     describe: 'The order to sort the documentation',
     array: true,
-    choices: ['source', 'alpha', 'kind', 'access'],
+    choices: ['source', 'alpha', 'kind', 'access', 'memberof'],
     default: ['source']
   },
   resolve: {
diff --git a/src/config.js b/src/config.js
index 17d49d3d4..260c67e03 100644
--- a/src/config.js
+++ b/src/config.js
@@ -1,11 +1,11 @@
 const defaultConfig = {
-  // package.json ignored and don't get project infromation
+  // package.json ignored and don't get project information
   'no-package': false,
-  // Extenstions which by dafault are parse
+  // Extensions which by default are parsed
   parseExtension: ['.mjs', '.js', '.jsx', '.es5', '.es6', '.vue', '.ts', '.tsx']
 };
 
-function normalaze(config, global) {
+function normalize(config, global) {
   if (config.parseExtension) {
     config.parseExtension = Array.from(
       new Set([...config.parseExtension, ...global.parseExtension])
@@ -24,6 +24,6 @@ export default {
     this.globalConfig.parseExtension = [...defaultConfig.parseExtension];
   },
   add(parameters) {
-    Object.assign(this.globalConfig, normalaze(parameters, this.globalConfig));
+    Object.assign(this.globalConfig, normalize(parameters, this.globalConfig));
   }
 };
diff --git a/src/index.js b/src/index.js
index abe624c7c..dd0b21394 100644
--- a/src/index.js
+++ b/src/index.js
@@ -26,6 +26,7 @@ import md from './output/markdown.js';
 import json from './output/json.js';
 import createFormatters from './output/util/formatters.js';
 import LinkerStack from './output/util/linker_stack.js';
+import pluginAPI from './plugin_api.js';
 
 /**
  * Build a pipeline of comment handlers.
@@ -104,6 +105,14 @@ function buildInternal(inputsAndConfig) {
   ]);
 
   const extractedComments = _.flatMap(inputs, function (sourceFile) {
+    if (config.plugin) {
+      for (const plugin of config.plugin) {
+        if (config._module[plugin].parse) {
+          const r = config._module[plugin].parse(sourceFile, config, pluginAPI);
+          if (r) return r.map(buildPipeline);
+        }
+      }
+    }
     return parseJavaScript(sourceFile, config).map(buildPipeline);
   }).filter(Boolean);
 
@@ -132,6 +141,14 @@ function lintInternal(inputsAndConfig) {
   ]);
 
   const extractedComments = _.flatMap(inputs, sourceFile => {
+    if (config.plugin) {
+      for (const plugin of config.plugin) {
+        if (config._module[plugin].parse) {
+          const r = config._module[plugin].parse(sourceFile, config, pluginAPI);
+          if (r) return r.map(lintPipeline);
+        }
+      }
+    }
     return parseJavaScript(sourceFile, config).map(lintPipeline);
   }).filter(Boolean);
 
@@ -180,6 +197,7 @@ export const lint = (indexes, args) =>
  * @param {Array<string>} args.external a string regex / glob match pattern
  * that defines what external modules will be whitelisted and included in the
  * generated documentation.
+ * @param {Array<string>} [args.plugin=[]] load plugins
  * @param {boolean} [args.shallow=false] whether to avoid dependency parsing
  * even in JavaScript code.
  * @param {Array<string|Object>} [args.order=[]] optional array that
diff --git a/src/merge_config.js b/src/merge_config.js
index cea2591c6..6ad688d8c 100644
--- a/src/merge_config.js
+++ b/src/merge_config.js
@@ -79,6 +79,36 @@ export default async function mergeConfig(config = {}) {
   conf.add(config);
   conf.add(await readConfigFile(conf.globalConfig.config));
   conf.add(await readPackage(conf.globalConfig['no-package']));
+  if (conf.globalConfig.plugin) {
+    await loadPlugins(conf.globalConfig);
+  }
 
   return conf.globalConfig;
 }
+
+/**
+ * Load the external plugins
+ *
+ * @param {Object} configuration plugins section of the configuration
+ * @returns {void}
+ */
+async function loadPlugins(config) {
+  if (!config._module)
+    Object.defineProperty(config, '_module', {
+      enumerable: false,
+      writable: false,
+      configurable: false,
+      value: {}
+    });
+  for (const plugin of config.plugin) {
+    try {
+      config._module[plugin] = await import(plugin);
+      if (config._module[plugin].init) {
+        await config._module[plugin].init(config);
+      }
+    } catch (e) {
+      console.error(`Failed loading ${plugin}`);
+      throw e;
+    }
+  }
+}
diff --git a/src/mock_plugin.js b/src/mock_plugin.js
new file mode 100644
index 000000000..2e4d25018
--- /dev/null
+++ b/src/mock_plugin.js
@@ -0,0 +1,50 @@
+let initCb, parseCb, dummy;
+
+export function mockInit(init, parse) {
+  initCb = init;
+  parseCb = parse;
+}
+
+export async function init() {
+  if (initCb) initCb(...arguments);
+  dummy = [
+    {
+      value: '*\n * @method dummy\n ',
+      context: {
+        file: 'plugin.txt',
+        loc: { start: { line: 5, column: 1 }, end: { line: 5, column: 4 } },
+        sortKey: 'a'
+      },
+      loc: { start: { line: 0, column: 1 }, end: { line: 2, column: 1 } }
+    },
+    {
+      value: '*\n * @param {number} dummy_param\n ',
+      context: {
+        file: 'plugin.txt',
+        loc: { start: { line: 5, column: 1 }, end: { line: 5, column: 4 } },
+        sortKey: 'b',
+        kind: 'method',
+        name: 'dummy_method'
+      },
+      loc: { start: { line: 0, column: 1 }, end: { line: 2, column: 1 } }
+    },
+    {
+      value: '*\n * @method not_so_dummy\n ',
+      context: {
+        file: 'plugin.txt',
+        loc: { start: { line: 5, column: 1 }, end: { line: 5, column: 4 } },
+        sortKey: 'c',
+        kind: 'SHOULD_NOT_APPEAR_IN_THE_RESULT',
+        name: 'SHOULD_NOT_APPEAR_IN_THE_RESULT'
+      },
+      loc: { start: { line: 0, column: 1 }, end: { line: 2, column: 1 } }
+    }
+  ];
+}
+
+export function parse(file, _config, api) {
+  if (parseCb) parseCb(...arguments);
+  if (file.file.includes('plugin.txt'))
+    return dummy.map(c => api.parseJSDoc(c.value, c.log, c.context));
+  return false;
+}
diff --git a/src/parse.js b/src/parse.js
index a10c7b2b8..d756440a5 100644
--- a/src/parse.js
+++ b/src/parse.js
@@ -636,6 +636,22 @@ export default function parseJSDoc(comment, loc, context) {
   result.todos = [];
   result.yields = [];
 
+  if (context) {
+    for (const tag of [
+      'kind',
+      'name',
+      'returns',
+      'params',
+      'properties',
+      'errors',
+      'augments',
+      'throws',
+      'yields',
+      'implements'
+    ])
+      if (context[tag]) result[tag] = context[tag];
+  }
+
   if (result.description) {
     result.description = parseMarkdown(result.description);
   }
@@ -669,7 +685,7 @@ export default function parseJSDoc(comment, loc, context) {
   // Using the @name tag, or any other tag that sets the name of a comment,
   // disconnects the comment from its surrounding code.
   if (context && result.name) {
-    delete context.ast;
+    if (context.ast) delete context.ast;
   }
 
   return result;
diff --git a/src/plugin_api.js b/src/plugin_api.js
new file mode 100644
index 000000000..6cfdda6d7
--- /dev/null
+++ b/src/plugin_api.js
@@ -0,0 +1,8 @@
+import parseJSDoc from './parse.js';
+import isJSDocComment from './is_jsdoc_comment.js';
+const pluginAPI = {
+  parseJSDoc,
+  isJSDocComment
+};
+
+export default pluginAPI;
diff --git a/src/sort.js b/src/sort.js
index a5bde334f..76551823e 100644
--- a/src/sort.js
+++ b/src/sort.js
@@ -103,13 +103,20 @@ export default function (comments, options) {
   return fixed.concat(unfixed);
 }
 
-function compareCommentsByField(field, a, b) {
+function compareCommentsByField(field, a, b, customOrder) {
   const akey = a[field];
   const bkey = b[field];
 
   if (akey && bkey) {
+    if (customOrder) {
+      const aIdx = customOrder.findIndex(o => o == akey);
+      const bIdx = customOrder.findIndex(o => o == bkey);
+      return aIdx - bIdx;
+    }
     return akey.localeCompare(bkey, undefined, { caseFirst: 'upper' });
   }
+  if (akey) return 1;
+  if (bkey) return -1;
   return 0;
 }
 
@@ -121,13 +128,19 @@ const sortFns = {
   alpha: compareCommentsByField.bind(null, 'name'),
   source: compareCommentsBySourceLocation,
   kind: compareCommentsByField.bind(null, 'kind'),
-  access: compareCommentsByField.bind(null, 'access')
+  access: compareCommentsByField.bind(null, 'access'),
+  memberof: compareCommentsByField.bind(null, 'memberof')
 };
 
 function sortComments(comments, sortOrder) {
   return comments.sort((a, b) => {
     for (const sortMethod of sortOrder || ['source']) {
-      const r = sortFns[sortMethod](a, b);
+      const sortMethodName =
+        typeof sortMethod === 'object'
+          ? Object.keys(sortMethod)[0]
+          : sortMethod;
+      const customOrder = sortMethod[sortMethodName];
+      const r = sortFns[sortMethodName](a, b, customOrder);
       if (r !== 0) return r;
     }
     return 0;