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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-hooks.d.ts","sourceRoot":"","sources":["../../src/plugin/merge-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,MAAM,EAAE,MAAM,yBAAyB,CAAA;AACjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAGjE,eAAO,MAAM,UAAU,WAAY,kBAAkB,UAAU,MAAM,SAwBpE,CAAA"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeHooks = void 0;
4
+ const conflict_1 = require("@dotcom-tool-kit/conflict");
5
+ const mergeHooks = (config, plugin) => {
6
+ if (plugin.rcFile) {
7
+ // add hooks to the registry, handling any conflicts
8
+ // TODO refactor with command conflict handler
9
+ for (const [hookName, hookSpec] of Object.entries(plugin.rcFile.installs || {})) {
10
+ const existingHookId = config.hooks[hookName];
11
+ const entryPoint = {
12
+ plugin,
13
+ modulePath: hookSpec.entryPoint
14
+ };
15
+ if (existingHookId) {
16
+ const conflicting = (0, conflict_1.isConflict)(existingHookId) ? existingHookId.conflicting : [existingHookId];
17
+ config.hooks[hookName] = {
18
+ plugin,
19
+ conflicting: conflicting.concat(entryPoint)
20
+ };
21
+ }
22
+ else {
23
+ config.hooks[hookName] = entryPoint;
24
+ hookSpec.managesFiles?.forEach((file) => config.hookManagedFiles.add(file));
25
+ }
26
+ }
27
+ }
28
+ };
29
+ exports.mergeHooks = mergeHooks;
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from '@dotcom-tool-kit/plugin';
2
+ import type { ValidPluginsConfig } from '@dotcom-tool-kit/config';
3
+ export declare const mergeInits: (config: ValidPluginsConfig, plugin: Plugin) => void;
4
+ //# sourceMappingURL=merge-inits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-inits.d.ts","sourceRoot":"","sources":["../../src/plugin/merge-inits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,eAAO,MAAM,UAAU,WAAY,kBAAkB,UAAU,MAAM,SAUpE,CAAA"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeInits = void 0;
4
+ const mergeInits = (config, plugin) => {
5
+ if (plugin.rcFile) {
6
+ // no conflict resolution needed; we'll just run them all ig
7
+ config.inits.push(...plugin.rcFile.init.map((init) => ({
8
+ plugin,
9
+ modulePath: init
10
+ })));
11
+ }
12
+ };
13
+ exports.mergeInits = mergeInits;
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from '@dotcom-tool-kit/plugin';
2
+ import type { ValidPluginsConfig } from '@dotcom-tool-kit/config';
3
+ export declare const mergePluginOptions: (config: ValidPluginsConfig, plugin: Plugin) => void;
4
+ //# sourceMappingURL=merge-plugin-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-plugin-options.d.ts","sourceRoot":"","sources":["../../src/plugin/merge-plugin-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAoB,MAAM,yBAAyB,CAAA;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAMjE,eAAO,MAAM,kBAAkB,WAAY,kBAAkB,UAAU,MAAM,SAwC5E,CAAA"}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergePluginOptions = void 0;
4
+ const is_descendent_1 = require("./is-descendent");
5
+ const conflict_1 = require("@dotcom-tool-kit/conflict");
6
+ // merge options from this plugin's config with any options we've collected already
7
+ // TODO this is almost the exact same code as for command tasks, refactor
8
+ const mergePluginOptions = (config, plugin) => {
9
+ if (plugin.rcFile) {
10
+ for (const [id, configOptions] of Object.entries(plugin.rcFile.options.plugins)) {
11
+ // users can specify root options with the dotcom-tool-kit key to mirror
12
+ // the name of the root npm package
13
+ const pluginId = id === 'dotcom-tool-kit' ? 'app root' : id;
14
+ const existingOptions = config.pluginOptions[pluginId];
15
+ const pluginOptions = {
16
+ options: configOptions,
17
+ plugin,
18
+ forPlugin: config.plugins[pluginId]
19
+ };
20
+ if (existingOptions) {
21
+ const existingFromDescendent = (0, is_descendent_1.isDescendent)(plugin, existingOptions.plugin);
22
+ // plugins can only override options from their descendents, otherwise it's a conflict
23
+ // return a conflict either listing these options and the sibling's,
24
+ // or merging in previously-generated options
25
+ if (!existingFromDescendent) {
26
+ const conflicting = (0, conflict_1.isConflict)(existingOptions) ? existingOptions.conflicting : [existingOptions];
27
+ const conflict = {
28
+ plugin,
29
+ conflicting: conflicting.concat(pluginOptions)
30
+ };
31
+ config.pluginOptions[pluginId] = conflict;
32
+ }
33
+ else {
34
+ // if we're here, any existing options are from a child plugin,
35
+ // so merge in overrides from the parent
36
+ config.pluginOptions[pluginId] = { ...existingOptions, ...pluginOptions };
37
+ }
38
+ }
39
+ else {
40
+ // this options key might not have been set yet, in which case use the new one
41
+ config.pluginOptions[pluginId] = pluginOptions;
42
+ }
43
+ }
44
+ }
45
+ };
46
+ exports.mergePluginOptions = mergePluginOptions;
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from '@dotcom-tool-kit/plugin';
2
+ import type { ValidPluginsConfig } from '@dotcom-tool-kit/config';
3
+ export declare const mergeTaskOptions: (config: ValidPluginsConfig, plugin: Plugin) => void;
4
+ //# sourceMappingURL=merge-task-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-task-options.d.ts","sourceRoot":"","sources":["../../src/plugin/merge-task-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAkB,MAAM,yBAAyB,CAAA;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAOjE,eAAO,MAAM,gBAAgB,WAAY,kBAAkB,UAAU,MAAM,SAqC1E,CAAA"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeTaskOptions = void 0;
4
+ const conflict_1 = require("@dotcom-tool-kit/conflict");
5
+ const is_descendent_1 = require("./is-descendent");
6
+ // merge options from this plugin's config with any options we've collected already
7
+ // TODO this is almost the exact same code as for command tasks, refactor
8
+ const mergeTaskOptions = (config, plugin) => {
9
+ if (plugin.rcFile) {
10
+ for (const [taskId, configOptions] of Object.entries(plugin.rcFile.options.tasks)) {
11
+ const existingOptions = config.taskOptions[taskId];
12
+ const taskOptions = {
13
+ options: configOptions,
14
+ plugin,
15
+ task: taskId
16
+ };
17
+ if (existingOptions) {
18
+ const existingFromDescendent = (0, is_descendent_1.isDescendent)(plugin, existingOptions.plugin);
19
+ // plugins can only override options from their descendents, otherwise it's a conflict
20
+ // return a conflict either listing these options and the sibling's,
21
+ // or merging in previously-generated options
22
+ if (!existingFromDescendent) {
23
+ const conflicting = (0, conflict_1.isConflict)(existingOptions) ? existingOptions.conflicting : [existingOptions];
24
+ const conflict = {
25
+ plugin,
26
+ conflicting: conflicting.concat(taskOptions)
27
+ };
28
+ config.taskOptions[taskId] = conflict;
29
+ }
30
+ else {
31
+ // if we're here, any existing options are from a child plugin,
32
+ // so merge in overrides from the parent
33
+ config.taskOptions[taskId] = { ...existingOptions, ...taskOptions };
34
+ }
35
+ }
36
+ else {
37
+ // this options key might not have been set yet, in which case use the new one
38
+ config.taskOptions[taskId] = taskOptions;
39
+ }
40
+ }
41
+ }
42
+ };
43
+ exports.mergeTaskOptions = mergeTaskOptions;
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from '@dotcom-tool-kit/plugin';
2
+ import type { ValidPluginsConfig } from '@dotcom-tool-kit/config';
3
+ export declare const mergeTasks: (config: ValidPluginsConfig, plugin: Plugin) => void;
4
+ //# sourceMappingURL=merge-tasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-tasks.d.ts","sourceRoot":"","sources":["../../src/plugin/merge-tasks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,MAAM,EAAE,MAAM,yBAAyB,CAAA;AACjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAIjE,eAAO,MAAM,UAAU,WAAY,kBAAkB,UAAU,MAAM,SAqBpE,CAAA"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeTasks = void 0;
4
+ const conflict_1 = require("@dotcom-tool-kit/conflict");
5
+ // add plugin tasks to our task registry, handling any conflicts
6
+ const mergeTasks = (config, plugin) => {
7
+ if (plugin.rcFile) {
8
+ for (const [taskName, modulePath] of Object.entries(plugin.rcFile.tasks || {})) {
9
+ const existingTaskId = config.tasks[taskName];
10
+ const entryPoint = {
11
+ plugin,
12
+ modulePath
13
+ };
14
+ if (existingTaskId) {
15
+ const conflicting = (0, conflict_1.isConflict)(existingTaskId) ? existingTaskId.conflicting : [existingTaskId];
16
+ config.tasks[taskName] = {
17
+ plugin,
18
+ conflicting: conflicting.concat(entryPoint)
19
+ };
20
+ }
21
+ else {
22
+ config.tasks[taskName] = entryPoint;
23
+ }
24
+ }
25
+ }
26
+ };
27
+ exports.mergeTasks = mergeTasks;
@@ -0,0 +1,7 @@
1
+ import { ValidPluginsConfig } from '@dotcom-tool-kit/config';
2
+ import { type Plugin } from '@dotcom-tool-kit/plugin';
3
+ import type { Logger } from 'winston';
4
+ import { InvalidOption } from '../messages';
5
+ export declare const validatePluginOptions: (logger: Logger, config: ValidPluginsConfig) => InvalidOption[];
6
+ export declare const substituteOptionTags: (plugin: Plugin, config: ValidPluginsConfig) => void;
7
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/plugin/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE5D,OAAO,EAA4B,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAI/E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAKrC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,eAAO,MAAM,qBAAqB,WAAY,MAAM,UAAU,kBAAkB,KAAG,aAAa,EAoD/F,CAAA;AAED,eAAO,MAAM,oBAAoB,WAAY,MAAM,UAAU,kBAAkB,KAAG,IAgHjF,CAAA"}
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.substituteOptionTags = exports.validatePluginOptions = void 0;
4
+ const conflict_1 = require("@dotcom-tool-kit/conflict");
5
+ const schemas_1 = require("@dotcom-tool-kit/schemas");
6
+ const validated_1 = require("@dotcom-tool-kit/validated");
7
+ const zod_1 = require("zod");
8
+ const logger_1 = require("@dotcom-tool-kit/logger");
9
+ const rc_file_1 = require("../rc-file");
10
+ const validatePluginOptions = (logger, config) => {
11
+ const invalidOptions = [];
12
+ for (const [id, plugin] of Object.entries(config.plugins)) {
13
+ const pluginId = id;
14
+ const pluginOptions = config.pluginOptions[pluginId];
15
+ if (pluginOptions && (0, conflict_1.isConflict)(pluginOptions)) {
16
+ continue;
17
+ }
18
+ const pluginSchema = schemas_1.PluginSchemas[pluginId];
19
+ if (!pluginSchema) {
20
+ logger.silly(`skipping validation of ${pluginId} plugin as no schema can be found`);
21
+ // TODO:KB:20240412 remove legacyPluginOptions in a future major version
22
+ if (pluginOptions && pluginId in schemas_1.legacyPluginOptions) {
23
+ const movedToTask = schemas_1.legacyPluginOptions[pluginId];
24
+ invalidOptions.push([
25
+ id,
26
+ new zod_1.ZodError([
27
+ {
28
+ message: `options for ${logger_1.styles.plugin(id)} have moved to the ${logger_1.styles.task(movedToTask)} task`,
29
+ code: zod_1.ZodIssueCode.custom,
30
+ path: []
31
+ }
32
+ ])
33
+ ]);
34
+ }
35
+ continue;
36
+ }
37
+ const result = pluginSchema.safeParse(pluginOptions?.options ?? {});
38
+ if (result.success) {
39
+ // Set up options entry for plugins that don't have options specified
40
+ // explicitly. They could still have default options that are set by zod.
41
+ if (!pluginOptions) {
42
+ config.pluginOptions[pluginId] = {
43
+ options: result.data,
44
+ plugin: config.plugins['app root'],
45
+ forPlugin: plugin
46
+ };
47
+ }
48
+ else {
49
+ pluginOptions.options = result.data;
50
+ }
51
+ }
52
+ else {
53
+ invalidOptions.push([id, result.error]);
54
+ }
55
+ }
56
+ return invalidOptions;
57
+ };
58
+ exports.validatePluginOptions = validatePluginOptions;
59
+ const substituteOptionTags = (plugin, config) => {
60
+ // foo.bar gets the 'bar' option set for the 'foo' plugin
61
+ const resolveOptionPath = (optionPath) => {
62
+ const [pluginName, optionName] = optionPath.split('.', 2);
63
+ return config.pluginOptions[pluginName]?.options[optionName];
64
+ };
65
+ // throw an error if there are tags in plugin option fields to avoid circular
66
+ // references
67
+ const validateTagPath = (path) => {
68
+ if (path[0] === 'options' && path[1] === 'plugins') {
69
+ return `YAML tag referencing options used at path '${path.join('.')}'`;
70
+ }
71
+ };
72
+ // recursively walk through the parsed config, searching for the tag
73
+ // identifiers we've inserted during parsing, and substitute them for
74
+ // resolved option values
75
+ const deeplySubstitute = (node, path) => {
76
+ if (Array.isArray(node)) {
77
+ return (0, validated_1.reduceValidated)(node.map((item, i) => deeplySubstitute(item, [...path, i])));
78
+ }
79
+ else if (node && typeof node === 'object') {
80
+ const entries = Object.entries(node);
81
+ const substituted = [];
82
+ for (const entry of entries) {
83
+ const subbedEntry = (0, validated_1.reduceValidated)(
84
+ // allow both keys and (string) values to be substituted by options
85
+ entry.map((val) => {
86
+ if (typeof val === 'string' && val.startsWith(rc_file_1.toolKitOptionIdent)) {
87
+ // check the tag path each time so that we can have a separate
88
+ // error for each incorrect use of the tag
89
+ const validationError = validateTagPath([...path, entry[0]]);
90
+ if (validationError) {
91
+ return (0, validated_1.invalid)([validationError]);
92
+ }
93
+ else {
94
+ // the option path is concatenated after the !toolkit/option
95
+ // identifier
96
+ const optionPath = val.slice(rc_file_1.toolKitOptionIdent.length);
97
+ const resolvedOption = resolveOptionPath(optionPath);
98
+ if (typeof resolvedOption === 'string') {
99
+ return (0, validated_1.valid)(resolvedOption);
100
+ }
101
+ else {
102
+ return (0, validated_1.invalid)([
103
+ `Option '${optionPath}' referenced at path '${path.join('.')}' does not resolve to a string (resolved to ${resolvedOption})`
104
+ ]);
105
+ }
106
+ }
107
+ }
108
+ else {
109
+ return (0, validated_1.valid)(val);
110
+ }
111
+ }));
112
+ if (!subbedEntry.valid) {
113
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any --
114
+ * Invalid objects don't need to match the inner type
115
+ **/
116
+ substituted.push(subbedEntry);
117
+ continue;
118
+ }
119
+ const [key, value] = subbedEntry.value;
120
+ if (key.startsWith(rc_file_1.toolKitIfDefinedIdent)) {
121
+ const validationError = validateTagPath(path);
122
+ if (validationError) {
123
+ substituted.push((0, validated_1.invalid)([validationError]));
124
+ }
125
+ // the option path is concatenated after the !toolkit/if-defined
126
+ // identifier
127
+ const optionPath = key.slice(rc_file_1.toolKitIfDefinedIdent.length);
128
+ const optionValue = resolveOptionPath(optionPath);
129
+ // keep walking the path if we've found an error here so we can
130
+ // gather even more errors to show the user. else skip traversal if
131
+ // we aren't going to include the node
132
+ if (optionValue || validationError) {
133
+ const subbedValues = deeplySubstitute(value, path);
134
+ if (subbedValues.valid) {
135
+ substituted.push(...Object.entries(subbedValues.value).map((v) => (0, validated_1.valid)(v)));
136
+ }
137
+ else {
138
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any --
139
+ * Invalid objects don't need to match the inner type
140
+ **/
141
+ substituted.push(subbedValues);
142
+ }
143
+ }
144
+ }
145
+ else {
146
+ substituted.push(deeplySubstitute(value, [...path, key]).map((subbedValue) => [key, subbedValue]));
147
+ }
148
+ }
149
+ return (0, validated_1.reduceValidated)(substituted).map(Object.fromEntries);
150
+ }
151
+ else {
152
+ return (0, validated_1.valid)(node);
153
+ }
154
+ };
155
+ // avoid running substitution over a config repeatedly – all substitutions
156
+ // will have been made in the first pass
157
+ if (config.resolutionTrackers.substitutedPlugins.has(plugin.id)) {
158
+ return;
159
+ }
160
+ if (plugin.children) {
161
+ for (const child of plugin.children) {
162
+ (0, exports.substituteOptionTags)(child, config);
163
+ }
164
+ }
165
+ if (plugin.rcFile) {
166
+ plugin.rcFile = deeplySubstitute(plugin.rcFile, []).unwrap('cannot reference plugin options when specifying options');
167
+ }
168
+ config.resolutionTrackers.substitutedPlugins.add(plugin.id);
169
+ };
170
+ exports.substituteOptionTags = substituteOptionTags;
@@ -0,0 +1,7 @@
1
+ import type { Logger } from 'winston';
2
+ import type { HookClass, HookInstallation } from '@dotcom-tool-kit/base';
3
+ import type { Plugin } from '@dotcom-tool-kit/plugin';
4
+ import type { ValidConfig } from '@dotcom-tool-kit/config';
5
+ import { Conflict } from '@dotcom-tool-kit/conflict';
6
+ export declare function reducePluginHookInstallations(logger: Logger, config: ValidConfig, hookClasses: Record<string, HookClass>, plugin: Plugin): Promise<(HookInstallation | Conflict<HookInstallation>)[]>;
7
+ //# sourceMappingURL=reduce-installations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reduce-installations.d.ts","sourceRoot":"","sources":["../../src/plugin/reduce-installations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAE1D,OAAO,EAAE,QAAQ,EAAc,MAAM,2BAA2B,CAAA;AAgChE,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EACtC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,CAAC,gBAAgB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAwC5D"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.reducePluginHookInstallations = void 0;
4
+ const schemas_1 = require("@dotcom-tool-kit/schemas");
5
+ const conflict_1 = require("@dotcom-tool-kit/conflict");
6
+ const lodash_1 = require("lodash");
7
+ const extractForHook = (installation) => (0, conflict_1.isConflict)(installation) ? installation.conflicting[0].forHook : installation.forHook;
8
+ // this function recursively collects all the hook installation requests from all plugins,
9
+ // and merges them into a single, flat array of HookInstallation objects and/or Conflicts.
10
+ //
11
+ // it works depth-first (i.e. recurses into child plugins first), and considers how to
12
+ // merge options or create conflicts in two stages: 1) when considering all installations
13
+ // from child plugins, and 2) when considering how a parent plugin would override its
14
+ // children. these steps are separate as a particular parent might not provide an override
15
+ // for all its children, and different hooks could expect different ways of resolving
16
+ // conflicts.
17
+ //
18
+ // the actual logic for this is delegated to static methods on Hook classes,
19
+ // `Hook.mergeChildInstallations` and `Hook.overrideChildInstallations`, so separate hooks
20
+ // can provide different logic for these steps.
21
+ //
22
+ // the default logic in the base Hook class is to always consider multiple installations
23
+ // from child plugins as a conflict, and always consider a installation in a parent as
24
+ // completely replacing any installations from children.
25
+ //
26
+ // for example, for a plugin `p` that depends on children `a`, `b`, and `c` that all provide
27
+ // configuration for the `PackageJson` hook, this function will:
28
+ // - do all this logic for `a`, `b`, and `c`
29
+ // - call `Hook.mergeChildInstallations` with the appropriate concrete Hook class, and
30
+ // the resulting installations and/or conflicts from `a`, `b`, and `c`
31
+ // - call `Hook.overrideChildInstallations` with the appropriate concrete Hook class, and
32
+ // the resulting installations and/or conflicts from `Hook.mergeChildInstallations` and `p`
33
+ async function reducePluginHookInstallations(logger, config, hookClasses, plugin) {
34
+ if (!plugin.rcFile || config.resolutionTrackers.reducedInstallationPlugins.has(plugin.id)) {
35
+ return [];
36
+ }
37
+ config.resolutionTrackers.reducedInstallationPlugins.add(plugin.id);
38
+ const rawChildInstallations = await Promise.all((plugin.children ?? []).map((child) => reducePluginHookInstallations(logger, config, hookClasses, child))).then((installations) => installations.flat());
39
+ const childInstallations = Object.entries((0, lodash_1.groupBy)(rawChildInstallations, extractForHook)).flatMap(([forHook, installations]) => {
40
+ const hookClass = hookClasses[forHook];
41
+ return hookClass.mergeChildInstallations(plugin, installations);
42
+ });
43
+ if (plugin.rcFile.options.hooks.length === 0) {
44
+ return childInstallations;
45
+ }
46
+ return plugin.rcFile.options.hooks.flatMap((hookEntry) => Object.entries(hookEntry).flatMap(([id, configHookOptions]) => {
47
+ const hookClass = hookClasses[id];
48
+ const parsedOptions = schemas_1.HookSchemas[id].parse(configHookOptions);
49
+ const installation = {
50
+ options: parsedOptions,
51
+ plugin,
52
+ forHook: id,
53
+ hookConstructor: hookClass
54
+ };
55
+ const childInstallationsForHook = childInstallations.filter((childInstallation) => id === extractForHook(childInstallation));
56
+ return hookClass.overrideChildInstallations(plugin, installation, childInstallationsForHook);
57
+ }));
58
+ }
59
+ exports.reducePluginHookInstallations = reducePluginHookInstallations;
package/lib/plugin.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { Plugin, PluginModule, Validated } from '@dotcom-tool-kit/types';
1
+ import { type Plugin } from '@dotcom-tool-kit/plugin';
2
+ import type { RawConfig, ValidPluginsConfig } from '@dotcom-tool-kit/config';
3
+ import { Validated } from '@dotcom-tool-kit/validated';
2
4
  import type { Logger } from 'winston';
3
- import { RawConfig, ValidPluginsConfig } from './config';
4
- export declare function validatePlugin(plugin: unknown): Validated<PluginModule>;
5
5
  export declare function loadPlugin(id: string, config: RawConfig, logger: Logger, parent?: Plugin): Promise<Validated<Plugin>>;
6
+ export declare function resolvePluginOptions(plugin: Plugin, config: ValidPluginsConfig): void;
6
7
  export declare function resolvePlugin(plugin: Plugin, config: ValidPluginsConfig, logger: Logger): void;
7
8
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,MAAM,EACN,YAAY,EAIZ,SAAS,EACV,MAAM,wBAAwB,CAAA;AAG/B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,EAAiB,SAAS,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAmBvE,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC,YAAY,CAAC,CAgDvE;AAoBD,wBAAsB,UAAU,CAC9B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CA8D5B;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAyI9F"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAA2B,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAC9E,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5E,OAAO,EAAmC,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAEvF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAUrC,wBAAsB,UAAU,CAC9B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAmD5B;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAiBrF;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAqB9F"}