@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.
- 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,376 @@
|
|
|
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
|
+
// https://www.npmjs.com/package/liquidjs
|
|
15
|
+
import * as liquidjs from 'liquidjs'
|
|
16
|
+
|
|
17
|
+
// https://www.npmjs.com/package/@xpack/logger
|
|
18
|
+
import { Logger } from '@xpack/logger'
|
|
19
|
+
|
|
20
|
+
// ----------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
import { LiquidSubstitutionsStrings } from '../data/substitutions-variables.js'
|
|
23
|
+
import { isJsonObject } from '../functions/is-something.js'
|
|
24
|
+
import { TemplateError } from './errors.js'
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
// https://liquidjs.com/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Configuration parameters for constructing a properties drop instance.
|
|
32
|
+
*
|
|
33
|
+
* @remarks
|
|
34
|
+
* This interface defines the required configuration for creating an
|
|
35
|
+
* instance of {@link LiquidPropertiesDrop}. All properties are mandatory.
|
|
36
|
+
*
|
|
37
|
+
* The parameters provide the Liquid engine for nested template evaluation,
|
|
38
|
+
* the properties map for value lookups, and the logger for diagnostic
|
|
39
|
+
* output during property resolution and substitution.
|
|
40
|
+
*/
|
|
41
|
+
export interface LiquidPropertiesDropConstructorParameters {
|
|
42
|
+
/**
|
|
43
|
+
* The Liquid engine used to render nested substitutions.
|
|
44
|
+
*/
|
|
45
|
+
engine: liquidjs.Liquid
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The properties map used for substitutions.
|
|
49
|
+
*/
|
|
50
|
+
properties: LiquidSubstitutionsStrings
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The logger instance for output and diagnostics.
|
|
54
|
+
*/
|
|
55
|
+
log: Logger
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Liquid drop that resolves `property` values for template substitutions.
|
|
60
|
+
*
|
|
61
|
+
* @remarks
|
|
62
|
+
* This drop exposes properties to the Liquid engine and performs
|
|
63
|
+
* additional substitutions when a property value itself contains Liquid syntax.
|
|
64
|
+
*
|
|
65
|
+
* Implements the Liquid Drop pattern to provide lazy property resolution and
|
|
66
|
+
* recursive template evaluation. When a template accesses `properties.foo`,
|
|
67
|
+
* the Liquid engine calls {@link LiquidPropertiesDrop.liquidMethodMissing}
|
|
68
|
+
* which:
|
|
69
|
+
*
|
|
70
|
+
* <ol>
|
|
71
|
+
* <li>Looks up the property value in the properties map.</li>
|
|
72
|
+
* <li>Checks if the value contains Liquid syntax
|
|
73
|
+
* (<code>\{\{</code> or <code>\{%</code>).</li>
|
|
74
|
+
* <li>If yes, recursively evaluates the value as a Liquid template.</li>
|
|
75
|
+
* <li>Returns the final resolved value.</li>
|
|
76
|
+
* </ol>
|
|
77
|
+
*
|
|
78
|
+
* This enables multi-level property references where one property can
|
|
79
|
+
* reference another, which can reference yet another, with the engine
|
|
80
|
+
* automatically resolving the entire chain.
|
|
81
|
+
*/
|
|
82
|
+
export class LiquidPropertiesDrop extends liquidjs.Drop {
|
|
83
|
+
// --------------------------------------------------------------------------
|
|
84
|
+
// Public Members.
|
|
85
|
+
|
|
86
|
+
// --------------------------------------------------------------------------
|
|
87
|
+
// Protected Members.
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The logger instance for output and diagnostics.
|
|
91
|
+
*/
|
|
92
|
+
protected readonly _log: Logger
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The properties map used for substitutions.
|
|
96
|
+
*/
|
|
97
|
+
protected readonly _properties: LiquidSubstitutionsStrings
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The Liquid engine used to render nested substitutions.
|
|
101
|
+
*/
|
|
102
|
+
protected readonly _engine: liquidjs.Liquid
|
|
103
|
+
|
|
104
|
+
// --------------------------------------------------------------------------
|
|
105
|
+
// Constructor.
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Constructs a properties drop.
|
|
109
|
+
*
|
|
110
|
+
* @param engine - The Liquid engine used to render nested substitutions.
|
|
111
|
+
* @param properties - The properties map used for substitutions.
|
|
112
|
+
* @param log - The logger instance for output and diagnostics.
|
|
113
|
+
*/
|
|
114
|
+
constructor({
|
|
115
|
+
engine,
|
|
116
|
+
properties,
|
|
117
|
+
log,
|
|
118
|
+
}: LiquidPropertiesDropConstructorParameters) {
|
|
119
|
+
super()
|
|
120
|
+
|
|
121
|
+
log.trace(`${LiquidPropertiesDrop.name}()`)
|
|
122
|
+
|
|
123
|
+
this._log = log
|
|
124
|
+
this._engine = engine
|
|
125
|
+
this._properties = properties
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// --------------------------------------------------------------------------
|
|
129
|
+
// Public Methods.
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Resolves a missing property and performs nested substitutions.
|
|
133
|
+
*
|
|
134
|
+
* @remarks
|
|
135
|
+
* Called by the Liquid engine when a property is accessed that doesn't
|
|
136
|
+
* exist as a regular method. This implements the Drop pattern for dynamic
|
|
137
|
+
* property resolution.
|
|
138
|
+
*
|
|
139
|
+
* Resolution process:
|
|
140
|
+
*
|
|
141
|
+
* <ol>
|
|
142
|
+
* <li>Validate the property exists, throw <code>InputError</code> if
|
|
143
|
+
* not.</li>
|
|
144
|
+
* <li>Retrieve the property value (string, array, or object).</li>
|
|
145
|
+
* <li>If object, return as-is for Liquid to access nested properties.</li>
|
|
146
|
+
* <li>If array, join elements into a single string for processing.</li>
|
|
147
|
+
* <li>If the value contains Liquid syntax, recursively render it with the
|
|
148
|
+
* current context to resolve nested references.</li>
|
|
149
|
+
* <li>Return the final resolved value.</li>
|
|
150
|
+
* </ol>
|
|
151
|
+
*
|
|
152
|
+
* Array values are concatenated without separators, allowing properties
|
|
153
|
+
* to span multiple lines in JSON while producing a single string output.
|
|
154
|
+
*
|
|
155
|
+
* @param key - The property name requested by the template.
|
|
156
|
+
* @param context - The Liquid rendering context.
|
|
157
|
+
* @returns The resolved property value.
|
|
158
|
+
*
|
|
159
|
+
* @throws {@link TemplateError}
|
|
160
|
+
* If the property is not defined.
|
|
161
|
+
*/
|
|
162
|
+
override async liquidMethodMissing(
|
|
163
|
+
key: string,
|
|
164
|
+
context: liquidjs.Context
|
|
165
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
166
|
+
): Promise<any> {
|
|
167
|
+
// console.log(key)
|
|
168
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
169
|
+
if (this._properties[key] === undefined) {
|
|
170
|
+
throw new TemplateError(`"properties.${key}" not defined`)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const log = this._log
|
|
174
|
+
|
|
175
|
+
const value = this._properties[key]
|
|
176
|
+
log.trace(
|
|
177
|
+
`${LiquidPropertiesDrop.name}.liquidMethodMissing('${key}') in (`,
|
|
178
|
+
value,
|
|
179
|
+
')'
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
let result: string | string[]
|
|
183
|
+
|
|
184
|
+
if (isJsonObject(value)) {
|
|
185
|
+
return value
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// If the property value is an array, merge them into a single string.
|
|
189
|
+
const valueString = Array.isArray(value) ? value.join('') : value
|
|
190
|
+
if (valueString.includes('{{') || valueString.includes('{%')) {
|
|
191
|
+
result = (await this._engine.parseAndRender(
|
|
192
|
+
valueString,
|
|
193
|
+
context
|
|
194
|
+
)) as string
|
|
195
|
+
} else {
|
|
196
|
+
result = value
|
|
197
|
+
}
|
|
198
|
+
log.trace(
|
|
199
|
+
`${LiquidPropertiesDrop.name}.liquidMethodMissing('${key}')` + ` => (`,
|
|
200
|
+
result,
|
|
201
|
+
')'
|
|
202
|
+
)
|
|
203
|
+
return result
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ============================================================================
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Configuration parameters for constructing a matrix drop instance.
|
|
211
|
+
*
|
|
212
|
+
* @remarks
|
|
213
|
+
* This interface defines the required configuration for creating an
|
|
214
|
+
* instance of {@link LiquidMatrixDrop}. All properties are mandatory.
|
|
215
|
+
*
|
|
216
|
+
* The parameters provide the Liquid engine for nested template evaluation,
|
|
217
|
+
* the matrix parameters map for value lookups, and the logger for diagnostic
|
|
218
|
+
* output during matrix parameter resolution and substitution.
|
|
219
|
+
*/
|
|
220
|
+
export interface LiquidMatrixDropConstructorParameters {
|
|
221
|
+
/**
|
|
222
|
+
* The Liquid engine used to render nested substitutions.
|
|
223
|
+
*/
|
|
224
|
+
engine: liquidjs.Liquid
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* The matrix parameters map used for substitutions.
|
|
228
|
+
*/
|
|
229
|
+
matrix: LiquidSubstitutionsStrings
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* The logger instance for output and diagnostics.
|
|
233
|
+
*/
|
|
234
|
+
log: Logger
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Liquid drop that resolves `matrix` parameter values for templates.
|
|
239
|
+
*
|
|
240
|
+
* @remarks
|
|
241
|
+
* This drop exposes matrix values to the Liquid engine and performs
|
|
242
|
+
* nested substitutions when a matrix value contains Liquid syntax.
|
|
243
|
+
*
|
|
244
|
+
* Matrix parameters are used during template expansion to generate multiple
|
|
245
|
+
* actions or build configurations from a single template definition. Each
|
|
246
|
+
* expanded instance receives a specific combination of matrix values.
|
|
247
|
+
*
|
|
248
|
+
* Implements the same Drop pattern as {@link LiquidPropertiesDrop} but
|
|
249
|
+
* for matrix-scoped variables. When a template accesses `matrix.arch`, the
|
|
250
|
+
* engine calls {@link LiquidMatrixDrop.liquidMethodMissing} which resolves
|
|
251
|
+
* the parameter value and recursively evaluates any nested Liquid syntax.
|
|
252
|
+
*
|
|
253
|
+
* Matrix parameters are isolated per template instance, ensuring that each
|
|
254
|
+
* generated action or configuration has access only to its specific matrix
|
|
255
|
+
* combination.
|
|
256
|
+
*/
|
|
257
|
+
export class LiquidMatrixDrop extends liquidjs.Drop {
|
|
258
|
+
// --------------------------------------------------------------------------
|
|
259
|
+
// Protected Members.
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* The logger instance for output and diagnostics.
|
|
263
|
+
*/
|
|
264
|
+
protected readonly _log: Logger
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* The matrix parameters map used for substitutions.
|
|
268
|
+
*/
|
|
269
|
+
protected readonly _matrix: LiquidSubstitutionsStrings
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* The Liquid engine used to render nested substitutions.
|
|
273
|
+
*/
|
|
274
|
+
protected readonly _engine: liquidjs.Liquid
|
|
275
|
+
|
|
276
|
+
// --------------------------------------------------------------------------
|
|
277
|
+
// Constructor.
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Constructs a matrix drop.
|
|
281
|
+
*
|
|
282
|
+
* @param engine - The Liquid engine used to render nested substitutions.
|
|
283
|
+
* @param matrix - The matrix parameters map used for substitutions.
|
|
284
|
+
* @param log - The logger instance for output and diagnostics.
|
|
285
|
+
*/
|
|
286
|
+
constructor({ engine, matrix, log }: LiquidMatrixDropConstructorParameters) {
|
|
287
|
+
super()
|
|
288
|
+
|
|
289
|
+
log.trace(`${LiquidMatrixDrop.name}()`)
|
|
290
|
+
|
|
291
|
+
this._log = log
|
|
292
|
+
this._engine = engine
|
|
293
|
+
this._matrix = matrix
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// --------------------------------------------------------------------------
|
|
297
|
+
// Public Methods.
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Resolves a missing matrix parameter and performs nested substitutions.
|
|
301
|
+
*
|
|
302
|
+
* @remarks
|
|
303
|
+
* Called by the Liquid engine when a matrix parameter is accessed that
|
|
304
|
+
* doesn't exist as a regular method. This implements the Drop pattern for
|
|
305
|
+
* dynamic matrix parameter resolution.
|
|
306
|
+
*
|
|
307
|
+
* Resolution process:
|
|
308
|
+
*
|
|
309
|
+
* <ol>
|
|
310
|
+
* <li>Validate the parameter exists, throw <code>InputError</code> if
|
|
311
|
+
* not.</li>
|
|
312
|
+
* <li>Retrieve the parameter value (string, array, or object).</li>
|
|
313
|
+
* <li>If object, return as-is for Liquid to access nested properties.</li>
|
|
314
|
+
* <li>If array, join elements into a single string for processing.</li>
|
|
315
|
+
* <li>If the value contains Liquid syntax, recursively render it with the
|
|
316
|
+
* current context to resolve nested references.</li>
|
|
317
|
+
* <li>Return the final resolved value.</li>
|
|
318
|
+
* </ol>
|
|
319
|
+
*
|
|
320
|
+
* This mirrors the behavior of {@link LiquidPropertiesDrop} but operates
|
|
321
|
+
* on matrix parameters instead of properties. Matrix values can reference
|
|
322
|
+
* other substitution variables, enabling complex template expansions.
|
|
323
|
+
*
|
|
324
|
+
* @param key - The matrix parameter name requested by the template.
|
|
325
|
+
* @param context - The Liquid rendering context.
|
|
326
|
+
* @returns The resolved matrix parameter value.
|
|
327
|
+
*
|
|
328
|
+
* @throws {@link TemplateError}
|
|
329
|
+
* If the matrix parameter is not defined.
|
|
330
|
+
*/
|
|
331
|
+
override async liquidMethodMissing(
|
|
332
|
+
key: string,
|
|
333
|
+
context: liquidjs.Context
|
|
334
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
335
|
+
): Promise<any> {
|
|
336
|
+
// console.log(key)
|
|
337
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
338
|
+
if (this._matrix[key] === undefined) {
|
|
339
|
+
throw new TemplateError(`"matrix.${key}" not defined`)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const log = this._log
|
|
343
|
+
|
|
344
|
+
const value = this._matrix[key]
|
|
345
|
+
log.trace(
|
|
346
|
+
`${LiquidMatrixDrop.name}.liquidMethodMissing('${key}') in (`,
|
|
347
|
+
value,
|
|
348
|
+
')'
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
let result: string | string[]
|
|
352
|
+
|
|
353
|
+
if (isJsonObject(value)) {
|
|
354
|
+
return value
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// If the property value is an array, merge them into a single string.
|
|
358
|
+
const valueString = Array.isArray(value) ? value.join('') : value
|
|
359
|
+
if (valueString.includes('{{') || valueString.includes('{%')) {
|
|
360
|
+
result = (await this._engine.parseAndRender(
|
|
361
|
+
valueString,
|
|
362
|
+
context
|
|
363
|
+
)) as string
|
|
364
|
+
} else {
|
|
365
|
+
result = value
|
|
366
|
+
}
|
|
367
|
+
log.trace(
|
|
368
|
+
`${LiquidMatrixDrop.name}.liquidMethodMissing('${key}')` + ` => (`,
|
|
369
|
+
result,
|
|
370
|
+
')'
|
|
371
|
+
)
|
|
372
|
+
return result
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ----------------------------------------------------------------------------
|
|
@@ -0,0 +1,249 @@
|
|
|
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 * as os from 'node:os'
|
|
15
|
+
import * as path from 'node:path'
|
|
16
|
+
import * as util from 'node:util'
|
|
17
|
+
|
|
18
|
+
// https://www.npmjs.com/package/liquidjs
|
|
19
|
+
import * as liquidjs from 'liquidjs'
|
|
20
|
+
|
|
21
|
+
// ----------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
import { isJsonObject } from '../functions/is-something.js'
|
|
24
|
+
import { PlatformDetector } from './platform-detector.js'
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Liquid engine configured for <b>xpm</b> templates.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* This class extends the Liquid engine and registers custom filters
|
|
33
|
+
* for path manipulation, string formatting, and convenience helpers used across
|
|
34
|
+
* <b>xpm</b> templates.
|
|
35
|
+
*
|
|
36
|
+
* The engine is configured with strict parsing options to catch template
|
|
37
|
+
* errors early during development. Custom filters are organized into
|
|
38
|
+
* categories:
|
|
39
|
+
*
|
|
40
|
+
* <ol>
|
|
41
|
+
* <li><b>Path manipulation:</b> Platform-specific and cross-platform path
|
|
42
|
+
* operations
|
|
43
|
+
* (<code>basename</code>, <code>dirname</code>, <code>join</code>,
|
|
44
|
+
* <code>relative</code>, <code>normalize</code>) for default, POSIX, and
|
|
45
|
+
* Win32 paths.</li>
|
|
46
|
+
* <li><b>String formatting:</b> Utilities for printf-style formatting and
|
|
47
|
+
* filename
|
|
48
|
+
* sanitization.</li>
|
|
49
|
+
* <li><b>Array/string conversion:</b> Filters for joining and splitting
|
|
50
|
+
* lines.</li>
|
|
51
|
+
* <li><b>Object introspection:</b> Filters for extracting object keys.</li>
|
|
52
|
+
* </ol>
|
|
53
|
+
*
|
|
54
|
+
* These filters enable templates to perform complex path manipulations and
|
|
55
|
+
* string transformations without requiring external dependencies or custom
|
|
56
|
+
* template tags.
|
|
57
|
+
*/
|
|
58
|
+
export class LiquidEngine extends liquidjs.Liquid {
|
|
59
|
+
// --------------------------------------------------------------------------
|
|
60
|
+
// Private Members.
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The platform detector instance for platform-specific behaviour.
|
|
64
|
+
*/
|
|
65
|
+
private readonly platformDetector: PlatformDetector
|
|
66
|
+
|
|
67
|
+
// --------------------------------------------------------------------------
|
|
68
|
+
// Constructor.
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Constructs a Liquid engine instance with xpm-specific settings and
|
|
72
|
+
* filters.
|
|
73
|
+
*
|
|
74
|
+
* @remarks
|
|
75
|
+
* The constructor configures strict parsing options and registers
|
|
76
|
+
* filters for path handling, formatting, and list operations.
|
|
77
|
+
*
|
|
78
|
+
* Configuration options:
|
|
79
|
+
*
|
|
80
|
+
* <ul>
|
|
81
|
+
* <li><b>strictFilters:</b> Throw errors for undefined filters rather than
|
|
82
|
+
* silently ignoring them.</li>
|
|
83
|
+
* <li><b>strictVariables:</b> Throw errors for undefined variables rather
|
|
84
|
+
* than
|
|
85
|
+
* rendering empty strings.</li>
|
|
86
|
+
* <li><b>trimTagLeft/Right:</b> Preserve whitespace around template
|
|
87
|
+
* tags.</li>
|
|
88
|
+
* <li><b>trimOutputLeft/Right:</b> Preserve whitespace around output
|
|
89
|
+
* expressions.</li>
|
|
90
|
+
* <li><b>greedy:</b> Use non-greedy matching for better template
|
|
91
|
+
* compatibility.</li>
|
|
92
|
+
* <li><b>lenientIf:</b> Allow flexible truthiness in conditional
|
|
93
|
+
* expressions.</li>
|
|
94
|
+
* </ul>
|
|
95
|
+
*
|
|
96
|
+
* Filter registration:
|
|
97
|
+
*
|
|
98
|
+
* <ul>
|
|
99
|
+
* <li><b>Platform-aware path filters (default, posix, win32):</b> delegate to
|
|
100
|
+
* Node.js path module for consistent cross-platform behavior.</li>
|
|
101
|
+
* <li><b>Custom filters (to_filename, join_lines, split_lines, keys):</b>
|
|
102
|
+
* provide
|
|
103
|
+
* template-specific functionality not available in standard Liquid.</li>
|
|
104
|
+
* <li>All filters are registered during construction for immediate
|
|
105
|
+
* availability in templates.</li>
|
|
106
|
+
* </ul>
|
|
107
|
+
*
|
|
108
|
+
* @param platformDetector - The platform detector instance for
|
|
109
|
+
* platform-specific behaviour. Defaults to a new {@link PlatformDetector}
|
|
110
|
+
* instance.
|
|
111
|
+
*/
|
|
112
|
+
constructor(platformDetector: PlatformDetector = new PlatformDetector()) {
|
|
113
|
+
super({
|
|
114
|
+
strictFilters: true,
|
|
115
|
+
strictVariables: true,
|
|
116
|
+
trimTagLeft: false,
|
|
117
|
+
trimTagRight: false,
|
|
118
|
+
trimOutputLeft: false,
|
|
119
|
+
trimOutputRight: false,
|
|
120
|
+
greedy: false,
|
|
121
|
+
lenientIf: true,
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
this.platformDetector = platformDetector
|
|
125
|
+
|
|
126
|
+
// https://liquidjs.com/api/classes/liquid_.liquid.html#registerFilter
|
|
127
|
+
// https://nodejs.org/dist/latest-v16.x/docs/api/path.html
|
|
128
|
+
|
|
129
|
+
// Add the main path manipulation functions.
|
|
130
|
+
this.registerFilter('path_basename', (p: string, ...arg) =>
|
|
131
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
132
|
+
path.basename(p, ...arg)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
this.registerFilter('path_dirname', (p: string) => path.dirname(p))
|
|
136
|
+
|
|
137
|
+
this.registerFilter('path_normalize', (p: string) => path.normalize(p))
|
|
138
|
+
|
|
139
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
140
|
+
this.registerFilter('path_join', (p, ...args) => path.join(p, ...args))
|
|
141
|
+
|
|
142
|
+
this.registerFilter('path_relative', (from: string, to: string) =>
|
|
143
|
+
path.relative(from, to)
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
this.registerFilter('path_posix_basename', (p: string, ...arg) =>
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
148
|
+
path.posix.basename(p, ...arg)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
this.registerFilter('path_posix_dirname', (p: string) =>
|
|
152
|
+
path.posix.dirname(p)
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
this.registerFilter('path_posix_normalize', (p: string) =>
|
|
156
|
+
path.posix.normalize(p)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
this.registerFilter('path_posix_join', (p, ...args) =>
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
161
|
+
path.posix.join(p, ...args)
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
this.registerFilter('path_posix_relative', (from: string, to: string) =>
|
|
165
|
+
path.posix.relative(from, to)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
this.registerFilter('path_win32_basename', (p: string, ...arg) =>
|
|
169
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
170
|
+
path.win32.basename(p, ...arg)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
this.registerFilter('path_win32_dirname', (p: string) =>
|
|
174
|
+
path.win32.dirname(p)
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
this.registerFilter('path_win32_normalize', (p: string) =>
|
|
178
|
+
path.win32.normalize(p)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
this.registerFilter('path_win32_join', (p, ...args) =>
|
|
182
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
183
|
+
path.win32.join(p, ...args)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
this.registerFilter('path_win32_relative', (from: string, to: string) =>
|
|
187
|
+
path.win32.relative(from, to)
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
// https://nodejs.org/dist/latest-v16.x/docs/api/util.html
|
|
191
|
+
|
|
192
|
+
this.registerFilter('util_format', (format, ...args) => {
|
|
193
|
+
// console.log([...args])
|
|
194
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
195
|
+
return util.format(format, ...args)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
// Custom action.
|
|
199
|
+
this.registerFilter(
|
|
200
|
+
'to_filename',
|
|
201
|
+
// Replace non alphanumeric chars with dashes to make the paths
|
|
202
|
+
// comply with filesystem names.
|
|
203
|
+
(input: string): string => {
|
|
204
|
+
/* c8 ignore start - windows specific code cannot be tested
|
|
205
|
+
on other platforms */
|
|
206
|
+
const fixed = this.platformDetector.isWindows()
|
|
207
|
+
? input.replace(/[^a-zA-Z0-9\\:]+/g, '-')
|
|
208
|
+
: input.replace(/[^a-zA-Z0-9/]+/g, '-')
|
|
209
|
+
/* c8 ignore stop */
|
|
210
|
+
|
|
211
|
+
return fixed.replace(/--/g, '-')
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
this.registerFilter('join_lines', (input: string[]): string => {
|
|
216
|
+
// Convert an array into a string with each element on a separate line.
|
|
217
|
+
if (Array.isArray(input)) {
|
|
218
|
+
return input.join(os.EOL)
|
|
219
|
+
}
|
|
220
|
+
return String(input)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
// Convert a string with lines into an array.
|
|
224
|
+
this.registerFilter('split_lines', (input: string | string[]): string[] => {
|
|
225
|
+
if (Array.isArray(input)) {
|
|
226
|
+
// If already an array, first flatten it, then split it.
|
|
227
|
+
// This is needed in case any of the lines include EOLs.
|
|
228
|
+
return input.join(os.EOL).split(os.EOL)
|
|
229
|
+
}
|
|
230
|
+
return input.split(os.EOL)
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
this.registerFilter('keys', (input: unknown): string[] | string => {
|
|
234
|
+
if (isJsonObject(input)) {
|
|
235
|
+
const keys = Object.keys(input as object)
|
|
236
|
+
// console.log('input object', input)
|
|
237
|
+
// console.log('input keys', keys)
|
|
238
|
+
return keys
|
|
239
|
+
} else if (Array.isArray(input)) {
|
|
240
|
+
const keys = Object.keys(input)
|
|
241
|
+
return keys
|
|
242
|
+
} else {
|
|
243
|
+
return String(input)
|
|
244
|
+
}
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ----------------------------------------------------------------------------
|