@xpack/xpm-lib 3.1.2 → 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.
Files changed (202) hide show
  1. package/README.md +16 -212
  2. package/dist/classes/actions.d.ts +58 -0
  3. package/dist/classes/actions.d.ts.map +1 -0
  4. package/dist/classes/actions.js +250 -0
  5. package/dist/classes/actions.js.map +1 -0
  6. package/dist/classes/build-configurations.d.ts +78 -0
  7. package/dist/classes/build-configurations.d.ts.map +1 -0
  8. package/dist/classes/build-configurations.js +489 -0
  9. package/dist/classes/build-configurations.js.map +1 -0
  10. package/dist/classes/combinations-generator.d.ts +19 -0
  11. package/dist/classes/combinations-generator.d.ts.map +1 -0
  12. package/dist/classes/combinations-generator.js +48 -0
  13. package/dist/classes/combinations-generator.js.map +1 -0
  14. package/dist/classes/data-model.d.ts +21 -0
  15. package/dist/classes/data-model.d.ts.map +1 -0
  16. package/dist/classes/data-model.js +47 -0
  17. package/dist/classes/data-model.js.map +1 -0
  18. package/dist/classes/errors.d.ts +13 -0
  19. package/dist/classes/errors.d.ts.map +1 -0
  20. package/dist/classes/errors.js +13 -0
  21. package/dist/classes/errors.js.map +1 -0
  22. package/dist/classes/init-template-base.d.ts +47 -0
  23. package/dist/classes/init-template-base.d.ts.map +1 -0
  24. package/dist/classes/init-template-base.js +358 -0
  25. package/dist/classes/init-template-base.js.map +1 -0
  26. package/dist/classes/liquid-drop.d.ts +28 -0
  27. package/dist/classes/liquid-drop.d.ts.map +1 -0
  28. package/dist/classes/liquid-drop.js +70 -0
  29. package/dist/classes/liquid-drop.js.map +1 -0
  30. package/dist/classes/liquid-engine.d.ts +7 -0
  31. package/dist/classes/liquid-engine.d.ts.map +1 -0
  32. package/dist/classes/liquid-engine.js +72 -0
  33. package/dist/classes/liquid-engine.js.map +1 -0
  34. package/dist/classes/package.d.ts +31 -0
  35. package/dist/classes/package.d.ts.map +1 -0
  36. package/dist/classes/package.js +268 -0
  37. package/dist/classes/package.js.map +1 -0
  38. package/dist/classes/platform-detector.d.ts +14 -0
  39. package/dist/classes/platform-detector.d.ts.map +1 -0
  40. package/dist/classes/platform-detector.js +26 -0
  41. package/dist/classes/platform-detector.js.map +1 -0
  42. package/dist/classes/policies.d.ts +14 -0
  43. package/dist/classes/policies.d.ts.map +1 -0
  44. package/dist/classes/policies.js +20 -0
  45. package/dist/classes/policies.js.map +1 -0
  46. package/dist/classes/template-expander.d.ts +29 -0
  47. package/dist/classes/template-expander.d.ts.map +1 -0
  48. package/dist/classes/template-expander.js +62 -0
  49. package/dist/classes/template-expander.js.map +1 -0
  50. package/dist/data/substitutions-variables.d.ts +43 -0
  51. package/dist/data/substitutions-variables.d.ts.map +1 -0
  52. package/dist/{lib → data}/substitutions-variables.js +1 -16
  53. package/dist/data/substitutions-variables.js.map +1 -0
  54. package/dist/functions/chmod-recursively.d.ts +9 -0
  55. package/dist/functions/chmod-recursively.d.ts.map +1 -0
  56. package/dist/functions/chmod-recursively.js +66 -0
  57. package/dist/functions/chmod-recursively.js.map +1 -0
  58. package/dist/functions/filter-paths.d.ts +5 -0
  59. package/dist/functions/filter-paths.d.ts.map +1 -0
  60. package/dist/functions/filter-paths.js +16 -0
  61. package/dist/functions/filter-paths.js.map +1 -0
  62. package/dist/functions/is-something.d.ts +9 -0
  63. package/dist/functions/is-something.d.ts.map +1 -0
  64. package/dist/functions/is-something.js +25 -0
  65. package/dist/functions/is-something.js.map +1 -0
  66. package/dist/functions/matrix-expander.d.ts +17 -0
  67. package/dist/functions/matrix-expander.d.ts.map +1 -0
  68. package/dist/functions/matrix-expander.js +52 -0
  69. package/dist/functions/matrix-expander.js.map +1 -0
  70. package/dist/functions/perform-substitutions.d.ts +12 -0
  71. package/dist/functions/perform-substitutions.d.ts.map +1 -0
  72. package/dist/functions/perform-substitutions.js +76 -0
  73. package/dist/functions/perform-substitutions.js.map +1 -0
  74. package/dist/functions/utils.d.ts +8 -0
  75. package/dist/functions/utils.d.ts.map +1 -0
  76. package/dist/functions/utils.js +16 -0
  77. package/dist/functions/utils.js.map +1 -0
  78. package/dist/index.d.ts +22 -15
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +22 -29
  81. package/dist/index.js.map +1 -1
  82. package/dist/{lib/types.d.ts → types/json.d.ts} +31 -22
  83. package/dist/types/json.d.ts.map +1 -0
  84. package/dist/types/json.js +2 -0
  85. package/dist/types/json.js.map +1 -0
  86. package/dist/types/xpm-init-template.d.ts +21 -0
  87. package/dist/types/xpm-init-template.d.ts.map +1 -0
  88. package/dist/types/xpm-init-template.js +2 -0
  89. package/dist/types/xpm-init-template.js.map +1 -0
  90. package/dist/types/xpm.d.ts +16 -0
  91. package/dist/types/xpm.d.ts.map +1 -0
  92. package/dist/types/xpm.js +2 -0
  93. package/dist/types/xpm.js.map +1 -0
  94. package/package.json +53 -44
  95. package/src/CODE-REVIEW.md +2167 -0
  96. package/src/README.md +393 -6
  97. package/src/classes/actions.ts +1157 -0
  98. package/src/classes/build-configurations.ts +2127 -0
  99. package/src/classes/combinations-generator.ts +331 -0
  100. package/src/classes/data-model.ts +337 -0
  101. package/src/classes/errors.ts +105 -0
  102. package/src/classes/init-template-base.ts +1028 -0
  103. package/src/classes/liquid-drop.ts +376 -0
  104. package/src/classes/liquid-engine.ts +249 -0
  105. package/src/classes/package.ts +765 -0
  106. package/src/classes/platform-detector.ts +237 -0
  107. package/src/classes/policies.ts +200 -0
  108. package/src/classes/template-expander.ts +330 -0
  109. package/src/data/substitutions-variables.ts +390 -0
  110. package/src/functions/chmod-recursively.ts +195 -0
  111. package/src/functions/filter-paths.ts +126 -0
  112. package/src/functions/is-something.ts +223 -0
  113. package/src/functions/matrix-expander.ts +172 -0
  114. package/src/functions/perform-substitutions.ts +253 -0
  115. package/src/functions/utils.ts +151 -0
  116. package/src/index.ts +72 -19
  117. package/src/types/json.ts +519 -0
  118. package/src/types/xpm-init-template.ts +282 -0
  119. package/src/types/xpm.ts +162 -0
  120. package/dist/lib/chmod-recursive.d.ts +0 -7
  121. package/dist/lib/chmod-recursive.d.ts.map +0 -1
  122. package/dist/lib/chmod-recursive.js +0 -81
  123. package/dist/lib/chmod-recursive.js.map +0 -1
  124. package/dist/lib/errors.d.ts +0 -11
  125. package/dist/lib/errors.d.ts.map +0 -1
  126. package/dist/lib/errors.js +0 -26
  127. package/dist/lib/errors.js.map +0 -1
  128. package/dist/lib/functions/chmod-recursive.d.ts +0 -7
  129. package/dist/lib/functions/chmod-recursive.d.ts.map +0 -1
  130. package/dist/lib/functions/chmod-recursive.js +0 -81
  131. package/dist/lib/functions/chmod-recursive.js.map +0 -1
  132. package/dist/lib/functions/perform-substitutions.d.ts +0 -20
  133. package/dist/lib/functions/perform-substitutions.d.ts.map +0 -1
  134. package/dist/lib/functions/perform-substitutions.js +0 -85
  135. package/dist/lib/functions/perform-substitutions.js.map +0 -1
  136. package/dist/lib/functions/utils.d.ts +0 -30
  137. package/dist/lib/functions/utils.d.ts.map +0 -1
  138. package/dist/lib/functions/utils.js +0 -70
  139. package/dist/lib/functions/utils.js.map +0 -1
  140. package/dist/lib/init-template-base.d.ts +0 -46
  141. package/dist/lib/init-template-base.d.ts.map +0 -1
  142. package/dist/lib/init-template-base.js +0 -281
  143. package/dist/lib/init-template-base.js.map +0 -1
  144. package/dist/lib/liquid-actions.d.ts +0 -37
  145. package/dist/lib/liquid-actions.d.ts.map +0 -1
  146. package/dist/lib/liquid-actions.js +0 -148
  147. package/dist/lib/liquid-actions.js.map +0 -1
  148. package/dist/lib/liquid-build-configurations.d.ts +0 -47
  149. package/dist/lib/liquid-build-configurations.d.ts.map +0 -1
  150. package/dist/lib/liquid-build-configurations.js +0 -282
  151. package/dist/lib/liquid-build-configurations.js.map +0 -1
  152. package/dist/lib/liquid-drop.d.ts +0 -13
  153. package/dist/lib/liquid-drop.d.ts.map +0 -1
  154. package/dist/lib/liquid-drop.js +0 -56
  155. package/dist/lib/liquid-drop.js.map +0 -1
  156. package/dist/lib/liquid-engine.d.ts +0 -5
  157. package/dist/lib/liquid-engine.d.ts.map +0 -1
  158. package/dist/lib/liquid-engine.js +0 -85
  159. package/dist/lib/liquid-engine.js.map +0 -1
  160. package/dist/lib/liquid-package.d.ts +0 -17
  161. package/dist/lib/liquid-package.d.ts.map +0 -1
  162. package/dist/lib/liquid-package.js +0 -70
  163. package/dist/lib/liquid-package.js.map +0 -1
  164. package/dist/lib/package.d.ts +0 -66
  165. package/dist/lib/package.d.ts.map +0 -1
  166. package/dist/lib/package.js +0 -700
  167. package/dist/lib/package.js.map +0 -1
  168. package/dist/lib/perform-substitutions.d.ts +0 -20
  169. package/dist/lib/perform-substitutions.d.ts.map +0 -1
  170. package/dist/lib/perform-substitutions.js +0 -85
  171. package/dist/lib/perform-substitutions.js.map +0 -1
  172. package/dist/lib/policies.d.ts +0 -14
  173. package/dist/lib/policies.d.ts.map +0 -1
  174. package/dist/lib/policies.js +0 -33
  175. package/dist/lib/policies.js.map +0 -1
  176. package/dist/lib/substitutions-variables.d.ts +0 -117
  177. package/dist/lib/substitutions-variables.d.ts.map +0 -1
  178. package/dist/lib/substitutions-variables.js.map +0 -1
  179. package/dist/lib/types.d.ts.map +0 -1
  180. package/dist/lib/types.js +0 -13
  181. package/dist/lib/types.js.map +0 -1
  182. package/dist/lib/utils.d.ts +0 -30
  183. package/dist/lib/utils.d.ts.map +0 -1
  184. package/dist/lib/utils.js +0 -70
  185. package/dist/lib/utils.js.map +0 -1
  186. package/dist/tsconfig.tsbuildinfo +0 -1
  187. package/src/lib/errors.ts +0 -29
  188. package/src/lib/functions/chmod-recursive.ts +0 -103
  189. package/src/lib/functions/perform-substitutions.ts +0 -116
  190. package/src/lib/functions/utils.ts +0 -88
  191. package/src/lib/init-template-base.ts +0 -408
  192. package/src/lib/liquid-actions.ts +0 -223
  193. package/src/lib/liquid-build-configurations.ts +0 -433
  194. package/src/lib/liquid-drop.ts +0 -99
  195. package/src/lib/liquid-engine.ts +0 -135
  196. package/src/lib/liquid-package.ts +0 -108
  197. package/src/lib/package.ts +0 -947
  198. package/src/lib/policies.ts +0 -51
  199. package/src/lib/substitutions-variables.ts +0 -177
  200. package/src/lib/types.ts +0 -109
  201. package/src/package.json +0 -3
  202. package/src/tsconfig.json +0 -10
