@backstage/cli-node 0.2.19-next.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/config/nodeTransform.cjs +87 -0
  3. package/config/nodeTransformHooks.mjs +294 -0
  4. package/dist/auth/CliAuth.cjs.js +108 -0
  5. package/dist/auth/CliAuth.cjs.js.map +1 -0
  6. package/dist/auth/authIdentifiers.cjs.js +8 -0
  7. package/dist/auth/authIdentifiers.cjs.js.map +1 -0
  8. package/dist/auth/httpJson.cjs.js +21 -0
  9. package/dist/auth/httpJson.cjs.js.map +1 -0
  10. package/dist/auth/secretStore.cjs.js +96 -0
  11. package/dist/auth/secretStore.cjs.js.map +1 -0
  12. package/dist/auth/storage.cjs.js +161 -0
  13. package/dist/auth/storage.cjs.js.map +1 -0
  14. package/dist/cache/SuccessCache.cjs.js +85 -0
  15. package/dist/cache/SuccessCache.cjs.js.map +1 -0
  16. package/dist/cli-internal/src/InternalCliModule.cjs.js +11 -0
  17. package/dist/cli-internal/src/InternalCliModule.cjs.js.map +1 -0
  18. package/dist/cli-internal/src/InternalCommandNode.cjs.js +25 -0
  19. package/dist/cli-internal/src/InternalCommandNode.cjs.js.map +1 -0
  20. package/dist/cli-internal/src/knownPluginPackages.cjs.js +40 -0
  21. package/dist/cli-internal/src/knownPluginPackages.cjs.js.map +1 -0
  22. package/dist/cli-module/createCliModule.cjs.js +25 -0
  23. package/dist/cli-module/createCliModule.cjs.js.map +1 -0
  24. package/dist/cli-module/runCliModule.cjs.js +138 -0
  25. package/dist/cli-module/runCliModule.cjs.js.map +1 -0
  26. package/dist/index.cjs.js +14 -4
  27. package/dist/index.cjs.js.map +1 -1
  28. package/dist/index.d.ts +320 -57
  29. package/dist/monorepo/Lockfile.cjs.js +20 -2
  30. package/dist/monorepo/Lockfile.cjs.js.map +1 -1
  31. package/dist/monorepo/isMonoRepo.cjs.js +1 -1
  32. package/dist/monorepo/isMonoRepo.cjs.js.map +1 -1
  33. package/dist/opaque-internal/src/OpaqueType.cjs.js +105 -0
  34. package/dist/opaque-internal/src/OpaqueType.cjs.js.map +1 -0
  35. package/dist/roles/PackageRoles.cjs.js +22 -17
  36. package/dist/roles/PackageRoles.cjs.js.map +1 -1
  37. package/dist/yarn/yarnPlugin.cjs.js +49 -0
  38. package/dist/yarn/yarnPlugin.cjs.js.map +1 -0
  39. package/package.json +29 -9
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,310 @@
1
1
  import { Package } from '@manypkg/get-packages';
2
2
  import { JsonValue } from '@backstage/types';
3
3
 
