dotcom-tool-kit 3.5.0 → 4.0.0-beta.1

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.
Files changed (77) hide show
  1. package/bin/run +11 -1
  2. package/lib/config/hash.d.ts +6 -0
  3. package/lib/config/hash.d.ts.map +1 -0
  4. package/lib/config/hash.js +43 -0
  5. package/lib/config/validate-plugins.d.ts +4 -0
  6. package/lib/config/validate-plugins.d.ts.map +1 -0
  7. package/lib/config/validate-plugins.js +9 -0
  8. package/lib/config.d.ts +2 -54
  9. package/lib/config.d.ts.map +1 -1
  10. package/lib/config.js +62 -113
  11. package/lib/fetch.d.ts +2 -0
  12. package/lib/fetch.d.ts.map +1 -0
  13. package/lib/fetch.js +19 -0
  14. package/lib/help.d.ts +1 -1
  15. package/lib/help.d.ts.map +1 -1
  16. package/lib/help.js +72 -39
  17. package/lib/index.d.ts +3 -4
  18. package/lib/index.d.ts.map +1 -1
  19. package/lib/index.js +12 -99
  20. package/lib/init.d.ts +4 -0
  21. package/lib/init.d.ts.map +1 -0
  22. package/lib/init.js +16 -0
  23. package/lib/install.d.ts +6 -1
  24. package/lib/install.d.ts.map +1 -1
  25. package/lib/install.js +69 -13
  26. package/lib/messages.d.ts +14 -13
  27. package/lib/messages.d.ts.map +1 -1
  28. package/lib/messages.js +38 -27
  29. package/lib/plugin/entry-point.d.ts +7 -0
  30. package/lib/plugin/entry-point.d.ts.map +1 -0
  31. package/lib/plugin/entry-point.js +46 -0
  32. package/lib/plugin/is-descendent.d.ts +3 -0
  33. package/lib/plugin/is-descendent.d.ts.map +1 -0
  34. package/lib/plugin/is-descendent.js +15 -0
  35. package/lib/plugin/merge-commands.d.ts +5 -0
  36. package/lib/plugin/merge-commands.d.ts.map +1 -0
  37. package/lib/plugin/merge-commands.js +59 -0
  38. package/lib/plugin/merge-hooks.d.ts +4 -0
  39. package/lib/plugin/merge-hooks.d.ts.map +1 -0
  40. package/lib/plugin/merge-hooks.js +29 -0
  41. package/lib/plugin/merge-inits.d.ts +4 -0
  42. package/lib/plugin/merge-inits.d.ts.map +1 -0
  43. package/lib/plugin/merge-inits.js +13 -0
  44. package/lib/plugin/merge-plugin-options.d.ts +4 -0
  45. package/lib/plugin/merge-plugin-options.d.ts.map +1 -0
  46. package/lib/plugin/merge-plugin-options.js +46 -0
  47. package/lib/plugin/merge-task-options.d.ts +4 -0
  48. package/lib/plugin/merge-task-options.d.ts.map +1 -0
  49. package/lib/plugin/merge-task-options.js +43 -0
  50. package/lib/plugin/merge-tasks.d.ts +4 -0
  51. package/lib/plugin/merge-tasks.d.ts.map +1 -0
  52. package/lib/plugin/merge-tasks.js +27 -0
  53. package/lib/plugin/options.d.ts +7 -0
  54. package/lib/plugin/options.d.ts.map +1 -0
  55. package/lib/plugin/options.js +170 -0
  56. package/lib/plugin/reduce-installations.d.ts +7 -0
  57. package/lib/plugin/reduce-installations.d.ts.map +1 -0
  58. package/lib/plugin/reduce-installations.js +59 -0
  59. package/lib/plugin.d.ts +4 -3
  60. package/lib/plugin.d.ts.map +1 -1
  61. package/lib/plugin.js +56 -217
  62. package/lib/rc-file.d.ts +4 -9
  63. package/lib/rc-file.d.ts.map +1 -1
  64. package/lib/rc-file.js +80 -20
  65. package/lib/tasks.d.ts +3 -0
  66. package/lib/tasks.d.ts.map +1 -0
  67. package/lib/tasks.js +79 -0
  68. package/package.json +28 -22
  69. package/lib/conflict.d.ts +0 -9
  70. package/lib/conflict.d.ts.map +0 -1
  71. package/lib/conflict.js +0 -27
  72. package/lib/hook.d.ts +0 -7
  73. package/lib/hook.d.ts.map +0 -1
  74. package/lib/hook.js +0 -2
  75. package/lib/postInstall.d.ts +0 -7
  76. package/lib/postInstall.d.ts.map +0 -1
  77. package/lib/postInstall.js +0 -53