@@ -1,116 +0,0 @@
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
- /* eslint max-len: [ "error", 80, { "ignoreUrls": true } ] */
13
-
14
- // ----------------------------------------------------------------------------
15
-
16
- import assert from 'node:assert'
17
-
18
- import { Context } from 'liquidjs'
19
-
20
- import { Logger } from '@xpack/logger'
21
-
22
- import { XpmLiquidEngine } from '../liquid-engine.js'
23
- import { XpmLiquidPropertiesDrop } from '../liquid-drop.js'
24
- import { XpmLiquidSubstitutionsVariables } from '../substitutions-variables.js'
25
-
26
- // ----------------------------------------------------------------------------
27
- /**
28
- * Perform substitution on the input string.
29
- * Repeat until no more Liquid variables or tags are identified.
30
- *
31
- * @param input - The input string, possibly with substitutions.
32
- * @param map - The substitution map.
33
- * @returns The substituted string.
34
- *
35
- * @throws Liquid exceptions
36
- */
37
-
38
- export async function performSubstitutions({
39
- log,
40
- engine,
41
- input,
42
- substitutionsVariables,
43
- }: {
44
- log: Logger
45
- engine: XpmLiquidEngine
46
- input: string
47
- substitutionsVariables: XpmLiquidSubstitutionsVariables
48
- }): Promise<string> {
49
- assert(substitutionsVariables)
50
-
51
- if (input.trim() === '') {
52
- // Spare it the trouble for empty strings.
53
- return input
54
- }
55
-
56
- let context
57
- // Wrap properties into a liquid drop (a mechanism to process
58
- // substitutions immediately).
59
- if (Object.keys(substitutionsVariables.properties).length > 0) {
60
- context = new Context({
61
- ...substitutionsVariables,
62
- properties: new XpmLiquidPropertiesDrop({
63
- log,
64
- engine,
65
- properties: substitutionsVariables.properties,
66
- }),
67
- })
68
- } else {
69
- context = new Context(substitutionsVariables)
70
- }
71
-
72
- log.trace(`performSubstitutions('${input}')`)
73
-
74
- let current: string = input
75
- let substituted: string = current
76
- let count = 0
77
-
78
- // Iterate until all substitutions are done.
79
- while (current.includes('{{') || current.includes('{%')) {
80
- ++count
81
- // May throw.
82
- try {
83
- substituted = (await engine.parseAndRender(current, context)) as string
84
-
85
- /* c8 ignore start */ /* istanbul ignore next */
86
- if (substituted === current) {
87
- // If nothing changed, we're done.
88
- // This test is just a safety net, normally should not get there.
89
- log.warn(
90
- `performSubstitutions() ${String(count)} => |`,
91
- substituted,
92
- '| did not change'
93
- )
94
-
95
- break
96
- } /* c8 ignore stop */
97
- } catch (ex) {
98
- if (ex instanceof Error) {
99
- // log.error(ex)
100
- log.error(ex.message)
101
- } else {
102
- log.error(new Error(String(ex)))
103
- }
104
- // Return the current (unsubstituted) value.
105
- substituted = current
106
- break
107
- }
108
-
109
- log.trace(`performSubstitutions() ${String(count)} => |`, substituted, '|')
110
- current = substituted
111
- }
112
-
113
- return substituted
114
- }
115
-
116
- // ----------------------------------------------------------------------------
@@ -1,88 +0,0 @@
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
- /* eslint max-len: [ "error", 80, { "ignoreUrls": true } ] */
13
-
14
- // ----------------------------------------------------------------------------
15
-
16
- import * as os from 'node:os'
17
-
18
- // ----------------------------------------------------------------------------
19
-
20
- export function isPrimitive(value: unknown): boolean {
21
- return (
22
- (typeof value !== 'object' && typeof value !== 'function') || value === null
23
- )
24
- }
25
-
26
- export function isJsonObject(value: unknown): boolean {
27
- return value !== undefined && !isPrimitive(value) && !Array.isArray(value)
28
- }
29
-
30
- export function isJsonArray(value: unknown): boolean {
31
- return value !== undefined && Array.isArray(value)
32
- }
33
-
34
- export function isNonEmptyJsonObject(value: unknown): boolean {
35
- return isJsonObject(value) && Object.keys(value as object).length > 0
36
- }
37
-
38
- export function isString(value: unknown): value is string {
39
- return typeof value === 'string'
40
- }
41
-
42
- /**
43
- * Replace non alphanumeric chars with dashes to make the paths
44
- * comply with filesystem names.
45
- *
46
- * @param {string} input A path candidate.
47
- * @returns {string} A validated path.
48
- */
49
- export function filterPath(input: string): string {
50
- /* c8 ignore start */ /* istanbul ignore next */
51
- const fixed =
52
- os.platform() === 'win32'
53
- ? input.replace(/[^a-zA-Z0-9\\:]+/g, '-')
54
- : input.replace(/[^a-zA-Z0-9/]+/g, '-')
55
- /* c8 ignore stop */
56
-
57
- return fixed.replace(/--/g, '-')
58
- }
59
-
60
- /**
61
- * Replace non alphanumeric chars with dashes to make the paths
62
- * comply with Posix filesystem names.
63
- *
64
- * @param {string} input A path candidate.
65
- * @returns {string} A validated path.
66
- */
67
- export function filterPosixPath(input: string): string {
68
- /* istanbul ignore next */
69
- const fixed = input.replace(/[^a-zA-Z0-9/]+/g, '-')
70
-
71
- return fixed.replace(/--/g, '-')
72
- }
73
-
74
- /**
75
- * Replace non alphanumeric chars with dashes to make the paths
76
- * comply with Windows filesystem names.
77
- *
78
- * @param {string} input A path candidate.
79
- * @returns {string} A validated path.
80
- */
81
- export function filterWin32Path(input: string): string {
82
- /* istanbul ignore next */
83
- const fixed = input.replace(/[^a-zA-Z0-9\\:]+/g, '-')
84
-
85
- return fixed.replace(/--/g, '-')
86
- }
87
-
88
- // ----------------------------------------------------------------------------
@@ -1,408 +0,0 @@
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
- /* eslint max-len: [ "error", 80, { "ignoreUrls": true } ] */
13
-
14
- // ----------------------------------------------------------------------------
15
-
16
- import assert from 'node:assert'
17
- import * as util from 'node:util'
18
- import * as readline from 'node:readline/promises'
19
- import * as path from 'node:path'
20
- import * as fs from 'node:fs/promises'
21
-
22
- // https://www.npmjs.com/package/make-dir
23
- import { makeDirectory } from 'make-dir'
24
-
25
- // https://www.npmjs.com/package/cp-file
26
- import { copyFile } from 'cp-file'
27
-
28
- // https://www.npmjs.com/package/liquidjs
29
- import { Liquid } from 'liquidjs'
30
-
31
- import { Logger } from '@xpack/logger'
32
-
33
- import { XpmContext } from './types.js'
34
- import { XpmOutputError, XpmSyntaxError } from './errors.js'
35
-
36
- // ----------------------------------------------------------------------------
37
-
38
- export type XpmInitTemplatePropertiesDefinitions = Record<
39
- string,
40
- XpmInitTemplatePropertiesDefinition
41
- >
42
-
43
- export interface XpmInitTemplatePropertiesDefinition {
44
- label: string
45
- description: string
46
- type: 'select' | 'string' | 'number' | 'boolean'
47
- items: Record<string, string | XpmInitTemplateItemValue>
48
- isMandatory?: boolean
49
- default?: string | number | boolean
50
- }
51
-
52
- export type XpmInitTemplatePlatform =
53
- | 'linux'
54
- | 'linux-x64'
55
- | 'linux-arm64'
56
- | 'win32'
57
- | 'darwin'
58
- | 'darwin-x64'
59
- | 'darwin-arm64'
60
-
61
- export interface XpmInitTemplateItemValue {
62
- platforms: XpmInitTemplatePlatform[]
63
- message: string
64
- }
65
-
66
- export interface XpmInitTemplateSubstitutionsVariables {
67
- properties: Record<string, string | boolean | number>
68
- [key: string]: unknown
69
- }
70
-
71
- // ----------------------------------------------------------------------------
72
-
73
- export abstract class XpmInitTemplateBase {
74
- // --------------------------------------------------------------------------
75
- // Members.
76
-
77
- context: XpmContext
78
- log: Logger
79
-
80
- propertiesDefinitions: XpmInitTemplatePropertiesDefinitions = {}
81
- __dirname: string
82
- templatesPath: string
83
- engine: Liquid
84
- substitutionsVariables?: XpmInitTemplateSubstitutionsVariables
85
-
86
- // --------------------------------------------------------------------------
87
- // Constructor.
88
-
89
- constructor({
90
- context,
91
- __dirname,
92
- templatesPath,
93
- propertiesDefinitions,
94
- }: {
95
- context: XpmContext
96
- __dirname: string
97
- templatesPath: string
98
- propertiesDefinitions: XpmInitTemplatePropertiesDefinitions
99
- }) {
100
- assert(context)
101
- assert(context.log)
102
- assert(__dirname)
103
- assert(templatesPath)
104
- assert(propertiesDefinitions)
105
-
106
- this.context = context
107
- this.log = context.log
108
- this.propertiesDefinitions = propertiesDefinitions
109
- this.__dirname = __dirname
110
- this.templatesPath = templatesPath
111
-
112
- // https://liquidjs.com
113
- this.engine = new Liquid({
114
- root: this.templatesPath,
115
- cache: false,
116
- strictFilters: true, // default: false
117
- strictVariables: true, // default: false
118
- trimTagRight: false, // default: false
119
- trimTagLeft: false, // default: false
120
- greedy: false,
121
- })
122
- }
123
-
124
- async run(): Promise<number> {
125
- const log = this.log
126
- log.trace(`${this.constructor.name}.run()`)
127
-
128
- log.info()
129
-
130
- const context = this.context
131
- const config = context.config
132
-
133
- assert(config.properties)
134
-
135
- let isError = false
136
- for (const [key, val] of Object.entries(config.properties)) {
137
- try {
138
- config.properties[key] = this.validateValue(key, val as string)
139
- } catch (err) {
140
- if (err instanceof Error) {
141
- log.error(err.message)
142
- }
143
- isError = true
144
- }
145
- }
146
- if (isError) {
147
- throw new XpmSyntaxError()
148
- }
149
-
150
- // Properties set by `--property name=value` are in `config.properties`.
151
-
152
- // If there is at least one mandatory property without an explicit value,
153
- // enter the interactive mode and ask for the missing values.
154
-
155
- const mustAsk = Object.keys(this.propertiesDefinitions).some((key) => {
156
- return (
157
- this.propertiesDefinitions[key].isMandatory && !config.properties?.[key]
158
- )
159
- })
160
-
161
- let isInteractive
162
- if (mustAsk) {
163
- // Need to ask for more values.
164
- if (!(process.stdin.isTTY && process.stdout.isTTY)) {
165
- throw new XpmSyntaxError('Interactive mode not possible without a TTY.')
166
- }
167
-
168
- await this.askForMoreValues()
169
- log.trace(util.inspect(config.properties))
170
-
171
- // Reset start time to skip interactive time.
172
- context.startTime = Date.now()
173
- isInteractive = true
174
- } else {
175
- // Properties without explicit values get their defaults.
176
- Object.entries(this.propertiesDefinitions).forEach(([key, val]) => {
177
- assert(config.properties)
178
- if (!config.properties[key] && val.default) {
179
- config.properties[key] = val.default
180
- }
181
- })
182
- isInteractive = false
183
- }
184
-
185
- const currentTime = new Date()
186
-
187
- const substitutionsVariables: XpmInitTemplateSubstitutionsVariables = {
188
- // Spread all config properties.
189
- ...config.properties,
190
- // Also pass the properties grouped.
191
- properties: config.properties,
192
- propertiesNames: Object.keys(config.properties),
193
- projectName: config.projectName,
194
- year: currentTime.getFullYear().toString(),
195
- }
196
-
197
- this.substitutionsVariables = substitutionsVariables
198
- await this.generate(isInteractive)
199
-
200
- return 0 // success
201
- }
202
-
203
- abstract generate(isInteractive: boolean): Promise<void>
204
-
205
- validateValue(name: string, value: string): string | boolean | number {
206
- const propDef = this.propertiesDefinitions[name]
207
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
208
- if (propDef === undefined) {
209
- throw new Error(`Unsupported property '${name}'`)
210
- }
211
- if (propDef.type === 'select') {
212
- if (propDef.items[value]) {
213
- if (typeof propDef.items[value] === 'string') {
214
- return value
215
- } else if (
216
- typeof propDef.items[value] === 'object' &&
217
- this.isPlatformSupported(propDef.items[value].platforms)
218
- ) {
219
- return value
220
- }
221
- }
222
- } else if (propDef.type === 'boolean') {
223
- if (value === 'true') {
224
- return true
225
- } else if (value === 'false') {
226
- return false
227
- }
228
- } else if (propDef.type === 'number') {
229
- return Number(value)
230
- }
231
-
232
- if (value === '' && propDef.default !== undefined) {
233
- return propDef.default
234
- }
235
-
236
- throw new Error(`Unsupported value '${value}' for property '${name}'`)
237
- }
238
-
239
- async askForMoreValues() {
240
- const context = this.context
241
- const config = context.config
242
-
243
- assert(config.properties)
244
-
245
- const rl = readline.createInterface({
246
- input: process.stdin,
247
- output: process.stdout,
248
- })
249
-
250
- for (const name of Object.keys(this.propertiesDefinitions)) {
251
- if (config.properties[name]) {
252
- continue
253
- }
254
- const definition = this.propertiesDefinitions[name]
255
- let prompt = `${definition.label}?`
256
- if (definition.type === 'select') {
257
- prompt += ' ('
258
- const validItems = []
259
- for (const [ikey, ival] of Object.entries(definition.items)) {
260
- if (typeof ival === 'string') {
261
- validItems.push(ikey)
262
- } else if (
263
- typeof ival === 'object' &&
264
- this.isPlatformSupported(ival.platforms)
265
- ) {
266
- validItems.push(ikey)
267
- }
268
- }
269
- prompt += validItems.join(', ')
270
- prompt += ', ?)'
271
- } else if (definition.type === 'boolean') {
272
- prompt += ' (true, false, ?)'
273
- }
274
- if (definition.default !== undefined) {
275
- prompt += ` [${String(definition.default)}]`
276
- }
277
- prompt += ': '
278
-
279
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
280
- while (true) {
281
- const answer = (await rl.question(prompt)).trim()
282
- try {
283
- const value = this.validateValue(name, answer)
284
- config.properties[name] = value
285
- break
286
- } catch (err) {
287
- if (err instanceof Error) {
288
- this.log.trace(err.message)
289
- }
290
- console.log(definition.description)
291
- if (definition.type === 'select') {
292
- for (const [ikey, ival] of Object.entries(definition.items)) {
293
- if (typeof ival === 'string') {
294
- console.log(`- ${ikey}: ${ival}`)
295
- } else if (
296
- typeof ival === 'object' &&
297
- this.isPlatformSupported(ival.platforms)
298
- ) {
299
- console.log(`- ${ikey}: ${ival.message}`)
300
- }
301
- }
302
- }
303
- }
304
- }
305
- }
306
- }
307
-
308
- isPlatformSupported(platforms: string[] | undefined): boolean {
309
- if (!platforms || platforms.length === 0) {
310
- return false
311
- }
312
-
313
- if (platforms.includes(`${process.platform}-${process.arch}`)) {
314
- return true
315
- }
316
-
317
- if (platforms.includes(process.platform)) {
318
- return true
319
- }
320
-
321
- return false
322
- }
323
-
324
- async copyFile(
325
- sourceFileRelativePath: string,
326
- destinationFilePath = sourceFileRelativePath
327
- ): Promise<void> {
328
- const log = this.log
329
-
330
- await makeDirectory(path.dirname(destinationFilePath))
331
-
332
- const sourceFileAbsolutePath = path.resolve(
333
- this.templatesPath,
334
- sourceFileRelativePath
335
- )
336
- await copyFile(sourceFileAbsolutePath, destinationFilePath)
337
- log.info(`File '${destinationFilePath}' copied.`)
338
- }
339
-
340
- async copyFolder(source: string, destination = source): Promise<void> {
341
- const log = this.log
342
-
343
- await this.copyFolderRecursive(
344
- path.resolve(this.templatesPath, source),
345
- path.resolve(destination)
346
- )
347
- log.info(`Folder '${destination}' copied.`)
348
- }
349
-
350
- async copyFolderRecursive(
351
- sourceFolderPath: string,
352
- destinationFolderPath: string
353
- ): Promise<void> {
354
- // const log = this.log
355
-
356
- await makeDirectory(path.dirname(destinationFolderPath))
357
-
358
- const dirents = await fs.readdir(sourceFolderPath, {
359
- withFileTypes: true,
360
- })
361
-
362
- for (const dirent of dirents) {
363
- // log.trace(dirent.name)
364
-
365
- if (dirent.isDirectory()) {
366
- await this.copyFolderRecursive(
367
- path.join(sourceFolderPath, dirent.name),
368
- path.join(destinationFolderPath, dirent.name)
369
- )
370
- } else {
371
- await copyFile(
372
- path.join(sourceFolderPath, dirent.name),
373
- path.join(destinationFolderPath, dirent.name)
374
- )
375
- }
376
- }
377
- }
378
-
379
- async render(
380
- inputFileRelativePath: string,
381
- outputFileRelativePath: string,
382
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
383
- substitutionsVariables = this.substitutionsVariables!
384
- ): Promise<void> {
385
- const log = this.log
386
-
387
- log.trace(`render(${inputFileRelativePath}, ${outputFileRelativePath})`)
388
-
389
- await makeDirectory(path.dirname(outputFileRelativePath))
390
-
391
- // const headerPath = path.resolve(codePath, `${pnam}.h`)
392
- try {
393
- const fileContent = (await this.engine.renderFile(
394
- inputFileRelativePath,
395
- substitutionsVariables
396
- )) as string
397
-
398
- await fs.writeFile(outputFileRelativePath, fileContent, 'utf8')
399
- } catch (err) {
400
- if (err instanceof Error) {
401
- throw new XpmOutputError(err.message)
402
- }
403
- }
404
- log.info(`File '${outputFileRelativePath}' generated.`)
405
- }
406
- }
407
-
408
- // ----------------------------------------------------------------------------