@package-pal/core 0.0.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 (139) hide show
  1. package/dist/index.d.ts +18 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +1431 -0
  4. package/dist/index.js.map +50 -0
  5. package/dist/lib/api.d.ts +45 -0
  6. package/dist/lib/api.d.ts.map +1 -0
  7. package/dist/lib/configuration/functions/check-bun.d.ts +2 -0
  8. package/dist/lib/configuration/functions/check-bun.d.ts.map +1 -0
  9. package/dist/lib/configuration/functions/get-default-logger.d.ts +4 -0
  10. package/dist/lib/configuration/functions/get-default-logger.d.ts.map +1 -0
  11. package/dist/lib/configuration/functions/is-root-dir.d.ts +2 -0
  12. package/dist/lib/configuration/functions/is-root-dir.d.ts.map +1 -0
  13. package/dist/lib/configuration/functions/load-config.d.ts +3 -0
  14. package/dist/lib/configuration/functions/load-config.d.ts.map +1 -0
  15. package/dist/lib/configuration/functions/parse-config.d.ts +27 -0
  16. package/dist/lib/configuration/functions/parse-config.d.ts.map +1 -0
  17. package/dist/lib/configuration/functions/search-config-path.d.ts +2 -0
  18. package/dist/lib/configuration/functions/search-config-path.d.ts.map +1 -0
  19. package/dist/lib/configuration/functions/validate-config-path.d.ts +6 -0
  20. package/dist/lib/configuration/functions/validate-config-path.d.ts.map +1 -0
  21. package/dist/lib/configuration/functions/z-loose-function.d.ts +3 -0
  22. package/dist/lib/configuration/functions/z-loose-function.d.ts.map +1 -0
  23. package/dist/lib/configuration/functions/z-loose-object-without-index-signature.d.ts +4 -0
  24. package/dist/lib/configuration/functions/z-loose-object-without-index-signature.d.ts.map +1 -0
  25. package/dist/lib/configuration/schemas/config.d.ts +121 -0
  26. package/dist/lib/configuration/schemas/config.d.ts.map +1 -0
  27. package/dist/lib/configuration/types/activated-config.d.ts +5 -0
  28. package/dist/lib/configuration/types/activated-config.d.ts.map +1 -0
  29. package/dist/lib/configuration/types/config-validation-result.d.ts +10 -0
  30. package/dist/lib/configuration/types/config-validation-result.d.ts.map +1 -0
  31. package/dist/lib/configuration/types/config.d.ts +25 -0
  32. package/dist/lib/configuration/types/config.d.ts.map +1 -0
  33. package/dist/lib/configuration/types/logger.d.ts +7 -0
  34. package/dist/lib/configuration/types/logger.d.ts.map +1 -0
  35. package/dist/lib/configuration/types/packages-ready-callback-props.d.ts +7 -0
  36. package/dist/lib/configuration/types/packages-ready-callback-props.d.ts.map +1 -0
  37. package/dist/lib/configuration/types/process-package-callback-props.d.ts +10 -0
  38. package/dist/lib/configuration/types/process-package-callback-props.d.ts.map +1 -0
  39. package/dist/lib/configuration/types/subprocess-callback.d.ts +2 -0
  40. package/dist/lib/configuration/types/subprocess-callback.d.ts.map +1 -0
  41. package/dist/lib/configuration/types/supported-config-type.d.ts +11 -0
  42. package/dist/lib/configuration/types/supported-config-type.d.ts.map +1 -0
  43. package/dist/lib/graph/functions/dfs-traverse-graph-paths.d.ts +3 -0
  44. package/dist/lib/graph/functions/dfs-traverse-graph-paths.d.ts.map +1 -0
  45. package/dist/lib/graph/functions/dfs-traverse-graph.d.ts +4 -0
  46. package/dist/lib/graph/functions/dfs-traverse-graph.d.ts.map +1 -0
  47. package/dist/lib/graph/functions/extract-subgraph.d.ts +3 -0
  48. package/dist/lib/graph/functions/extract-subgraph.d.ts.map +1 -0
  49. package/dist/lib/graph/functions/generate-graphs.d.ts +8 -0
  50. package/dist/lib/graph/functions/generate-graphs.d.ts.map +1 -0
  51. package/dist/lib/graph/functions/generate-package-circular-dependency-paths.d.ts +5 -0
  52. package/dist/lib/graph/functions/generate-package-circular-dependency-paths.d.ts.map +1 -0
  53. package/dist/lib/graph/functions/generate-topological-ranking-range.d.ts +3 -0
  54. package/dist/lib/graph/functions/generate-topological-ranking-range.d.ts.map +1 -0
  55. package/dist/lib/graph/functions/generate-topological-ranking.d.ts +2 -0
  56. package/dist/lib/graph/functions/generate-topological-ranking.d.ts.map +1 -0
  57. package/dist/lib/graph/functions/generate-topological-sorted-groups.d.ts +5 -0
  58. package/dist/lib/graph/functions/generate-topological-sorted-groups.d.ts.map +1 -0
  59. package/dist/lib/graph/functions/is-disjoint.d.ts +3 -0
  60. package/dist/lib/graph/functions/is-disjoint.d.ts.map +1 -0
  61. package/dist/lib/graph/functions/is-ranked-greater-than-or-equal.d.ts +3 -0
  62. package/dist/lib/graph/functions/is-ranked-greater-than-or-equal.d.ts.map +1 -0
  63. package/dist/lib/graph/functions/is-subgraph.d.ts +3 -0
  64. package/dist/lib/graph/functions/is-subgraph.d.ts.map +1 -0
  65. package/dist/lib/graph/types/package-graph.d.ts +3 -0
  66. package/dist/lib/graph/types/package-graph.d.ts.map +1 -0
  67. package/dist/lib/graph/types/package-graphs.d.ts +6 -0
  68. package/dist/lib/graph/types/package-graphs.d.ts.map +1 -0
  69. package/dist/lib/graph/types/package-node.d.ts +6 -0
  70. package/dist/lib/graph/types/package-node.d.ts.map +1 -0
  71. package/dist/lib/graph/types/package-order.d.ts +14 -0
  72. package/dist/lib/graph/types/package-order.d.ts.map +1 -0
  73. package/dist/lib/package/functions/load-packages.d.ts +4 -0
  74. package/dist/lib/package/functions/load-packages.d.ts.map +1 -0
  75. package/dist/lib/package/functions/parse-package.d.ts +3 -0
  76. package/dist/lib/package/functions/parse-package.d.ts.map +1 -0
  77. package/dist/lib/package/functions/scan-package-paths.d.ts +2 -0
  78. package/dist/lib/package/functions/scan-package-paths.d.ts.map +1 -0
  79. package/dist/lib/package/functions/scan-packages.d.ts +4 -0
  80. package/dist/lib/package/functions/scan-packages.d.ts.map +1 -0
  81. package/dist/lib/package/functions/update-package-version.d.ts +5 -0
  82. package/dist/lib/package/functions/update-package-version.d.ts.map +1 -0
  83. package/dist/lib/package/types/package-data.d.ts +11 -0
  84. package/dist/lib/package/types/package-data.d.ts.map +1 -0
  85. package/dist/lib/types/bump-package-version-options.d.ts +11 -0
  86. package/dist/lib/types/bump-package-version-options.d.ts.map +1 -0
  87. package/dist/lib/types/bump-version-type.d.ts +12 -0
  88. package/dist/lib/types/bump-version-type.d.ts.map +1 -0
  89. package/dist/lib/types/config-options.d.ts +5 -0
  90. package/dist/lib/types/config-options.d.ts.map +1 -0
  91. package/dist/lib/types/get-config-options.d.ts +5 -0
  92. package/dist/lib/types/get-config-options.d.ts.map +1 -0
  93. package/dist/lib/types/get-package-circular-dependency-paths-options.d.ts +6 -0
  94. package/dist/lib/types/get-package-circular-dependency-paths-options.d.ts.map +1 -0
  95. package/dist/lib/types/get-package-data-options.d.ts +4 -0
  96. package/dist/lib/types/get-package-data-options.d.ts.map +1 -0
  97. package/dist/lib/types/get-package-graph-options.d.ts +5 -0
  98. package/dist/lib/types/get-package-graph-options.d.ts.map +1 -0
  99. package/dist/lib/types/get-package-order-options.d.ts +5 -0
  100. package/dist/lib/types/get-package-order-options.d.ts.map +1 -0
  101. package/dist/lib/types/package-data-options.d.ts +5 -0
  102. package/dist/lib/types/package-data-options.d.ts.map +1 -0
  103. package/dist/lib/types/package-graphs-options.d.ts +5 -0
  104. package/dist/lib/types/package-graphs-options.d.ts.map +1 -0
  105. package/dist/lib/types/package-name-options.d.ts +4 -0
  106. package/dist/lib/types/package-name-options.d.ts.map +1 -0
  107. package/dist/lib/types/package-order-options.d.ts +5 -0
  108. package/dist/lib/types/package-order-options.d.ts.map +1 -0
  109. package/dist/lib/types/watch-packages-options.d.ts +6 -0
  110. package/dist/lib/types/watch-packages-options.d.ts.map +1 -0
  111. package/dist/lib/watch/functions/filter-files-modified-since.d.ts +2 -0
  112. package/dist/lib/watch/functions/filter-files-modified-since.d.ts.map +1 -0
  113. package/dist/lib/watch/functions/get-commands-for-shell.d.ts +2 -0
  114. package/dist/lib/watch/functions/get-commands-for-shell.d.ts.map +1 -0
  115. package/dist/lib/watch/functions/get-line-buffered-writer.d.ts +2 -0
  116. package/dist/lib/watch/functions/get-line-buffered-writer.d.ts.map +1 -0
  117. package/dist/lib/watch/functions/normalise-watched-file-path.d.ts +2 -0
  118. package/dist/lib/watch/functions/normalise-watched-file-path.d.ts.map +1 -0
  119. package/dist/lib/watch/functions/read-stream.d.ts +2 -0
  120. package/dist/lib/watch/functions/read-stream.d.ts.map +1 -0
  121. package/dist/lib/watch/functions/run-async.d.ts +3 -0
  122. package/dist/lib/watch/functions/run-async.d.ts.map +1 -0
  123. package/dist/lib/watch/functions/run-subprocess.d.ts +11 -0
  124. package/dist/lib/watch/functions/run-subprocess.d.ts.map +1 -0
  125. package/dist/lib/watch/functions/watch-package-changes.d.ts +8 -0
  126. package/dist/lib/watch/functions/watch-package-changes.d.ts.map +1 -0
  127. package/dist/lib/watch/types/change-action.d.ts +8 -0
  128. package/dist/lib/watch/types/change-action.d.ts.map +1 -0
  129. package/dist/lib/watch/types/exit-state.d.ts +8 -0
  130. package/dist/lib/watch/types/exit-state.d.ts.map +1 -0
  131. package/dist/lib/watch/types/package-changes.d.ts +2 -0
  132. package/dist/lib/watch/types/package-changes.d.ts.map +1 -0
  133. package/dist/lib/watch/types/run-async-type.d.ts +7 -0
  134. package/dist/lib/watch/types/run-async-type.d.ts.map +1 -0
  135. package/dist/lib/watch/types/spawn-options.d.ts +4 -0
  136. package/dist/lib/watch/types/spawn-options.d.ts.map +1 -0
  137. package/dist/lib/watch/types/std-type.d.ts +7 -0
  138. package/dist/lib/watch/types/std-type.d.ts.map +1 -0
  139. package/package.json +44 -0