4
+ /**
5
+ * Options for creating a {@link CliAuth} instance.
6
+ *
7
+ * @public
8
+ */
9
+ interface CliAuthCreateOptions {
10
+ /**
11
+ * An explicit instance name to resolve. When omitted the currently
12
+ * selected instance is used.
13
+ */
14
+ instanceName?: string;
15
+ }
16
+ /**
17
+ * Manages authentication state for Backstage CLI commands.
18
+ *
19
+ * Reads the currently selected (or explicitly named) auth instance from
20
+ * the on-disk instance store, transparently refreshes expired access
21
+ * tokens, and exposes helpers that other CLI modules need to talk to a
22
+ * Backstage backend.
23
+ *
24
+ * @public
25
+ */
26
+ declare class CliAuth {
27
+ #private;
28
+ /**
29
+ * Resolve the current auth instance and return a ready-to-use
30
+ * {@link CliAuth} object. Throws when no instance can be found.
31
+ */
32
+ static create(options?: CliAuthCreateOptions): Promise<CliAuth>;
33
+ private constructor();
34
+ /** Returns the name of the resolved auth instance. */
35
+ getInstanceName(): string;
36
+ /** Returns the base URL of the resolved auth instance. */
37
+ getBaseUrl(): string;
38
+ /**
39
+ * Returns a valid access token, refreshing it first if the current
40
+ * token is expired or about to expire.
41
+ */
42
+ getAccessToken(): Promise<string>;
43
+ /**
44
+ * Reads a per-instance metadata value previously stored by the
45
+ * auth module (e.g. `pluginSources`).
46
+ */
47
+ getMetadata(key: string): Promise<unknown>;
48
+ /**
49
+ * Writes a per-instance metadata value to the on-disk instance store.
50
+ */
51
+ setMetadata(key: string, value: unknown): Promise<void>;
52
+ }
53
+
54
+ /**
55
+ * A file-system-based cache that tracks successful operations by storing
56
+ * timestamped marker files.
57
+ *
58
+ * @public
59
+ */
60
+ declare class SuccessCache {
61
+ #private;
62
+ /**
63
+ * Trim any occurrences of the workspace root path from the input string. This
64
+ * is useful to ensure stable hashes that don't vary based on the workspace
65
+ * location.
66
+ */
67
+ static trimPaths(input: string): string;
68
+ static create(options: {
69
+ name: string;
70
+ basePath?: string;
71
+ }): SuccessCache;
72
+ private constructor();
73
+ read(): Promise<Set<string>>;
74
+ write(newEntries: Iterable<string>): Promise<void>;
75
+ }
76
+
77
+ /**
78
+ * The context provided to a CLI command at the time of execution.
79
+ *
80
+ * Contains the parsed arguments and metadata about the command being run.
81
+ *
82
+ * @public
83
+ */
84
+ interface CliCommandContext {
85
+ /**
86
+ * The remaining arguments passed to the command after the command path
87
+ * has been resolved. This includes both positional arguments and flags.
88
+ *
89
+ * For example, running `backstage-cli repo test --verbose src/` would
90
+ * result in `args` being `['--verbose', 'src/']`.
91
+ */
92
+ args: string[];
93
+ /**
94
+ * Metadata about the command being executed.
95
+ */
96
+ info: {
97
+ /**
98
+ * The full usage string of the command including the program name,
99
+ * for example `"backstage-cli repo test"`.
100
+ */
101
+ usage: string;
102
+ /**
103
+ * The name of the command as defined by its path,
104
+ * for example `"repo test"`.
105
+ */
106
+ name: string;
107
+ };
108
+ }
109
+ /**
110
+ * A command definition for a Backstage CLI plugin.
111
+ *
112
+ * Each command is identified by a `path` that determines its position in
113
+ * the command tree. For example, a path of `['repo', 'test']` registers
114
+ * the command as `backstage-cli repo test`.
115
+ *
116
+ * Commands can either provide an `execute` function directly, or use a
117
+ * `loader` for deferred loading of the implementation. The loader pattern
118
+ * is recommended for commands with heavy dependencies, as it avoids
119
+ * loading the implementation until the command is actually invoked.
120
+ *
121
+ * @public
122
+ */
123
+ interface CliCommand {
124
+ /**
125
+ * The path segments that define the command's position in the CLI tree.
126
+ * For example, `['repo', 'test']` maps to `backstage-cli repo test`.
127
+ */
128
+ path: string[];
129
+ /**
130
+ * A short description of the command, displayed in help output.
131
+ */
132
+ description: string;
133
+ /**
134
+ * If `true`, the command is deprecated and will be hidden from help output
135
+ * but can still be invoked.
136
+ */
137
+ deprecated?: boolean;
138
+ /**
139
+ * If `true`, the command is experimental and will be hidden from help
140
+ * output but can still be invoked.
141
+ */
142
+ experimental?: boolean;
143
+ /**
144
+ * The command implementation, either as a direct function or as a loader
145
+ * that returns the implementation as a default export. The loader form
146
+ * is useful for deferring heavy imports until the command is invoked.
147
+ *
148
+ * @example
149
+ * Direct execution:
150
+ * ```
151
+ * execute: async ({ args }) => { ... }
152
+ * ```
153
+ *
154
+ * @example
155
+ * Deferred loading:
156
+ * ```
157
+ * execute: { loader: () => import('./my-command') }
158
+ * ```
159
+ */
160
+ execute: ((context: CliCommandContext) => Promise<void>) | {
161
+ loader: () => Promise<{
162
+ default: (context: CliCommandContext) => Promise<void>;
163
+ }>;
164
+ };
165
+ }
166
+ /**
167
+ * An opaque representation of a Backstage CLI plugin, created
168
+ * using {@link createCliModule}.
169
+ *
170
+ * @public
171
+ */
172
+ interface CliModule {
173
+ readonly $$type: '@backstage/CliModule';
174
+ }
175
+
176
+ /**
177
+ * Creates a new CLI plugin that provides commands to the Backstage CLI.
178
+ *
179
+ * The `init` callback is invoked immediately at creation time and is used
180
+ * to register commands via the provided registry. The commands are then
181
+ * made available to the CLI host once the returned promise resolves.
182
+ *
183
+ * @example
184
+ * ```
185
+ * import { createCliModule } from '@backstage/cli-node';
186
+ * import packageJson from '../package.json';
187
+ *
188
+ * export default createCliModule({
189
+ * packageJson,
190
+ * init: async reg => {
191
+ * reg.addCommand({
192
+ * path: ['repo', 'test'],
193
+ * description: 'Run tests across the repository',
194
+ * execute: { loader: () => import('./commands/test') },
195
+ * });
196
+ * },
197
+ * });
198
+ * ```
199
+ *
200
+ * @public
201
+ */
202
+ declare function createCliModule(options: {
203
+ /** The `package.json` contents of the plugin package, used to identify the plugin. */
204
+ packageJson: {
205
+ name: string;
206
+ };
207
+ /**
208
+ * An initialization callback that registers commands with the CLI.
209
+ * Called immediately when the plugin is created.
210
+ */
211
+ init: (registry: {
212
+ /** Registers a new command with the CLI. */
213
+ addCommand: (command: CliCommand) => void;
214
+ }) => Promise<void>;
215
+ }): CliModule;
216
+
217
+ /**
218
+ * Runs a CLI module as a standalone program.
219
+ *
220
+ * This helper extracts the commands from a {@link CliModule} and exposes
221
+ * them as a fully functional CLI with help output and argument parsing.
222
+ * It is intended to be called from a module package's `bin` entry point
223
+ * so that the module can be executed directly without being wired into
224
+ * a larger CLI host.
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * #!/usr/bin/env node
229
+ * import { runCliModule } from '@backstage/cli-node';
230
+ * import cliModule from './index';
231
+ *
232
+ * runCliModule({
233
+ * module: cliModule,
234
+ * name: 'backstage-auth',
235
+ * version: require('../package.json').version,
236
+ * });
237
+ * ```
238
+ *
239
+ * @public
240
+ */
241
+ declare function runCliModule(options: {
242
+ /** The CLI module to run. */
243
+ module: CliModule;
244
+ /** The program name shown in help output and usage strings. */
245
+ name: string;
246
+ /** The version string shown when `--version` is passed. */
247
+ version?: string;
248
+ }): Promise<void>;
249
+
250
+ /**
251
+ * Options for {@link runConcurrentTasks}.
252
+ *
253
+ * @public
254
+ */
255
+ type ConcurrentTasksOptions<TItem> = {
256
+ /**
257
+ * Decides the number of concurrent workers by multiplying
258
+ * this with the configured concurrency.
259
+ *
260
+ * Defaults to 1.
261
+ */
262
+ concurrencyFactor?: number;
263
+ items: Iterable<TItem>;
264
+ worker: (item: TItem) => Promise<void>;
265
+ };
266
+ /**
267
+ * Runs items through a worker function concurrently across multiple async workers.
268
+ *
269
+ * @public
270
+ */
271
+ declare function runConcurrentTasks<TItem>(options: ConcurrentTasksOptions<TItem>): Promise<void>;
272
+
273
+ /**
274
+ * Options for {@link runWorkerQueueThreads}.
275
+ *
276
+ * @public
277
+ */
278
+ type WorkerQueueThreadsOptions<TItem, TResult, TContext> = {
279
+ /** The items to process */
280
+ items: Iterable<TItem>;
281
+ /**
282
+ * A function that will be called within each worker thread at startup,
283
+ * which should return the worker function that will be called for each item.
284
+ *
285
+ * This function must be defined as an arrow function or using the
286
+ * function keyword, and must be entirely self contained, not referencing
287
+ * any variables outside of its scope. This is because the function source
288
+ * is stringified and evaluated in the worker thread.
289
+ *
290
+ * To pass data to the worker, use the `context` option and `items`, but
291
+ * note that they are both copied by value into the worker thread, except for
292
+ * types that are explicitly shareable across threads, such as `SharedArrayBuffer`.
293
+ */
294
+ workerFactory: (context: TContext) => ((item: TItem) => Promise<TResult>) | Promise<(item: TItem) => Promise<TResult>>;
295
+ /** Context data supplied to each worker factory */
296
+ context?: TContext;
297
+ };
298
+ /**
299
+ * Spawns one or more worker threads using the `worker_threads` module.
300
+ * Each thread processes one item at a time from the provided `options.items`.
301
+ *
302
+ * @public
303
+ */
304
+ declare function runWorkerQueueThreads<TItem, TResult, TContext>(options: WorkerQueueThreadsOptions<TItem, TResult, TContext>): Promise<{
305
+ results: TResult[];
306
+ }>;
307
+
4
308
  /**
5
309
  * Utilities for working with git.
6
310
  *
@@ -19,7 +323,10 @@ declare class GitUtils {
19
323
  }
20
324
 
21
325
  /**
22
- * Returns try if the current project is a monorepo.
326
+ * Returns true if the current project is a monorepo.
327
+ *
328
+ * Uses a simple presence check on the `workspaces` field. Empty or invalid
329
+ * workspace config is treated as a monorepo; we do not validate patterns.
23
330
  *
24
331
  * @public
25
332
  */
