@contember/cli-common 1.3.0-alpha.8 → 1.3.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/src/application/Argument.d.ts +1 -1
  2. package/dist/src/application/Argument.d.ts.map +1 -1
  3. package/dist/src/application/CommandManager.d.ts +1 -1
  4. package/dist/src/application/CommandManager.d.ts.map +1 -1
  5. package/dist/src/application/Input.d.ts +2 -2
  6. package/dist/src/application/Input.d.ts.map +1 -1
  7. package/dist/src/application/Option.d.ts +1 -1
  8. package/dist/src/application/Option.d.ts.map +1 -1
  9. package/dist/src/application/Option.js +1 -1
  10. package/dist/src/application/Option.js.map +1 -1
  11. package/dist/src/application/UsageFormatter.d.ts +2 -2
  12. package/dist/src/application/UsageFormatter.d.ts.map +1 -1
  13. package/dist/src/index.d.ts +1 -0
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +1 -0
  16. package/dist/src/index.js.map +1 -1
  17. package/dist/src/npm/FsManager.d.ts +9 -0
  18. package/dist/src/npm/FsManager.d.ts.map +1 -0
  19. package/dist/src/npm/FsManager.js +41 -0
  20. package/dist/src/npm/FsManager.js.map +1 -0
  21. package/dist/src/npm/Package.d.ts +8 -0
  22. package/dist/src/npm/Package.d.ts.map +1 -0
  23. package/dist/src/npm/Package.js +12 -0
  24. package/dist/src/npm/Package.js.map +1 -0
  25. package/dist/src/npm/PackageJson.d.ts +13 -0
  26. package/dist/src/npm/PackageJson.d.ts.map +1 -0
  27. package/dist/src/npm/PackageJson.js +3 -0
  28. package/dist/src/npm/PackageJson.js.map +1 -0
  29. package/dist/src/npm/PackageWorkspace.d.ts +32 -0
  30. package/dist/src/npm/PackageWorkspace.d.ts.map +1 -0
  31. package/dist/src/npm/PackageWorkspace.js +110 -0
  32. package/dist/src/npm/PackageWorkspace.js.map +1 -0
  33. package/dist/src/npm/index.d.ts +2 -0
  34. package/dist/src/npm/index.d.ts.map +1 -0
  35. package/dist/src/npm/index.js +18 -0
  36. package/dist/src/npm/index.js.map +1 -0
  37. package/dist/src/npm/packageManagers/Npm.d.ts +22 -0
  38. package/dist/src/npm/packageManagers/Npm.d.ts.map +1 -0
  39. package/dist/src/npm/packageManagers/Npm.js +35 -0
  40. package/dist/src/npm/packageManagers/Npm.js.map +1 -0
  41. package/dist/src/npm/packageManagers/PackageManager.d.ts +18 -0
  42. package/dist/src/npm/packageManagers/PackageManager.d.ts.map +1 -0
  43. package/dist/src/npm/packageManagers/PackageManager.js +3 -0
  44. package/dist/src/npm/packageManagers/PackageManager.js.map +1 -0
  45. package/dist/src/npm/packageManagers/PackageManagerHelpers.d.ts +15 -0
  46. package/dist/src/npm/packageManagers/PackageManagerHelpers.d.ts.map +1 -0
  47. package/dist/src/npm/packageManagers/PackageManagerHelpers.js +30 -0
  48. package/dist/src/npm/packageManagers/PackageManagerHelpers.js.map +1 -0
  49. package/dist/src/npm/packageManagers/Pnpm.d.ts +22 -0
  50. package/dist/src/npm/packageManagers/Pnpm.d.ts.map +1 -0
  51. package/dist/src/npm/packageManagers/Pnpm.js +37 -0
  52. package/dist/src/npm/packageManagers/Pnpm.js.map +1 -0
  53. package/dist/src/npm/packageManagers/Yarn.d.ts +22 -0
  54. package/dist/src/npm/packageManagers/Yarn.d.ts.map +1 -0
  55. package/dist/src/npm/packageManagers/Yarn.js +36 -0
  56. package/dist/src/npm/packageManagers/Yarn.js.map +1 -0
  57. package/dist/src/npm/packageManagers/YarnClassic.d.ts +22 -0
  58. package/dist/src/npm/packageManagers/YarnClassic.d.ts.map +1 -0
  59. package/dist/src/npm/packageManagers/YarnClassic.js +36 -0
  60. package/dist/src/npm/packageManagers/YarnClassic.js.map +1 -0
  61. package/dist/src/tsconfig.tsbuildinfo +1 -1
  62. package/dist/src/utils/PathMapping.d.ts +1 -1
  63. package/dist/src/utils/PathMapping.d.ts.map +1 -1
  64. package/dist/src/utils/Workspace.d.ts +5 -4
  65. package/dist/src/utils/Workspace.d.ts.map +1 -1
  66. package/dist/src/utils/Workspace.js +19 -6
  67. package/dist/src/utils/Workspace.js.map +1 -1
  68. package/dist/src/utils/commands.d.ts +21 -0
  69. package/dist/src/utils/commands.d.ts.map +1 -0
  70. package/dist/src/utils/commands.js +57 -0
  71. package/dist/src/utils/commands.js.map +1 -0
  72. package/dist/src/utils/json.d.ts +4 -4
  73. package/dist/src/utils/json.d.ts.map +1 -1
  74. package/dist/src/utils/version.d.ts +1 -1
  75. package/dist/src/utils/version.d.ts.map +1 -1
  76. package/dist/src/utils/version.js.map +1 -1
  77. package/package.json +3 -2
  78. package/src/.eslintrc.js +5 -0
  79. package/src/index.ts +1 -0
  80. package/src/npm/FsManager.ts +38 -0
  81. package/src/npm/Package.ts +10 -0
  82. package/src/npm/PackageJson.ts +11 -0
  83. package/src/npm/PackageWorkspace.ts +126 -0
  84. package/src/npm/index.ts +1 -0
  85. package/src/npm/packageManagers/Npm.ts +39 -0
  86. package/src/npm/packageManagers/PackageManager.ts +11 -0
  87. package/src/npm/packageManagers/PackageManagerHelpers.ts +35 -0
  88. package/src/npm/packageManagers/Pnpm.ts +42 -0
  89. package/src/npm/packageManagers/Yarn.ts +40 -0
  90. package/src/npm/packageManagers/YarnClassic.ts +40 -0
  91. package/src/utils/Workspace.ts +20 -7
  92. package/src/utils/commands.ts +65 -0
  93. package/src/utils/version.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/utils/version.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,WAG7B,CAAA"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/utils/version.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,QAAO,MAGpC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/utils/version.ts"],"names":[],"mappings":";;;AAAA,yCAAgC;AAChC,4CAA0C;AAEnC,MAAM,iBAAiB,GAAG,GAAG,EAAE;IACrC,8DAA8D;IAC9D,OAAO,OAAO,CAAC,IAAA,gBAAI,EAAC,uBAAW,EAAE,cAAc,CAAC,CAAC,CAAC,OAAO,CAAA;AAC1D,CAAC,CAAA;AAHY,QAAA,iBAAiB,qBAG7B"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/utils/version.ts"],"names":[],"mappings":";;;AAAA,yCAAgC;AAChC,4CAA0C;AAEnC,MAAM,iBAAiB,GAAG,GAAW,EAAE;IAC7C,8DAA8D;IAC9D,OAAO,OAAO,CAAC,IAAA,gBAAI,EAAC,uBAAW,EAAE,cAAc,CAAC,CAAC,CAAC,OAAO,CAAA;AAC1D,CAAC,CAAA;AAHY,QAAA,iBAAiB,qBAG7B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contember/cli-common",
