dotcom-tool-kit 2.1.1 → 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;AAYnF,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2FtG;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,15 +1,13 @@
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");
10
+ const lodash_isplainobject_1 = (0, tslib_1.__importDefault)(require("lodash.isplainobject"));
13
11
  function isDescendent(possibleAncestor, possibleDescendent) {
14
12
  if (!possibleDescendent.parent) {
15
13
  return false;
@@ -21,129 +19,168 @@ function isDescendent(possibleAncestor, possibleDescendent) {
21
19
  return isDescendent(possibleAncestor, possibleDescendent.parent);
22
20
  }
23
21
  }
24
- async function loadPluginConfig(logger, plugin, config) {
25
- const { plugins = [], hooks = {}, options = {} } = await (0, rc_file_1.loadToolKitRC)(plugin.root);
26
- // load any plugins requested by this plugin
27
- await loadPlugins(logger, plugins, config, plugin);
28
- // load plugin hook tasks. do this after loading child plugins, so
29
- // parent hooks get assigned after child hooks and can override them
30
- (0, lodash_mergewith_1.default)(config.hookTasks, hooks,
31
- // handle conflicts between hooks from different plugins
32
- (existingHookTask, configHookTask, id) => {
33
- const newHookTask = {
34
- id,
35
- plugin,
36
- tasks: Array.isArray(configHookTask) ? configHookTask : [configHookTask]
37
- };
38
- // this hook task might not have been set yet, in which case use the new one
39
- if (!existingHookTask) {
40
- return newHookTask;
41
- }
42
- const existingFromDescendent = isDescendent(plugin, existingHookTask.plugin);
43
- // plugins can only override hook tasks from their descendents, otherwise that's a conflict
44
- // return a conflict either listing this hook and the siblings,
45
- // or merging in a previously-generated hook
46
- if (!existingFromDescendent) {
47
- const conflicting = (0, conflict_1.isConflict)(existingHookTask) ? existingHookTask.conflicting : [existingHookTask];
48
- const conflict = {
49
- plugin,
50
- conflicting: conflicting.concat(newHookTask)
51
- };
52
- return conflict;
53
- }
54
- // if we're here, any existing hook is from a child plugin,
55
- // so the parent always overrides it
56
- return newHookTask;
57
- });
58
- // merge options from this plugin's config with any options we've collected already
59
- // TODO this is almost the exact same code as for hooks, refactor
60
- (0, lodash_mergewith_1.default)(config.options, options, (existingOptions, configOptions, id) => {
61
- const pluginOptions = {
62
- options: configOptions,
63
- plugin,
64
- forPlugin: config.plugins[id]
65
- };
66
- // this options key might not have been set yet, in which case use the new one
67
- if (!existingOptions) {
68
- return pluginOptions;
69
- }
70
- const existingFromDescendent = isDescendent(plugin, existingOptions.plugin);
71
- // plugins can only override options from their descendents, otherwise it's a conflict
72
- // return a conflict either listing these options and the sibling's,
73
- // or merging in previously-generated options
74
- if (!existingFromDescendent) {
75
- const conflicting = (0, conflict_1.isConflict)(existingOptions) ? existingOptions.conflicting : [existingOptions];
76
- const conflict = {
77
- plugin,
78
- conflicting: conflicting.concat(pluginOptions)
79
- };
80
- return conflict;
81
- }
82
- // if we're here, any existing options are from a child plugin,
83
- // so merge in overrides from the parent
84
- return { ...existingOptions, ...pluginOptions };
85
- });
86
- return config;
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
+ }
87
33
  }
88
- exports.loadPluginConfig = loadPluginConfig;
89
- 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) {
90
42
  // don't load duplicate plugins
91
43
  if (id in config.plugins) {
92
44
  return config.plugins[id];
93
45
  }
94
- const root = parent ? parent.root : process.cwd();
95
46
  // load plugin relative to the parent plugin
96
- const pluginRoot = (0, resolve_from_1.default)(root, id);
97
- const rawPlugin = (0, import_from_1.default)(root, id);
98
- let basePlugin;
99
- try {
100
- basePlugin = (0, types_1.instantiatePlugin)(rawPlugin, logger);
101
- }
102
- catch (error) {
103
- if (error instanceof error_1.ToolKitError) {
104
- error.details = `the package ${logger_1.styles.plugin(id)} at ${logger_1.styles.filepath(pluginRoot)} is not a valid plugin`;
105
- }
106
- throw error;
107
- }
47
+ const root = parent ? parent.root : process.cwd();
48
+ const pluginRoot = id === 'app root' ? root : (0, resolve_from_1.default)(root, id);
108
49
  const plugin = {
109
- ...basePlugin,
110
50
  id,
111
51
  root: pluginRoot,
112
52
  parent
113
53
  };
114
54
  config.plugins[id] = plugin;
115
- // add plugin tasks to our task registry, handling any conflicts
116
- (0, lodash_mergewith_1.default)(config.tasks, Object.fromEntries((plugin.tasks || []).map((task) => [task.name, task])), (existingTask, newTask, taskId) => {
117
- newTask.plugin = plugin;
118
- newTask.id = taskId;
119
- if (!existingTask) {
120
- return newTask;
121
- }
122
- const conflicting = (0, conflict_1.isConflict)(existingTask) ? existingTask.conflicting : [existingTask];
123
- return {
124
- plugin,
125
- conflicting: conflicting.concat(newTask)
126
- };
127
- });
128
- // add hooks to the registry, handling any conflicts
129
- // TODO refactor with command conflict handler
130
- (0, lodash_mergewith_1.default)(config.hooks, plugin.hooks, (existingHook, newHook, hookId) => {
131
- newHook.id = hookId;
132
- newHook.plugin = plugin;
133
- if (!existingHook) {
134
- return newHook;
135
- }
136
- const conflicting = (0, conflict_1.isConflict)(existingHook) ? existingHook.conflicting : [existingHook];
137
- return {
138
- plugin,
139
- conflicting: conflicting.concat(newHook)
140
- };
141
- });
142
- 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]);
143
63
  return plugin;
144
64
  }
145
65
  exports.loadPlugin = loadPlugin;
146
- function loadPlugins(logger, plugins, config, parent) {
147
- 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);
148
185
  }
149
- 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.1.1",
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",
@@ -32,13 +42,11 @@
32
42
  "dependencies": {
33
43
  "@dotcom-tool-kit/error": "^2.0.0",
34
44
  "@dotcom-tool-kit/logger": "^2.0.0",
35
- "@dotcom-tool-kit/options": "^2.0.1",
36
- "@dotcom-tool-kit/types": "^2.1.0",
45
+ "@dotcom-tool-kit/options": "^2.0.2",
46
+ "@dotcom-tool-kit/types": "^2.2.0",
37
47
  "@dotcom-tool-kit/wait-for-ok": "^2.0.0",
38
48
  "cosmiconfig": "^7.0.0",
39
- "import-from": "^3.0.0",
40
49
  "lodash.merge": "^4.6.2",
41
- "lodash.mergewith": "^4.6.2",
42
50
  "minimist": "^1.2.5",
43
51
  "resolve-from": "^5.0.0",
44
52
  "tslib": "^1.14.1",