@@ -30,7 +337,7 @@ declare function isMonoRepo(): Promise<boolean>;
30
337
  *
31
338
  * @public
32
339
  */
33
- type PackageRole = 'frontend' | 'backend' | 'cli' | 'web-library' | 'node-library' | 'common-library' | 'frontend-plugin' | 'frontend-plugin-module' | 'backend-plugin' | 'backend-plugin-module';
340
+ type PackageRole = 'frontend' | 'backend' | 'cli' | 'cli-module' | 'web-library' | 'node-library' | 'common-library' | 'frontend-plugin' | 'frontend-plugin-module' | 'backend-plugin' | 'backend-plugin-module';
34
341
  /**
35
342
  * A type of platform that a package can be built for.
36
343
  *
@@ -286,11 +593,16 @@ declare class Lockfile {
286
593
  static parse(content: string): Lockfile;
287
594
  private readonly packages;
288
595
  private readonly data;
596
+ private readonly legacy;
289
597
  private constructor();
290
598
  /** Returns the name of all packages available in the lockfile */
291
599
  get(name: string): LockfileQueryEntry[] | undefined;
292
600
  /** Get the entries for a single package in the lockfile */
293
601
  keys(): IterableIterator<string>;
602
+ /**
603
+ * Serialize the lockfile back to a string.
604
+ */
605
+ toString(): string;
294
606
  /**
295
607
  * Creates a simplified dependency graph from the lockfile data, where each
296
608
  * key is a package, and the value is a set of all packages that it depends on
@@ -309,62 +621,13 @@ declare class Lockfile {
309
621
  }
310
622
 
311
623
  /**
312
- * Options for {@link runConcurrentTasks}.
313
- *
314
- * @public
315
- */
316
- type ConcurrentTasksOptions<TItem> = {
317
- /**
318
- * Decides the number of concurrent workers by multiplying
319
- * this with the configured concurrency.
320
- *
321
- * Defaults to 1.
322
- */
323
- concurrencyFactor?: number;
324
- items: Iterable<TItem>;
325
- worker: (item: TItem) => Promise<void>;
326
- };
327
- /**
328
- * Runs items through a worker function concurrently across multiple async workers.
329
- *
330
- * @public
331
- */
332
- declare function runConcurrentTasks<TItem>(options: ConcurrentTasksOptions<TItem>): Promise<void>;
333
-
334
- /**
335
- * Options for {@link runWorkerQueueThreads}.
336
- *
337
- * @public
338
- */
339
- type WorkerQueueThreadsOptions<TItem, TResult, TContext> = {
340
- /** The items to process */
341
- items: Iterable<TItem>;
342
- /**
343
- * A function that will be called within each worker thread at startup,
344
- * which should return the worker function that will be called for each item.
345
- *
346
- * This function must be defined as an arrow function or using the
347
- * function keyword, and must be entirely self contained, not referencing
348
- * any variables outside of its scope. This is because the function source
349
- * is stringified and evaluated in the worker thread.
350
- *
351
- * To pass data to the worker, use the `context` option and `items`, but
352
- * note that they are both copied by value into the worker thread, except for
353
- * types that are explicitly shareable across threads, such as `SharedArrayBuffer`.
354
- */
355
- workerFactory: (context: TContext) => ((item: TItem) => Promise<TResult>) | Promise<(item: TItem) => Promise<TResult>>;
356
- /** Context data supplied to each worker factory */
357
- context?: TContext;
358
- };
359
- /**
360
- * Spawns one or more worker threads using the `worker_threads` module.
361
- * Each thread processes one item at a time from the provided `options.items`.
624
+ * Detects whether the Backstage Yarn plugin is installed in the given workspace directory.
362
625
  *
626
+ * @param workspaceDir - The workspace root directory to check. Defaults to the target root.
627
+ * @returns Promise resolving to true if the plugin is installed, false otherwise
363
628
  * @public
364
629
  */