3
- "version": "1.3.0-alpha.8",
3
+ "version": "1.3.0-beta.1",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/src/index.js",
6
6
  "typings": "dist/src/index.d.ts",
@@ -19,10 +19,11 @@
19
19
  "dependencies": {
20
20
  "chalk": "^4.1.2",
21
21
  "download-tarball": "^2.0.0",
22
+ "fast-glob": "^3.2.12",
22
23
  "fs-extra": "^10.0.0",
23
24
  "get-package-json-from-registry": "^2.2.2",
24
25
  "js-yaml": "^4.1.0",
25
26
  "npm-package-arg": "^9.0.2",
26
27
  "registry-info": "^1.0.0"
27
28
  }
28
- }
29
+ }
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ rules: {
3
+ "no-console": "off",
4
+ },
5
+ };
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { Application, Input, Command, CommandManager, CommandConfiguration, CommandFactoryList } from './application'
2
2
  export * from './utils'
3
+ export * from './npm'
@@ -0,0 +1,38 @@
1
+ import jsyaml from 'js-yaml'
2
+ import fs from 'node:fs/promises'
3
+ import glob from 'fast-glob'
4
+ import { pathExists } from '../utils'
5
+
6
+ export class FsManager {
7
+ constructor() {
8
+ }
9
+
10
+ public async exists(filename: string): Promise<boolean> {
11
+ return await pathExists(filename)
12
+ }
13
+
14
+ public async tryReadJson<T = any>(filename: string): Promise<T | null> {
15
+ const content = await this.tryReadFile(filename)
16
+ return content !== null ? JSON.parse(content) : null
17
+ }
18
+
19
+ public async tryReadYaml<T = any>(filename: string): Promise<T | null> {
20
+ const content = await this.tryReadFile(filename)
21
+ return content !== null ? jsyaml.load(content) as T : null
22
+ }
23
+
24
+ public async tryReadFile(filename: string): Promise<string | null> {
25
+ try {
26
+ return await fs.readFile(filename, 'utf8')
27
+ } catch (error: any) {
28
+ if (error.code === 'ENOENT') {
29
+ return null
30
+ }
31
+ throw error
32
+ }
33
+ }
34
+
35
+ public async listDirectories(dir: string, patterns: string[]): Promise<string[]> {
36
+ return await glob(patterns, { onlyDirectories: true, cwd: dir })
37
+ }
38
+ }
@@ -0,0 +1,10 @@
1
+ import { PackageJson } from './PackageJson'
2
+
3
+ export class Package {
4
+ constructor(
5
+ public readonly dir: string,
6
+ public readonly isRoot: boolean,
7
+ public readonly packageJson: PackageJson,
8
+ ) {
9
+ }
10
+ }
@@ -0,0 +1,11 @@
1
+ export type PackageJson =
2
+ & {
3
+ name: string
4
+ version: string
5
+ dependencies?: Record<string, string>
6
+ devDependencies?: Record<string, string>
7
+ workspaces?: string[] | { packages?: string[]; nohoist?: string[] }
8
+ }
9
+ & {
10
+ [prop: string]: undefined
11
+ }
@@ -0,0 +1,126 @@
1
+ import { PackageManager } from './packageManagers/PackageManager'
2
+ import { Package } from './Package'
3
+ import { FsManager } from './FsManager'
4
+ import { dirname, join } from 'node:path'
5
+ import { PackageJson } from './PackageJson'
6
+ import { pathExists } from '../utils'
7
+
8
+ export type Dependency = {
9
+ pckg: Package
10
+ isDev: boolean
11
+ version: string
12
+ name: string
13
+ }
14
+
15
+
16
+ export class PackageWorkspace {
17
+ constructor(
18
+ private readonly packageManager: PackageManager,
19
+ private readonly fsManager: FsManager,
20
+ public readonly rootPackage: Package,
21
+ public readonly packages: Package[],
22
+ ) {
23
+ }
24
+
25
+ get allPackages() {
26
+ return [this.rootPackage, ...this.packages]
27
+ }
28
+
29
+ public findDefinedDependencies(packageName: string): Dependency[] {
30
+ return this.allPackages.flatMap<Dependency>(it => {
31
+ for (const type of ['dependencies', 'devDependencies'] as const) {
32
+ const version = it.packageJson[type]?.[packageName]
33
+ if (version) {
34
+ return [{ pckg: it, isDev: type === 'devDependencies', version: version, name: packageName }]
35
+ }
36
+ }
37
+ return []
38
+ })
39
+ }
40
+
41
+ public async findInstalledDependencies(packageName: string): Promise<{ installed?: Dependency; defined: Dependency }[]> {
42
+ const definedDependencies = this.findDefinedDependencies(packageName)
43
+
44
+ const deps: { installed?: Dependency; defined: Dependency }[] = []
45
+ for (const defined of definedDependencies) {
46
+ const packageJsonPath = await this.resolvePackageJson(defined.pckg.dir, packageName)
47
+ if (!packageJsonPath) {
48
+ deps.push({ defined })
49
+ continue
50
+ }
51
+ const packageJson = await this.fsManager.tryReadJson<PackageJson>(packageJsonPath)
52
+ deps.push({ defined, installed: { ...defined, version: packageJson?.version ?? '' } })
53
+ }
54
+ return deps
55
+ }
56
+
57
+ private async resolvePackageJson(dir: string, packageName: string) {
58
+ while (true) {
59
+ const fullPath = join(dir, 'node_modules', packageName, 'package.json')
60
+ if (await pathExists(fullPath)) {
61
+ return fullPath
62
+ }
63
+ if (dir === '/') {
64
+ return null
65
+ }
66
+ dir = dirname(dir)
67
+ }
68
+ }
69
+
70
+ async updateEverywhere(updates: Record<string, string>): Promise<Dependency[]> {
71
+ const defined = Object.keys(updates).flatMap(it => this.findDefinedDependencies(it))
72
+ const byPckg = new Map<Package, Dependency[]>()
73
+ for (const it of defined) {
74
+ const arr = byPckg.get(it.pckg) ?? []
75
+ byPckg.set(it.pckg, arr)
76
+ arr.push(it)
77
+ }
78
+
79
+ const updated: Dependency[] = []
80
+ for (const [pckg, deps] of byPckg.entries()) {
81
+ const prod = deps.filter(it => !it.isDev)
82
+ const dev = deps.filter(it => it.isDev)
83
+ for (const [isDev, deps] of [[false, prod], [true, dev]] as const) {
84
+ if (deps.length === 0) {
85
+ continue
86
+ }
87
+ await this.packageManager.install({
88
+ pckg,
89
+ isDev,
90
+ dependencies: Object.fromEntries(deps.map(it => [it.name, updates[it.name]])),
91
+ })
92
+ updated.push(...deps)
93
+ }
94
+ }
95
+ return updated
96
+ }
97
+ }
98
+
99
+ export class PackageWorkspaceResolver {
100
+ constructor(
101
+ private fsManager: FsManager,
102
+ private packageManagers: PackageManager[],
103
+ ) {
104
+ }
105
+
106
+ public async resolve(dir: string): Promise<PackageWorkspace> {
107
+ const packageJson = await this.fsManager.tryReadJson<PackageJson>(join(dir, 'package.json'))
108
+ if (!packageJson) {
109
+ throw `package.json not found.`
110
+ }
111
+ const pm = await this.resolvePackageManager({ dir, packageJson })
112
+ const rootPackage = new Package(dir, true, packageJson)
113
+ const workspacePackages = await pm.readWorkspacePackages({ dir, packageJson })
114
+
115
+ return new PackageWorkspace(pm, this.fsManager, rootPackage, workspacePackages)
116
+ }
117
+
118
+ private async resolvePackageManager(args: { dir: string; packageJson: PackageJson }): Promise<PackageManager> {
119
+ for (const pm of this.packageManagers) {
120
+ if (await pm.isActive(args)) {
121
+ return pm
122
+ }
123
+ }
124
+ throw `No lockfile found. Please install dependencies using package manager of your choice.`
125
+ }
126
+ }
@@ -0,0 +1 @@
1
+ export * from './PackageWorkspace'
@@ -0,0 +1,39 @@
1
+ import { PackageManager } from './PackageManager'
2
+ import { FsManager } from '../FsManager'
3
+ import { Package } from '../Package'
4
+ import { PackageJson } from '../PackageJson'
5
+ import { join } from 'node:path'
6
+ import { PackageManagerHelpers } from './PackageManagerHelpers'
7
+ import { runCommand } from '../../utils/commands'
8
+
9
+ export class Npm implements PackageManager {
10
+ constructor(
11
+ private readonly fsManager: FsManager,
12
+ ) {
13
+ }
14
+
15
+ async install({ pckg, dependencies, isDev }: { pckg: Package; isDev: boolean; dependencies: Record<string, string> }): Promise<void> {
16
+ const { output } = runCommand('npm', [
17
+ 'install',
18
+ isDev ? '--save-dev' : '--save',
19
+ ...PackageManagerHelpers.formatPackagesToInstall(dependencies),
20
+ ], {
21
+ cwd: pckg.dir,
22
+ stderr: process.stderr,
23
+ stdout: process.stdout,
24
+ })
25
+ await output
26
+ }
27
+
28
+ async isActive({ dir, packageJson }: { dir: string; packageJson: PackageJson }): Promise<boolean> {
29
+ return await this.fsManager.exists(join(dir, 'package-lock.json'))
30
+ }
31
+
32
+ async readWorkspacePackages({ dir, packageJson }: { dir: string; packageJson: PackageJson }): Promise<Package[]> {
33
+ return await PackageManagerHelpers.readWorkspacePackages({
34
+ fsManager: this.fsManager,
35
+ dir,
36
+ workspaces: PackageManagerHelpers.getWorkspacesFromPackageJson({ packageJson }),
37
+ })
38
+ }
39
+ }
@@ -0,0 +1,11 @@
1
+ import { PackageJson } from '../PackageJson'
2
+
3
+ import { Package } from '../Package'
4
+
5
+ export interface PackageManager {
6
+ isActive(args: { dir: string; packageJson: PackageJson }): Promise<boolean>
7
+
8
+ readWorkspacePackages(args: { dir: string; packageJson: PackageJson }): Promise<Package[]>
9
+
10
+ install(args: { pckg: Package; isDev: boolean; dependencies: Record<string, string> }): Promise<void>
11
+ }
@@ -0,0 +1,35 @@
1
+ import { FsManager } from '../FsManager'
2
+ import { Package } from '../Package'
3
+ import { PackageJson } from '../PackageJson'
4
+ import { join } from 'node:path'
5
+
6
+ export class PackageManagerHelpers {
7
+ static async readWorkspacePackages({ fsManager, dir, workspaces }: {
8
+ fsManager: FsManager
9
+ dir: string
10
+ workspaces: string[]
11
+ }): Promise<Package[]> {
12
+ const dirs = await fsManager.listDirectories(dir, workspaces)
13
+
14
+ const packageJson = await Promise.all(dirs.map(async it => {
15
+ const packageJson = await fsManager.tryReadJson<PackageJson>(join(it, 'package.json'))
16
+ return packageJson ? [packageJson, it] as const : null
17
+ }))
18
+
19
+ return packageJson.filter(<T>(it: T | null): it is T => it !== null).map(([packageJson, dir]) => new Package(dir, false, packageJson))
20
+ }
21
+
22
+ static getWorkspacesFromPackageJson({ packageJson }: { packageJson: PackageJson }): string[] {
23
+ if (!packageJson.workspaces) {
24
+ return []
25
+ }
26
+ if (Array.isArray(packageJson.workspaces)) {
27
+ return packageJson.workspaces
28
+ }
29
+ return [...packageJson.workspaces?.packages ?? [], ...packageJson.workspaces?.nohoist ?? []]
30
+ }
31
+
32
+ static formatPackagesToInstall(packages: Record<string, string>): string[] {
33
+ return Object.entries(packages).map(([name, version]) => `${name}@${version}`)
34
+ }
35
+ }
@@ -0,0 +1,42 @@
1
+ import { FsManager } from '../FsManager'
2
+ import { join } from 'node:path'
3
+
4
+ import { PackageJson } from '../PackageJson'
5
+ import { PackageManager } from './PackageManager'
6
+ import { Package } from '../Package'
7
+ import { PackageManagerHelpers } from './PackageManagerHelpers'
8
+ import { runCommand } from '../../utils/commands'
9
+
10
+ export class Pnpm implements PackageManager {
11
+ constructor(
12
+ private readonly fsManager: FsManager,
13
+ ) {
14
+ }
15
+
16
+ async install({ pckg, dependencies, isDev }: { pckg: Package; isDev: boolean; dependencies: Record<string, string> }): Promise<void> {
17
+ const { output } = runCommand('pnpm', [
18
+ 'add',
19
+ isDev ? '--save-dev' : '--save',
20
+ pckg.isRoot ? '--ignore-workspace-root-check' : undefined,
21
+ ...PackageManagerHelpers.formatPackagesToInstall(dependencies),
22
+ ], {
23
+ cwd: pckg.dir,
24
+ stderr: process.stderr,
25
+ stdout: process.stdout,
26
+ })
27
+ await output
28
+ }
29
+
30
+ async isActive({ dir, packageJson }: { dir: string; packageJson: PackageJson }): Promise<boolean> {
31
+ return await this.fsManager.exists(join(dir, 'pnpm-lock.yaml'))
32
+ }
33
+
34
+ async readWorkspacePackages({ dir, packageJson }: { dir: string; packageJson: PackageJson }): Promise<Package[]> {
35
+ const pnpmWorkspaces = await this.fsManager.tryReadJson(join(dir, 'pnpm-workspace.yaml'))
36
+ return await PackageManagerHelpers.readWorkspacePackages({
37
+ fsManager: this.fsManager,
38
+ dir,
39
+ workspaces: pnpmWorkspaces.packages,
40
+ })
41
+ }
42
+ }
@@ -0,0 +1,40 @@
1
+ import { PackageManager } from './PackageManager'
2
+ import { FsManager } from '../FsManager'
3
+ import { Package } from '../Package'
4
+ import { PackageJson } from '../PackageJson'
5
+ import { join } from 'node:path'
6
+ import { PackageManagerHelpers } from './PackageManagerHelpers'
7
+ import { runCommand } from '../../utils/commands'
8
+
9
+ export class Yarn implements PackageManager {
10
+ constructor(
11
+ private readonly fsManager: FsManager,
12
+ ) {
13
+ }
14
+
15
+ async install({ pckg, dependencies, isDev }: { pckg: Package; isDev: boolean; dependencies: Record<string, string> }): Promise<void> {
16
+ const { output } = runCommand('yarn', [
17
+ 'add',
18
+ isDev ? '--dev' : undefined,
19
+ ...PackageManagerHelpers.formatPackagesToInstall(dependencies),
20
+ ], {
21
+ cwd: pckg.dir,
22
+ stderr: process.stderr,
23
+ stdout: process.stdout,
24
+ })
25
+ await output
26
+ }
27
+
28
+ async isActive({ dir, packageJson }: { dir: string; packageJson: PackageJson }): Promise<boolean> {
29
+ return await this.fsManager.exists(join(dir, 'yarn.lock'))
30
+ && (!packageJson.packageManager || (packageJson.packageManager as string).startsWith('yarn@3') || (packageJson.packageManager as string).startsWith('yarn@2'))
31
+ }
32
+
33
+ async readWorkspacePackages({ dir, packageJson }: { dir: string; packageJson: PackageJson }): Promise<Package[]> {
34
+ return await PackageManagerHelpers.readWorkspacePackages({
35
+ fsManager: this.fsManager,
36
+ dir,
37
+ workspaces: PackageManagerHelpers.getWorkspacesFromPackageJson({ packageJson }),
38
+ })
39
+ }
40
+ }
@@ -0,0 +1,40 @@
1
+ import { PackageManager } from './PackageManager'
2
+ import { FsManager } from '../FsManager'
3
+ import { Package } from '../Package'
4
+ import { PackageJson } from '../PackageJson'
5
+ import { join } from 'node:path'
6
+ import { PackageManagerHelpers } from './PackageManagerHelpers'
7
+ import { runCommand } from '../../utils/commands'
8
+
9
+ export class YarnClassic implements PackageManager {
10
+ constructor(
11
+ private readonly fsManager: FsManager,
12
+ ) {
13
+ }
14
+
15
+ async install({ pckg, dependencies, isDev }: { pckg: Package; isDev: boolean; dependencies: Record<string, string> }): Promise<void> {
16
+ const { output } = runCommand('yarn', [
17
+ 'add',
18
+ isDev ? '--dev' : undefined,
19
+ pckg.isRoot ? '--ignore-workspace-root-check' : undefined,
20
+ ...PackageManagerHelpers.formatPackagesToInstall(dependencies),
21
+ ], {
22
+ cwd: pckg.dir,
23
+ stderr: process.stderr,
24
+ stdout: process.stdout,
25
+ })
26
+ await output
27
+ }
28
+
29
+ async isActive({ dir, packageJson }: { dir: string; packageJson: PackageJson }): Promise<boolean> {
30
+ return await this.fsManager.exists(join(dir, 'yarn.lock'))
31
+ }
32
+
33
+ async readWorkspacePackages({ dir, packageJson }: { dir: string; packageJson: PackageJson }): Promise<Package[]> {
34
+ return await PackageManagerHelpers.readWorkspacePackages({
35
+ fsManager: this.fsManager,
36
+ dir,
37
+ workspaces: PackageManagerHelpers.getWorkspacesFromPackageJson({ packageJson }),
38
+ })
39
+ }
40
+ }
@@ -6,6 +6,12 @@ import { getPackageVersion } from './version'
6
6
  import { readYaml } from './yaml'
