dotcom-tool-kit 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/run CHANGED
@@ -1,17 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const argv = require('minimist')(process.argv.slice(2), {
4
- boolean: ['help', 'install'],
4
+ boolean: ['help', 'install', 'listPlugins'],
5
5
  '--': true
6
6
  })
7
7
 
8
- const { runTasks, showHelp, installHooks } = require('../lib')
8
+ const { runTasks, showHelp, installHooks, listPlugins } = require('../lib')
9
9
  const { rootLogger, styles } = require('@dotcom-tool-kit/logger')
10
10
 
11
11
  async function main() {
12
12
  try {
13
13
  if (argv.install) {
14
14
  await installHooks(rootLogger)
15
+ } else if (argv.listPlugins) {
16
+ await listPlugins(rootLogger)
15
17
  } else if (argv.help || argv._.length === 0) {
16
18
  await showHelp(rootLogger, argv._)
17
19
  } else {
package/lib/config.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { Logger } from 'winston';
2
2
  import type { HookTask } from './hook';
3
3
  import { Conflict } from './conflict';
4
- import type { TaskClass, Hook, Plugin } from '@dotcom-tool-kit/types';
4
+ import { TaskClass, Hook, Plugin } from '@dotcom-tool-kit/types';
5
5
  export interface PluginOptions {
6
6
  options: Record<string, unknown>;
7
7
  plugin: Plugin;
@@ -12,6 +12,7 @@ export interface Config {
12
12
  plugins: {
13
13
  [id: string]: Plugin;
14
14
  };
15
+ resolvedPlugins: Set<Plugin>;
15
16
  tasks: {
16
17
  [id: string]: TaskClass | Conflict<TaskClass>;
17
18
  };
@@ -39,7 +40,8 @@ export interface ValidConfig extends Config {
39
40
  [id: string]: Hook;
40
41
  };
41
42
  }
42
- export declare function validateConfig(config: Config): Promise<ValidConfig>;
43
+ export declare const createConfig: () => Config;
44
+ export declare function validateConfig(config: Config): asserts config is ValidConfig;
43
45
  export declare function checkInstall(config: ValidConfig): Promise<void>;
44
46
  export declare function loadConfig(logger: Logger, options?: {
45
47
  validate?: true;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEtC,OAAO,EAAE,QAAQ,EAA+C,MAAM,YAAY,CAAA;AAElF,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAYrE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IACjC,KAAK,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAA;KAAE,CAAA;IACxD,SAAS,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAA;IAC1D,OAAO,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,SAAS,CAAA;KAAE,CAAA;IAC9E,KAAK,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;KAAE,CAAA;CAC/C;AAED,MAAM,WAAW,WAAY,SAAQ,MAAM;IACzC,KAAK,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAA;IAClC,SAAS,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,CAAA;KAAE,CAAA;IACrC,OAAO,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,CAAA;KAAE,CAAA;IACxC,KAAK,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CAC9B;AAmBD,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAyFzE;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;AAC/F,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEtC,OAAO,EAAE,QAAQ,EAA+C,MAAM,YAAY,CAAA;AAElF,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAYhE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IACjC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC5B,KAAK,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAA;KAAE,CAAA;IACxD,SAAS,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAA;IAC1D,OAAO,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,SAAS,CAAA;KAAE,CAAA;IAC9E,KAAK,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;KAAE,CAAA;CAC/C;AAED,MAAM,WAAW,WAAY,SAAQ,MAAM;IACzC,KAAK,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAA;IAClC,SAAS,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,CAAA;KAAE,CAAA;IACrC,OAAO,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,CAAA;KAAE,CAAA;IACxC,KAAK,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CAC9B;AAID,eAAO,MAAM,YAAY,QAAO,MAQ9B,CAAA;AAQF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,WAAW,CAuF5E;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;AAC/F,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA"}
package/lib/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadConfig = exports.checkInstall = exports.validateConfig = void 0;
3
+ exports.loadConfig = exports.checkInstall = exports.validateConfig = exports.createConfig = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const path_1 = (0, tslib_1.__importDefault)(require("path"));
6
6
  const plugin_1 = require("./plugin");
@@ -11,16 +11,18 @@ const coreRoot = path_1.default.resolve(__dirname, '../');
11
11
  const createConfig = () => ({
12
12
  root: coreRoot,
13
13
  plugins: {},
14
+ resolvedPlugins: new Set(),
14
15
  tasks: {},
15
16
  hookTasks: {},
16
17
  options: {},
17
18
  hooks: {}
18
19
  });
20
+ exports.createConfig = createConfig;
19
21
  async function asyncFilter(items, predicate) {
20
22
  const results = await Promise.all(items.map(async (item) => ({ item, keep: await predicate(item) })));
21
23
  return results.filter(({ keep }) => keep).map(({ item }) => item);
22
24
  }
23
- async function validateConfig(config) {
25
+ function validateConfig(config) {
24
26
  const hookTaskConflicts = (0, conflict_1.findConflicts)(Object.values(config.hookTasks));
25
27
  const hookConflicts = (0, conflict_1.findConflicts)(Object.values(config.hooks));
26
28
  const taskConflicts = (0, conflict_1.findConflicts)(Object.values(config.tasks));
@@ -84,7 +86,6 @@ async function validateConfig(config) {
84
86
  if (shouldThrow) {
85
87
  throw error;
86
88
  }
87
- return config;
88
89
  }
89
90
  exports.validateConfig = validateConfig;
90
91
  async function checkInstall(config) {
@@ -100,15 +101,14 @@ async function checkInstall(config) {
100
101
  }
101
102
  exports.checkInstall = checkInstall;
102
103
  async function loadConfig(logger, { validate = true } = {}) {
104
+ const config = (0, exports.createConfig)();
103
105
  // start loading config and child plugins, starting from the consumer app directory
104
- const config = await (0, plugin_1.loadPluginConfig)(logger, {
105
- id: 'app root',
106
- root: process.cwd()
107
- }, createConfig());
108
- return validate ? validateConfig(config) : config;
106
+ const rootPlugin = await (0, plugin_1.loadPlugin)('app root', config, logger);
107
+ // collate root plugin and descendent hooks, options etc into config
108
+ (0, plugin_1.resolvePlugin)(rootPlugin, config, logger);
109
+ if (validate) {
110
+ validateConfig(config);
111
+ }
112
+ return config;
109
113
  }
110
114
  exports.loadConfig = loadConfig;
111
- // abstract class TestBase {}
112
- // class Test extends TestBase {}
113
- // const testBase = TestBase
114
- // const test = new testBase()
package/lib/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Logger } from 'winston';
2
2
  export declare function runTasks(logger: Logger, hooks: string[], files?: string[]): Promise<void>;
3
+ export declare function listPlugins(logger: Logger): Promise<void>;
3
4
  export { default as showHelp } from './help';
4
5
  export { default as installHooks } from './install';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAQrC,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8E/F;AAED,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AASrC,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8E/F;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO/D;AAED,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,WAAW,CAAA"}
package/lib/index.js CHANGED
@@ -3,11 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.installHooks = exports.showHelp = exports.runTasks = void 0;
6
+ exports.installHooks = exports.showHelp = exports.listPlugins = exports.runTasks = void 0;
7
7
  const error_1 = require("@dotcom-tool-kit/error");
8
8
  const config_1 = require("./config");
9
9
  const options_1 = require("@dotcom-tool-kit/options");
10
10
  const logger_1 = require("@dotcom-tool-kit/logger");
11
+ const messages_1 = require("./messages");
11
12
  async function runTasks(logger, hooks, files) {
12
13
  const config = await (0, config_1.loadConfig)(logger);
13
14
  const availableHooks = Object.keys(config.hooks)
@@ -72,6 +73,14 @@ ${error.details}`
72
73
  }
73
74
  }
74
75
  exports.runTasks = runTasks;
76
+ async function listPlugins(logger) {
77
+ const config = await (0, config_1.loadConfig)(logger, { validate: false });
78
+ const rootPlugin = config.plugins['app root'];
79
+ if (rootPlugin) {
80
+ logger.info((0, messages_1.formatPluginTree)(rootPlugin).join('\n'));
81
+ }
82
+ }
83
+ exports.listPlugins = listPlugins;
75
84
  var help_1 = require("./help");
76
85
  Object.defineProperty(exports, "showHelp", { enumerable: true, get: function () { return __importDefault(help_1).default; } });
77
86
  var install_1 = require("./install");
package/lib/messages.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { PluginOptions } from './config';
2
2
  import type { Conflict } from './conflict';
3
3
  import type { HookTask } from './hook';
4
- import type { Hook, TaskClass } from '@dotcom-tool-kit/types';
4
+ import type { Plugin, Hook, TaskClass } from '@dotcom-tool-kit/types';
5
5
  export declare const formatTaskConflicts: (conflicts: Conflict<TaskClass>[]) => string;
6
6
  export declare const formatHookConflicts: (conflicts: Conflict<Hook>[]) => string;
7
7
  export declare const formatHookTaskConflicts: (conflicts: Conflict<HookTask>[]) => string;
@@ -14,5 +14,6 @@ declare type Missing = {
14
14
  tasks: string[];
15
15
  };
16
16
  export declare const formatMissingTasks: (missingTasks: Missing[], tasks: string[]) => string;
17
+ export declare function formatPluginTree(plugin: Plugin): string[];
17
18
  export {};
18
19
  //# sourceMappingURL=messages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEtC,OAAO,KAAK,EAAU,IAAI,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AASrE,eAAO,MAAM,mBAAmB,cAAe,SAAS,SAAS,CAAC,EAAE,KAAG,MAKE,CAAA;AASzE,eAAO,MAAM,mBAAmB,cAAe,SAAS,IAAI,CAAC,EAAE,KAAG,MAKO,CAAA;AAazE,eAAO,MAAM,uBAAuB,cAAe,SAAS,QAAQ,CAAC,EAAE,KAAG,MAQzE,CAAA;AAOD,eAAO,MAAM,qBAAqB,cAAe,SAAS,aAAa,CAAC,EAAE,KAAG,MAU5E,CAAA;AAMD,eAAO,MAAM,wBAAwB,mBACnB,QAAQ,EAAE,gBACZ,MAAM,EAAE,KACrB,MAWF,CAAA;AAED,eAAO,MAAM,mBAAmB,kBACf,MAAM,EAAE,kBACP,MAAM,EAAE,KACvB,MAWF,CAAA;AAED,eAAO,MAAM,sBAAsB,qBACf,IAAI,EAAE,KACvB,MAKF,CAAA;AAED,aAAK,OAAO,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AAOlD,eAAO,MAAM,kBAAkB,iBACf,OAAO,EAAE,SAChB,MAAM,EAAE,KACd,MAOF,CAAA"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEtC,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AASrE,eAAO,MAAM,mBAAmB,cAAe,SAAS,SAAS,CAAC,EAAE,KAAG,MAKE,CAAA;AASzE,eAAO,MAAM,mBAAmB,cAAe,SAAS,IAAI,CAAC,EAAE,KAAG,MAKO,CAAA;AAazE,eAAO,MAAM,uBAAuB,cAAe,SAAS,QAAQ,CAAC,EAAE,KAAG,MAQzE,CAAA;AAOD,eAAO,MAAM,qBAAqB,cAAe,SAAS,aAAa,CAAC,EAAE,KAAG,MAU5E,CAAA;AAMD,eAAO,MAAM,wBAAwB,mBACnB,QAAQ,EAAE,gBACZ,MAAM,EAAE,KACrB,MAWF,CAAA;AAED,eAAO,MAAM,mBAAmB,kBACf,MAAM,EAAE,kBACP,MAAM,EAAE,KACvB,MAWF,CAAA;AAED,eAAO,MAAM,sBAAsB,qBACf,IAAI,EAAE,KACvB,MAKF,CAAA;AAED,aAAK,OAAO,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AAOlD,eAAO,MAAM,kBAAkB,iBACf,OAAO,EAAE,SAChB,MAAM,EAAE,KACd,MAOF,CAAA;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAezD"}
package/lib/messages.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.formatMissingTasks = exports.formatUninstalledHooks = exports.formatUnusedOptions = exports.formatUndefinedHookTasks = exports.formatOptionConflicts = exports.formatHookTaskConflicts = exports.formatHookConflicts = exports.formatTaskConflicts = void 0;
3
+ exports.formatPluginTree = exports.formatMissingTasks = exports.formatUninstalledHooks = exports.formatUnusedOptions = exports.formatUndefinedHookTasks = exports.formatOptionConflicts = exports.formatHookTaskConflicts = exports.formatHookConflicts = exports.formatTaskConflicts = void 0;
4
4
  const logger_1 = require("@dotcom-tool-kit/logger");
5
5
  const formatTaskConflict = (conflict) => `- ${logger_1.styles.task(conflict.conflicting[0].id || 'unknown task')} ${logger_1.styles.dim('from plugins')} ${conflict.conflicting
6
6
  .map((task) => logger_1.styles.plugin(task.plugin ? task.plugin.id : 'unknown plugin'))
@@ -80,3 +80,17 @@ They could be misspelt, or defined by a Tool Kit plugin that isn't used by this
80
80
  Available tasks are: ${tasks.map(logger_1.styles.task).join(', ')}.
81
81
  `;
82
82
  exports.formatMissingTasks = formatMissingTasks;
83
+ function formatPluginTree(plugin) {
84
+ var _a;
85
+ return [
86
+ logger_1.styles.plugin(plugin.id),
87
+ ...((_a = plugin.children) !== null && _a !== void 0 ? _a : []).flatMap((child, childIndex, children) => formatPluginTree(child).map((line, lineIndex) => lineIndex === 0
88
+ ? childIndex === children.length - 1
89
+ ? `└ ${line}`
90
+ : `├ ${line}`
91
+ : childIndex === children.length - 1
92
+ ? ` ${line}`
93
+ : `│ ${line}`))
94
+ ];
95
+ }
96
+ exports.formatPluginTree = formatPluginTree;
package/lib/plugin.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { Logger } from 'winston';
2
2
  import { Config } from './config';
3
- import { Plugin } from '@dotcom-tool-kit/types';
4
- export declare function loadPluginConfig(logger: Logger, plugin: Plugin, config: Config): Promise<Config>;
5
- export declare function loadPlugin(logger: Logger, id: string, config: Config, parent?: Plugin): Promise<Plugin>;
6
- export declare function loadPlugins(logger: Logger, plugins: string[], config: Config, parent?: Plugin): Promise<Plugin[]>;
3
+ import { Plugin, PluginModule } from '@dotcom-tool-kit/types';
4
+ export declare function validatePlugin(plugin: unknown): asserts plugin is PluginModule;
5
+ export declare function loadPlugin(id: string, config: Config, logger: Logger, parent?: Plugin): Promise<Plugin>;
6
+ export declare function resolvePlugin(plugin: Plugin, config: Config, logger: Logger): void;
7
7
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAGrC,OAAO,EAAE,MAAM,EAAiB,MAAM,UAAU,CAAA;AAKhD,OAAO,EAA2B,MAAM,EAAa,MAAM,wBAAwB,CAAA;AAEnF,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6FtG;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAkFjB;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,EAAE,CAAC,CAEnB"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAGrC,OAAO,EAAE,MAAM,EAAiB,MAAM,UAAU,CAAA;AAIhD,OAAO,EAAQ,MAAM,EAAE,YAAY,EAAQ,MAAM,wBAAwB,CAAA;AAezE,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,IAAI,YAAY,CAmB9E;AASD,wBAAsB,UAAU,CAC9B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAmCjB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAsIlF"}
package/lib/plugin.js CHANGED
@@ -1,138 +1,186 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadPlugins = exports.loadPlugin = exports.loadPluginConfig = void 0;
3
+ exports.resolvePlugin = exports.loadPlugin = exports.validatePlugin = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const import_from_1 = (0, tslib_1.__importDefault)(require("import-from"));
6
5
  const resolve_from_1 = (0, tslib_1.__importDefault)(require("resolve-from"));
7
- const lodash_mergewith_1 = (0, tslib_1.__importDefault)(require("lodash.mergewith"));
8
6
  const conflict_1 = require("./conflict");
9
7
  const rc_file_1 = require("./rc-file");
10
8
  const error_1 = require("@dotcom-tool-kit/error");
11
- const logger_1 = require("@dotcom-tool-kit/logger");
12
9
  const types_1 = require("@dotcom-tool-kit/types");
13
- async function loadPluginConfig(logger, plugin, config) {
14
- const { plugins = [], hooks = {}, options = {} } = await (0, rc_file_1.loadToolKitRC)(plugin.root);
15
- // load any plugins requested by this plugin
16
- await loadPlugins(logger, plugins, config, plugin);
17
- // load plugin hook tasks. do this after loading child plugins, so
18
- // parent hooks get assigned after child hooks and can override them
19
- (0, lodash_mergewith_1.default)(config.hookTasks, hooks,
20
- // handle conflicts between hooks from different plugins
21
- (existingHookTask, configHookTask, id) => {
22
- const newHookTask = {
23
- id,
24
- plugin,
25
- tasks: Array.isArray(configHookTask) ? configHookTask : [configHookTask]
26
- };
27
- // this hook task might not have been set yet, in which case use the new one
28
- if (!existingHookTask) {
29
- return newHookTask;
30
- }
31
- const existingFromSibling = existingHookTask.plugin.parent && existingHookTask.plugin.parent === plugin.parent;
32
- // if the existing hook was from a sibling, that's a conflict
33
- // return a conflict either listing this hook and the siblings,
34
- // or merging in a previously-generated hook
35
- if (existingFromSibling) {
36
- const conflicting = (0, conflict_1.isConflict)(existingHookTask) ? existingHookTask.conflicting : [existingHookTask];
37
- const conflict = {
38
- plugin,
39
- conflicting: conflicting.concat(newHookTask)
40
- };
41
- return conflict;
42
- }
43
- // if we're here, any existing hook is from a child plugin,
44
- // so the parent always overrides it
45
- return newHookTask;
46
- });
47
- // merge options from this plugin's config with any options we've collected already
48
- // TODO this is almost the exact same code as for hooks, refactor
49
- (0, lodash_mergewith_1.default)(config.options, options, (existingOptions, configOptions, id) => {
50
- const pluginOptions = {
51
- options: configOptions,
52
- plugin,
53
- forPlugin: config.plugins[id]
54
- };
55
- // this options key might not have been set yet, in which case use the new one
56
- if (!existingOptions) {
57
- return pluginOptions;
58
- }
59
- const existingFromSibling = existingOptions.plugin.parent && existingOptions.plugin.parent === plugin.parent;
60
- // if the existing options were from a sibling, that's a conflict
61
- // return a conflict either listing these options and the sibling's,
62
- // or merging in previously-generated options
63
- if (existingFromSibling) {
64
- const conflicting = (0, conflict_1.isConflict)(existingOptions) ? existingOptions.conflicting : [existingOptions];
65
- const conflict = {
66
- plugin,
67
- conflicting: conflicting.concat(pluginOptions)
68
- };
69
- return conflict;
70
- }
71
- // if we're here, any existing options are from a child plugin,
72
- // so merge in overrides from the parent
73
- return { ...existingOptions, ...pluginOptions };
74
- });
75
- return config;
10
+ const lodash_isplainobject_1 = (0, tslib_1.__importDefault)(require("lodash.isplainobject"));
11
+ function isDescendent(possibleAncestor, possibleDescendent) {
12
+ if (!possibleDescendent.parent) {
13
+ return false;
14
+ }
15
+ else if (possibleDescendent.parent === possibleAncestor) {
16
+ return true;
17
+ }
18
+ else {
19
+ return isDescendent(possibleAncestor, possibleDescendent.parent);
20
+ }
21
+ }
22
+ function validatePlugin(plugin) {
23
+ const rawPlugin = plugin;
24
+ if (rawPlugin.tasks &&
25
+ !(Array.isArray(rawPlugin.tasks) && rawPlugin.tasks.every((task) => task.prototype instanceof types_1.Task))) {
26
+ throw new error_1.ToolKitError('tasks are not valid');
27
+ }
28
+ if (rawPlugin.hooks &&
29
+ !((0, lodash_isplainobject_1.default)(rawPlugin.hooks) &&
30
+ Object.values(rawPlugin.hooks).every((hook) => hook.prototype instanceof types_1.Hook))) {
31
+ throw new error_1.ToolKitError('hooks are not valid');
32
+ }
76
33
  }
77
- exports.loadPluginConfig = loadPluginConfig;
78
- async function loadPlugin(logger, id, config, parent) {
34
+ exports.validatePlugin = validatePlugin;
35
+ async function importPlugin(pluginPath) {
36
+ // pluginPath is an absolute resolved path to a plugin module as found from its parent
37
+ const pluginModule = await Promise.resolve().then(() => (0, tslib_1.__importStar)(require(pluginPath)));
38
+ validatePlugin(pluginModule);
39
+ return pluginModule;
40
+ }
41
+ async function loadPlugin(id, config, logger, parent) {
79
42
  // don't load duplicate plugins
80
43
  if (id in config.plugins) {
81
44
  return config.plugins[id];
82
45
  }
83
- const root = parent ? parent.root : process.cwd();
84
46
  // load plugin relative to the parent plugin
85
- const pluginRoot = (0, resolve_from_1.default)(root, id);
86
- const rawPlugin = (0, import_from_1.default)(root, id);
87
- let basePlugin;
88
- try {
89
- basePlugin = (0, types_1.instantiatePlugin)(rawPlugin, logger);
90
- }
91
- catch (error) {
92
- if (error instanceof error_1.ToolKitError) {
93
- error.details = `the package ${logger_1.styles.plugin(id)} at ${logger_1.styles.filepath(pluginRoot)} is not a valid plugin`;
94
- }
95
- throw error;
96
- }
47
+ const root = parent ? parent.root : process.cwd();
48
+ const pluginRoot = id === 'app root' ? root : (0, resolve_from_1.default)(root, id);
97
49
  const plugin = {
98
- ...basePlugin,
99
50
  id,
100
51
  root: pluginRoot,
101
52
  parent
102
53
  };
103
54
  config.plugins[id] = plugin;
104
- // add plugin tasks to our task registry, handling any conflicts
105
- (0, lodash_mergewith_1.default)(config.tasks, Object.fromEntries((plugin.tasks || []).map((task) => [task.name, task])), (existingTask, newTask, taskId) => {
106
- newTask.plugin = plugin;
107
- newTask.id = taskId;
108
- if (!existingTask) {
109
- return newTask;
110
- }
111
- const conflicting = (0, conflict_1.isConflict)(existingTask) ? existingTask.conflicting : [existingTask];
112
- return {
113
- plugin,
114
- conflicting: conflicting.concat(newTask)
115
- };
116
- });
117
- // add hooks to the registry, handling any conflicts
118
- // TODO refactor with command conflict handler
119
- (0, lodash_mergewith_1.default)(config.hooks, plugin.hooks, (existingHook, newHook, hookId) => {
120
- newHook.id = hookId;
121
- newHook.plugin = plugin;
122
- if (!existingHook) {
123
- return newHook;
124
- }
125
- const conflicting = (0, conflict_1.isConflict)(existingHook) ? existingHook.conflicting : [existingHook];
126
- return {
127
- plugin,
128
- conflicting: conflicting.concat(newHook)
129
- };
130
- });
131
- await loadPluginConfig(logger, plugin, config);
55
+ // start loading rc file in the background
56
+ const rcFilePromise = (0, rc_file_1.loadToolKitRC)(pluginRoot);
57
+ // start loading module in the background
58
+ const pluginModulePromise = id === 'app root' ? Promise.resolve(undefined) : importPlugin(pluginRoot);
59
+ plugin.rcFile = await rcFilePromise;
60
+ // start loading child plugins in the background
61
+ const childrenPromise = Promise.all(plugin.rcFile.plugins.map((child) => loadPlugin(child, config, logger, plugin)));
62
+ [plugin.module, plugin.children] = await Promise.all([pluginModulePromise, childrenPromise]);
132
63
  return plugin;
133
64
  }
134
65
  exports.loadPlugin = loadPlugin;
135
- function loadPlugins(logger, plugins, config, parent) {
136
- return Promise.all(plugins.map((plugin) => loadPlugin(logger, plugin, config, parent)));
66
+ function resolvePlugin(plugin, config, logger) {
67
+ // don't resolve plugins that have already been resolved to prevent self-conflicts
68
+ // between plugins included at multiple points in the tree
69
+ if (config.resolvedPlugins.has(plugin)) {
70
+ return;
71
+ }
72
+ if (plugin.children) {
73
+ // resolve child plugins first so parents can override the things their children set
74
+ for (const child of plugin.children) {
75
+ resolvePlugin(child, config, logger);
76
+ }
77
+ }
78
+ if (plugin.module) {
79
+ // add plugin tasks to our task registry, handling any conflicts
80
+ for (const newTask of plugin.module.tasks || []) {
81
+ const taskId = newTask.name;
82
+ const existingTask = config.tasks[taskId];
83
+ newTask.plugin = plugin;
84
+ newTask.id = taskId;
85
+ if (existingTask) {
86
+ const conflicting = (0, conflict_1.isConflict)(existingTask) ? existingTask.conflicting : [existingTask];
87
+ config.tasks[taskId] = {
88
+ plugin,
89
+ conflicting: conflicting.concat(newTask)
90
+ };
91
+ }
92
+ else {
93
+ config.tasks[taskId] = newTask;
94
+ }
95
+ }
96
+ // add hooks to the registry, handling any conflicts
97
+ // TODO refactor with command conflict handler
98
+ for (const [hookId, hookClass] of Object.entries(plugin.module.hooks || [])) {
99
+ const existingHook = config.hooks[hookId];
100
+ const newHook = new hookClass(logger);
101
+ newHook.id = hookId;
102
+ newHook.plugin = plugin;
103
+ if (existingHook) {
104
+ const conflicting = (0, conflict_1.isConflict)(existingHook) ? existingHook.conflicting : [existingHook];
105
+ config.hooks[hookId] = {
106
+ plugin,
107
+ conflicting: conflicting.concat(newHook)
108
+ };
109
+ }
110
+ else {
111
+ config.hooks[hookId] = newHook;
112
+ }
113
+ }
114
+ }
115
+ if (plugin.rcFile) {
116
+ // load plugin hook tasks. do this after loading child plugins, so
117
+ // parent hooks get assigned after child hooks and can override them
118
+ for (const [id, configHookTask] of Object.entries(plugin.rcFile.hooks)) {
119
+ // handle conflicts between hooks from different plugins
120
+ const existingHookTask = config.hookTasks[id];
121
+ const newHookTask = {
122
+ id,
123
+ plugin,
124
+ tasks: Array.isArray(configHookTask) ? configHookTask : [configHookTask]
125
+ };
126
+ if (existingHookTask) {
127
+ const existingFromDescendent = isDescendent(plugin, existingHookTask.plugin);
128
+ // plugins can only override hook tasks from their descendents, otherwise that's a conflict
129
+ // return a conflict either listing this hook and the siblings,
130
+ // or merging in a previously-generated hook
131
+ if (!existingFromDescendent) {
132
+ const conflicting = (0, conflict_1.isConflict)(existingHookTask) ? existingHookTask.conflicting : [existingHookTask];
133
+ const conflict = {
134
+ plugin,
135
+ conflicting: conflicting.concat(newHookTask)
136
+ };
137
+ config.hookTasks[id] = conflict;
138
+ }
139
+ else {
140
+ // if we're here, any existing hook is from a child plugin,
141
+ // so the parent always overrides it
142
+ config.hookTasks[id] = newHookTask;
143
+ }
144
+ }
145
+ else {
146
+ // this hook task might not have been set yet, in which case use the new one
147
+ config.hookTasks[id] = newHookTask;
148
+ }
149
+ }
150
+ // merge options from this plugin's config with any options we've collected already
151
+ // TODO this is almost the exact same code as for hooks, refactor
152
+ for (const [id, configOptions] of Object.entries(plugin.rcFile.options)) {
153
+ const existingOptions = config.options[id];
154
+ const pluginOptions = {
155
+ options: configOptions,
156
+ plugin,
157
+ forPlugin: config.plugins[id]
158
+ };
159
+ if (existingOptions) {
160
+ const existingFromDescendent = isDescendent(plugin, existingOptions.plugin);
161
+ // plugins can only override options from their descendents, otherwise it's a conflict
162
+ // return a conflict either listing these options and the sibling's,
163
+ // or merging in previously-generated options
164
+ if (!existingFromDescendent) {
165
+ const conflicting = (0, conflict_1.isConflict)(existingOptions) ? existingOptions.conflicting : [existingOptions];
166
+ const conflict = {
167
+ plugin,
168
+ conflicting: conflicting.concat(pluginOptions)
169
+ };
170
+ config.options[id] = conflict;
171
+ }
172
+ else {
173
+ // if we're here, any existing options are from a child plugin,
174
+ // so merge in overrides from the parent
175
+ config.options[id] = { ...existingOptions, ...pluginOptions };
176
+ }
177
+ }
178
+ else {
179
+ // this options key might not have been set yet, in which case use the new one
180
+ config.options[id] = pluginOptions;
181
+ }
182
+ }
183
+ }
184
+ config.resolvedPlugins.add(plugin);
137
185
  }
138
- exports.loadPlugins = loadPlugins;
186
+ exports.resolvePlugin = resolvePlugin;
package/lib/rc-file.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { RCFile } from '@dotcom-tool-kit/types/src';
1
2
  export declare const explorer: {
2
3
  readonly search: (searchFrom?: string | undefined) => Promise<import("cosmiconfig/dist/types").CosmiconfigResult>;
3
4
  readonly load: (filepath: string) => Promise<import("cosmiconfig/dist/types").CosmiconfigResult>;
@@ -5,14 +6,5 @@ export declare const explorer: {
5
6
  readonly clearSearchCache: () => void;
6
7
  readonly clearCaches: () => void;
7
8
  };
8
- export interface RCFile {
9
- plugins: string[];
10
- hooks: {
11
- [id: string]: string | string[];
12
- };
13
- options: {
14
- [id: string]: Record<string, unknown>;
15
- };
16
- }
17
9
  export declare function loadToolKitRC(root: string): Promise<RCFile>;
18
10
  //# sourceMappingURL=rc-file.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rc-file.d.ts","sourceRoot":"","sources":["../src/rc-file.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ;;;;;;CAA6D,CAAA;AAElF,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,KAAK,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,CAAA;KAAE,CAAA;IAC1C,OAAO,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAA;CACnD;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKjE"}
1
+ {"version":3,"file":"rc-file.d.ts","sourceRoot":"","sources":["../src/rc-file.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAA;AAEnD,eAAO,MAAM,QAAQ;;;;;;CAA6D,CAAA;AAMlF,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASjE"}
package/lib/rc-file.js CHANGED
@@ -4,9 +4,14 @@ exports.loadToolKitRC = exports.explorer = void 0;
4
4
  const cosmiconfig_1 = require("cosmiconfig");
5
5
  exports.explorer = (0, cosmiconfig_1.cosmiconfig)('toolkit', { ignoreEmptySearchPlaces: false });
6
6
  async function loadToolKitRC(root) {
7
- const result = await exports.explorer.search(root);
7
+ var _a, _b, _c;
8
+ const result = (await exports.explorer.search(root));
8
9
  if (!result || !result.config)
9
10
  return { plugins: [], hooks: {}, options: {} };
10
- return result.config;
11
+ return {
12
+ plugins: (_a = result.config.plugins) !== null && _a !== void 0 ? _a : [],
13
+ hooks: (_b = result.config.hooks) !== null && _b !== void 0 ? _b : {},
14
+ options: (_c = result.config.options) !== null && _c !== void 0 ? _c : {}
15
+ };
11
16
  }
12
17
  exports.loadToolKitRC = loadToolKitRC;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dotcom-tool-kit",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "modern, maintainable, modular developer tooling for FT.com projects",
5
5
  "author": "FT.com Platforms Team <platforms-team.customer-products@ft.com>",
6
6
  "license": "MIT",
@@ -22,8 +22,18 @@
22
22
  "devDependencies": {
23
23
  "@jest/globals": "^27.4.6",
24
24
  "@types/lodash.merge": "^4.6.6",
25
- "@types/lodash.mergewith": "^4.6.6",
26
25
  "@types/node": "^12.20.24",
26
+ "@dotcom-tool-kit/backend-app": "^2.0.3",
27
+ "@dotcom-tool-kit/heroku": "^2.0.2",
28
+ "@dotcom-tool-kit/webpack": "^2.1.1",
29
+ "@dotcom-tool-kit/babel": "^2.0.2",
30
+ "@dotcom-tool-kit/circleci": "^2.1.0",
31
+ "@dotcom-tool-kit/npm": "^2.0.3",
32
+ "@dotcom-tool-kit/circleci-heroku": "^2.0.3",
33
+ "@dotcom-tool-kit/frontend-app": "^2.1.1",
34
+ "@dotcom-tool-kit/eslint": "^2.1.0",
35
+ "@dotcom-tool-kit/mocha": "^2.0.2",
36
+ "@dotcom-tool-kit/n-test": "^2.0.2",
27
37
  "chai": "^4.3.4",
28
38
  "globby": "^10.0.2",
29
39
  "ts-node": "^8.10.2",
@@ -31,15 +41,12 @@
31
41
  },
32
42
  "dependencies": {
33
43
  "@dotcom-tool-kit/error": "^2.0.0",
34
- "@dotcom-tool-kit/hook": "^2.0.0",
35
44
  "@dotcom-tool-kit/logger": "^2.0.0",
36
- "@dotcom-tool-kit/options": "^2.0.0",
37
- "@dotcom-tool-kit/types": "^2.0.0",
45
+ "@dotcom-tool-kit/options": "^2.0.2",
46
+ "@dotcom-tool-kit/types": "^2.2.0",
38
47
  "@dotcom-tool-kit/wait-for-ok": "^2.0.0",
39
48
  "cosmiconfig": "^7.0.0",
40
- "import-from": "^3.0.0",
41
49
  "lodash.merge": "^4.6.2",
42
- "lodash.mergewith": "^4.6.2",
43
50
  "minimist": "^1.2.5",
44
51
  "resolve-from": "^5.0.0",
45
52
  "tslib": "^1.14.1",