365
- declare function runWorkerQueueThreads<TItem, TResult, TContext>(options: WorkerQueueThreadsOptions<TItem, TResult, TContext>): Promise<{
366
- results: TResult[];
367
- }>;
630
+ declare function hasBackstageYarnPlugin(workspaceDir?: string): Promise<boolean>;
368
631
 
369
- export { GitUtils, Lockfile, PackageGraph, PackageRoles, isMonoRepo, packageFeatureType, runConcurrentTasks, runWorkerQueueThreads };
370
- export type { BackstagePackage, BackstagePackageFeatureType, BackstagePackageJson, ConcurrentTasksOptions, LockfileDiff, LockfileDiffEntry, LockfileQueryEntry, PackageGraphNode, PackageOutputType, PackagePlatform, PackageRole, PackageRoleInfo, WorkerQueueThreadsOptions };
632
+ export { CliAuth, GitUtils, Lockfile, PackageGraph, PackageRoles, SuccessCache, createCliModule, hasBackstageYarnPlugin, isMonoRepo, packageFeatureType, runCliModule, runConcurrentTasks, runWorkerQueueThreads };
633
+ export type { BackstagePackage, BackstagePackageFeatureType, BackstagePackageJson, CliAuthCreateOptions, CliCommand, CliCommandContext, CliModule, ConcurrentTasksOptions, LockfileDiff, LockfileDiffEntry, LockfileQueryEntry, PackageGraphNode, PackageOutputType, PackagePlatform, PackageRole, PackageRoleInfo, WorkerQueueThreadsOptions };
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var parsers = require('@yarnpkg/parsers');
4
+ var lockfile = require('@yarnpkg/lockfile');
4
5
  var crypto = require('node:crypto');
5
6
  var fs = require('fs-extra');
6
7
 
@@ -10,6 +11,14 @@ var crypto__default = /*#__PURE__*/_interopDefaultCompat(crypto);
10
11
  var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
11
12
 
12
13
  const ENTRY_PATTERN = /^((?:@[^/]+\/)?[^@/]+)@(.+)$/;