package/bin/run CHANGED
@@ -1,22 +1,32 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ /* eslint-disable @typescript-eslint/no-var-requires --
4
+ * this is raw CJS so the lint doesn't apply
5
+ */
6
+
3
7
  const argv = require('minimist')(process.argv.slice(2), {
4
8
  boolean: ['help', 'install', 'listPlugins'],
5
9
  '--': true
6
10
  })
7
11
 
8
- const { runTasks, showHelp, installHooks, listPlugins } = require('../lib')
9
12
  const { rootLogger, styles } = require('@dotcom-tool-kit/logger')
10
13
 
11
14
  async function main() {
12
15
  try {
13
16
  if (argv.install) {
17
+ const installHooks = require('../lib/install').default
14
18
  await installHooks(rootLogger)
15
19
  } else if (argv.listPlugins) {
20
+ const { listPlugins } = require('../lib')
16
21
  await listPlugins(rootLogger)
22
+ } else if (argv.printConfig) {
23
+ const { printConfig } = require('../lib')
24
+ await printConfig(rootLogger)
17
25
  } else if (argv.help || argv._.length === 0) {
26
+ const showHelp = require('../lib/help').default
18
27
  await showHelp(rootLogger, argv._)
19
28
  } else {
29
+ const { runTasks } = require('../lib')
20
30
  if (argv['--'].length > 0) {
21
31
  // The `--` in a command such as `dotcom-tool-kit test:staged --`
22
32
  // delineates between hooks and file patterns. For example, when the
@@ -0,0 +1,6 @@
1
+ import { ValidConfig } from '@dotcom-tool-kit/config';
2
+ import { Logger } from 'winston';
3
+ export declare function fileHash(path: string): Promise<string>;
4
+ export declare function updateHashes(config: ValidConfig): Promise<void>;
5
+ export declare function hasConfigChanged(logger: Logger, config: ValidConfig): Promise<boolean>;
6
+ //# sourceMappingURL=hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/config/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAIrD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAEhC,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAY5D;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAOrE;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAiB5F"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasConfigChanged = exports.updateHashes = exports.fileHash = void 0;
4
+ const state_1 = require("@dotcom-tool-kit/state");
5
+ const node_crypto_1 = require("node:crypto");
6
+ const promises_1 = require("node:fs/promises");
7
+ async function fileHash(path) {
8
+ const hashFunc = (0, node_crypto_1.createHash)('sha512');
9
+ try {
10
+ hashFunc.update(await (0, promises_1.readFile)(path));
11
+ return hashFunc.digest('base64');
12
+ }
13
+ catch (error) {
14
+ if (error?.code === 'ENOENT') {
15
+ return 'n/a';
16
+ }
17
+ else {
18
+ throw error;
19
+ }
20
+ }
21
+ }
22
+ exports.fileHash = fileHash;
23
+ async function updateHashes(config) {
24
+ const hashes = Object.fromEntries(await Promise.all(['.toolkitrc.yml', ...config.hookManagedFiles].map(async (path) => [path, await fileHash(path)])));
25
+ (0, state_1.writeState)('install', hashes);
26
+ }
27
+ exports.updateHashes = updateHashes;
28
+ async function hasConfigChanged(logger, config) {
29
+ const hashes = (0, state_1.readState)('install');
30
+ if (!hashes) {
31
+ return true;
32
+ }
33
+ for (const path of ['.toolkitrc.yml', ...config.hookManagedFiles]) {
34
+ const newHash = await fileHash(path);
35
+ const prevHash = hashes[path];
36
+ if (newHash !== prevHash) {
37
+ logger.debug(`hash for path ${path} has changed, running hook checks`);
38
+ return true;
39
+ }
40
+ }
41
+ return false;
42
+ }
43
+ exports.hasConfigChanged = hasConfigChanged;
@@ -0,0 +1,4 @@
1
+ import type { RawConfig, ValidPluginsConfig } from '@dotcom-tool-kit/config';
2
+ import { Validated } from '@dotcom-tool-kit/validated';
3
+ export declare function validatePlugins(config: RawConfig): Validated<ValidPluginsConfig>;
4
+ //# sourceMappingURL=validate-plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-plugins.d.ts","sourceRoot":"","sources":["../../src/config/validate-plugins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5E,OAAO,EAAE,SAAS,EAAmB,MAAM,4BAA4B,CAAA;AAEvE,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC,kBAAkB,CAAC,CAKhF"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validatePlugins = void 0;
4
+ const validated_1 = require("@dotcom-tool-kit/validated");
5
+ function validatePlugins(config) {
6
+ const validatedPlugins = (0, validated_1.reduceValidated)(Object.entries(config.plugins).map(([id, plugin]) => plugin.map((p) => [id, p])));
7
+ return validatedPlugins.map((plugins) => ({ ...config, plugins: Object.fromEntries(plugins) }));
8
+ }
9
+ exports.validatePlugins = validatePlugins;
package/lib/config.d.ts CHANGED
@@ -1,59 +1,7 @@
1
1
  import type { Logger } from 'winston';
2
- import type { HookTask } from './hook';
3
- import { Conflict } from './conflict';
4
- import { TaskClass, Hook, Plugin, Validated } from '@dotcom-tool-kit/types';
5
- import { Options as SchemaOptions } from '@dotcom-tool-kit/types/lib/schema';
6
- export interface PluginOptions {
7
- options: Record<string, unknown>;
8
- plugin: Plugin;
9
- forPlugin: Plugin;
10
- }
11
- export interface RawConfig {
12
- root: string;
13
- plugins: {
14
- [id: string]: Validated<Plugin>;
15
- };
16
- resolvedPlugins: Set<Plugin>;
17
- tasks: {
18
- [id: string]: TaskClass | Conflict<TaskClass>;
19
- };
20
- hookTasks: {
21
- [id: string]: HookTask | Conflict<HookTask>;
22
- };
23
- options: {
24
- [id: string]: PluginOptions | Conflict<PluginOptions> | undefined;
25
- };
26
- hooks: {
27
- [id: string]: Hook<unknown> | Conflict<Hook<unknown>>;
28
- };
29
- }
30
- export type ValidPluginsConfig = Omit<RawConfig, 'plugins'> & {
31
- plugins: {
32
- [id: string]: Plugin;
33
- };
34
- };
35
- export type ValidPluginOptions<Id extends keyof SchemaOptions> = Omit<PluginOptions, 'options'> & {
36
- options: SchemaOptions[Id];
37
- };
38
- export type ValidOptions = {
39
- [Id in keyof SchemaOptions]: ValidPluginOptions<Id>;
40
- };
41
- export type ValidConfig = Omit<ValidPluginsConfig, 'tasks' | 'hookTasks' | 'options' | 'hooks'> & {
42
- tasks: {
43
- [id: string]: TaskClass;
44
- };
45
- hookTasks: {
46
- [id: string]: HookTask;
47
- };
48
- options: ValidOptions;
49
- hooks: {
50
- [id: string]: Hook<unknown>;
51
- };
52
- };
2
+ import { RawConfig, ValidConfig, ValidPluginsConfig } from '@dotcom-tool-kit/config';
53
3
  export declare const createConfig: () => RawConfig;
54
- export declare function validateConfig(config: ValidPluginsConfig, logger: Logger): ValidConfig;
55
- export declare function validatePlugins(config: RawConfig): Validated<ValidPluginsConfig>;
56
- export declare function checkInstall(config: ValidConfig): Promise<void>;
4
+ export declare function validateConfig(config: ValidPluginsConfig): ValidConfig;
57
5
  export declare function loadConfig(logger: Logger, options?: {
58
6
  validate?: true;
59
7
  }): Promise<ValidConfig>;
@@ -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,EAAE,SAAS,EAAE,IAAI,EAAgB,MAAM,EAAmB,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAC1G,OAAO,EAAE,OAAO,IAAI,aAAa,EAAW,MAAM,mCAAmC,CAAA;AAcrF,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,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;KAAE,CAAA;IAC5C,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,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;KAAE,CAAA;CACjE;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG;IAC5D,OAAO,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,kBAAkB,CAAC,EAAE,SAAS,MAAM,aAAa,IAAI,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG;IAChG,OAAO,EAAE,aAAa,CAAC,EAAE,CAAC,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;KACxB,EAAE,IAAI,MAAM,aAAa,GAAG,kBAAkB,CAAC,EAAE,CAAC;CACpD,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC,GAAG;IAChG,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,YAAY,CAAA;IACrB,KAAK,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAA;KAAE,CAAA;CACvC,CAAA;AAID,eAAO,MAAM,YAAY,QAAO,SAQ9B,CAAA;AAQF,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAkItF;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC,kBAAkB,CAAC,CAKhF;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,SAAS,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;AAUrC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAiBpF,eAAO,MAAM,YAAY,QAAO,SAgB9B,CAAA;AAEF,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,WAAW,CAiGtE;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,SAAS,CAAC,CAAA"}
package/lib/config.js CHANGED
@@ -1,124 +1,90 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadConfig = exports.checkInstall = exports.validatePlugins = exports.validateConfig = exports.createConfig = void 0;
3
+ exports.loadConfig = exports.validateConfig = exports.createConfig = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const path_1 = tslib_1.__importDefault(require("path"));
6
6
  const plugin_1 = require("./plugin");
7
- const conflict_1 = require("./conflict");
7
+ const conflict_1 = require("@dotcom-tool-kit/conflict");
8
8
  const error_1 = require("@dotcom-tool-kit/error");
9
- const types_1 = require("@dotcom-tool-kit/types");
10
- const schema_1 = require("@dotcom-tool-kit/types/lib/schema");
11
9
  const messages_1 = require("./messages");
10
+ const validate_plugins_1 = require("./config/validate-plugins");
11
+ const options_1 = require("./plugin/options");
12
12
  const coreRoot = path_1.default.resolve(__dirname, '../');
13
13
  const createConfig = () => ({
14
14
  root: coreRoot,
15
15
  plugins: {},
16
- resolvedPlugins: new Set(),
16
+ resolutionTrackers: {
17
+ resolvedPluginOptions: new Set(),
18
+ substitutedPlugins: new Set(),
19
+ resolvedPlugins: new Set(),
20
+ reducedInstallationPlugins: new Set()
21
+ },
17
22
  tasks: {},
18
- hookTasks: {},
19
- options: {},
20
- hooks: {}
23
+ commandTasks: {},
24
+ pluginOptions: {},
25
+ taskOptions: {},
26
+ hooks: {},
27
+ inits: [],
28
+ hookManagedFiles: new Set()
21
29
  });
22
30
  exports.createConfig = createConfig;
23
- async function asyncFilter(items, predicate) {
24
- const results = await Promise.all(items.map(async (item) => ({ item, keep: await predicate(item) })));
25
- return results.filter(({ keep }) => keep).map(({ item }) => item);
26
- }
27
- function validateConfig(config, logger) {
31
+ function validateConfig(config) {
28
32
  const validConfig = config;
29
- const hookTaskConflicts = (0, conflict_1.findConflicts)(Object.values(config.hookTasks));
30
- const hookConflicts = (0, conflict_1.findConflicts)(Object.values(config.hooks));
31
- const taskConflicts = (0, conflict_1.findConflicts)(Object.values(config.tasks));
32
- const optionConflicts = (0, conflict_1.findConflicts)(Object.values(config.options));
33
- const definedHookTaskConflicts = hookTaskConflicts.filter((conflict) => {
33
+ const commandTaskConflicts = (0, conflict_1.findConflicts)(Object.values(config.commandTasks));
34
+ const hookConflicts = (0, conflict_1.findConflictingEntries)(config.hooks);
35
+ const taskConflicts = (0, conflict_1.findConflictingEntries)(config.tasks);
36
+ const pluginOptionConflicts = (0, conflict_1.findConflicts)(Object.values(config.pluginOptions));
37
+ const taskOptionConflicts = (0, conflict_1.findConflicts)(Object.values(config.taskOptions));
38
+ const definedCommandTaskConflicts = commandTaskConflicts.filter((conflict) => {
34
39
  return conflict.conflicting[0].id in config.hooks;
35
40
  });
36
41
  let shouldThrow = false;
37
- const error = new error_1.ToolKitConflictError('There are problems with your Tool Kit configuration.', hookTaskConflicts.map((conflict) => ({
38
- hook: conflict.conflicting[0].id,
39
- conflictingTasks: conflict.conflicting.flatMap((hook) => hook.tasks.map((task) => ({ task, plugin: hook.plugin.id })))
42
+ const error = new error_1.ToolKitConflictError('There are problems with your Tool Kit configuration.', commandTaskConflicts.map((conflict) => ({
43
+ command: conflict.conflicting[0].id,
44
+ conflictingTasks: conflict.conflicting.flatMap((command) => command.tasks.map((task) => ({ task: task.task, plugin: command.plugin.id })))
40
45
  })));
41
46
  error.details = '';
42
47
  if (hookConflicts.length > 0 ||
43
- definedHookTaskConflicts.length > 0 ||
48
+ definedCommandTaskConflicts.length > 0 ||
44
49
  taskConflicts.length > 0 ||
45
- optionConflicts.length > 0) {
50
+ pluginOptionConflicts.length > 0 ||
51
+ taskOptionConflicts.length > 0) {
46
52
  shouldThrow = true;
47
53
  if (hookConflicts.length) {
48
54
  error.details += (0, messages_1.formatHookConflicts)(hookConflicts);
49
55
  }
50
- if (definedHookTaskConflicts.length) {
51
- error.details += (0, messages_1.formatHookTaskConflicts)(definedHookTaskConflicts);
56
+ if (definedCommandTaskConflicts.length) {
57
+ error.details += (0, messages_1.formatCommandTaskConflicts)(definedCommandTaskConflicts);
52
58
  }
53
59
  if (taskConflicts.length) {
54
60
  error.details += (0, messages_1.formatTaskConflicts)(taskConflicts);
55
61
  }
56
- if (optionConflicts.length) {
57
- error.details += (0, messages_1.formatOptionConflicts)(optionConflicts);
58
- }
59
- }
60
- const configuredHookTasks = (0, conflict_1.withoutConflicts)(Object.values(config.hookTasks));
61
- const definedHookIds = new Set(Object.keys(config.hooks));
62
- const undefinedHookTasks = configuredHookTasks.filter((hookTask) => {
63
- // we only care about undefined hooks that were configured by the app, not default config from plugins
64
- const fromApp = hookTask.plugin.root === process.cwd();
65
- const hookDefined = definedHookIds.has(hookTask.id);
66
- return fromApp && !hookDefined;
67
- });
68
- if (undefinedHookTasks.length > 0) {
69
- shouldThrow = true;
70
- error.details += (0, messages_1.formatUndefinedHookTasks)(undefinedHookTasks, Array.from(definedHookIds));
71
- }
72
- const invalidOptions = [];
73
- for (const [id, plugin] of Object.entries(config.plugins)) {
74
- const pluginId = id;
75
- const pluginOptions = config.options[pluginId];
76
- if (pluginOptions && (0, conflict_1.isConflict)(pluginOptions)) {
77
- continue;
78
- }
79
- const pluginSchema = schema_1.Schemas[pluginId];
80
- if (!pluginSchema) {
81
- logger.silly(`skipping validation of ${pluginId} plugin as no schema can be found`);
82
- continue;
83
- }
84
- const result = pluginSchema.safeParse(pluginOptions?.options ?? {});
85
- if (result.success) {
86
- // Set up options entry for plugins that don't have options specified
87
- // explicitly. They could still have default options that are set by zod.
88
- if (!pluginOptions) {
89
- // TypeScript struggles with this type as it sees one side as
90
- // `Foo<a | b | c>` and the other as `Foo<a> | Foo<b> | Foo<c>` for
91
- // some reason (something to do with the record indexing) and it can't
92
- // unify them. But they are equivalent so let's force it with a cast.
93
- config.options[pluginId] = {
94
- options: result.data,
95
- plugin: config.plugins['app root'],
96
- forPlugin: plugin
97
- }; // eslint-disable-line @typescript-eslint/no-explicit-any
98
- }
99
- else {
100
- pluginOptions.options = result.data;
101
- }
62
+ if (pluginOptionConflicts.length) {
63
+ error.details += (0, messages_1.formatPluginOptionConflicts)(pluginOptionConflicts);
102
64
  }
103
- else {
104
- invalidOptions.push([id, result.error]);
65
+ if (taskOptionConflicts.length) {
66
+ error.details += (0, messages_1.formatTaskOptionConflicts)(taskOptionConflicts);
105
67
  }
106
68
  }
107
- if (invalidOptions.length > 0) {
69
+ const unusedPluginOptions = Object.entries(config.pluginOptions)
70
+ .filter(([, option]) => option && !(0, conflict_1.isConflict)(option) && !option.forPlugin && option.plugin.root === process.cwd())
71
+ .map(([id]) => id);
72
+ if (unusedPluginOptions.length > 0) {
108
73
  shouldThrow = true;
109
- error.details += (0, messages_1.formatInvalidOptions)(invalidOptions);
74
+ error.details += (0, messages_1.formatUnusedPluginOptions)(unusedPluginOptions, Object.keys(config.plugins));
110
75
  }
111
- const unusedOptions = Object.entries(config.options)
112
- .filter(([, option]) => option && !(0, conflict_1.isConflict)(option) && !option.forPlugin && option.plugin.root === process.cwd())
76
+ const unusedTaskOptions = Object.entries(config.taskOptions)
77
+ .filter(([, option]) => option && !(0, conflict_1.isConflict)(option) && !option.task && option.plugin.root === process.cwd())
113
78
  .map(([id]) => id);
114
- if (unusedOptions.length > 0) {
79
+ if (unusedTaskOptions.length > 0) {
115
80
  shouldThrow = true;
116
- error.details += (0, messages_1.formatUnusedOptions)(unusedOptions, Object.keys(config.plugins));
81
+ error.details += (0, messages_1.formatUnusedTaskOptions)(unusedTaskOptions, Object.keys(config.tasks));
117
82
  }
118
- const missingTasks = configuredHookTasks
119
- .map((hook) => ({
120
- hook,
121
- tasks: hook.tasks.filter((id) => !config.tasks[id])
83
+ const configuredCommandTasks = (0, conflict_1.withoutConflicts)(Object.values(config.commandTasks));
84
+ const missingTasks = configuredCommandTasks
85
+ .map((command) => ({
86
+ command,
87
+ tasks: command.tasks.filter((task) => !config.tasks[task.task])
122
88
  }))
123
89
  .filter(({ tasks }) => tasks.length > 0);
124
90
  if (missingTasks.length > 0) {
@@ -131,42 +97,25 @@ function validateConfig(config, logger) {
131
97
  return validConfig;
132
98
  }
133
99
  exports.validateConfig = validateConfig;
134
- function validatePlugins(config) {
135
- const validatedPlugins = (0, types_1.reduceValidated)(Object.entries(config.plugins).map(([id, plugin]) => (0, types_1.mapValidated)(plugin, (p) => [id, p])));
136
- return (0, types_1.mapValidated)(validatedPlugins, (plugins) => ({ ...config, plugins: Object.fromEntries(plugins) }));
137
- }
138
- exports.validatePlugins = validatePlugins;
139
- async function checkInstall(config) {
140
- const definedHooks = (0, conflict_1.withoutConflicts)(Object.values(config.hooks));
141
- const uninstalledHooks = await asyncFilter(definedHooks, async (hook) => {
142
- return !(await hook.check());
143
- });
144
- if (uninstalledHooks.length > 0) {
145
- const error = new error_1.ToolKitError('There are problems with your Tool Kit installation.');
146
- error.details = (0, messages_1.formatUninstalledHooks)(uninstalledHooks);
147
- throw error;
148
- }
149
- }
150
- exports.checkInstall = checkInstall;
151
100
  async function loadConfig(logger, { validate = true } = {}) {
152
101
  const config = (0, exports.createConfig)();
153
102
  // start loading config and child plugins, starting from the consumer app directory
154
103
  const rootPlugin = await (0, plugin_1.loadPlugin)('app root', config, logger);
155
- if (!rootPlugin.valid) {
156
- const error = new error_1.ToolKitError('root plugin was not valid!');
157
- error.details = rootPlugin.reasons.join('\n\n');
158
- throw error;
159
- }
160
- const validRootPlugin = rootPlugin.value;
161
- const validatedPluginConfig = validatePlugins(config);
162
- if (!validatedPluginConfig.valid) {
163
- const error = new error_1.ToolKitError('config was not valid!');
164
- error.details = validatedPluginConfig.reasons.join('\n\n');
104
+ const validRootPlugin = rootPlugin.unwrap('root plugin was not valid!');
105
+ const validatedPluginConfig = (0, validate_plugins_1.validatePlugins)(config);
106
+ const validPluginConfig = validatedPluginConfig.unwrap('config was not valid!');
107
+ // collate root plugin and descendent hooks, options etc into config
108
+ // start with options so we can substitute resolved values into other parts
109
+ // of the config
110
+ (0, plugin_1.resolvePluginOptions)(validRootPlugin, validPluginConfig);
111
+ const invalidOptions = (0, options_1.validatePluginOptions)(logger, validPluginConfig);
112
+ if (invalidOptions.length > 0 && validate) {
113
+ const error = new error_1.ToolKitError('There are problems with your plugin options.');
114
+ error.details = (0, messages_1.formatInvalidOptions)(invalidOptions);
165
115
  throw error;
166
116
  }
167
- const validPluginConfig = validatedPluginConfig.value;
168
- // collate root plugin and descendent hooks, options etc into config
117
+ (0, options_1.substituteOptionTags)(validRootPlugin, validPluginConfig);
169
118
  (0, plugin_1.resolvePlugin)(validRootPlugin, validPluginConfig, logger);
170
- return validate ? validateConfig(validPluginConfig, logger) : config;
119
+ return validate ? validateConfig(validPluginConfig) : config;
171
120
  }
172
121
  exports.loadConfig = loadConfig;
package/lib/fetch.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare const shouldDisableNativeFetch: () => boolean;
2
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,wBAAwB,QAAO,OAY3C,CAAA"}
package/lib/fetch.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shouldDisableNativeFetch = void 0;
4
+ const options_1 = require("@dotcom-tool-kit/options");
5
+ // function that plugins can check if they need to implement their own logic to
6
+ // disable Node 18's native fetch
7
+ const shouldDisableNativeFetch = () => {
8
+ // disable Node 18's native fetch if the Node runtime supports it (older
9
+ // runtimes don't support the flag, implying they also don't use native
10
+ // fetch) and the user hasn't opted out of the behaviour
11
+ return (
12
+ /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion --
13
+ * the root plugin has default options and it always exists so is always
14
+ * defined
15
+ **/
16
+ !(0, options_1.getOptions)('app root').allowNativeFetch &&
17
+ process.allowedNodeEnvironmentFlags.has('--no-experimental-fetch'));
18
+ };
19
+ exports.shouldDisableNativeFetch = shouldDisableNativeFetch;
package/lib/help.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import type { Logger } from 'winston';
2
- export default function showHelp(logger: Logger, hooks: string[]): Promise<void>;
2
+ export default function showHelp(logger: Logger, commands: string[]): Promise<void>;
3
3
  //# sourceMappingURL=help.d.ts.map
package/lib/help.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErC,wBAA8B,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiErF"}
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AA4ErC,wBAA8B,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4CxF"}
package/lib/help.js CHANGED
@@ -1,54 +1,87 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
3
4
  const config_1 = require("./config");
4
5
  const options_1 = require("@dotcom-tool-kit/options");
5
6
  const logger_1 = require("@dotcom-tool-kit/logger");
6
- async function showHelp(logger, hooks) {
7
+ const yaml_1 = tslib_1.__importDefault(require("yaml"));
8
+ const endent_1 = tslib_1.__importDefault(require("endent"));
9
+ const toolKitIntro = logger_1.styles.box((0, endent_1.default) `
10
+ Tool Kit is modern, maintainable & modular developer tooling for FT.com projects.
11
+ ${logger_1.styles.URL('https://github.com/financial-times/dotcom-tool-kit')}
12
+ `, { title: `🧰 ${logger_1.styles.title(`welcome to ${logger_1.styles.app('Tool Kit')}!`)}` });
13
+ const formatTask = ({ task, options }) => (0, endent_1.default) `
14
+ ${logger_1.styles.task(task)}${Object.keys(options).length > 0
15
+ ? ` ${logger_1.styles.dim('with options:')}
16
+ ${yaml_1.default.stringify(options).trim()}`
17
+ : ''}`;
18
+ const formatCommandTasks = (config, commands) => logger_1.styles.box((0, endent_1.default) `
19
+ ${logger_1.styles.help(`${logger_1.styles.command('commands')} run Tool Kit tasks with ${logger_1.styles.code('npx dotcom-tool-kit $command')}, or via configuration installed by hooks in your repository.`)}
20
+ ${commands
21
+ .filter((command) => config.commandTasks[command])
22
+ .map((command) => formatCommandTask(command, config.commandTasks[command]))
23
+ .join('\n')}
24
+
25
+ `, {
26
+ title: '⛭ ' + logger_1.styles.title('available commands')
27
+ });
28
+ const formatCommandTask = (command, { tasks, plugin }) => (0, endent_1.default) `
29
+ ${logger_1.styles.groupHeader(logger_1.styles.command(command))}
30
+ ${tasks.length
31
+ ? (0, endent_1.default) `
32
+ ${logger_1.styles.info(`${plugin.id !== 'app root' ? `from plugin ${logger_1.styles.plugin(plugin.id)}. ` : ''}runs tasks:`)}
33
+ ${tasks.map((task) => ` - ${formatTask(task)}`).join('\n')}
34
+ `
35
+ : logger_1.styles.warning(`no tasks configured to run for ${logger_1.styles.command(command)}`)}
36
+ `;
37
+ const formatHooks = (config) => logger_1.styles.box((0, endent_1.default) `
38
+ ${logger_1.styles.help(`${logger_1.styles.hook('hooks')} manage configuration files in your repository, for running Tool Kit commands.`)}
39
+ ${Object.entries(config.hooks)
40
+ .map(([hook, entryPoint]) => {
41
+ const managesFiles = entryPoint.plugin.rcFile?.installs[hook].managesFiles ?? [];
42
+ return (0, endent_1.default) `
43
+ ${logger_1.styles.groupHeader(logger_1.styles.hook(hook))}
44
+ ${logger_1.styles.info((0, endent_1.default) `
45
+ from plugin ${logger_1.styles.plugin(entryPoint.plugin.id)}
46
+ `)}
47
+ ${managesFiles.length ? 'manages files:' : ''}
48
+ ${managesFiles.map((file) => ` - ${logger_1.styles.filepath(file)}`).join('\n')}
49
+ `;
50
+ })
51
+ .join('\n')}
52
+ `, { title: logger_1.styles.title('🎣 installed hooks') });
53
+ async function showHelp(logger, commands) {
7
54
  const config = await (0, config_1.loadConfig)(logger);
8
- if (hooks.length === 0) {
9
- hooks = Object.keys(config.hooks).sort();
55
+ const printAllCommands = commands.length === 0;
56
+ if (printAllCommands) {
57
+ commands = Object.keys(config.commandTasks).sort();
10
58
  }
11
- for (const pluginOptions of Object.values(config.options)) {
59
+ for (const pluginOptions of Object.values(config.pluginOptions)) {
12
60
  if (pluginOptions.forPlugin) {
13
61
  (0, options_1.setOptions)(pluginOptions.forPlugin.id, pluginOptions.options);
14
62
  }
15
63
  }
16
- await (0, config_1.checkInstall)(config);
17
- const missingHooks = hooks.filter((hook) => !config.hooks[hook]);
18
- logger.info(`
19
- 🧰 ${logger_1.styles.title(`welcome to ${logger_1.styles.app('Tool Kit')}!`)}
20
-
21
- Tool Kit is modern, maintainable & modular developer tooling for FT.com projects.
22
-
23
- ${logger_1.styles.URL('https://github.com/financial-times/dotcom-tool-kit')}
24
-
25
- ${logger_1.styles.ruler()}
26
- ${Object.keys(config.hooks).length === 0
27
- ? `there are no hooks available. you'll need to install plugins that define hooks to be able to run Tool Kit tasks.`
28
- : logger_1.styles.dim(hooks.length === 0
29
- ? 'available hooks'
30
- : `help for ${hooks.length - missingHooks.length} ${hooks.length - missingHooks.length > 1 ? 'hooks' : 'hook'}`)}:
31
- `);
32
- for (const hook of hooks) {
33
- const Hook = config.hooks[hook];
34
- if (Hook) {
35
- const tasks = config.hookTasks[hook];
36
- /* eslint-disable @typescript-eslint/no-explicit-any -- Object.constructor does not consider static properties */
37
- logger.info(`${logger_1.styles.heading(hook)}
38
- ${Hook.constructor.description ? Hook.constructor.description + '\n' : ''}
39
- ${tasks && tasks.tasks.length
40
- ? `runs ${tasks.tasks.length > 1 ? 'these tasks' : 'this task'}:
41
- ${tasks.tasks
42
- .map((task) => `- ${logger_1.styles.task(task)} ${logger_1.styles.dim(config.tasks[task].description)}`)
43
- .join('\n')}`
44
- : logger_1.styles.dim('no tasks configured to run on this hook.')}
45
- ${logger_1.styles.ruler()}
46
- `);
47
- /*eslint-enable @typescript-eslint/no-explicit-any */
48
- }
64
+ logger.info(toolKitIntro);
65
+ const definedCommands = commands.filter((command) => config.commandTasks[command]);
66
+ const missingCommands = commands.filter((command) => !config.commandTasks[command]);
67
+ if (printAllCommands && Object.keys(config.hooks).length) {
68
+ logger.info(formatHooks(config));
69
+ }
70
+ if (Object.keys(config.commandTasks).length === 0) {
71
+ logger.warn(logger_1.styles.warning((0, endent_1.default) `
72
+ there are no commands available. add some commands by defining them in your ${logger_1.styles.filepath('.toolkitrc.yml')} or installing plugins that define commands.
73
+ `));
74
+ }
75
+ else if (definedCommands.length > 0) {
76
+ logger.info(formatCommandTasks(config, definedCommands));
49
77
  }
50
- if (missingHooks.length) {
51
- logger.warn(`no such ${missingHooks.length > 1 ? 'hooks' : 'hook'} ${missingHooks.map(logger_1.styles.hook).join(', ')}`);
78
+ else if (missingCommands.length) {
79
+ logger.warn(logger_1.styles.error((0, endent_1.default) `
80
+ no such ${missingCommands.length > 1 ? 'commands' : 'command'} ${missingCommands
81
+ .map((id) => logger_1.styles.command(id))
82
+ .join(', ')}
83
+ `));
52
84
  }
85
+ logger.info('\n');
53
86
  }
54
87
  exports.default = showHelp;
package/lib/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import type { Logger } from 'winston';
2
- export declare const shouldDisableNativeFetch: () => boolean;
3
- export declare function runTasks(logger: Logger, hooks: string[], files?: string[]): Promise<void>;
2
+ export { runTasks } from './tasks';
3
+ export { shouldDisableNativeFetch } from './fetch';
4
4
  export declare function listPlugins(logger: Logger): Promise<void>;
5
- export { default as showHelp } from './help';
6
- export { default as installHooks } from './install';
5
+ export declare function printConfig(logger: Logger): Promise<void>;
7
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;AAWrC,eAAO,MAAM,wBAAwB,QAAO,OAY3C,CAAA;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyF/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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAIrC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AAElD,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO/D;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/D"}