@xpack/xpm-lib 3.1.3 → 4.0.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.
- package/README.md +16 -212
- package/dist/classes/actions.d.ts +58 -0
- package/dist/classes/actions.d.ts.map +1 -0
- package/dist/classes/actions.js +250 -0
- package/dist/classes/actions.js.map +1 -0
- package/dist/classes/build-configurations.d.ts +78 -0
- package/dist/classes/build-configurations.d.ts.map +1 -0
- package/dist/classes/build-configurations.js +489 -0
- package/dist/classes/build-configurations.js.map +1 -0
- package/dist/classes/combinations-generator.d.ts +19 -0
- package/dist/classes/combinations-generator.d.ts.map +1 -0
- package/dist/classes/combinations-generator.js +48 -0
- package/dist/classes/combinations-generator.js.map +1 -0
- package/dist/classes/data-model.d.ts +21 -0
- package/dist/classes/data-model.d.ts.map +1 -0
- package/dist/classes/data-model.js +47 -0
- package/dist/classes/data-model.js.map +1 -0
- package/dist/classes/errors.d.ts +13 -0
- package/dist/classes/errors.d.ts.map +1 -0
- package/dist/classes/errors.js +13 -0
- package/dist/classes/errors.js.map +1 -0
- package/dist/classes/init-template-base.d.ts +47 -0
- package/dist/classes/init-template-base.d.ts.map +1 -0
- package/dist/classes/init-template-base.js +358 -0
- package/dist/classes/init-template-base.js.map +1 -0
- package/dist/classes/liquid-drop.d.ts +28 -0
- package/dist/classes/liquid-drop.d.ts.map +1 -0
- package/dist/classes/liquid-drop.js +70 -0
- package/dist/classes/liquid-drop.js.map +1 -0
- package/dist/classes/liquid-engine.d.ts +7 -0
- package/dist/classes/liquid-engine.d.ts.map +1 -0
- package/dist/classes/liquid-engine.js +72 -0
- package/dist/classes/liquid-engine.js.map +1 -0
- package/dist/classes/package.d.ts +31 -0
- package/dist/classes/package.d.ts.map +1 -0
- package/dist/classes/package.js +268 -0
- package/dist/classes/package.js.map +1 -0
- package/dist/classes/platform-detector.d.ts +14 -0
- package/dist/classes/platform-detector.d.ts.map +1 -0
- package/dist/classes/platform-detector.js +26 -0
- package/dist/classes/platform-detector.js.map +1 -0
- package/dist/classes/policies.d.ts +14 -0
- package/dist/classes/policies.d.ts.map +1 -0
- package/dist/classes/policies.js +20 -0
- package/dist/classes/policies.js.map +1 -0
- package/dist/classes/template-expander.d.ts +29 -0
- package/dist/classes/template-expander.d.ts.map +1 -0
- package/dist/classes/template-expander.js +62 -0
- package/dist/classes/template-expander.js.map +1 -0
- package/dist/data/substitutions-variables.d.ts +43 -0
- package/dist/data/substitutions-variables.d.ts.map +1 -0
- package/dist/{lib → data}/substitutions-variables.js +1 -16
- package/dist/data/substitutions-variables.js.map +1 -0
- package/dist/functions/chmod-recursively.d.ts +9 -0
- package/dist/functions/chmod-recursively.d.ts.map +1 -0
- package/dist/functions/chmod-recursively.js +66 -0
- package/dist/functions/chmod-recursively.js.map +1 -0
- package/dist/functions/filter-paths.d.ts +5 -0
- package/dist/functions/filter-paths.d.ts.map +1 -0
- package/dist/functions/filter-paths.js +16 -0
- package/dist/functions/filter-paths.js.map +1 -0
- package/dist/functions/is-something.d.ts +9 -0
- package/dist/functions/is-something.d.ts.map +1 -0
- package/dist/functions/is-something.js +25 -0
- package/dist/functions/is-something.js.map +1 -0
- package/dist/functions/matrix-expander.d.ts +17 -0
- package/dist/functions/matrix-expander.d.ts.map +1 -0
- package/dist/functions/matrix-expander.js +52 -0
- package/dist/functions/matrix-expander.js.map +1 -0
- package/dist/functions/perform-substitutions.d.ts +12 -0
- package/dist/functions/perform-substitutions.d.ts.map +1 -0
- package/dist/functions/perform-substitutions.js +76 -0
- package/dist/functions/perform-substitutions.js.map +1 -0
- package/dist/functions/utils.d.ts +8 -0
- package/dist/functions/utils.d.ts.map +1 -0
- package/dist/functions/utils.js +16 -0
- package/dist/functions/utils.js.map +1 -0
- package/dist/index.d.ts +22 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -29
- package/dist/index.js.map +1 -1
- package/dist/{lib/types.d.ts → types/json.d.ts} +31 -22
- package/dist/types/json.d.ts.map +1 -0
- package/dist/types/json.js +2 -0
- package/dist/types/json.js.map +1 -0
- package/dist/types/xpm-init-template.d.ts +21 -0
- package/dist/types/xpm-init-template.d.ts.map +1 -0
- package/dist/types/xpm-init-template.js +2 -0
- package/dist/types/xpm-init-template.js.map +1 -0
- package/dist/types/xpm.d.ts +16 -0
- package/dist/types/xpm.d.ts.map +1 -0
- package/dist/types/xpm.js +2 -0
- package/dist/types/xpm.js.map +1 -0
- package/package.json +53 -44
- package/src/CODE-REVIEW.md +2167 -0
- package/src/README.md +393 -6
- package/src/classes/actions.ts +1157 -0
- package/src/classes/build-configurations.ts +2127 -0
- package/src/classes/combinations-generator.ts +331 -0
- package/src/classes/data-model.ts +337 -0
- package/src/classes/errors.ts +105 -0
- package/src/classes/init-template-base.ts +1028 -0
- package/src/classes/liquid-drop.ts +376 -0
- package/src/classes/liquid-engine.ts +249 -0
- package/src/classes/package.ts +765 -0
- package/src/classes/platform-detector.ts +237 -0
- package/src/classes/policies.ts +200 -0
- package/src/classes/template-expander.ts +330 -0
- package/src/data/substitutions-variables.ts +390 -0
- package/src/functions/chmod-recursively.ts +195 -0
- package/src/functions/filter-paths.ts +126 -0
- package/src/functions/is-something.ts +223 -0
- package/src/functions/matrix-expander.ts +172 -0
- package/src/functions/perform-substitutions.ts +253 -0
- package/src/functions/utils.ts +151 -0
- package/src/index.ts +72 -19
- package/src/types/json.ts +519 -0
- package/src/types/xpm-init-template.ts +282 -0
- package/src/types/xpm.ts +162 -0
- package/dist/lib/chmod-recursive.d.ts +0 -7
- package/dist/lib/chmod-recursive.d.ts.map +0 -1
- package/dist/lib/chmod-recursive.js +0 -81
- package/dist/lib/chmod-recursive.js.map +0 -1
- package/dist/lib/errors.d.ts +0 -11
- package/dist/lib/errors.d.ts.map +0 -1
- package/dist/lib/errors.js +0 -26
- package/dist/lib/errors.js.map +0 -1
- package/dist/lib/functions/chmod-recursive.d.ts +0 -7
- package/dist/lib/functions/chmod-recursive.d.ts.map +0 -1
- package/dist/lib/functions/chmod-recursive.js +0 -81
- package/dist/lib/functions/chmod-recursive.js.map +0 -1
- package/dist/lib/functions/perform-substitutions.d.ts +0 -20
- package/dist/lib/functions/perform-substitutions.d.ts.map +0 -1
- package/dist/lib/functions/perform-substitutions.js +0 -85
- package/dist/lib/functions/perform-substitutions.js.map +0 -1
- package/dist/lib/functions/utils.d.ts +0 -30
- package/dist/lib/functions/utils.d.ts.map +0 -1
- package/dist/lib/functions/utils.js +0 -70
- package/dist/lib/functions/utils.js.map +0 -1
- package/dist/lib/init-template-base.d.ts +0 -46
- package/dist/lib/init-template-base.d.ts.map +0 -1
- package/dist/lib/init-template-base.js +0 -281
- package/dist/lib/init-template-base.js.map +0 -1
- package/dist/lib/liquid-actions.d.ts +0 -37
- package/dist/lib/liquid-actions.d.ts.map +0 -1
- package/dist/lib/liquid-actions.js +0 -148
- package/dist/lib/liquid-actions.js.map +0 -1
- package/dist/lib/liquid-build-configurations.d.ts +0 -47
- package/dist/lib/liquid-build-configurations.d.ts.map +0 -1
- package/dist/lib/liquid-build-configurations.js +0 -282
- package/dist/lib/liquid-build-configurations.js.map +0 -1
- package/dist/lib/liquid-drop.d.ts +0 -13
- package/dist/lib/liquid-drop.d.ts.map +0 -1
- package/dist/lib/liquid-drop.js +0 -56
- package/dist/lib/liquid-drop.js.map +0 -1
- package/dist/lib/liquid-engine.d.ts +0 -5
- package/dist/lib/liquid-engine.d.ts.map +0 -1
- package/dist/lib/liquid-engine.js +0 -85
- package/dist/lib/liquid-engine.js.map +0 -1
- package/dist/lib/liquid-package.d.ts +0 -17
- package/dist/lib/liquid-package.d.ts.map +0 -1
- package/dist/lib/liquid-package.js +0 -70
- package/dist/lib/liquid-package.js.map +0 -1
- package/dist/lib/package.d.ts +0 -66
- package/dist/lib/package.d.ts.map +0 -1
- package/dist/lib/package.js +0 -700
- package/dist/lib/package.js.map +0 -1
- package/dist/lib/perform-substitutions.d.ts +0 -20
- package/dist/lib/perform-substitutions.d.ts.map +0 -1
- package/dist/lib/perform-substitutions.js +0 -85
- package/dist/lib/perform-substitutions.js.map +0 -1
- package/dist/lib/policies.d.ts +0 -14
- package/dist/lib/policies.d.ts.map +0 -1
- package/dist/lib/policies.js +0 -33
- package/dist/lib/policies.js.map +0 -1
- package/dist/lib/substitutions-variables.d.ts +0 -117
- package/dist/lib/substitutions-variables.d.ts.map +0 -1
- package/dist/lib/substitutions-variables.js.map +0 -1
- package/dist/lib/types.d.ts.map +0 -1
- package/dist/lib/types.js +0 -13
- package/dist/lib/types.js.map +0 -1
- package/dist/lib/utils.d.ts +0 -30
- package/dist/lib/utils.d.ts.map +0 -1
- package/dist/lib/utils.js +0 -70
- package/dist/lib/utils.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/lib/errors.ts +0 -29
- package/src/lib/functions/chmod-recursive.ts +0 -103
- package/src/lib/functions/perform-substitutions.ts +0 -116
- package/src/lib/functions/utils.ts +0 -88
- package/src/lib/init-template-base.ts +0 -408
- package/src/lib/liquid-actions.ts +0 -223
- package/src/lib/liquid-build-configurations.ts +0 -433
- package/src/lib/liquid-drop.ts +0 -99
- package/src/lib/liquid-engine.ts +0 -135
- package/src/lib/liquid-package.ts +0 -108
- package/src/lib/package.ts +0 -947
- package/src/lib/policies.ts +0 -51
- package/src/lib/substitutions-variables.ts +0 -177
- package/src/lib/types.ts +0 -109
- package/src/package.json +0 -3
- package/src/tsconfig.json +0 -10
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the xPack project (http://xpack.github.io).
|
|
3
|
+
* Copyright (c) 2021-2026 Liviu Ionescu. All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* Permission to use, copy, modify, and/or distribute this software
|
|
6
|
+
* for any purpose is hereby granted, under the terms of the MIT license.
|
|
7
|
+
*
|
|
8
|
+
* If a copy of the license was not distributed with this file, it can
|
|
9
|
+
* be obtained from https://opensource.org/license/mit.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// ----------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
import assert from 'node:assert'
|
|
15
|
+
import * as fs from 'node:fs/promises'
|
|
16
|
+
import * as path from 'node:path'
|
|
17
|
+
|
|
18
|
+
// https://www.npmjs.com/package/semver
|
|
19
|
+
import semver from 'semver'
|
|
20
|
+
|
|
21
|
+
// https://www.npmjs.com/package/@xpack/logger
|
|
22
|
+
import { Logger } from '@xpack/logger'
|
|
23
|
+
|
|
24
|
+
// ----------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
import { ConfigurationError, InputError, PrerequisitesError } from './errors.js'
|
|
27
|
+
import { isString } from '../functions/is-something.js'
|
|
28
|
+
import { hasLiquidSyntax } from '../functions/utils.js'
|
|
29
|
+
import {
|
|
30
|
+
JsonBuildConfiguration,
|
|
31
|
+
JsonBuildConfigurationContent,
|
|
32
|
+
JsonBuildConfigurationTemplate,
|
|
33
|
+
JsonPackageSpecifier,
|
|
34
|
+
JsonXpmPackage,
|
|
35
|
+
} from '../types/json.js'
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Configuration parameters for constructing a package instance.
|
|
41
|
+
*
|
|
42
|
+
* @remarks
|
|
43
|
+
* This interface defines the required configuration for creating an
|
|
44
|
+
* instance of {@link Package}. Both properties are mandatory.
|
|
45
|
+
*
|
|
46
|
+
* The parameters provide the absolute path to the package folder containing
|
|
47
|
+
* (or that will contain) the <code>package.json</code> file, and the logger
|
|
48
|
+
* for diagnostic output during package operations.
|
|
49
|
+
*/
|
|
50
|
+
export interface PackageConstructorParameters {
|
|
51
|
+
/**
|
|
52
|
+
* The absolute path to the package folder.
|
|
53
|
+
*/
|
|
54
|
+
packageFolderPath: string
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The logger instance for output and diagnostics.
|
|
58
|
+
*/
|
|
59
|
+
log: Logger
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Provides access to package metadata and xpm-specific validation.
|
|
64
|
+
*
|
|
65
|
+
* @remarks
|
|
66
|
+
* This class loads and validates `package.json` content, determines
|
|
67
|
+
* package capabilities, and provides helper methods used across <b>xpm</b>
|
|
68
|
+
* workflows.
|
|
69
|
+
*
|
|
70
|
+
* The package abstraction provides a layer over `package.json` processing
|
|
71
|
+
* with progressive validation:
|
|
72
|
+
*
|
|
73
|
+
* <ol>
|
|
74
|
+
* <li><b>Basic file I/O:</b> Read and write <code>package.json</code> with
|
|
75
|
+
* error handling.</li>
|
|
76
|
+
* <li><b>npm validation:</b> Check for valid npm package structure (name,
|
|
77
|
+
* version).</li>
|
|
78
|
+
* <li><b>xpm validation:</b> Verify <code>xpack</code> section presence
|
|
79
|
+
* and structure.</li>
|
|
80
|
+
* <li><b>Binary package validation:</b> Validate binary-specific metadata
|
|
81
|
+
* (executables, binaries, platforms).</li>
|
|
82
|
+
* <li><b>Capability detection:</b> Determine package features (scripts,
|
|
83
|
+
* actions, build configurations).</li>
|
|
84
|
+
* <li><b>Version checking:</b> Validate minimum <b>xpm</b> version
|
|
85
|
+
* requirements.</li>
|
|
86
|
+
* <li><b>Specifier parsing:</b> Extract scope, name, and version from package
|
|
87
|
+
* identifiers.</li>
|
|
88
|
+
* </ol>
|
|
89
|
+
*
|
|
90
|
+
* This hierarchy allows validation to be performed incrementally as needed,
|
|
91
|
+
* avoiding unnecessary checks for packages that don't meet earlier criteria.
|
|
92
|
+
*/
|
|
93
|
+
export class Package {
|
|
94
|
+
// --------------------------------------------------------------------------
|
|
95
|
+
// Public Members.
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The absolute path to the package folder.
|
|
99
|
+
*
|
|
100
|
+
* @remarks
|
|
101
|
+
* This path serves as the base folder for all package operations,
|
|
102
|
+
* including reading/writing `package.json` and resolving relative paths.
|
|
103
|
+
*
|
|
104
|
+
* Path requirements:
|
|
105
|
+
*
|
|
106
|
+
* <ol>
|
|
107
|
+
* <li>Must be an absolute path to a folder.</li>
|
|
108
|
+
* <li>Folder should contain (or will contain) a <code>package.json</code>
|
|
109
|
+
* file.</li>
|
|
110
|
+
* <li>Used to construct the path to <code>package.json</code> as
|
|
111
|
+
* <code>\{packageFolderPath\}/package.json</code>.</li>
|
|
112
|
+
* <li>Remains constant throughout the lifecycle of the
|
|
113
|
+
* <code>Package</code> instance.</li>
|
|
114
|
+
* </ol>
|
|
115
|
+
*
|
|
116
|
+
* The path is set during construction and used by all methods that access
|
|
117
|
+
* or modify `package.json`.
|
|
118
|
+
*/
|
|
119
|
+
packageFolderPath: string
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The parsed `package.json` content, when available.
|
|
123
|
+
*
|
|
124
|
+
* @remarks
|
|
125
|
+
* This property caches the parsed `package.json` content after successful
|
|
126
|
+
* reading, avoiding repeated file I/O and parsing operations.
|
|
127
|
+
*
|
|
128
|
+
* Lifecycle states:
|
|
129
|
+
*
|
|
130
|
+
* <ol>
|
|
131
|
+
* <li>Initially undefined when the <code>Package</code> instance
|
|
132
|
+
* is created.</li>
|
|
133
|
+
* <li>Populated by <code>Package.readPackageDotJson()</code> upon
|
|
134
|
+
* successful read and parse.</li>
|
|
135
|
+
* <li>Cleared to undefined if parsing fails with
|
|
136
|
+
* <code>withThrow</code> enabled.</li>
|
|
137
|
+
* <li>Used by validation methods (<code>isNpmPackage</code>,
|
|
138
|
+
* <code>isxpm.Package</code>,
|
|
139
|
+
* <code>isBinaryXpmPackage</code>) to check package capabilities.</li>
|
|
140
|
+
* <li>Not automatically updated when <code>package.json</code> is
|
|
141
|
+
* modified externally;
|
|
142
|
+
* call <code>Package.readPackageDotJson()</code> again to refresh.</li>
|
|
143
|
+
* </ol>
|
|
144
|
+
*
|
|
145
|
+
* The cached content improves performance for packages that perform
|
|
146
|
+
* multiple validation checks without file system access overhead.
|
|
147
|
+
*/
|
|
148
|
+
jsonPackage?: JsonXpmPackage
|
|
149
|
+
|
|
150
|
+
// --------------------------------------------------------------------------
|
|
151
|
+
// Protected Members.
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The logger instance for output and diagnostics.
|
|
155
|
+
*
|
|
156
|
+
* @remarks
|
|
157
|
+
* This logger provides trace-level diagnostics for package operations,
|
|
158
|
+
* including file I/O, parsing, validation, and version checking.
|
|
159
|
+
*
|
|
160
|
+
* Logging use cases:
|
|
161
|
+
*
|
|
162
|
+
* <ol>
|
|
163
|
+
* <li>Trace package folder path during construction.</li>
|
|
164
|
+
* <li>Log file read errors when investigating missing
|
|
165
|
+
* <code>package.json</code>.</li>
|
|
166
|
+
* <li>Trace JSON parsing errors for debugging invalid
|
|
167
|
+
* <code>package.json</code>.</li>
|
|
168
|
+
* <li>Log version validation details during <code>minimumXpmRequired</code>
|
|
169
|
+
* checks.</li>
|
|
170
|
+
* <li>Trace package specifier parsing for debugging dependency
|
|
171
|
+
* resolution.</li>
|
|
172
|
+
* </ol>
|
|
173
|
+
*
|
|
174
|
+
* The logger enables detailed diagnostics without affecting normal
|
|
175
|
+
* operation, as trace-level output is typically disabled in production.
|
|
176
|
+
*/
|
|
177
|
+
protected readonly _log: Logger
|
|
178
|
+
|
|
179
|
+
// --------------------------------------------------------------------------
|
|
180
|
+
// Constructor.
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Constructs a package helper bound to a specific folder.
|
|
184
|
+
*
|
|
185
|
+
* @param packageFolderPath - The absolute path to the package folder.
|
|
186
|
+
* @param log - The logger instance for output and diagnostics.
|
|
187
|
+
*
|
|
188
|
+
* @throws {@link InputError}
|
|
189
|
+
* If packageFolderPath is not provided or is not an absolute path.
|
|
190
|
+
*/
|
|
191
|
+
constructor({ packageFolderPath, log }: PackageConstructorParameters) {
|
|
192
|
+
assert(
|
|
193
|
+
packageFolderPath && path.isAbsolute(packageFolderPath),
|
|
194
|
+
`packageFolderPath must be an absolute path, got: ${packageFolderPath}`
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
this._log = log
|
|
198
|
+
this.packageFolderPath = packageFolderPath
|
|
199
|
+
|
|
200
|
+
log.trace(`${Package.name}(${packageFolderPath})`)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// --------------------------------------------------------------------------
|
|
204
|
+
// Public Methods.
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Reads and parses `package.json` from the package folder.
|
|
208
|
+
*
|
|
209
|
+
* @remarks
|
|
210
|
+
* This method provides flexible error handling for scenarios where a
|
|
211
|
+
* missing or invalid `package.json` may be expected (e.g., checking whether
|
|
212
|
+
* a folder is a package) versus scenarios where it indicates a critical
|
|
213
|
+
* error (e.g., operating on a known package).
|
|
214
|
+
*
|
|
215
|
+
* When `withThrow` is false, the method returns undefined for missing or
|
|
216
|
+
* invalid files, allowing callers to handle the absence gracefully. When
|
|
217
|
+
* `withThrow` is true, errors are thrown as {@link InputError} for
|
|
218
|
+
* consistent error handling across the application.
|
|
219
|
+
*
|
|
220
|
+
* @param withThrow - Whether to throw on missing or invalid `package.json`.
|
|
221
|
+
* @returns The parsed `package.json` content, or undefined when missing or
|
|
222
|
+
* invalid and `withThrow` is false.
|
|
223
|
+
*
|
|
224
|
+
* @throws {@link InputError}
|
|
225
|
+
* If `package.json` is missing or invalid and `withThrow` is true.
|
|
226
|
+
*/
|
|
227
|
+
async readPackageDotJson({
|
|
228
|
+
withThrow = false,
|
|
229
|
+
}: {
|
|
230
|
+
withThrow?: boolean
|
|
231
|
+
} = {}): Promise<JsonXpmPackage | undefined> {
|
|
232
|
+
const jsonFilePath = path.join(this.packageFolderPath, 'package.json')
|
|
233
|
+
|
|
234
|
+
let fileContent: string | Buffer
|
|
235
|
+
try {
|
|
236
|
+
fileContent = await fs.readFile(jsonFilePath)
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (withThrow) {
|
|
239
|
+
if (error instanceof Error) {
|
|
240
|
+
this._log.trace(error.message)
|
|
241
|
+
}
|
|
242
|
+
throw new InputError(
|
|
243
|
+
`no package.json in folder ‘${this.packageFolderPath}’`
|
|
244
|
+
)
|
|
245
|
+
} else {
|
|
246
|
+
return undefined
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
this.jsonPackage = JSON.parse(fileContent.toString()) as JsonXpmPackage
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (withThrow) {
|
|
254
|
+
this.jsonPackage = undefined
|
|
255
|
+
if (error instanceof Error) {
|
|
256
|
+
this._log.trace(error.message)
|
|
257
|
+
}
|
|
258
|
+
throw new InputError(
|
|
259
|
+
`invalid package.json in folder ‘${this.packageFolderPath}’`
|
|
260
|
+
)
|
|
261
|
+
} else {
|
|
262
|
+
return undefined
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return this.jsonPackage
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Writes the provided `package.json` content to disk.
|
|
270
|
+
*
|
|
271
|
+
* @remarks
|
|
272
|
+
* The JSON content is passed explicitly rather than using the cached
|
|
273
|
+
* value.
|
|
274
|
+
*
|
|
275
|
+
* @param jsonPackage - The `package.json` content to write.
|
|
276
|
+
* @returns A promise that resolves when the file has been written.
|
|
277
|
+
*/
|
|
278
|
+
async rewritePackageDotJson(jsonPackage: JsonXpmPackage): Promise<void> {
|
|
279
|
+
const log = this._log
|
|
280
|
+
|
|
281
|
+
assert(jsonPackage, 'jsonPackage is required')
|
|
282
|
+
const jsonString = JSON.stringify(jsonPackage, null, 2) + '\n'
|
|
283
|
+
|
|
284
|
+
const jsonFilePath = path.join(this.packageFolderPath, 'package.json')
|
|
285
|
+
log.trace(`write filePath: '${jsonFilePath}'`)
|
|
286
|
+
await fs.writeFile(jsonFilePath, jsonString)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Determines whether the `package.json` content represents a valid
|
|
291
|
+
* npm package.
|
|
292
|
+
*
|
|
293
|
+
* @returns `true` if the package has a valid name and version, `false`
|
|
294
|
+
* otherwise.
|
|
295
|
+
*/
|
|
296
|
+
isNpmPackage(): boolean {
|
|
297
|
+
const jsonPackage = this.jsonPackage
|
|
298
|
+
if (!jsonPackage) {
|
|
299
|
+
return false
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (jsonPackage.name === undefined || jsonPackage.version === undefined) {
|
|
303
|
+
return false
|
|
304
|
+
}
|
|
305
|
+
const name = jsonPackage.name.trim()
|
|
306
|
+
if (name.length === 0) {
|
|
307
|
+
return false
|
|
308
|
+
}
|
|
309
|
+
const version = jsonPackage.version.trim()
|
|
310
|
+
if (version.length === 0) {
|
|
311
|
+
return false
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return true
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Determines whether the package is an <b>xpm</b> package.
|
|
319
|
+
*
|
|
320
|
+
* @returns `true` if the package is a valid npm package with an xpack
|
|
321
|
+
* section, `false` otherwise.
|
|
322
|
+
*/
|
|
323
|
+
isXpmPackage(): boolean {
|
|
324
|
+
const jsonPackage = this.jsonPackage
|
|
325
|
+
|
|
326
|
+
if (!this.isNpmPackage()) {
|
|
327
|
+
return false
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (jsonPackage?.xpack === undefined) {
|
|
331
|
+
return false
|
|
332
|
+
}
|
|
333
|
+
return true
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Determines whether the package is a binary <b>xpm</b> package.
|
|
338
|
+
*
|
|
339
|
+
* @remarks
|
|
340
|
+
* Binary packages must have both executables and binaries. The
|
|
341
|
+
* presence of one implies the other, so this method validates consistency.
|
|
342
|
+
*
|
|
343
|
+
* Validation rules:
|
|
344
|
+
*
|
|
345
|
+
* <ol>
|
|
346
|
+
* <li>If <code>xpack.executables</code> (or deprecated
|
|
347
|
+
* <code>xpack.bin</code>) exists, then
|
|
348
|
+
* <code>xpack.binaries</code> and <code>xpack.binaries.platforms</code>
|
|
349
|
+
* must also exist.</li>
|
|
350
|
+
* <li>If <code>xpack.binaries</code> exists, then
|
|
351
|
+
* <code>xpack.binaries.platforms</code> and
|
|
352
|
+
* <code>xpack.executables</code> (or deprecated
|
|
353
|
+
* <code>xpack.bin</code>) must also exist.</li>
|
|
354
|
+
* </ol>
|
|
355
|
+
*
|
|
356
|
+
* This bidirectional validation ensures package metadata consistency and
|
|
357
|
+
* catches incomplete binary package configurations early. The check helps
|
|
358
|
+
* prevent runtime errors when attempting to install or use binary packages
|
|
359
|
+
* with missing metadata.
|
|
360
|
+
*
|
|
361
|
+
* @returns `true` if the package defines binaries and executables, `false`
|
|
362
|
+
* otherwise.
|
|
363
|
+
*
|
|
364
|
+
* @throws {@link InputError}
|
|
365
|
+
* If required binary package fields are missing.
|
|
366
|
+
*/
|
|
367
|
+
isBinaryXpmPackage() {
|
|
368
|
+
const jsonPackage = this.jsonPackage
|
|
369
|
+
|
|
370
|
+
if (!this.isXpmPackage()) {
|
|
371
|
+
return false
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Since Nov. 2024, `executables` is preferred to `bin`.
|
|
375
|
+
if (jsonPackage?.xpack.executables ?? jsonPackage?.xpack.bin) {
|
|
376
|
+
// If it has `executables` or `bin`, it must have `binaries` and
|
|
377
|
+
// `binaries.platforms` too.
|
|
378
|
+
if (!jsonPackage.xpack.binaries) {
|
|
379
|
+
throw new ConfigurationError(
|
|
380
|
+
"doesn't look like a proper binary xpm package, " +
|
|
381
|
+
'package.json has no "xpack.binaries"'
|
|
382
|
+
)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
386
|
+
if (!jsonPackage.xpack.binaries.platforms) {
|
|
387
|
+
throw new ConfigurationError(
|
|
388
|
+
"doesn't look like a proper binary xpm package, " +
|
|
389
|
+
'package.json has no "xpack.binaries.platforms"'
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
return true
|
|
393
|
+
}
|
|
394
|
+
if (jsonPackage?.xpack.binaries) {
|
|
395
|
+
// If it has `binaries`, it must have `binaries.platforms` and
|
|
396
|
+
// `executables` too.
|
|
397
|
+
|
|
398
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
399
|
+
if (!jsonPackage.xpack.binaries.platforms) {
|
|
400
|
+
throw new ConfigurationError(
|
|
401
|
+
"doesn't look like a proper binary xpm package, " +
|
|
402
|
+
'package.json has no "xpack.binaries.platforms"'
|
|
403
|
+
)
|
|
404
|
+
}
|
|
405
|
+
// if (!(jsonPackage.xpack.executables ?? jsonPackage.xpack.bin)) {
|
|
406
|
+
throw new ConfigurationError(
|
|
407
|
+
"doesn't look like a proper binary xpm package, " +
|
|
408
|
+
'package.json has no "xpack.executables"'
|
|
409
|
+
)
|
|
410
|
+
//}
|
|
411
|
+
//return true
|
|
412
|
+
}
|
|
413
|
+
return false
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Determines whether the package is a Node module without <b>xpm</b>
|
|
418
|
+
* metadata.
|
|
419
|
+
*
|
|
420
|
+
* @returns `true` if the package is a Node module without <b>xpm</b>
|
|
421
|
+
* metadata, `false` otherwise.
|
|
422
|
+
*/
|
|
423
|
+
isNodeModule() {
|
|
424
|
+
const jsonPackage = this.jsonPackage
|
|
425
|
+
|
|
426
|
+
if (!this.isNpmPackage()) {
|
|
427
|
+
return false
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (jsonPackage?.xpack) {
|
|
431
|
+
return false
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return true
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Determines whether the package is a Node module with a binary entry.
|
|
439
|
+
*
|
|
440
|
+
* @returns `true` if the package is a Node module with a bin entry,
|
|
441
|
+
* `false` otherwise.
|
|
442
|
+
*/
|
|
443
|
+
isBinaryNodeModule() {
|
|
444
|
+
const jsonPackage = this.jsonPackage
|
|
445
|
+
|
|
446
|
+
if (!this.isNodeModule()) {
|
|
447
|
+
return false
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (jsonPackage?.bin === undefined) {
|
|
451
|
+
return false
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return true
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Determines whether the package defines any npm scripts.
|
|
459
|
+
*
|
|
460
|
+
* @returns `true` if at least one script is defined, `false` otherwise.
|
|
461
|
+
*/
|
|
462
|
+
hasNpmScripts(): boolean {
|
|
463
|
+
const jsonPackage = this.jsonPackage
|
|
464
|
+
|
|
465
|
+
if (
|
|
466
|
+
jsonPackage?.scripts !== undefined &&
|
|
467
|
+
Object.keys(jsonPackage.scripts).length > 0
|
|
468
|
+
) {
|
|
469
|
+
return true
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return false
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Determines whether the package defines any <b>xpm</b> actions.
|
|
477
|
+
*
|
|
478
|
+
* @remarks
|
|
479
|
+
* This method performs a comprehensive search for action definitions at
|
|
480
|
+
* both the package level and within build configurations, including
|
|
481
|
+
* template-based configurations.
|
|
482
|
+
*
|
|
483
|
+
* Action detection strategy:
|
|
484
|
+
*
|
|
485
|
+
* <ol>
|
|
486
|
+
* <li>Check for package-level actions in <code>xpack.actions</code>.</li>
|
|
487
|
+
* <li>If no package-level actions, iterate through all build
|
|
488
|
+
* configurations.</li>
|
|
489
|
+
* <li>For each configuration, determine if it's a template (name contains
|
|
490
|
+
* Liquid syntax) or a regular configuration.</li>
|
|
491
|
+
* <li>For templates: Check <code>template.actions</code> for action
|
|
492
|
+
* definitions.</li>
|
|
493
|
+
* <li>For regular configurations: Check <code>actions</code> directly.</li>
|
|
494
|
+
* <li>Return true if any actions are found at any level.</li>
|
|
495
|
+
* </ol>
|
|
496
|
+
*
|
|
497
|
+
* This comprehensive check is useful for determining whether <b>xpm</b>
|
|
498
|
+
* action
|
|
499
|
+
* commands should be available or whether the package requires <b>xpm</b> for
|
|
500
|
+
* build automation.
|
|
501
|
+
*
|
|
502
|
+
* @returns `true` if actions are defined directly or within build
|
|
503
|
+
* configurations, `false` otherwise.
|
|
504
|
+
*/
|
|
505
|
+
hasXpmActions(): boolean {
|
|
506
|
+
const json = this.jsonPackage
|
|
507
|
+
|
|
508
|
+
try {
|
|
509
|
+
if (
|
|
510
|
+
json?.xpack.actions !== undefined &&
|
|
511
|
+
Object.keys(json.xpack.actions).length > 0
|
|
512
|
+
) {
|
|
513
|
+
return true
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (
|
|
517
|
+
json?.xpack.buildConfigurations !== undefined &&
|
|
518
|
+
Object.keys(json.xpack.buildConfigurations).length > 0
|
|
519
|
+
) {
|
|
520
|
+
for (const buildConfigurationName of Object.keys(
|
|
521
|
+
json.xpack.buildConfigurations
|
|
522
|
+
)) {
|
|
523
|
+
const buildConfiguration: JsonBuildConfiguration =
|
|
524
|
+
json.xpack.buildConfigurations[buildConfigurationName]
|
|
525
|
+
if (hasLiquidSyntax(buildConfigurationName)) {
|
|
526
|
+
const buildConfigurationTemplate =
|
|
527
|
+
buildConfiguration as JsonBuildConfigurationTemplate
|
|
528
|
+
if (
|
|
529
|
+
buildConfigurationTemplate.template.actions !== undefined &&
|
|
530
|
+
Object.keys(buildConfigurationTemplate.template.actions).length >
|
|
531
|
+
0
|
|
532
|
+
) {
|
|
533
|
+
return true
|
|
534
|
+
}
|
|
535
|
+
} else {
|
|
536
|
+
const buildConfigurationContent =
|
|
537
|
+
buildConfiguration as JsonBuildConfigurationContent
|
|
538
|
+
if (
|
|
539
|
+
buildConfigurationContent.actions !== undefined &&
|
|
540
|
+
Object.keys(buildConfigurationContent.actions).length > 0
|
|
541
|
+
) {
|
|
542
|
+
return true
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
548
|
+
} catch (error) {
|
|
549
|
+
// In case xpack is not an option to get its properties.
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
return false
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Retrieves the minimum required <b>xpm</b> version specified by the package.
|
|
557
|
+
*
|
|
558
|
+
* @returns The minimum required <b>xpm</b> version without pre-release
|
|
559
|
+
* suffixes, or
|
|
560
|
+
* undefined if not specified.
|
|
561
|
+
*/
|
|
562
|
+
getMinimumXpmRequired(): string | undefined {
|
|
563
|
+
const log = this._log
|
|
564
|
+
const jsonPackage = this.jsonPackage
|
|
565
|
+
|
|
566
|
+
log.trace(`${Package.name}.getMinimumXpmRequired()`)
|
|
567
|
+
|
|
568
|
+
const version = jsonPackage?.xpack.minimumXpmRequired
|
|
569
|
+
if (version === undefined) {
|
|
570
|
+
return undefined
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (!isString(version)) {
|
|
574
|
+
return undefined
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Remove the pre-release part.
|
|
578
|
+
return version.replace(/-.*$/, '')
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Validates the minimum required <b>xpm</b> version against the
|
|
583
|
+
* installed CLI.
|
|
584
|
+
*
|
|
585
|
+
* @remarks
|
|
586
|
+
* This method ensures that packages requiring specific <b>xpm</b>
|
|
587
|
+
* features or bug
|
|
588
|
+
* fixes can enforce a minimum version requirement, preventing runtime
|
|
589
|
+
* errors or unexpected behavior with older <b>xpm</b> versions.
|
|
590
|
+
*
|
|
591
|
+
* Validation workflow:
|
|
592
|
+
*
|
|
593
|
+
* <ol>
|
|
594
|
+
* <li>Check if package is an <b>xpm</b> package with
|
|
595
|
+
* <code>minimumXpmRequired</code> set.</li>
|
|
596
|
+
* <li>Clean the required version by removing pre-release suffixes.</li>
|
|
597
|
+
* <li>Load the <b>xpm</b> CLI's <code>package.json</code> from the
|
|
598
|
+
* provided root folder.</li>
|
|
599
|
+
* <li>Extract and clean the installed <b>xpm</b> version.</li>
|
|
600
|
+
* <li>Compare versions using semver to determine if upgrade is needed.</li>
|
|
601
|
+
* <li>Throw <code>PrerequisitesError</code> if installed version is
|
|
602
|
+
* too old.</li>
|
|
603
|
+
* </ol>
|
|
604
|
+
*
|
|
605
|
+
* Pre-release suffixes are stripped from both versions to ensure that
|
|
606
|
+
* pre-release builds satisfy version requirements (e.g., 1.0.0-beta
|
|
607
|
+
* satisfies minimumXpmRequired: 1.0.0).
|
|
608
|
+
*
|
|
609
|
+
* @param xpmRootFolderPath - The folder path to the <b>xpm</b> CLI package.
|
|
610
|
+
* @returns The cleaned minimum required version, or undefined if no check is
|
|
611
|
+
* required.
|
|
612
|
+
*
|
|
613
|
+
* @throws {@link PrerequisitesError}
|
|
614
|
+
* If the installed <b>xpm</b> version is lower than the required minimum.
|
|
615
|
+
*/
|
|
616
|
+
async checkMinimumXpmRequired({
|
|
617
|
+
xpmRootFolderPath,
|
|
618
|
+
}: {
|
|
619
|
+
xpmRootFolderPath: string
|
|
620
|
+
}): Promise<string | undefined> {
|
|
621
|
+
const log = this._log
|
|
622
|
+
const jsonPackage = this.jsonPackage
|
|
623
|
+
|
|
624
|
+
log.trace(`${Package.name}.checkMinimumXpmRequired()`)
|
|
625
|
+
|
|
626
|
+
if (!this.isXpmPackage()) {
|
|
627
|
+
// Not in an xpm package.
|
|
628
|
+
return undefined
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const minimumXpmRequired = this.getMinimumXpmRequired()
|
|
632
|
+
if (!minimumXpmRequired) {
|
|
633
|
+
log.trace('minimumXpmRequired not used, no checks')
|
|
634
|
+
return undefined
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
log.trace(`minimumXpmRequired: ${minimumXpmRequired}`)
|
|
638
|
+
|
|
639
|
+
let jsonXpmCliPackage: JsonXpmPackage | undefined
|
|
640
|
+
try {
|
|
641
|
+
const cliXpmPackage = new Package({
|
|
642
|
+
log,
|
|
643
|
+
packageFolderPath: xpmRootFolderPath,
|
|
644
|
+
})
|
|
645
|
+
jsonXpmCliPackage = await cliXpmPackage.readPackageDotJson({
|
|
646
|
+
withThrow: true,
|
|
647
|
+
})
|
|
648
|
+
} catch (error) {
|
|
649
|
+
if (error instanceof Error) {
|
|
650
|
+
log.trace(error.message)
|
|
651
|
+
// Safety net: This handles non-Error exceptions. Node.js fs operations
|
|
652
|
+
// and the Package class consistently throw Error instances, but this
|
|
653
|
+
// provides defensive handling for unexpected error types that might
|
|
654
|
+
// occur in edge cases or future code changes.
|
|
655
|
+
/* c8 ignore start - safety net, currently all are Errors */
|
|
656
|
+
} else {
|
|
657
|
+
log.trace(error)
|
|
658
|
+
}
|
|
659
|
+
/* c8 ignore stop */
|
|
660
|
+
return undefined
|
|
661
|
+
}
|
|
662
|
+
assert(jsonXpmCliPackage, 'jsonXpmCliPackage is required')
|
|
663
|
+
log.trace(jsonXpmCliPackage.version)
|
|
664
|
+
|
|
665
|
+
if (!jsonXpmCliPackage.version) {
|
|
666
|
+
return undefined
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Remove the pre-release part.
|
|
670
|
+
const xpmVersion = semver.clean(
|
|
671
|
+
jsonXpmCliPackage.version.replace(/-.*$/, '')
|
|
672
|
+
)
|
|
673
|
+
if (!xpmVersion) {
|
|
674
|
+
return undefined
|
|
675
|
+
}
|
|
676
|
+
if (semver.lt(xpmVersion, minimumXpmRequired)) {
|
|
677
|
+
assert(jsonPackage?.name, 'jsonPackage.name is required')
|
|
678
|
+
throw new PrerequisitesError(
|
|
679
|
+
`package '${jsonPackage.name}' ` +
|
|
680
|
+
`requires xpm v${minimumXpmRequired} or later, please upgrade`
|
|
681
|
+
)
|
|
682
|
+
}
|
|
683
|
+
// Check passed.
|
|
684
|
+
return minimumXpmRequired
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Parses an npm package specifier into its components.
|
|
689
|
+
*
|
|
690
|
+
* @remarks
|
|
691
|
+
* npm package specifiers can take several forms:
|
|
692
|
+
*
|
|
693
|
+
* <ul>
|
|
694
|
+
* <li><b>Unscoped without version:</b> <code>package-name</code></li>
|
|
695
|
+
* <li><b>Unscoped with version:</b> <code>package-name\@1.2.3</code></li>
|
|
696
|
+
* <li><b>Scoped without version:</b> <code>\@scope/package-name</code></li>
|
|
697
|
+
* <li><b>Scoped with version:</b>
|
|
698
|
+
* <code>\@scope/package-name\@1.2.3</code></li>
|
|
699
|
+
* </ul>
|
|
700
|
+
*
|
|
701
|
+
* Parsing strategy:
|
|
702
|
+
*
|
|
703
|
+
* <ol>
|
|
704
|
+
* <li>If specifier starts with <code>\@</code>, extract scope and handle
|
|
705
|
+
* scoped format.</li>
|
|
706
|
+
* <li>Split on <code>/</code> to separate scope from name\@version.</li>
|
|
707
|
+
* <li>Split the second part on <code>\@</code> to separate name from
|
|
708
|
+
* version.</li>
|
|
709
|
+
* <li>For unscoped packages, split directly on <code>\@</code> to separate
|
|
710
|
+
* name from version.</li>
|
|
711
|
+
* </ol>
|
|
712
|
+
*
|
|
713
|
+
* The parser handles all valid npm package specifier formats and returns
|
|
714
|
+
* structured components for downstream processing. Invalid formats with
|
|
715
|
+
* multiple slashes are rejected.
|
|
716
|
+
*
|
|
717
|
+
* @param npmPackageSpecifier - The npm package specifier to parse.
|
|
718
|
+
* @returns The parsed package specifier components.
|
|
719
|
+
*
|
|
720
|
+
* @throws {@link InputError}
|
|
721
|
+
* If the specifier is not a valid package name format.
|
|
722
|
+
*/
|
|
723
|
+
parsePackageSpecifier({
|
|
724
|
+
npmPackageSpecifier,
|
|
725
|
+
}: {
|
|
726
|
+
npmPackageSpecifier: string
|
|
727
|
+
}): JsonPackageSpecifier {
|
|
728
|
+
assert(npmPackageSpecifier, 'npmPackageSpecifier is required')
|
|
729
|
+
|
|
730
|
+
const log = this._log
|
|
731
|
+
|
|
732
|
+
let scope
|
|
733
|
+
let name
|
|
734
|
+
let version
|
|
735
|
+
|
|
736
|
+
if (npmPackageSpecifier.startsWith('@')) {
|
|
737
|
+
const arr = npmPackageSpecifier.split('/')
|
|
738
|
+
if (arr.length > 2) {
|
|
739
|
+
throw new InputError(`'${npmPackageSpecifier}' not a package name`)
|
|
740
|
+
}
|
|
741
|
+
scope = arr[0]
|
|
742
|
+
if (arr.length > 1) {
|
|
743
|
+
const arr2 = arr[1].split('@')
|
|
744
|
+
name = arr2[0]
|
|
745
|
+
if (arr2.length > 1) {
|
|
746
|
+
version = arr2[1]
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
} else {
|
|
750
|
+
const arr2 = npmPackageSpecifier.split('@')
|
|
751
|
+
name = arr2[0]
|
|
752
|
+
if (arr2.length > 1) {
|
|
753
|
+
version = arr2[1]
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
log.trace(
|
|
757
|
+
`${npmPackageSpecifier} => ` +
|
|
758
|
+
`${scope ?? '?'} ${name ?? '?'} ${version ?? '?'}`
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
return { scope, name, version }
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// ----------------------------------------------------------------------------
|