14
+ const NEW_HEADER = `${[
15
+ `# This file is generated by running "yarn install" inside your project.
16
+ `,
17
+ `# Manual changes might be lost - proceed with caution!
18
+ `
19
+ ].join(``)}
20
+ `;
21
+ const LEGACY_REGEX = /^(#.*(\r?\n))*?#\s+yarn\s+lockfile\s+v1\r?\n/i;
13
22
  const SPECIAL_OBJECT_KEYS = [
14
23
  `__metadata`,
15
24
  `version`,
@@ -34,6 +43,7 @@ class Lockfile {
34
43
  * @public
35
44
  */
36
45
  static parse(content) {
46
+ const legacy = LEGACY_REGEX.test(content);
37
47
  let data;
38
48
  try {
39
49
  data = parsers.parseSyml(content);
@@ -62,13 +72,15 @@ class Lockfile {
62
72
  queries.push({ range, version: value.version, dataKey: key });
63
73
  }
64
74
  }
65
- return new Lockfile(packages, data);
75
+ return new Lockfile(packages, data, legacy);
66
76
  }
67
77
  packages;
68
78
  data;
69
- constructor(packages, data) {
79
+ legacy;
80
+ constructor(packages, data, legacy = false) {
70
81
  this.packages = packages;
71
82
  this.data = data;
83
+ this.legacy = legacy;
72
84
  }
73
85
  /** Returns the name of all packages available in the lockfile */
74
86
  get(name) {
@@ -78,6 +90,12 @@ class Lockfile {
78
90
  keys() {
79
91
  return this.packages.keys();
80
92
  }
93
+ /**
94
+ * Serialize the lockfile back to a string.
95
+ */
96
+ toString() {
97
+ return this.legacy ? lockfile.stringify(this.data) : NEW_HEADER + parsers.stringifySyml(this.data);
98
+ }
81
99
  /**
82
100
  * Creates a simplified dependency graph from the lockfile data, where each
83
101
  * key is a package, and the value is a set of all packages that it depends on
@@ -1 +1 @@
1
- {"version":3,"file":"Lockfile.cjs.js","sources":["../../src/monorepo/Lockfile.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { parseSyml } from '@yarnpkg/parsers';\nimport crypto from 'node:crypto';\nimport fs from 'fs-extra';\n\nconst ENTRY_PATTERN = /^((?:@[^/]+\\/)?[^@/]+)@(.+)$/;\n\n/** @internal */\ntype LockfileData = {\n [entry: string]: {\n version: string;\n resolved?: string;\n integrity?: string /* old */;\n checksum?: string /* new */;\n dependencies?: { [name: string]: string };\n peerDependencies?: { [name: string]: string };\n };\n};\n\n/**\n * A single entry in a {@link Lockfile}.\n *\n * @public\n */\nexport type LockfileQueryEntry = {\n range: string;\n version: string;\n dataKey: string;\n};\n\n/**\n * An entry for a single difference between two {@link Lockfile}s.\n *\n * @public\n */\nexport type LockfileDiffEntry = {\n name: string;\n range: string;\n};\n\n/**\n * Represents the difference between two {@link Lockfile}s.\n *\n * @public\n */\nexport type LockfileDiff = {\n added: LockfileDiffEntry[];\n changed: LockfileDiffEntry[];\n removed: LockfileDiffEntry[];\n};\n\n// these are special top level yarn keys.\n// https://github.com/yarnpkg/berry/blob/9bd61fbffb83d0b8166a9cc26bec3a58743aa453/packages/yarnpkg-parsers/sources/syml.ts#L9\nconst SPECIAL_OBJECT_KEYS = [\n `__metadata`,\n `version`,\n `resolution`,\n `dependencies`,\n `peerDependencies`,\n `dependenciesMeta`,\n `peerDependenciesMeta`,\n `binaries`,\n];\n\n/**\n * Represents a package manager lockfile.\n *\n * @public\n */\nexport class Lockfile {\n /**\n * Load a {@link Lockfile} from a file path.\n */\n static async load(path: string): Promise<Lockfile> {\n const lockfileContents = await fs.readFile(path, 'utf8');\n return Lockfile.parse(lockfileContents);\n }\n\n /**\n * Parse lockfile contents into a {@link Lockfile}.\n *\n * @public\n */\n static parse(content: string): Lockfile {\n let data: LockfileData;\n try {\n data = parseSyml(content);\n } catch (err) {\n throw new Error(`Failed yarn.lock parse, ${err}`);\n }\n\n const packages = new Map<string, LockfileQueryEntry[]>();\n\n for (const [key, value] of Object.entries(data)) {\n if (SPECIAL_OBJECT_KEYS.includes(key)) continue;\n\n const [, name, ranges] = ENTRY_PATTERN.exec(key) ?? [];\n if (!name) {\n throw new Error(`Failed to parse yarn.lock entry '${key}'`);\n }\n\n let queries = packages.get(name);\n if (!queries) {\n queries = [];\n packages.set(name, queries);\n }\n for (let range of ranges.split(/\\s*,\\s*/)) {\n if (range.startsWith(`${name}@`)) {\n range = range.slice(`${name}@`.length);\n }\n if (range.startsWith('npm:')) {\n range = range.slice('npm:'.length);\n }\n queries.push({ range, version: value.version, dataKey: key });\n }\n }\n\n return new Lockfile(packages, data);\n }\n\n private readonly packages: Map<string, LockfileQueryEntry[]>;\n private readonly data: LockfileData;\n\n private constructor(\n packages: Map<string, LockfileQueryEntry[]>,\n data: LockfileData,\n ) {\n this.packages = packages;\n this.data = data;\n }\n\n /** Returns the name of all packages available in the lockfile */\n get(name: string): LockfileQueryEntry[] | undefined {\n return this.packages.get(name);\n }\n\n /** Get the entries for a single package in the lockfile */\n keys(): IterableIterator<string> {\n return this.packages.keys();\n }\n\n /**\n * Creates a simplified dependency graph from the lockfile data, where each\n * key is a package, and the value is a set of all packages that it depends on\n * across all versions.\n */\n createSimplifiedDependencyGraph(): Map<string, Set<string>> {\n const graph = new Map<string, Set<string>>();\n\n for (const [name, entries] of this.packages) {\n const dependencies = new Set(\n entries.flatMap(e => {\n const data = this.data[e.dataKey];\n return [\n ...Object.keys(data?.dependencies ?? {}),\n ...Object.keys(data?.peerDependencies ?? {}),\n ];\n }),\n );\n graph.set(name, dependencies);\n }\n\n return graph;\n }\n\n /**\n * Diff with another lockfile, returning entries that have been\n * added, changed, and removed compared to the other lockfile.\n */\n diff(otherLockfile: Lockfile): LockfileDiff {\n const diff = {\n added: new Array<{ name: string; range: string }>(),\n changed: new Array<{ name: string; range: string }>(),\n removed: new Array<{ name: string; range: string }>(),\n };\n\n // Keeps track of packages that only exist in this lockfile\n const remainingOldNames = new Set(this.packages.keys());\n\n for (const [name, otherQueries] of otherLockfile.packages) {\n remainingOldNames.delete(name);\n\n const thisQueries = this.packages.get(name);\n // If the packages doesn't exist in this lockfile, add all entries\n if (!thisQueries) {\n diff.removed.push(...otherQueries.map(q => ({ name, range: q.range })));\n continue;\n }\n\n const remainingOldRanges = new Set(thisQueries.map(q => q.range));\n\n for (const otherQuery of otherQueries) {\n remainingOldRanges.delete(otherQuery.range);\n\n const thisQuery = thisQueries.find(q => q.range === otherQuery.range);\n if (!thisQuery) {\n diff.removed.push({ name, range: otherQuery.range });\n continue;\n }\n\n const otherPkg = otherLockfile.data[otherQuery.dataKey];\n const thisPkg = this.data[thisQuery.dataKey];\n if (otherPkg && thisPkg) {\n const thisCheck = thisPkg.integrity || thisPkg.checksum;\n const otherCheck = otherPkg.integrity || otherPkg.checksum;\n if (thisCheck !== otherCheck) {\n diff.changed.push({ name, range: otherQuery.range });\n }\n }\n }\n\n for (const thisRange of remainingOldRanges) {\n diff.added.push({ name, range: thisRange });\n }\n }\n\n for (const name of remainingOldNames) {\n const queries = this.packages.get(name) ?? [];\n diff.added.push(...queries.map(q => ({ name, range: q.range })));\n }\n\n return diff;\n }\n\n /**\n * Generates a sha1 hex hash of the dependency graph for a package.\n */\n getDependencyTreeHash(startName: string): string {\n if (!this.packages.has(startName)) {\n throw new Error(`Package '${startName}' not found in lockfile`);\n }\n\n const hash = crypto.createHash('sha1');\n\n const queue = [startName];\n const seen = new Set<string>();\n\n while (queue.length > 0) {\n const name = queue.pop()!;\n\n if (seen.has(name)) {\n continue;\n }\n seen.add(name);\n\n const entries = this.packages.get(name);\n if (!entries) {\n continue; // In case of missing optional peer dependencies\n }\n\n hash.update(`pkg:${name}`);\n hash.update('\\0');\n\n // TODO(Rugvip): This uses the same simplified lookup as createSimplifiedDependencyGraph()\n // we could match version queries to make the resulting tree a bit smaller.\n const deps = new Array<string>();\n for (const entry of entries) {\n // We're not being particular about stable ordering here. If the lockfile ordering changes, so will likely hash.\n hash.update(entry.version);\n\n const data = this.data[entry.dataKey];\n if (!data) {\n continue;\n }\n\n const checksum = data.checksum || data.integrity;\n if (checksum) {\n hash.update('#');\n hash.update(checksum);\n }\n\n hash.update(' ');\n\n deps.push(...Object.keys(data.dependencies ?? {}));\n deps.push(...Object.keys(data.peerDependencies ?? {}));\n }\n\n queue.push(...new Set(deps));\n }\n\n return hash.digest('hex');\n }\n}\n"],"names":["fs","parseSyml","crypto"],"mappings":"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"}
1
+ {"version":3,"file":"Lockfile.cjs.js","sources":["../../src/monorepo/Lockfile.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { parseSyml, stringifySyml } from '@yarnpkg/parsers';\nimport { stringify as legacyStringifyLockfile } from '@yarnpkg/lockfile';\nimport crypto from 'node:crypto';\nimport fs from 'fs-extra';\n\nconst ENTRY_PATTERN = /^((?:@[^/]+\\/)?[^@/]+)@(.+)$/;\n\n// https://github.com/yarnpkg/berry/blob/0c5974f193a9397630e9aee2b3876cca62611149/packages/yarnpkg-core/sources/Project.ts#L1741-L1746\nconst NEW_HEADER = `${[\n `# This file is generated by running \"yarn install\" inside your project.\\n`,\n `# Manual changes might be lost - proceed with caution!\\n`,\n].join(``)}\\n`;\n\n// https://github.com/yarnpkg/berry/blob/0c5974f193a9397630e9aee2b3876cca62611149/packages/yarnpkg-parsers/sources/syml.ts#L136\nconst LEGACY_REGEX = /^(#.*(\\r?\\n))*?#\\s+yarn\\s+lockfile\\s+v1\\r?\\n/i;\n\n/** @internal */\ntype LockfileData = {\n [entry: string]: {\n version: string;\n resolved?: string;\n integrity?: string /* old */;\n checksum?: string /* new */;\n dependencies?: { [name: string]: string };\n peerDependencies?: { [name: string]: string };\n };\n};\n\n/**\n * A single entry in a {@link Lockfile}.\n *\n * @public\n */\nexport type LockfileQueryEntry = {\n range: string;\n version: string;\n dataKey: string;\n};\n\n/**\n * An entry for a single difference between two {@link Lockfile}s.\n *\n * @public\n */\nexport type LockfileDiffEntry = {\n name: string;\n range: string;\n};\n\n/**\n * Represents the difference between two {@link Lockfile}s.\n *\n * @public\n */\nexport type LockfileDiff = {\n added: LockfileDiffEntry[];\n changed: LockfileDiffEntry[];\n removed: LockfileDiffEntry[];\n};\n\n// these are special top level yarn keys.\n// https://github.com/yarnpkg/berry/blob/9bd61fbffb83d0b8166a9cc26bec3a58743aa453/packages/yarnpkg-parsers/sources/syml.ts#L9\nconst SPECIAL_OBJECT_KEYS = [\n `__metadata`,\n `version`,\n `resolution`,\n `dependencies`,\n `peerDependencies`,\n `dependenciesMeta`,\n `peerDependenciesMeta`,\n `binaries`,\n];\n\n/**\n * Represents a package manager lockfile.\n *\n * @public\n */\nexport class Lockfile {\n /**\n * Load a {@link Lockfile} from a file path.\n */\n static async load(path: string): Promise<Lockfile> {\n const lockfileContents = await fs.readFile(path, 'utf8');\n return Lockfile.parse(lockfileContents);\n }\n\n /**\n * Parse lockfile contents into a {@link Lockfile}.\n *\n * @public\n */\n static parse(content: string): Lockfile {\n const legacy = LEGACY_REGEX.test(content);\n\n let data: LockfileData;\n try {\n data = parseSyml(content);\n } catch (err) {\n throw new Error(`Failed yarn.lock parse, ${err}`);\n }\n\n const packages = new Map<string, LockfileQueryEntry[]>();\n\n for (const [key, value] of Object.entries(data)) {\n if (SPECIAL_OBJECT_KEYS.includes(key)) continue;\n\n const [, name, ranges] = ENTRY_PATTERN.exec(key) ?? [];\n if (!name) {\n throw new Error(`Failed to parse yarn.lock entry '${key}'`);\n }\n\n let queries = packages.get(name);\n if (!queries) {\n queries = [];\n packages.set(name, queries);\n }\n for (let range of ranges.split(/\\s*,\\s*/)) {\n if (range.startsWith(`${name}@`)) {\n range = range.slice(`${name}@`.length);\n }\n if (range.startsWith('npm:')) {\n range = range.slice('npm:'.length);\n }\n queries.push({ range, version: value.version, dataKey: key });\n }\n }\n\n return new Lockfile(packages, data, legacy);\n }\n\n private readonly packages: Map<string, LockfileQueryEntry[]>;\n private readonly data: LockfileData;\n private readonly legacy: boolean;\n\n private constructor(\n packages: Map<string, LockfileQueryEntry[]>,\n data: LockfileData,\n legacy: boolean = false,\n ) {\n this.packages = packages;\n this.data = data;\n this.legacy = legacy;\n }\n\n /** Returns the name of all packages available in the lockfile */\n get(name: string): LockfileQueryEntry[] | undefined {\n return this.packages.get(name);\n }\n\n /** Get the entries for a single package in the lockfile */\n keys(): IterableIterator<string> {\n return this.packages.keys();\n }\n\n /**\n * Serialize the lockfile back to a string.\n */\n toString(): string {\n return this.legacy\n ? legacyStringifyLockfile(this.data)\n : NEW_HEADER + stringifySyml(this.data);\n }\n\n /**\n * Creates a simplified dependency graph from the lockfile data, where each\n * key is a package, and the value is a set of all packages that it depends on\n * across all versions.\n */\n createSimplifiedDependencyGraph(): Map<string, Set<string>> {\n const graph = new Map<string, Set<string>>();\n\n for (const [name, entries] of this.packages) {\n const dependencies = new Set(\n entries.flatMap(e => {\n const data = this.data[e.dataKey];\n return [\n ...Object.keys(data?.dependencies ?? {}),\n ...Object.keys(data?.peerDependencies ?? {}),\n ];\n }),\n );\n graph.set(name, dependencies);\n }\n\n return graph;\n }\n\n /**\n * Diff with another lockfile, returning entries that have been\n * added, changed, and removed compared to the other lockfile.\n */\n diff(otherLockfile: Lockfile): LockfileDiff {\n const diff = {\n added: new Array<{ name: string; range: string }>(),\n changed: new Array<{ name: string; range: string }>(),\n removed: new Array<{ name: string; range: string }>(),\n };\n\n // Keeps track of packages that only exist in this lockfile\n const remainingOldNames = new Set(this.packages.keys());\n\n for (const [name, otherQueries] of otherLockfile.packages) {\n remainingOldNames.delete(name);\n\n const thisQueries = this.packages.get(name);\n // If the packages doesn't exist in this lockfile, add all entries\n if (!thisQueries) {\n diff.removed.push(...otherQueries.map(q => ({ name, range: q.range })));\n continue;\n }\n\n const remainingOldRanges = new Set(thisQueries.map(q => q.range));\n\n for (const otherQuery of otherQueries) {\n remainingOldRanges.delete(otherQuery.range);\n\n const thisQuery = thisQueries.find(q => q.range === otherQuery.range);\n if (!thisQuery) {\n diff.removed.push({ name, range: otherQuery.range });\n continue;\n }\n\n const otherPkg = otherLockfile.data[otherQuery.dataKey];\n const thisPkg = this.data[thisQuery.dataKey];\n if (otherPkg && thisPkg) {\n const thisCheck = thisPkg.integrity || thisPkg.checksum;\n const otherCheck = otherPkg.integrity || otherPkg.checksum;\n if (thisCheck !== otherCheck) {\n diff.changed.push({ name, range: otherQuery.range });\n }\n }\n }\n\n for (const thisRange of remainingOldRanges) {\n diff.added.push({ name, range: thisRange });\n }\n }\n\n for (const name of remainingOldNames) {\n const queries = this.packages.get(name) ?? [];\n diff.added.push(...queries.map(q => ({ name, range: q.range })));\n }\n\n return diff;\n }\n\n /**\n * Generates a sha1 hex hash of the dependency graph for a package.\n */\n getDependencyTreeHash(startName: string): string {\n if (!this.packages.has(startName)) {\n throw new Error(`Package '${startName}' not found in lockfile`);\n }\n\n const hash = crypto.createHash('sha1');\n\n const queue = [startName];\n const seen = new Set<string>();\n\n while (queue.length > 0) {\n const name = queue.pop()!;\n\n if (seen.has(name)) {\n continue;\n }\n seen.add(name);\n\n const entries = this.packages.get(name);\n if (!entries) {\n continue; // In case of missing optional peer dependencies\n }\n\n hash.update(`pkg:${name}`);\n hash.update('\\0');\n\n // TODO(Rugvip): This uses the same simplified lookup as createSimplifiedDependencyGraph()\n // we could match version queries to make the resulting tree a bit smaller.\n const deps = new Array<string>();\n for (const entry of entries) {\n // We're not being particular about stable ordering here. If the lockfile ordering changes, so will likely hash.\n hash.update(entry.version);\n\n const data = this.data[entry.dataKey];\n if (!data) {\n continue;\n }\n\n const checksum = data.checksum || data.integrity;\n if (checksum) {\n hash.update('#');\n hash.update(checksum);\n }\n\n hash.update(' ');\n\n deps.push(...Object.keys(data.dependencies ?? {}));\n deps.push(...Object.keys(data.peerDependencies ?? {}));\n }\n\n queue.push(...new Set(deps));\n }\n\n return hash.digest('hex');\n }\n}\n"],"names":["fs","parseSyml","legacyStringifyLockfile","stringifySyml","crypto"],"mappings":"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"}
@@ -11,7 +11,7 @@ async function isMonoRepo() {
11
11
  const rootPackageJsonPath = cliCommon.targetPaths.resolveRoot("package.json");
12
12
  try {
13
13
  const pkg = await fs__default.default.readJson(rootPackageJsonPath);
14
- return Boolean(pkg?.workspaces?.packages);
14
+ return Boolean(pkg?.workspaces);
15
15
  } catch (error) {
16
16
  return false;
17
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"isMonoRepo.cjs.js","sources":["../../src/monorepo/isMonoRepo.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { targetPaths } from '@backstage/cli-common';\nimport fs from 'fs-extra';\n\n/**\n * Returns try if the current project is a monorepo.\n *\n * @public\n */\nexport async function isMonoRepo(): Promise<boolean> {\n const rootPackageJsonPath = targetPaths.resolveRoot('package.json');\n try {\n const pkg = await fs.readJson(rootPackageJsonPath);\n return Boolean(pkg?.workspaces?.packages);\n } catch (error) {\n return false;\n }\n}\n"],"names":["targetPaths","fs"],"mappings":";;;;;;;;;AAwBA,eAAsB,UAAA,GAA+B;AACnD,EAAA,MAAM,mBAAA,GAAsBA,qBAAA,CAAY,WAAA,CAAY,cAAc,CAAA;AAClE,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAMC,mBAAA,CAAG,QAAA,CAAS,mBAAmB,CAAA;AACjD,IAAA,OAAO,OAAA,CAAQ,GAAA,EAAK,UAAA,EAAY,QAAQ,CAAA;AAAA,EAC1C,SAAS,KAAA,EAAO;AACd,IAAA,OAAO,KAAA;AAAA,EACT;AACF;;;;"}
1
+ {"version":3,"file":"isMonoRepo.cjs.js","sources":["../../src/monorepo/isMonoRepo.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { targetPaths } from '@backstage/cli-common';\nimport fs from 'fs-extra';\n\n/**\n * Returns true if the current project is a monorepo.\n *\n * Uses a simple presence check on the `workspaces` field. Empty or invalid\n * workspace config is treated as a monorepo; we do not validate patterns.\n *\n * @public\n */\nexport async function isMonoRepo(): Promise<boolean> {\n const rootPackageJsonPath = targetPaths.resolveRoot('package.json');\n try {\n const pkg = await fs.readJson(rootPackageJsonPath);\n return Boolean(pkg?.workspaces);\n } catch (error) {\n return false;\n }\n}\n"],"names":["targetPaths","fs"],"mappings":";;;;;;;;;AA2BA,eAAsB,UAAA,GAA+B;AACnD,EAAA,MAAM,mBAAA,GAAsBA,qBAAA,CAAY,WAAA,CAAY,cAAc,CAAA;AAClE,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAMC,mBAAA,CAAG,QAAA,CAAS,mBAAmB,CAAA;AACjD,IAAA,OAAO,OAAA,CAAQ,KAAK,UAAU,CAAA;AAAA,EAChC,SAAS,KAAA,EAAO;AACd,IAAA,OAAO,KAAA;AAAA,EACT;AACF;;;;"}
@@ -0,0 +1,105 @@
1
+ 'use strict';
2
+
3
+ class OpaqueType {
4
+ /**
5
+ * Creates a new opaque type.
6
+ *
7
+ * @param options.type The type identifier of the opaque type
8
+ * @param options.versions The available versions of the opaque type
9
+ * @returns A new opaque type helper
10
+ */
11
+ static create(options) {
12
+ return new OpaqueType(options.type, new Set(options.versions));
13
+ }
14
+ #type;
15
+ #versions;
16
+ constructor(type, versions) {
17
+ this.#type = type;
18
+ this.#versions = versions;
19
+ }
20
+ /**
21
+ * The internal version of the opaque type, used like this: `typeof MyOpaqueType.TPublic`
22
+ *
23
+ * @remarks
24
+ *
25
+ * This property is only useful for type checking, its runtime value is `undefined`.
26
+ */
27
+ TPublic = void 0;
28
+ /**
29
+ * The internal version of the opaque type, used like this: `typeof MyOpaqueType.TInternal`
30
+ *
31
+ * @remarks
32
+ *
33
+ * This property is only useful for type checking, its runtime value is `undefined`.
34
+ */
35
+ TInternal = void 0;
36
+ /**
37
+ * @param value Input value expected to be an instance of this opaque type
38
+ * @returns True if the value matches this opaque type
39
+ */
40
+ isType = (value) => {
41
+ return this.#isThisInternalType(value);
42
+ };
43
+ /**
44
+ * @param value Input value expected to be an instance of this opaque type
45
+ * @throws If the value is not an instance of this opaque type or is of an unsupported version
46
+ * @returns The internal version of the opaque type
47
+ */
48
+ toInternal = (value) => {
49
+ if (!this.#isThisInternalType(value)) {
50
+ throw new TypeError(
51
+ `Invalid opaque type, expected '${this.#type}', but got '${this.#stringifyUnknown(value)}'`
52
+ );
53
+ }
54
+ if (!this.#versions.has(value.version)) {
55
+ const versions = Array.from(this.#versions).map(this.#stringifyVersion);
56
+ if (versions.length > 1) {
57
+ versions[versions.length - 1] = `or ${versions[versions.length - 1]}`;
58
+ }
59
+ const expected = versions.length > 2 ? versions.join(", ") : versions.join(" ");
60
+ throw new TypeError(
61
+ `Invalid opaque type instance, got version ${this.#stringifyVersion(
62
+ value.version
63
+ )}, expected ${expected}`
64
+ );
65
+ }
66
+ return value;
67
+ };
68
+ /**
69
+ * Creates an instance of the opaque type, returning the public type.
70
+ *
71
+ * @param version The version of the instance to create
72
+ * @param value The remaining public and internal properties of the instance
73
+ * @returns An instance of the opaque type
74
+ */
75
+ createInstance(version, props) {
76
+ return Object.assign(props, {
77
+ $$type: this.#type,
78
+ ...version && { version }
79
+ });
80
+ }
81
+ #isThisInternalType(value) {
82
+ if (value === null || typeof value !== "object") {
83
+ return false;
84
+ }
85
+ return value.$$type === this.#type;
86
+ }
87
+ #stringifyUnknown(value) {
88
+ if (typeof value !== "object") {
89
+ return `<${typeof value}>`;
90
+ }
91
+ if (value === null) {
92
+ return "<null>";
93
+ }
94
+ if ("$$type" in value) {
95
+ return String(value.$$type);
96
+ }
97
+ return String(value);
98
+ }
99
+ #stringifyVersion = (version) => {
100
+ return version ? `'${version}'` : "undefined";
101
+ };
102
+ }
103
+
104
+ exports.OpaqueType = OpaqueType;
105
+ //# sourceMappingURL=OpaqueType.cjs.js.map