package/dist/index.js ADDED
@@ -0,0 +1,1431 @@
1
+ // @bun
2
+ // src/lib/types/bump-version-type.ts
3
+ var BumpVersionType = {
4
+ Major: "major",
5
+ Minor: "minor",
6
+ Patch: "patch",
7
+ Premajor: "premajor",
8
+ Preminor: "preminor",
9
+ Prepatch: "prepatch",
10
+ Prerelease: "prerelease"
11
+ };
12
+ // src/lib/api.ts
13
+ import { isDefined as isDefined5 } from "@package-pal/util";
14
+
15
+ // src/lib/configuration/functions/check-bun.ts
16
+ var checkBun = () => {
17
+ if (!Bun) {
18
+ throw new Error("This package must be run with the Bun runtime.");
19
+ }
20
+ };
21
+
22
+ // src/lib/configuration/functions/load-config.ts
23
+ import {
24
+ deepMergeDefined,
25
+ formatSimpleLogObject,
26
+ noOp as noOp2
27
+ } from "@package-pal/util";
28
+ import {
29
+ bgGray,
30
+ dim
31
+ } from "yoctocolors";
32
+
33
+ // src/lib/configuration/functions/get-default-logger.ts
34
+ import {
35
+ assertDefined,
36
+ noOp
37
+ } from "@package-pal/util";
38
+ var levelOrder = {
39
+ debug: 0,
40
+ info: 1,
41
+ warn: 2,
42
+ error: 3,
43
+ silent: Infinity
44
+ };
45
+ var getDefaultLogger = (logLevel) => {
46
+ const level = assertDefined(levelOrder[logLevel]);
47
+ return {
48
+ debug: level <= levelOrder.debug ? console.debug : noOp,
49
+ info: level <= levelOrder.info ? console.info : noOp,
50
+ warn: level <= levelOrder.warn ? console.warn : noOp,
51
+ error: level <= levelOrder.error ? console.error : noOp
52
+ };
53
+ };
54
+
55
+ // src/lib/configuration/schemas/config.ts
56
+ import * as z3 from "zod/mini";
57
+
58
+ // src/lib/configuration/functions/z-loose-function.ts
59
+ import { z } from "zod/mini";
60
+ var zLooseFunction = () => {
61
+ return z.transform((arg, ctx) => {
62
+ if (typeof arg !== "function") {
63
+ ctx.issues.push({
64
+ message: "Expected a function",
65
+ code: "custom",
66
+ input: arg
67
+ });
68
+ return z.NEVER;
69
+ }
70
+ return arg;
71
+ });
72
+ };
73
+
74
+ // src/lib/configuration/functions/z-loose-object-without-index-signature.ts
75
+ import * as z2 from "zod/mini";
76
+ var zLooseObjectWithoutIndexSignature = (shape, params) => {
77
+ return z2.looseObject(shape, params);
78
+ };
79
+
80
+ // src/lib/configuration/schemas/config.ts
81
+ var LogLevel = z3.enum([
82
+ "debug",
83
+ "info",
84
+ "warn",
85
+ "error",
86
+ "silent"
87
+ ]);
88
+ var Config = z3.object({
89
+ packages: z3.optional(z3.union([z3.string(), z3.array(z3.string())])),
90
+ version: z3.optional(z3.object({
91
+ preId: z3.optional(z3.string()),
92
+ exact: z3.optional(z3.boolean())
93
+ })),
94
+ watch: z3.optional(zLooseObjectWithoutIndexSignature({
95
+ debounceMs: z3.optional(z3.number()),
96
+ hooks: z3.optional(z3.object({
97
+ onBeforeProcessPackage: z3.optional(zLooseFunction()),
98
+ onProcessPackage: z3.optional(zLooseFunction()),
99
+ onProcessPackageError: z3.optional(zLooseFunction()),
100
+ onAfterProcessPackage: z3.optional(zLooseFunction()),
101
+ onBeforePackagesReady: z3.optional(zLooseFunction()),
102
+ onPackagesReady: z3.optional(zLooseFunction()),
103
+ onAfterPackagesReady: z3.optional(zLooseFunction())
104
+ })),
105
+ subprocess: z3.optional(z3.object({
106
+ partialProcessing: z3.optional(z3.boolean()),
107
+ parallelProcessing: z3.optional(z3.boolean()),
108
+ matchLongRunningOutputAsReady: z3.nullish(z3.string()),
109
+ matchLongRunningOutputAsErrored: z3.nullish(z3.string())
110
+ }))
111
+ })),
112
+ logLevel: z3.optional(LogLevel)
113
+ });
114
+
115
+ // src/lib/configuration/functions/validate-config-path.ts
116
+ import {
117
+ basename,
118
+ extname
119
+ } from "path";
120
+
121
+ // src/lib/configuration/types/supported-config-type.ts
122
+ var SupportedConfigType = {
123
+ TS: "ts",
124
+ CTS: "cts",
125
+ MTS: "mts",
126
+ JS: "js",
127
+ CJS: "cjs",
128
+ MJS: "mjs"
129
+ };
130
+
131
+ // src/lib/configuration/functions/validate-config-path.ts
132
+ var CONFIG_SUPPORTED_BASE_NAME = "ppal.config";
133
+ var CONFIG_SUPPORTED_TYPES = Object.values(SupportedConfigType);
134
+ var CONFIG_SUPPORTED_NAMES = CONFIG_SUPPORTED_TYPES.map((type) => `${CONFIG_SUPPORTED_BASE_NAME}.${type}`);
135
+ var validateConfigPath = (path) => {
136
+ const name = basename(path);
137
+ const extension = extname(path);
138
+ const type = CONFIG_SUPPORTED_TYPES.find((option) => `.${option}` === extension);
139
+ if (!name.startsWith(CONFIG_SUPPORTED_BASE_NAME) || !type) {
140
+ return {
141
+ valid: false,
142
+ errorMessage: `'${name}' is not a valid config file. Only '${CONFIG_SUPPORTED_NAMES.join(", ")}' are supported.`
143
+ };
144
+ }
145
+ return {
146
+ valid: true,
147
+ name,
148
+ type
149
+ };
150
+ };
151
+
152
+ // src/lib/configuration/functions/parse-config.ts
153
+ var parseConfig = async (path) => {
154
+ const result = validateConfigPath(path);
155
+ if (!result.valid) {
156
+ throw new Error(result.errorMessage);
157
+ }
158
+ const module = await import(path);
159
+ const base = module.default ?? module;
160
+ return Config.parse(base);
161
+ };
162
+
163
+ // src/lib/configuration/functions/search-config-path.ts
164
+ import {
165
+ isAbsolute,
166
+ join,
167
+ dirname as dirname2
168
+ } from "path";
169
+ import { isDefined } from "@package-pal/util";
170
+
171
+ // src/lib/configuration/functions/is-root-dir.ts
172
+ import { dirname } from "path";
173
+ var isRootDir = (path) => {
174
+ return dirname(path) === path;
175
+ };
176
+
177
+ // src/lib/configuration/functions/search-config-path.ts
178
+ var dirDistLimit = 25;
179
+ var checkForConfigInDir = async (dir) => {
180
+ return new Promise((resolve) => {
181
+ let pending = CONFIG_SUPPORTED_NAMES.length;
182
+ for (const name of CONFIG_SUPPORTED_NAMES) {
183
+ const pathToCheck = join(dir, name);
184
+ Bun.file(pathToCheck).exists().then((exists) => {
185
+ pending--;
186
+ if (exists) {
187
+ resolve(pathToCheck);
188
+ return;
189
+ }
190
+ if (!pending) {
191
+ resolve(undefined);
192
+ }
193
+ });
194
+ }
195
+ });
196
+ };
197
+ var searchConfigPath = async (pathOverride) => {
198
+ let activeDir = process.cwd();
199
+ if (isDefined(pathOverride)) {
200
+ if (isAbsolute(pathOverride)) {
201
+ return pathOverride;
202
+ }
203
+ return join(activeDir, pathOverride);
204
+ }
205
+ let trackDist = 0;
206
+ while (!isRootDir(activeDir)) {
207
+ if (trackDist > dirDistLimit) {
208
+ throw new Error(`Maximum config search directory distance reached (${dirDistLimit.toString()}).`);
209
+ }
210
+ const configPath = await checkForConfigInDir(activeDir);
211
+ if (configPath) {
212
+ return configPath;
213
+ }
214
+ trackDist++;
215
+ activeDir = dirname2(activeDir);
216
+ }
217
+ return checkForConfigInDir(activeDir);
218
+ };
219
+
220
+ // src/lib/configuration/functions/load-config.ts
221
+ var defaultConfig = {
222
+ packages: "packages/*",
223
+ version: {
224
+ preId: "",
225
+ exact: false
226
+ },
227
+ watch: {
228
+ debounceMs: 500,
229
+ hooks: {
230
+ onBeforeProcessPackage: noOp2,
231
+ onProcessPackage: noOp2,
232
+ onProcessPackageError: noOp2,
233
+ onAfterProcessPackage: noOp2,
234
+ onBeforePackagesReady: noOp2,
235
+ onPackagesReady: noOp2,
236
+ onAfterPackagesReady: noOp2
237
+ },
238
+ subprocess: {
239
+ partialProcessing: false,
240
+ parallelProcessing: true,
241
+ matchLongRunningOutputAsReady: null,
242
+ matchLongRunningOutputAsErrored: null
243
+ }
244
+ },
245
+ logger: getDefaultLogger("info"),
246
+ logLevel: "info"
247
+ };
248
+ var loadConfig = async (overrideConfigPath) => {
249
+ const path = await searchConfigPath(overrideConfigPath);
250
+ if (!path) {
251
+ defaultConfig.logger.info("No config file found. Defaults will be applied.");
252
+ return defaultConfig;
253
+ }
254
+ const parsedConfig = await parseConfig(path);
255
+ const parsedLogger = parsedConfig["logger"];
256
+ const logger = parsedLogger ?? (!parsedConfig.logLevel || parsedConfig.logLevel === defaultConfig.logLevel ? defaultConfig.logger : getDefaultLogger(parsedConfig.logLevel));
257
+ logger.info(`Successfully loaded config file '${path}'.`);
258
+ logger.debug(dim(bgGray("User config:")), `
259
+ ${dim(formatSimpleLogObject(parsedConfig))}`);
260
+ logger.debug(dim(bgGray("Default config:")), `
261
+ ${dim(formatSimpleLogObject(defaultConfig))}`);
262
+ const activatedConfig = deepMergeDefined(defaultConfig, parsedConfig);
263
+ activatedConfig.logger = logger;
264
+ logger.debug(dim(bgGray("Activated config:")), `
265
+ ${dim(formatSimpleLogObject(activatedConfig))}`);
266
+ return activatedConfig;
267
+ };
268
+
269
+ // src/lib/graph/functions/generate-graphs.ts
270
+ import { assertDefined as assertDefined2 } from "@package-pal/util";
271
+ import { dim as dim2 } from "yoctocolors";
272
+ var trackPackageEntryDependencies = ({
273
+ trackedDependencies,
274
+ packageNames,
275
+ packageData
276
+ }) => {
277
+ const iterateEntries = [packageData.dependencies, packageData.peerDependencies];
278
+ for (const packageEntries of iterateEntries) {
279
+ if (!packageEntries) {
280
+ continue;
281
+ }
282
+ for (const trackPackageName of Object.keys(packageEntries)) {
283
+ if (!packageNames.has(trackPackageName)) {
284
+ continue;
285
+ }
286
+ trackedDependencies.add(trackPackageName);
287
+ }
288
+ }
289
+ };
290
+ var generateReverseGraph = (packages, packageGraph) => {
291
+ const reversePackageGraph = new Map(packages.map((packageData) => [packageData.name, {
292
+ packageData,
293
+ pointsToPackages: new Set
294
+ }]));
295
+ for (const packageData of packages) {
296
+ const packageNode = assertDefined2(packageGraph.get(packageData.name));
297
+ for (const dependency of packageNode.pointsToPackages) {
298
+ const reverseNode = assertDefined2(reversePackageGraph.get(dependency));
299
+ reverseNode.pointsToPackages.add(packageData.name);
300
+ }
301
+ }
302
+ return reversePackageGraph;
303
+ };
304
+ var generateGraphs = (packages, logger) => {
305
+ logger.debug(dim2(`Generating package graphs for ${packages.length.toString()} packages...`));
306
+ const packageNames = new Set(packages.map((packageData) => packageData.name));
307
+ const packageGraph = new Map;
308
+ for (const packageData of packages) {
309
+ const trackedDependencies = new Set;
310
+ trackPackageEntryDependencies({
311
+ trackedDependencies,
312
+ packageNames,
313
+ packageData
314
+ });
315
+ packageGraph.set(packageData.name, {
316
+ packageData,
317
+ pointsToPackages: trackedDependencies
318
+ });
319
+ }
320
+ const reversePackageGraph = generateReverseGraph(packages, packageGraph);
321
+ return {
322
+ dependencies: packageGraph,
323
+ dependents: reversePackageGraph
324
+ };
325
+ };
326
+
327
+ // src/lib/graph/functions/generate-package-circular-dependency-paths.ts
328
+ import { assertDefined as assertDefined6 } from "@package-pal/util";
329
+ import {
330
+ dim as dim3,
331
+ yellow
332
+ } from "yoctocolors";
333
+
334
+ // src/lib/graph/functions/dfs-traverse-graph-paths.ts
335
+ import { assertDefined as assertDefined3 } from "@package-pal/util";
336
+ var dfsTraverseGraphPaths = function* (graph, traverseFromPackages) {
337
+ const globalVisited = new Set;
338
+ const startPackages = Array.isArray(traverseFromPackages) ? traverseFromPackages : [traverseFromPackages];
339
+ for (const start of startPackages) {
340
+ const stack = [{
341
+ node: start,
342
+ path: [],
343
+ localVisited: new Set
344
+ }];
345
+ while (stack.length) {
346
+ const {
347
+ node,
348
+ path,
349
+ localVisited
350
+ } = assertDefined3(stack.pop());
351
+ if (localVisited.has(node)) {
352
+ const idx = path.indexOf(node);
353
+ if (idx !== -1) {
354
+ yield [...path.slice(idx), node];
355
+ }
356
+ continue;
357
+ }
358
+ if (globalVisited.has(node)) {
359
+ continue;
360
+ }
361
+ const newPath = [...path, node];
362
+ const newLocalVisited = new Set(localVisited);
363
+ newLocalVisited.add(node);
364
+ const neighbours = graph.get(node)?.pointsToPackages;
365
+ let pushed = false;
366
+ if (neighbours) {
367
+ for (const neighbour of neighbours) {
368
+ stack.push({
369
+ node: neighbour,
370
+ path: newPath,
371
+ localVisited: newLocalVisited
372
+ });
373
+ pushed = true;
374
+ }
375
+ }
376
+ if (!pushed) {
377
+ yield newPath;
378
+ }
379
+ globalVisited.add(node);
380
+ }
381
+ }
382
+ };
383
+
384
+ // src/lib/graph/functions/extract-subgraph.ts
385
+ import { assertDefined as assertDefined5 } from "@package-pal/util";
386
+
387
+ // src/lib/graph/functions/dfs-traverse-graph.ts
388
+ import { assertDefined as assertDefined4 } from "@package-pal/util";
389
+ var dfsTraverseGraph = function* (graph, traverseFromPackages) {
390
+ const visited = new Set;
391
+ const stack = Array.isArray(traverseFromPackages) ? [...traverseFromPackages] : [traverseFromPackages];
392
+ while (stack.length) {
393
+ const activePackage = assertDefined4(stack.pop());
394
+ if (visited.has(activePackage)) {
395
+ continue;
396
+ }
397
+ visited.add(activePackage);
398
+ const node = graph.get(activePackage);
399
+ if (!node) {
400
+ continue;
401
+ }
402
+ yield node.packageData;
403
+ for (const dep of node.pointsToPackages) {
404
+ if (!visited.has(dep)) {
405
+ stack.push(dep);
406
+ }
407
+ }
408
+ }
409
+ };
410
+
411
+ // src/lib/graph/functions/extract-subgraph.ts
412
+ var extractSubgraph = (graph, fromPackages) => {
413
+ const reachablePackages = new Set(dfsTraverseGraph(graph, fromPackages).map((packageData) => packageData.name));
414
+ const subgraph = new Map;
415
+ for (const packageName of reachablePackages) {
416
+ const node = assertDefined5(graph.get(packageName));
417
+ const filteredNeighbours = new Set([...node.pointsToPackages].filter((dep) => reachablePackages.has(dep)));
418
+ subgraph.set(packageName, {
419
+ packageData: node.packageData,
420
+ pointsToPackages: filteredNeighbours
421
+ });
422
+ }
423
+ return subgraph;
424
+ };
425
+
426
+ // src/lib/graph/functions/generate-package-circular-dependency-paths.ts
427
+ var findStronglyConnectedComponents = (graph) => {
428
+ let index = 0;
429
+ const indices = new Map;
430
+ const lowlinks = new Map;
431
+ const stack = [];
432
+ const onStack = new Set;
433
+ const sccs = [];
434
+ const strongConnect = (node) => {
435
+ indices.set(node, index);
436
+ lowlinks.set(node, index);
437
+ index++;
438
+ stack.push(node);
439
+ onStack.add(node);
440
+ const neighbors = graph.get(node)?.pointsToPackages ?? new Set;
441
+ for (const neighbor of neighbors) {
442
+ if (!indices.has(neighbor)) {
443
+ strongConnect(neighbor);
444
+ lowlinks.set(node, Math.min(assertDefined6(lowlinks.get(node)), assertDefined6(lowlinks.get(neighbor))));
445
+ continue;
446
+ }
447
+ if (onStack.has(neighbor)) {
448
+ lowlinks.set(node, Math.min(assertDefined6(lowlinks.get(node)), assertDefined6(indices.get(neighbor))));
449
+ }
450
+ }
451
+ if (lowlinks.get(node) === indices.get(node)) {
452
+ const scc = [];
453
+ let stackNode = assertDefined6(stack.pop());
454
+ onStack.delete(stackNode);
455
+ scc.push(stackNode);
456
+ while (stackNode !== node) {
457
+ stackNode = assertDefined6(stack.pop());
458
+ onStack.delete(stackNode);
459
+ scc.push(stackNode);
460
+ }
461
+ const firstNode = scc[0];
462
+ const isSelfCycle = !!firstNode && (graph.get(firstNode)?.pointsToPackages.has(firstNode) ?? false);
463
+ if (scc.length > 1 || isSelfCycle) {
464
+ sccs.push(scc);
465
+ }
466
+ }
467
+ };
468
+ for (const node of graph.keys()) {
469
+ if (!indices.has(node)) {
470
+ strongConnect(node);
471
+ }
472
+ }
473
+ return sccs;
474
+ };
475
+ var findSampleCyclePath = (scc, graph) => {
476
+ const sccSet = new Set(scc);
477
+ return assertDefined6(dfsTraverseGraphPaths(graph, scc).find((path) => {
478
+ const lastPackage = path[path.length - 1];
479
+ return path.length >= 2 && lastPackage && sccSet.has(lastPackage);
480
+ }));
481
+ };
482
+ var generatePackageCircularDependencyPaths = ({ dependents }, packageSorted, logger) => {
483
+ logger.debug(dim3("Generating circular dependency paths..."));
484
+ const subgraph = extractSubgraph(dependents, packageSorted.circular);
485
+ const scss = findStronglyConnectedComponents(subgraph);
486
+ const sampleCycles = scss.map((scc) => findSampleCyclePath(scc, subgraph).reverse());
487
+ if (sampleCycles.length) {
488
+ const sampleCycleContents = sampleCycles.map((cycle, index) => {
489
+ return ` ${(index + 1).toString()}. ` + cycle.map((name) => `'${name}'`).join(" \u2192 ");
490
+ }).join(`
491
+ `);
492
+ logger.warn(yellow(`${sampleCycles.length.toString()} cyclic paths found:
493
+ ${sampleCycleContents}`));
494
+ } else {
495
+ logger.debug(dim3(`No cyclic paths found.`));
496
+ }
497
+ return sampleCycles;
498
+ };
499
+
500
+ // src/lib/graph/functions/generate-topological-sorted-groups.ts
501
+ import { assertDefined as assertDefined7 } from "@package-pal/util";
502
+ import {
503
+ dim as dim4,
504
+ yellow as yellow2
505
+ } from "yoctocolors";
506
+ var getCircularDependencies = (inDegree) => {
507
+ return Array.from(inDegree.keys()).sort((a, b) => {
508
+ const degA = assertDefined7(inDegree.get(a));
509
+ const degB = assertDefined7(inDegree.get(b));
510
+ return degB - degA;
511
+ }).sort();
512
+ };
513
+ var generateTopologicalSortedGroups = (packageGraph, logger) => {
514
+ logger.debug(dim4("Generating topological sorted groups..."));
515
+ const graphEntries = Array.from(packageGraph.entries());
516
+ const inDegree = new Map(graphEntries.map(([packageName]) => [packageName, 0]));
517
+ const graph = new Map(graphEntries.map(([packageName]) => [packageName, new Set]));
518
+ for (const [packageName, node] of graphEntries) {
519
+ for (const dependencyName of node.pointsToPackages) {
520
+ assertDefined7(graph.get(dependencyName)).add(packageName);
521
+ inDegree.set(packageName, assertDefined7(inDegree.get(packageName)) + 1);
522
+ }
523
+ }
524
+ const result = [];
525
+ let ready = [];
526
+ for (const [packageName, deg] of inDegree) {
527
+ if (deg === 0) {
528
+ ready.push(packageName);
529
+ }
530
+ }
531
+ while (ready.length > 0) {
532
+ result.push(ready);
533
+ const nextReady = [];
534
+ for (const packageName of ready) {
535
+ inDegree.delete(packageName);
536
+ const dependentPackages = graph.get(packageName);
537
+ if (!dependentPackages) {
538
+ continue;
539
+ }
540
+ for (const dependentPackage of dependentPackages) {
541
+ inDegree.set(dependentPackage, assertDefined7(inDegree.get(dependentPackage)) - 1);
542
+ if (inDegree.get(dependentPackage) === 0) {
543
+ nextReady.push(dependentPackage);
544
+ }
545
+ }
546
+ }
547
+ ready = nextReady;
548
+ }
549
+ logger.debug(dim4(`Sorted packages into ${result.length.toString()} sequential groups of parallelizable dependencies.`));
550
+ const circular = getCircularDependencies(inDegree);
551
+ if (circular.length) {
552
+ logger.warn(yellow2(`${circular.length.toString()} packages could not be sorted due to circular dependencies. Correct ordering cannot be guaranteed.`));
553
+ }
554
+ return {
555
+ groups: result.map((group) => group.sort()),
556
+ circular
557
+ };
558
+ };
559
+
560
+ // src/lib/package/functions/load-packages.ts
561
+ import { dim as dim6 } from "yoctocolors";
562
+
563
+ // src/lib/package/functions/scan-packages.ts
564
+ import {
565
+ dirname as dirname4,
566
+ join as join2
567
+ } from "path";
568
+ import { formatUnknownError } from "@package-pal/util";
569
+ import {
570
+ dim as dim5,
571
+ red
572
+ } from "yoctocolors";
573
+
574
+ // src/lib/package/functions/parse-package.ts
575
+ import {
576
+ dirname as dirname3,
577
+ basename as basename2
578
+ } from "path";
579
+ import { isDefined as isDefined2 } from "@package-pal/util";
580
+ var parsePackage = (path, text) => {
581
+ const base = JSON.parse(text);
582
+ if (!isDefined2(base) || typeof base !== "object") {
583
+ return;
584
+ }
585
+ const props = base;
586
+ const {
587
+ dependencies,
588
+ peerDependencies,
589
+ devDependencies
590
+ } = props;
591
+ if (isDefined2(dependencies) && !(typeof dependencies === "object") || isDefined2(peerDependencies) && !(typeof peerDependencies === "object") || isDefined2(devDependencies) && !(typeof devDependencies === "object")) {
592
+ return;
593
+ }
594
+ return {
595
+ rawContent: text,
596
+ path,
597
+ name: props.name ?? path,
598
+ dir: basename2(dirname3(path)),
599
+ version: props.version ?? undefined,
600
+ dependencies: dependencies ?? undefined,
601
+ peerDependencies: peerDependencies ?? undefined,
602
+ devDependencies: devDependencies ?? undefined
603
+ };
604
+ };
605
+
606
+ // src/lib/package/functions/scan-package-paths.ts
607
+ import { scanGlobPatternPaths } from "@package-pal/util";
608
+ var scanPackagePaths = (packages) => {
609
+ return scanGlobPatternPaths(packages, {
610
+ absolute: true,
611
+ onlyFiles: false
612
+ });
613
+ };
614
+
615
+ // src/lib/package/functions/scan-packages.ts
616
+ var scanPackages = async function* (patterns, logger) {
617
+ for await (const path of scanPackagePaths(patterns)) {
618
+ const packagePath = join2(path, "package.json");
619
+ const dir = dirname4(packagePath);
620
+ try {
621
+ const file = Bun.file(packagePath);
622
+ const text = await file.text();
623
+ const packageData = parsePackage(packagePath, text);
624
+ if (!packageData) {
625
+ logger.debug(dim5(`Invalid package.json found in '${dir}'.`));
626
+ continue;
627
+ }
628
+ logger.debug(dim5(`Successfully read package.json in '${dir}'.`));
629
+ yield packageData;
630
+ } catch (e) {
631
+ logger.debug(dim5(`Failed to read package.json in '${dir}' - ${red(formatUnknownError(e))}.`));
632
+ }
633
+ }
634
+ };
635
+
636
+ // src/lib/package/functions/load-packages.ts
637
+ var loadPackages = async (packagePatterns, logger) => {
638
+ const patternContent = packagePatterns.map((pattern) => `'${pattern}'`).join(", ");
639
+ logger.debug(dim6(`Loading packages matching pattern/s ${patternContent}...`));
640
+ const packages = [];
641
+ const seen = new Set;
642
+ for await (const packageData of scanPackages(Array.from(new Set(packagePatterns)), logger)) {
643
+ if (seen.has(packageData.name)) {
644
+ continue;
645
+ }
646
+ seen.add(packageData.name);
647
+ packages.push(packageData);
648
+ }
649
+ if (!packages.length) {
650
+ throw new Error(`No packages found for pattern/s ${patternContent}.`);
651
+ }
652
+ return packages;
653
+ };
654
+
655
+ // src/lib/package/functions/update-package-version.ts
656
+ var {semver } = globalThis.Bun;
657
+ import { inc } from "semver";
658
+ import { dim as dim7 } from "yoctocolors";
659
+ var findAndReplaceVersion = ({
660
+ raw,
661
+ field,
662
+ packageName,
663
+ updatePackageName,
664
+ newVersion,
665
+ exact,
666
+ logger
667
+ }) => {
668
+ if (field === "version" && packageName === updatePackageName) {
669
+ const versionKey = `"version"`;
670
+ const keyIndex = raw.indexOf(versionKey);
671
+ if (keyIndex === -1) {
672
+ return raw;
673
+ }
674
+ const versionStart2 = raw.indexOf('"', keyIndex + versionKey.length);
675
+ const versionEnd2 = raw.indexOf('"', versionStart2 + 1);
676
+ if (versionStart2 === -1 || versionEnd2 === -1) {
677
+ return raw;
678
+ }
679
+ const currentVersionString2 = raw.slice(versionStart2 + 1, versionEnd2);
680
+ const updatedVersion2 = newVersion;
681
+ logger.info(`Updating '${updatePackageName}' version: ${currentVersionString2} \u2192 ${updatedVersion2}.`);
682
+ const before2 = raw.slice(0, versionStart2 + 1);
683
+ const after2 = raw.slice(versionEnd2);
684
+ return `${before2}${updatedVersion2}${after2}`;
685
+ }
686
+ const fieldIndex = raw.indexOf(`"${field}"`);
687
+ if (fieldIndex === -1) {
688
+ return raw;
689
+ }
690
+ const fieldStart = raw.indexOf("{", fieldIndex);
691
+ if (fieldStart === -1) {
692
+ return raw;
693
+ }
694
+ const fieldEnd = raw.indexOf("}", fieldStart);
695
+ if (fieldEnd === -1) {
696
+ return raw;
697
+ }
698
+ const fieldBlock = raw.slice(fieldStart, fieldEnd);
699
+ const depKey = `"${packageName}"`;
700
+ const depIndex = fieldBlock.indexOf(depKey);
701
+ if (depIndex === -1) {
702
+ return raw;
703
+ }
704
+ const versionStart = fieldBlock.indexOf('"', depIndex + depKey.length);
705
+ const versionEnd = fieldBlock.indexOf('"', versionStart + 1);
706
+ if (versionStart === -1 || versionEnd === -1) {
707
+ return raw;
708
+ }
709
+ const currentVersionString = fieldBlock.slice(versionStart + 1, versionEnd);
710
+ const preservedPrefix = exact ? "" : /^([~^><=]*)/.exec(currentVersionString)?.[1] ?? "";
711
+ const updatedVersion = `${preservedPrefix}${newVersion}`;
712
+ logger.info(`Updating '${updatePackageName}' ${field} '${packageName}': ${currentVersionString} \u2192 ${updatedVersion}.`);
713
+ const before = raw.slice(0, fieldStart + versionStart + 1);
714
+ const after = raw.slice(fieldStart + versionEnd);
715
+ return `${before}${updatedVersion}${after}`;
716
+ };
717
+ var updatePackageVersion = async (packageName, type, packageGraphs, preId, exact, logger) => {
718
+ logger.debug(dim7(`Bumping package '${packageName}'...`));
719
+ const packageNode = packageGraphs.dependencies.get(packageName);
720
+ if (!packageNode) {
721
+ throw new Error(`Package '${packageName}' not found.`);
722
+ }
723
+ const currentVersion = packageNode.packageData.version;
724
+ if (!currentVersion) {
725
+ throw new Error(`Package '${packageName}' has no version.`);
726
+ }
727
+ const bumpedVersion = inc(currentVersion, type, undefined, preId);
728
+ if (!bumpedVersion) {
729
+ throw new Error(`Package '${packageName}' version '${currentVersion}' is invalid.`);
730
+ }
731
+ const updatedContent = findAndReplaceVersion({
732
+ raw: packageNode.packageData.rawContent,
733
+ field: "version",
734
+ packageName,
735
+ updatePackageName: packageName,
736
+ newVersion: bumpedVersion,
737
+ exact,
738
+ logger
739
+ });
740
+ const baseWrite = Bun.write(packageNode.packageData.path, updatedContent);
741
+ const dependentWrites = Array.from(dfsTraverseGraph(packageGraphs.dependents, packageName).flatMap((dependent) => {
742
+ const fields = ["dependencies", "peerDependencies"];
743
+ return fields.flatMap((field) => {
744
+ const entry = dependent[field];
745
+ const depVersion = entry?.[packageName];
746
+ if (!depVersion) {
747
+ return [];
748
+ }
749
+ if (exact ? depVersion === bumpedVersion : semver.satisfies(bumpedVersion, depVersion)) {
750
+ logger.debug(dim7(`Skipping '${dependent.name}': ${field} version '${depVersion}' already satisfies '${bumpedVersion}'.`));
751
+ return [];
752
+ }
753
+ const updatedContent2 = findAndReplaceVersion({
754
+ raw: dependent.rawContent,
755
+ field,
756
+ packageName,
757
+ updatePackageName: dependent.name,
758
+ newVersion: bumpedVersion,
759
+ exact,
760
+ logger
761
+ });
762
+ return [Bun.write(dependent.path, updatedContent2)];
763
+ });
764
+ }));
765
+ return Promise.all([baseWrite, ...dependentWrites]).then(() => {
766
+ return;
767
+ });
768
+ };
769
+
770
+ // src/lib/watch/functions/watch-package-changes.ts
771
+ import {
772
+ watch
773
+ } from "fs";
774
+ import {
775
+ dirname as dirname6,
776
+ join as join4
777
+ } from "path";
778
+ import {
779
+ assertDefined as assertDefined9,
780
+ DedupePathsBy,
781
+ dedupeSharedPaths,
782
+ getDeferredPromise,
783
+ getStringMatcher,
784
+ isDefined as isDefined4
785
+ } from "@package-pal/util";
786
+ import {
787
+ dim as dim9,
788
+ red as red3
789
+ } from "yoctocolors";
790
+
791
+ // src/lib/graph/functions/generate-topological-ranking-range.ts
792
+ import { isDefined as isDefined3 } from "@package-pal/util";
793
+ var generateTopologicalRankingRange = (graph, ranking) => {
794
+ let min = Infinity;
795
+ let max = -Infinity;
796
+ for (const key of graph.keys()) {
797
+ const rank = ranking.get(key);
798
+ if (!isDefined3(rank)) {
799
+ continue;
800
+ }
801
+ if (rank < min) {
802
+ min = rank;
803
+ }
804
+ if (rank > max) {
805
+ max = rank;
806
+ }
807
+ }
808
+ if (min === Infinity) {
809
+ return [-1, -1];
810
+ }
811
+ return [min, max];
812
+ };
813
+
814
+ // src/lib/graph/functions/generate-topological-ranking.ts
815
+ import { assertDefined as assertDefined8 } from "@package-pal/util";
816
+ var generateTopologicalRanking = (packageProcessOrder) => {
817
+ const rankMap = new Map;
818
+ for (let i = 0;i < packageProcessOrder.length; i++) {
819
+ for (const packageName of assertDefined8(packageProcessOrder[i])) {
820
+ rankMap.set(packageName, i);
821
+ }
822
+ }
823
+ return rankMap;
824
+ };
825
+
826
+ // src/lib/graph/functions/is-disjoint.ts
827
+ var isDisjoint = (a, b) => {
828
+ for (const key of a.keys()) {
829
+ if (b.has(key)) {
830
+ return false;
831
+ }
832
+ }
833
+ return true;
834
+ };
835
+
836
+ // src/lib/graph/functions/is-ranked-greater-than-or-equal.ts
837
+ var isRankedGreaterThanOrEqual = (a, b, ranking) => {
838
+ const [prevMin] = generateTopologicalRankingRange(a, ranking);
839
+ for (const key of b.keys()) {
840
+ const rank = ranking.get(key);
841
+ if (rank !== undefined && rank < prevMin) {
842
+ return false;
843
+ }
844
+ }
845
+ return true;
846
+ };
847
+
848
+ // src/lib/graph/functions/is-subgraph.ts
849
+ var recordEqual = (a, b) => {
850
+ if (!a && !b) {
851
+ return true;
852
+ }
853
+ if (!a || !b) {
854
+ return false;
855
+ }
856
+ const aKeys = Object.keys(a);
857
+ const bKeys = Object.keys(b);
858
+ if (aKeys.length !== bKeys.length) {
859
+ return false;
860
+ }
861
+ return aKeys.every((key) => b[key] === a[key]);
862
+ };
863
+ var isPackageDataLooseEqual = (a, b) => {
864
+ return a.name === b.name && recordEqual(a.dependencies, b.dependencies) && recordEqual(a.peerDependencies, b.peerDependencies) && recordEqual(a.devDependencies, b.devDependencies);
865
+ };
866
+ var isSubgraph = (a, b) => {
867
+ for (const [key, nodeB] of b) {
868
+ const nodeA = a.get(key);
869
+ if (!nodeA) {
870
+ return false;
871
+ }
872
+ if (!isPackageDataLooseEqual(nodeA.packageData, nodeB.packageData)) {
873
+ return false;
874
+ }
875
+ for (const dep of nodeB.pointsToPackages) {
876
+ if (!nodeA.pointsToPackages.has(dep)) {
877
+ return false;
878
+ }
879
+ }
880
+ }
881
+ return true;
882
+ };
883
+
884
+ // src/lib/watch/types/change-action.ts
885
+ var ChangeAction = {
886
+ Ignore: "Ignore",
887
+ Partial: "Partial",
888
+ Restart: "Restart"
889
+ };
890
+
891
+ // src/lib/watch/types/exit-state.ts
892
+ var ExitState = {
893
+ Completed: "Completed",
894
+ Errored: "Errored",
895
+ Cancelled: "Cancelled"
896
+ };
897
+
898
+ // src/lib/watch/types/run-async-type.ts
899
+ var RunAsyncType = {
900
+ Sequential: "Sequential",
901
+ Parallel: "Parallel"
902
+ };
903
+
904
+ // src/lib/watch/functions/filter-files-modified-since.ts
905
+ var filterFilesModifiedSince = (paths, sinceMs) => {
906
+ return paths.filter((path) => {
907
+ const changedFile = Bun.file(path);
908
+ return changedFile.lastModified >= sinceMs;
909
+ });
910
+ };
911
+
912
+ // src/lib/watch/functions/normalise-watched-file-path.ts
913
+ import {
914
+ basename as basename3,
915
+ dirname as dirname5,
916
+ join as join3
917
+ } from "path";
918
+ var removeTrailing = [
919
+ "~",
920
+ ".tmp",
921
+ ".temp",
922
+ ".bak",
923
+ ".backup",
924
+ ".swp",
925
+ ".swo",
926
+ ".swn"
927
+ ];
928
+ var normaliseWatchedFilePath = (filePath) => {
929
+ const dir = dirname5(filePath);
930
+ let base = basename3(filePath);
931
+ for (const trailing of removeTrailing) {
932
+ if (base.toLowerCase().endsWith(trailing)) {
933
+ const sliceStart = base.startsWith(".") ? 1 : 0;
934
+ base = base.slice(sliceStart, -trailing.length);
935
+ break;
936
+ }
937
+ }
938
+ return join3(dir, base);
939
+ };
940
+
941
+ // src/lib/watch/functions/run-async.ts
942
+ var runAsync = async (type, tasks) => {
943
+ if (type === RunAsyncType.Parallel) {
944
+ return Promise.all(tasks.map((task) => task()));
945
+ }
946
+ const outputs = [];
947
+ for (const task of tasks) {
948
+ const result = await task();
949
+ outputs.push(result);
950
+ }
951
+ return outputs;
952
+ };
953
+
954
+ // src/lib/watch/functions/run-subprocess.ts
955
+ import {
956
+ dim as dim8,
957
+ red as red2
958
+ } from "yoctocolors";
959
+
960
+ // src/lib/watch/types/std-type.ts
961
+ var StdType = {
962
+ Out: "Out",
963
+ Err: "Err"
964
+ };
965
+
966
+ // src/lib/watch/functions/get-commands-for-shell.ts
967
+ var getCommandsForShell = (shellCommand) => {
968
+ const isWindows = process.platform === "win32";
969
+ if (!isWindows) {
970
+ return [
971
+ "sh",
972
+ "-c",
973
+ `"${shellCommand}"`
974
+ ];
975
+ }
976
+ const shell = Bun.which("pwsh") ? "pwsh" : Bun.which("powershell") ? "powershell" : "cmd";
977
+ if (shell === "cmd") {
978
+ return [
979
+ shell,
980
+ "/c",
981
+ `"${shellCommand}"`
982
+ ];
983
+ }
984
+ const encodedCommand = Buffer.from(shellCommand, "utf16le").toString("base64");
985
+ return [
986
+ shell,
987
+ "-EncodedCommand",
988
+ encodedCommand
989
+ ];
990
+ };
991
+
992
+ // src/lib/watch/functions/get-line-buffered-writer.ts
993
+ import { identity } from "@package-pal/util";
994
+ var getLineBufferedWriter = (prefix, style = identity, write = process.stdout.write.bind(process.stdout)) => {
995
+ let buffer = "";
996
+ return (chunk) => {
997
+ buffer += chunk;
998
+ const lines = buffer.split(`
999
+ `);
1000
+ buffer = lines.pop() ?? "";
1001
+ for (const line of lines) {
1002
+ write(`${prefix}${style(line)}
1003
+ `);
1004
+ }
1005
+ };
1006
+ };
1007
+
1008
+ // src/lib/watch/functions/read-stream.ts
1009
+ var readStream = async (stream, use) => {
1010
+ if (!stream) {
1011
+ return;
1012
+ }
1013
+ const decoder = new TextDecoder;
1014
+ const reader = stream.getReader();
1015
+ let result = await reader.read();
1016
+ while (!result.done) {
1017
+ use(decoder.decode(result.value, { stream: true }));
1018
+ result = await reader.read();
1019
+ }
1020
+ const flush = decoder.decode();
1021
+ if (flush) {
1022
+ use(flush);
1023
+ }
1024
+ };
1025
+
1026
+ // src/lib/watch/functions/run-subprocess.ts
1027
+ var sigintCode = 130;
1028
+ var sigtermCode = 143;
1029
+ var sigkillCode = 137;
1030
+ var cancelCodes = new Set([
1031
+ sigintCode,
1032
+ sigtermCode,
1033
+ sigkillCode
1034
+ ]);
1035
+ var runSubprocess = async (opts) => {
1036
+ if (opts.signal?.aborted) {
1037
+ opts.logger.debug(dim8(`Skipped '${opts.debugName}' subprocess command; signal already cancelled.`));
1038
+ return ExitState.Cancelled;
1039
+ }
1040
+ const commands = getCommandsForShell(opts.shellCommand);
1041
+ const subprocess = Bun.spawn(commands, {
1042
+ cwd: opts.cwd,
1043
+ stdout: "pipe",
1044
+ stderr: "pipe",
1045
+ stdin: "ignore",
1046
+ signal: opts.signal
1047
+ });
1048
+ const pid = subprocess.pid.toString();
1049
+ const [readStdout, readStderr] = [{
1050
+ source: subprocess.stdout,
1051
+ type: StdType.Out,
1052
+ write: getLineBufferedWriter(dim8(`[OUT-${pid}] `))
1053
+ }, {
1054
+ source: subprocess.stderr,
1055
+ type: StdType.Err,
1056
+ write: getLineBufferedWriter(red2(dim8(`[ERR-${pid}] `)))
1057
+ }].map(({
1058
+ source,
1059
+ type,
1060
+ write
1061
+ }) => {
1062
+ return readStream(source, (chunk) => {
1063
+ write(chunk);
1064
+ if (opts.onStdChunk) {
1065
+ opts.onStdChunk(chunk, type);
1066
+ }
1067
+ });
1068
+ });
1069
+ const executedCommand = commands.join(" ");
1070
+ opts.logger.debug(dim8(`Started '${opts.debugName}' subprocess command '${opts.shellCommand}' (${executedCommand}) with PID ${pid}.`));
1071
+ const [
1072
+ ,
1073
+ ,
1074
+ exitState
1075
+ ] = await Promise.all([
1076
+ readStdout,
1077
+ readStderr,
1078
+ subprocess.exited.then((exitCode) => {
1079
+ if (cancelCodes.has(exitCode)) {
1080
+ opts.logger.debug(dim8(`Cancelled '${opts.debugName}' subprocess command; PID ${pid} exited.`));
1081
+ return ExitState.Cancelled;
1082
+ }
1083
+ if (exitCode !== 0) {
1084
+ opts.logger.error(red2(`'${opts.debugName}' command '${opts.shellCommand}' (${executedCommand}) with PID ${pid} failed with exit code ${exitCode.toString()}.`));
1085
+ return ExitState.Errored;
1086
+ }
1087
+ opts.logger.debug(dim8(`Completed '${opts.debugName}' subprocess command; PID ${pid} exited.`));
1088
+ return ExitState.Completed;
1089
+ })
1090
+ ]);
1091
+ return exitState;
1092
+ };
1093
+
1094
+ // src/lib/watch/functions/watch-package-changes.ts
1095
+ var fileModifiedThresholdMs = 5000;
1096
+ var lastProcessedSubgraph;
1097
+ var getChangeLogic = (packageGraphs, packageChanges, config, logger) => {
1098
+ const changedPackages = Array.from(packageChanges.keys());
1099
+ const changedFilePaths = Array.from(packageChanges.values()).flat();
1100
+ if (packageChanges.size) {
1101
+ logger.debug(dim9(`Changes detected in ${changedPackages.map((packageName) => `'${packageName}'`).join(", ")}.`));
1102
+ logger.debug(dim9(`Changed file paths: ${changedFilePaths.map((filePath) => `'${filePath}'`).join(", ")}.`));
1103
+ }
1104
+ const packageOrder = generateTopologicalSortedGroups(packageGraphs.dependents, logger);
1105
+ const packageProcessOrder = packageOrder.groups.toReversed().concat(packageOrder.circular);
1106
+ const packageRankings = generateTopologicalRanking(packageProcessOrder);
1107
+ const changedPackageSubgraph = extractSubgraph(packageGraphs.dependents, changedPackages);
1108
+ const changedPackageOrder = generateTopologicalSortedGroups(changedPackageSubgraph, logger);
1109
+ const changedPackageProcessOrder = changedPackageOrder.groups.toReversed().concat(changedPackageOrder.circular);
1110
+ const isSubgraphOfPrevious = !!lastProcessedSubgraph && isSubgraph(lastProcessedSubgraph, changedPackageSubgraph);
1111
+ const isDisjointFromPrevious = !lastProcessedSubgraph || !isSubgraphOfPrevious && isDisjoint(lastProcessedSubgraph, changedPackageSubgraph);
1112
+ const isRankedGreaterThanOrEqualToPrevious = !!lastProcessedSubgraph && !isSubgraphOfPrevious && isRankedGreaterThanOrEqual(lastProcessedSubgraph, changedPackageSubgraph, packageRankings);
1113
+ logger.debug(dim9(`Changes are subgraph of previous: ${isSubgraphOfPrevious.toString()}.`));
1114
+ logger.debug(dim9(`Changes are disjoint from previous: ${isDisjointFromPrevious.toString()}.`));
1115
+ logger.debug(dim9(`Changes are ranked greater than or equal to previous: ${isRankedGreaterThanOrEqualToPrevious.toString()}.`));
1116
+ let action = ChangeAction.Restart;
1117
+ if (!packageChanges.size) {
1118
+ action = ChangeAction.Ignore;
1119
+ } else if (!config.subprocess.partialProcessing) {
1120
+ action = ChangeAction.Restart;
1121
+ } else if (isSubgraphOfPrevious) {
1122
+ action = ChangeAction.Ignore;
1123
+ } else if (isDisjointFromPrevious || isRankedGreaterThanOrEqualToPrevious) {
1124
+ action = ChangeAction.Partial;
1125
+ }
1126
+ if (action === ChangeAction.Partial && isRankedGreaterThanOrEqualToPrevious) {
1127
+ const [prevMinRank] = generateTopologicalRankingRange(assertDefined9(lastProcessedSubgraph), packageRankings);
1128
+ for (let i = 0;i < changedPackageProcessOrder.length; i++) {
1129
+ changedPackageProcessOrder[i] = assertDefined9(changedPackageProcessOrder[i]).filter((packageName) => {
1130
+ const rank = packageRankings.get(packageName);
1131
+ return rank !== undefined && rank >= prevMinRank;
1132
+ });
1133
+ }
1134
+ }
1135
+ logger.debug(dim9(`Determined change action: ${action}.`));
1136
+ return {
1137
+ changedPackageProcessOrder,
1138
+ changedPackageSubgraph,
1139
+ action
1140
+ };
1141
+ };
1142
+ var onProcessPackage = async (packageGraphs, packageChanges, watchConfig, determineAbortController, logger) => {
1143
+ const {
1144
+ action,
1145
+ changedPackageProcessOrder,
1146
+ changedPackageSubgraph
1147
+ } = getChangeLogic(packageGraphs, packageChanges, watchConfig, logger);
1148
+ const controller = determineAbortController(action === ChangeAction.Restart);
1149
+ const onProcessFailure = () => {
1150
+ logger.debug(dim9("Aborting controller: process failed."));
1151
+ controller.abort();
1152
+ lastProcessedSubgraph = undefined;
1153
+ };
1154
+ if (action === ChangeAction.Ignore && packageChanges.size) {
1155
+ logger.info(`Changes detected; but were ignored due to 'partialProcessing: true'. Waiting for changes..`);
1156
+ return;
1157
+ }
1158
+ if (packageChanges.size) {
1159
+ logger.info(`Changes detected. ${action === ChangeAction.Restart ? "Restarting processing" : "Initiating partial processing"} ${watchConfig.subprocess.parallelProcessing ? "in parallel" : "sequentially"}.`);
1160
+ lastProcessedSubgraph = changedPackageSubgraph;
1161
+ }
1162
+ for (const group of changedPackageProcessOrder) {
1163
+ const {
1164
+ promise: longRunningParallelProcessReady,
1165
+ resolve: matchedParallelLongReadyRunningOutput
1166
+ } = getDeferredPromise();
1167
+ let matchedLongRunningOutputCount = 0;
1168
+ await runAsync(watchConfig.subprocess.parallelProcessing ? RunAsyncType.Parallel : RunAsyncType.Sequential, group.map((packageName) => async () => {
1169
+ const {
1170
+ promise: longRunningSequentialProcessReady,
1171
+ resolve: matchedSequentialLongRunningReadyOutput
1172
+ } = getDeferredPromise();
1173
+ const packageNode = assertDefined9(packageGraphs.dependencies.get(packageName));
1174
+ const changedPaths = packageChanges.get(packageName) ?? [];
1175
+ const processPackageProps = {
1176
+ name: packageName,
1177
+ dir: packageNode.packageData.dir,
1178
+ filePaths: changedPaths,
1179
+ totalChanges: packageChanges,
1180
+ totalProcessOrder: changedPackageProcessOrder,
1181
+ signal: controller.signal
1182
+ };
1183
+ const processPackageCwd = dirname6(packageNode.packageData.path);
1184
+ const beforeProcessPackageShellCommand = await watchConfig.hooks.onBeforeProcessPackage(processPackageProps);
1185
+ if (beforeProcessPackageShellCommand) {
1186
+ await runSubprocess({
1187
+ debugName: `before process ${packageName}`,
1188
+ shellCommand: beforeProcessPackageShellCommand,
1189
+ cwd: processPackageCwd,
1190
+ signal: controller.signal,
1191
+ logger
1192
+ });
1193
+ }
1194
+ const processPackageShellCommand = await watchConfig.hooks.onProcessPackage(processPackageProps);
1195
+ if (processPackageShellCommand) {
1196
+ const longRunningOutputReadyText = watchConfig.subprocess.matchLongRunningOutputAsReady;
1197
+ const longRunningOutputErroredText = watchConfig.subprocess.matchLongRunningOutputAsErrored;
1198
+ const readyMatcher = longRunningOutputReadyText ? getStringMatcher(longRunningOutputReadyText) : undefined;
1199
+ const erroredMatcher = longRunningOutputErroredText ? getStringMatcher(longRunningOutputErroredText) : undefined;
1200
+ let ready = false;
1201
+ let errored = false;
1202
+ const exit = runSubprocess({
1203
+ debugName: `process ${packageName}`,
1204
+ shellCommand: processPackageShellCommand,
1205
+ cwd: processPackageCwd,
1206
+ signal: controller.signal,
1207
+ logger,
1208
+ onStdChunk: (chunk) => {
1209
+ if (!ready && readyMatcher?.push(chunk).matched()) {
1210
+ ready = true;
1211
+ matchedLongRunningOutputCount++;
1212
+ logger.debug(`'${packageName}' (${matchedLongRunningOutputCount.toString()}/${group.length.toString()}) subprocess matched ready text '${assertDefined9(longRunningOutputReadyText)}'.`);
1213
+ if (!watchConfig.subprocess.parallelProcessing && matchedLongRunningOutputCount) {
1214
+ matchedSequentialLongRunningReadyOutput();
1215
+ }
1216
+ }
1217
+ if (!errored && erroredMatcher?.push(chunk).matched()) {
1218
+ logger.error(red3(`'${packageName}' subprocess matched error text '${assertDefined9(longRunningOutputErroredText)}'.`));
1219
+ errored = true;
1220
+ Promise.resolve(watchConfig.hooks.onProcessPackageError(processPackageProps)).then((processPackageErrorCommand) => {
1221
+ if (!processPackageErrorCommand) {
1222
+ onProcessFailure();
1223
+ return;
1224
+ }
1225
+ return runSubprocess({
1226
+ debugName: `after process ${packageName}`,
1227
+ shellCommand: processPackageErrorCommand,
1228
+ cwd: processPackageCwd,
1229
+ signal: controller.signal,
1230
+ logger
1231
+ });
1232
+ }).then((exitState) => {
1233
+ if (exitState === ExitState.Errored) {
1234
+ onProcessFailure();
1235
+ }
1236
+ });
1237
+ }
1238
+ if (watchConfig.subprocess.parallelProcessing && matchedLongRunningOutputCount === group.length) {
1239
+ matchedParallelLongReadyRunningOutput();
1240
+ }
1241
+ }
1242
+ }).then((exitState) => {
1243
+ if (exitState === ExitState.Errored) {
1244
+ onProcessFailure();
1245
+ }
1246
+ });
1247
+ await Promise.race([
1248
+ longRunningParallelProcessReady,
1249
+ longRunningSequentialProcessReady,
1250
+ exit
1251
+ ]);
1252
+ }
1253
+ const afterProcessPackageShellCommand = await watchConfig.hooks.onAfterProcessPackage(processPackageProps);
1254
+ if (afterProcessPackageShellCommand) {
1255
+ await runSubprocess({
1256
+ debugName: `after process ${packageName}`,
1257
+ shellCommand: afterProcessPackageShellCommand,
1258
+ cwd: processPackageCwd,
1259
+ signal: controller.signal,
1260
+ logger
1261
+ });
1262
+ }
1263
+ }));
1264
+ }
1265
+ if (packageChanges.size) {
1266
+ logger.info(`Processing ${controller.signal.aborted ? "cancelled due to new changes" : "completed"}.`);
1267
+ }
1268
+ const packagesReadyProps = {
1269
+ totalChanges: packageChanges,
1270
+ totalProcessOrder: changedPackageProcessOrder,
1271
+ signal: controller.signal
1272
+ };
1273
+ const beforePackagesReadyShellCommand = await watchConfig.hooks.onBeforePackagesReady(packagesReadyProps);
1274
+ if (beforePackagesReadyShellCommand) {
1275
+ await runSubprocess({
1276
+ debugName: "before packages ready",
1277
+ shellCommand: beforePackagesReadyShellCommand,
1278
+ signal: controller.signal,
1279
+ logger
1280
+ });
1281
+ }
1282
+ const packagesReadyShellCommand = await watchConfig.hooks.onPackagesReady(packagesReadyProps);
1283
+ if (packagesReadyShellCommand) {
1284
+ await runSubprocess({
1285
+ debugName: "packages ready",
1286
+ shellCommand: packagesReadyShellCommand,
1287
+ signal: controller.signal,
1288
+ logger
1289
+ });
1290
+ }
1291
+ const afterPackagesReadyShellCommand = await watchConfig.hooks.onAfterPackagesReady(packagesReadyProps);
1292
+ if (afterPackagesReadyShellCommand) {
1293
+ await runSubprocess({
1294
+ debugName: "after packages ready",
1295
+ shellCommand: afterPackagesReadyShellCommand,
1296
+ logger
1297
+ });
1298
+ }
1299
+ };
1300
+ var watchPackageChanges = (packageData, packageGraphs, watchConfig, logger) => {
1301
+ const dedupedRootPackageData = dedupeSharedPaths(packageData.map((packageData2) => packageData2.path), DedupePathsBy.Parent).map((packagePath) => assertDefined9(packageData.find((data) => data.path === packagePath)));
1302
+ logger.debug(dim9(`Starting ${dedupedRootPackageData.length.toString()} watchers for ${packageData.length.toString()} packages.`));
1303
+ let closed = false;
1304
+ let debounceTimeout;
1305
+ let startedDebounceMs;
1306
+ let abortController;
1307
+ const changedPackagePaths = new Map;
1308
+ const useController = (reset) => {
1309
+ if (abortController && (reset || abortController.signal.aborted)) {
1310
+ if (reset) {
1311
+ logger.debug(dim9("Aborting controller: reset for new packages."));
1312
+ abortController.abort();
1313
+ }
1314
+ abortController = undefined;
1315
+ }
1316
+ abortController ??= new AbortController;
1317
+ return abortController;
1318
+ };
1319
+ const onWatchEvent = (watchPath, packageName, _event, filePath, forceEmpty = false) => {
1320
+ if (!isDefined4(startedDebounceMs)) {
1321
+ startedDebounceMs = Date.now();
1322
+ }
1323
+ if (debounceTimeout) {
1324
+ clearTimeout(debounceTimeout);
1325
+ }
1326
+ if (packageName && watchPath && filePath) {
1327
+ const changedPath = join4(watchPath, normaliseWatchedFilePath(filePath));
1328
+ const existingPaths = changedPackagePaths.get(packageName);
1329
+ if (existingPaths) {
1330
+ existingPaths.add(changedPath);
1331
+ } else {
1332
+ changedPackagePaths.set(packageName, new Set([changedPath]));
1333
+ }
1334
+ }
1335
+ debounceTimeout = setTimeout(() => {
1336
+ if (closed) {
1337
+ return;
1338
+ }
1339
+ const packageChanges = new Map;
1340
+ for (const [packageName2, paths] of changedPackagePaths) {
1341
+ const processedPaths = filterFilesModifiedSince(dedupeSharedPaths(Array.from(paths), DedupePathsBy.Child).sort(), assertDefined9(startedDebounceMs) - fileModifiedThresholdMs);
1342
+ if (processedPaths.length) {
1343
+ packageChanges.set(packageName2, processedPaths);
1344
+ }
1345
+ }
1346
+ startedDebounceMs = undefined;
1347
+ changedPackagePaths.clear();
1348
+ if (!packageChanges.size && !forceEmpty) {
1349
+ return;
1350
+ }
1351
+ onProcessPackage(packageGraphs, packageChanges, watchConfig, (reset) => useController(reset), logger);
1352
+ }, watchConfig.debounceMs);
1353
+ };
1354
+ const watchers = dedupedRootPackageData.map(({
1355
+ name,
1356
+ path
1357
+ }) => {
1358
+ const watchPath = dirname6(path);
1359
+ return watch(watchPath, { recursive: true }, (event, filePath) => {
1360
+ onWatchEvent(watchPath, name, event, filePath);
1361
+ });
1362
+ });
1363
+ const closeWatchers = () => {
1364
+ watchers.forEach((watcher) => {
1365
+ watcher.close();
1366
+ });
1367
+ logger.debug(dim9("Aborting controller: closing watchers."));
1368
+ abortController?.abort();
1369
+ closed = true;
1370
+ };
1371
+ process.on("SIGINT", () => {
1372
+ logger.debug(dim9("Received SIGINT: closing watchers."));
1373
+ closeWatchers();
1374
+ process.exit(0);
1375
+ });
1376
+ onWatchEvent(undefined, undefined, undefined, null, true);
1377
+ return { close: () => {
1378
+ if (closed) {
1379
+ logger.debug(dim9("Watchers already closed."));
1380
+ return;
1381
+ }
1382
+ logger.debug(dim9("Closing watchers."));
1383
+ closeWatchers();
1384
+ } };
1385
+ };
1386
+
1387
+ // src/lib/api.ts
1388
+ var readPackagePalConfig = (options) => {
1389
+ checkBun();
1390
+ return loadConfig(options?.overrideConfigPath);
1391
+ };
1392
+ var readPackageData = async (options) => {
1393
+ checkBun();
1394
+ const packagePatterns = Array.isArray(options.config.packages) ? options.config.packages : [options.config.packages];
1395
+ return loadPackages(packagePatterns, options.config.logger);
1396
+ };
1397
+ var getPackageGraphs = (options) => {
1398
+ checkBun();
1399
+ return generateGraphs(options.packageData, options.config.logger);
1400
+ };
1401
+ var getPackageOrder = (options) => {
1402
+ checkBun();
1403
+ return generateTopologicalSortedGroups(options.packageGraphs.dependencies, options.config.logger);
1404
+ };
1405
+ var getPackageCircularDependencyPaths = (options) => {
1406
+ checkBun();
1407
+ return generatePackageCircularDependencyPaths(options.packageGraphs, options.packageOrder, options.config.logger);
1408
+ };
1409
+ var bumpPackageVersion = (options) => {
1410
+ checkBun();
1411
+ const exact = isDefined5(options.exact) ? options.exact : options.config.version.exact;
1412
+ const preId = isDefined5(options.preId) ? options.preId : options.config.version.preId;
1413
+ return updatePackageVersion(options.packageName, options.type, options.packageGraphs, preId, exact, options.config.logger);
1414
+ };
1415
+ var watchPackages = (options) => {
1416
+ checkBun();
1417
+ watchPackageChanges(options.packageData, options.packageGraphs, options.config.watch, options.config.logger);
1418
+ };
1419
+ export {
1420
+ watchPackages,
1421
+ readPackagePalConfig,
1422
+ readPackageData,
1423
+ getPackageOrder,
1424
+ getPackageGraphs,
1425
+ getPackageCircularDependencyPaths,
1426
+ bumpPackageVersion,
1427
+ BumpVersionType
1428
+ };
1429
+
1430
+ //# debugId=E0ED0EB8F615FC9B64756E2164756E21
1431
+ //# sourceMappingURL=index.js.map