7
7
  import { CliEnv, readCliEnv } from '../application'
8
8
  import { pathExists } from './fs'
9
+ import { PackageWorkspace, PackageWorkspaceResolver } from '../npm/PackageWorkspace'
10
+ import { FsManager } from '../npm/FsManager'
11
+ import { Yarn } from '../npm/packageManagers/Yarn'
12
+ import { YarnClassic } from '../npm/packageManagers/YarnClassic'
13
+ import { Pnpm } from '../npm/packageManagers/Pnpm'
14
+ import { Npm } from '../npm/packageManagers/Npm'
9
15
 
10
16
  export interface WorkspaceDirectoryArgument {
11
17
  workspaceDirectory: string
@@ -28,7 +34,6 @@ export const createWorkspace = async ({ workspaceDirectory, workspaceName, templ
28
34
 
29
35
  export interface WorkspaceConfig {
30
36
  api?: {
31
- version?: string
32
37
  configFile?: string
33
38
  }
34
39
  admin?: {
@@ -45,11 +50,20 @@ export class Workspace {
45
50
  public readonly directory: string,
46
51
  public readonly env: CliEnv,
47
52
  public readonly config: WorkspaceConfig,
53
+ private readonly packageWorkspaceResolver: PackageWorkspaceResolver,
48
54
  ) {}
49
55
 
50
56
  public static async get(workspaceDirectory: string) {
51
57
  const config = await readWorkspaceConfig({ workspaceDirectory })
52
- return new Workspace(workspaceDirectory, readCliEnv(), config)
58
+ const fsManager = new FsManager()
59
+ const packageWorkspaceResolver = new PackageWorkspaceResolver(fsManager, [
60
+ new Yarn(fsManager),
61
+ new YarnClassic(fsManager),
62
+ new Pnpm(fsManager),
63
+ new Npm(fsManager),
64
+ ])
65
+
66
+ return new Workspace(workspaceDirectory, readCliEnv(), config, packageWorkspaceResolver)
53
67
  }
54
68
 
55
69
  get name() {
@@ -60,14 +74,13 @@ export class Workspace {
60
74
  return this.config?.admin?.enabled || false
61
75
  }
62
76
 
63
- get apiVersion(): string | undefined {
64
- return this.config?.api?.version || undefined
65
- }
66
-
67
-
68
77
  public isSingleProjectMode(): boolean {
69
78
  return !!this.env.projectName
70
79
  }
80
+
81
+ public async resolvePackageWorkspace(): Promise<PackageWorkspace> {
82
+ return await this.packageWorkspaceResolver.resolve(this.directory)
83
+ }
71
84
  }
72
85
 
73
86
  export const formatWorkspaceConfigPath = (workspaceDirectory: string) => [
@@ -0,0 +1,65 @@
1
+ import { ChildProcessWithoutNullStreams, spawn } from 'node:child_process'
2
+ import { Readable, Writable } from 'node:stream'
3
+ import chalk from 'chalk'
4
+
5
+ export type RunningCommand = { child: ChildProcessWithoutNullStreams; output: Promise<string> }
6
+ export const runCommand = (
7
+ command: string,
8
+ args: (string | undefined)[],
9
+ options: {
10
+ cwd: string
11
+ stdin?: Readable
12
+ stdout?: Writable
13
+ stderr?: Writable
14
+ env?: NodeJS.ProcessEnv
15
+ detached?: boolean
16
+ },
17
+ ): RunningCommand => {
18
+ const args2 = args.filter((it): it is string => it !== undefined)
19
+ if (!process.env.DISABLE_COMMAND_PRINTING) {
20
+ console.error(chalk.gray(`$ ${command} ${args2.map(it => `'${it.replace(/'/g, `'\\''`)}'`).join(' ')}`))
21
+ }
22
+ const child = spawn(command, args2, {
23
+ cwd: options.cwd,
24
+ env: { ...process.env, ...(options.env || {}) },
25
+ detached: options.detached,
26
+ })
27
+ if (options.stdin) {
28
+ options.stdin.pipe(child.stdin)
29
+ }
30
+
31
+ let stdout = ''
32
+ let stderr = ''
33
+
34
+ child.stdout.on('data', (chunk): void => {
35
+ stdout += chunk.toString()
36
+ })
37
+
38
+ child.stderr.on('data', (chunk): void => {
39
+ stderr += chunk.toString()
40
+ })
41
+ if (options.stdout) {
42
+ child.stdout.pipe(options.stdout)
43
+ }
44
+ if (options.stderr) {
45
+ child.stderr.pipe(options.stderr)
46
+ }
47
+
48
+ const output = new Promise<string>((resolve, reject) => {
49
+ child.on('exit', (exitCode): void => {
50
+ if (exitCode === 0) {
51
+ resolve(stdout)
52
+ } else {
53
+ reject(new ChildProcessError(exitCode, stderr))
54
+ }
55
+ })
56
+ })
57
+
58
+ return { output, child }
59
+ }
60
+
61
+ export class ChildProcessError extends Error {
62
+ constructor(public readonly exitCode: number | null, public readonly stderr: string) {
63
+ super(`Command has failed(${exitCode}): ${stderr} `)
64
+ }
65
+ }
@@ -1,7 +1,7 @@
1
1
  import { join } from 'node:path'
2
2
  import { packageRoot } from '../pathUtils'
3
3
 
4
- export const getPackageVersion = () => {
4
+ export const getPackageVersion = (): string => {
5
5
  // eslint-disable-next-line @typescript-eslint/no-var-requires
6
6
  return require(join(packageRoot, 'package.json')).version
7
7
  }