gulp-mu-gulp-api 0.3.1 → 0.3.2
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 +18 -8
- package/package.json +1 -1
- package/src/i18x.mjs +97 -20
- package/src/index.mjs +2 -6
package/README.md
CHANGED
|
@@ -20,6 +20,11 @@ The module is deliberately **dependency-free** and knows nothing about µGulp™
|
|
|
20
20
|
npm install gulp-mu-gulp-api
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
### 0.3.2
|
|
24
|
+
|
|
25
|
+
- **Added** `FormatValue()` — renders `<name format="…"/>` placeholders from `<format …>` XML definitions in the consumer project's `i18x/gulp/<lid>.json`.
|
|
26
|
+
- **Changed** built-in format IDs to CamelCase (`floatFix2`, `stdDate`, `progressDuration`, `byteSize`, …); lifecycle duration logs expect raw milliseconds with `format="progressDuration"`.
|
|
27
|
+
|
|
23
28
|
### 0.3.0
|
|
24
29
|
|
|
25
30
|
- **Added** integrated i18x console layer: `Log`, `Warn`, `LogError`, `Translate`, `GetLid`, `SetLid`, `GetTimeZone`, `DateInTimeZone`, `InstallStringExtensions` — translations live in the consumer project's `i18x/gulp/<lid>.json`; language follows `MICROGULP_LANG` (set by the µGulp dashboard) with locale fallback.
|
|
@@ -83,7 +88,7 @@ let values = await RequestForm({
|
|
|
83
88
|
|
|
84
89
|
| Function | Purpose |
|
|
85
90
|
| :--- | :--- |
|
|
86
|
-
| `IsMicroGulp()` / `IsµGulp()` | `true` when the task runs under µGulp™ with an attached dashboard
|
|
91
|
+
| `IsMicroGulp()` / `IsµGulp()` | `true` when the task runs under µGulp™ with an attached dashboard |
|
|
87
92
|
| `InstallStringExtensions()` | Installs µLib-compatible `String.prototype.i18xTrans()`/`i18xRegister()` (auto-run on import) |
|
|
88
93
|
| `Log(text, values?)` / `Warn` / `LogError` | Localized `console.*` via i18x (`<context="task log"/>` etc.) |
|
|
89
94
|
| `Translate(text, values?)` | Returns the translated string without logging |
|
|
@@ -105,8 +110,8 @@ Write the i18xe context tag **into** the source phrase and mark it with `.i18xRe
|
|
|
105
110
|
|
|
106
111
|
| Placeholder | Source |
|
|
107
112
|
| :--- | :--- |
|
|
108
|
-
| `version` | `µ
|
|
109
|
-
| `project` | `µ
|
|
113
|
+
| `version` | `µI18xContext.version` export, else `package.json` |
|
|
114
|
+
| `project` | `µI18xContext.project` export, else `package.json` `name` |
|
|
110
115
|
| `workspace` | workspace folder name |
|
|
111
116
|
| `source` | gulpfile path relative to the workspace |
|
|
112
117
|
| `gulpfile` | gulpfile file name |
|
|
@@ -116,7 +121,7 @@ Write the i18xe context tag **into** the source phrase and mark it with `.i18xRe
|
|
|
116
121
|
Export defaults from the gulpfile (overrides `package.json`):
|
|
117
122
|
|
|
118
123
|
```javascript
|
|
119
|
-
export const µ
|
|
124
|
+
export const µI18xContext = { version: '1.13', project: 'eMP' };
|
|
120
125
|
|
|
121
126
|
MAKE_BUILDS.µDisplayName = 'Make Build V<version/><context="µDisplayName"/>'.i18xRegister();
|
|
122
127
|
// => "Make Build V1.13" in the dashboard (translatable per language)
|
|
@@ -178,6 +183,11 @@ Das Modul ist bewusst **abhängigkeitsfrei** und kennt keine µGulp™-Interna:
|
|
|
178
183
|
npm install gulp-mu-gulp-api
|
|
179
184
|
```
|
|
180
185
|
|
|
186
|
+
### 0.3.2
|
|
187
|
+
|
|
188
|
+
- **Neu** `FormatValue()` — rendert `<name format="…"/>`-Platzhalter aus `<format …>`-XML-Definitionen in `i18x/gulp/<lid>.json` des Verbraucherprojekts.
|
|
189
|
+
- **Geändert** Format-IDs auf CamelCase (`floatFix2`, `stdDate`, `progressDuration`, `byteSize`, …); Lifecycle-Dauer-Logs erwarten Roh-Millisekunden mit `format="progressDuration"`.
|
|
190
|
+
|
|
181
191
|
### 0.3.0
|
|
182
192
|
|
|
183
193
|
- **Neu** integrierte i18x-Konsolenschicht: `Log`, `Warn`, `LogError`, `Translate`, `GetLid`, `SetLid`, `GetTimeZone`, `DateInTimeZone`, `InstallStringExtensions` — Übersetzungen liegen im Verbraucherprojekt unter `i18x/gulp/<lid>.json`; die Sprache folgt `MICROGULP_LANG` (vom µGulp-Dashboard gesetzt) mit Locale-Fallback.
|
|
@@ -241,7 +251,7 @@ let values = await RequestForm({
|
|
|
241
251
|
|
|
242
252
|
| Funktion | Zweck |
|
|
243
253
|
| :--- | :--- |
|
|
244
|
-
| `IsMicroGulp()` / `IsµGulp()` | `true`, wenn der Task unter µGulp™ mit angebundenem Dashboard läuft
|
|
254
|
+
| `IsMicroGulp()` / `IsµGulp()` | `true`, wenn der Task unter µGulp™ mit angebundenem Dashboard läuft |
|
|
245
255
|
| `InstallStringExtensions()` | Installiert µLib-kompatible `String.prototype.i18xTrans()`/`i18xRegister()` (läuft beim Import automatisch) |
|
|
246
256
|
| `Log(text, values?)` / `Warn` / `LogError` | Lokalisierte `console.*`-Ausgabe über i18x (`<context="task log"/>` usw.) |
|
|
247
257
|
| `Translate(text, values?)` | Gibt den übersetzten String zurück, ohne zu loggen |
|
|
@@ -263,8 +273,8 @@ Den i18xe-Kontext-Tag **in** die Textphrase schreiben und mit `.i18xRegister()`
|
|
|
263
273
|
|
|
264
274
|
| Platzhalter | Quelle |
|
|
265
275
|
| :--- | :--- |
|
|
266
|
-
| `version` | `µ
|
|
267
|
-
| `project` | `µ
|
|
276
|
+
| `version` | `µI18xContext.version`-Export, sonst `package.json` |
|
|
277
|
+
| `project` | `µI18xContext.project`-Export, sonst `package.json` `name` |
|
|
268
278
|
| `workspace` | Workspace-Ordnername |
|
|
269
279
|
| `source` | Gulpfile-Pfad relativ zum Workspace |
|
|
270
280
|
| `gulpfile` | Gulpfile-Dateiname |
|
|
@@ -274,7 +284,7 @@ Den i18xe-Kontext-Tag **in** die Textphrase schreiben und mit `.i18xRegister()`
|
|
|
274
284
|
Defaults im Gulpfile exportieren (`package.json` wird überschrieben):
|
|
275
285
|
|
|
276
286
|
```javascript
|
|
277
|
-
export const µ
|
|
287
|
+
export const µI18xContext = { version: '1.13', project: 'eMP' };
|
|
278
288
|
|
|
279
289
|
MAKE_BUILDS.µDisplayName = 'Make Build V<version/><context="µDisplayName"/>'.i18xRegister();
|
|
280
290
|
// => „Make Build V1.13“ im Dashboard (pro Sprache übersetzbar)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gulp-mu-gulp-api",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Public task API for the µGulp orchestrator: progress reporting, sound signals, speech output, localized console output (i18x) and interactive UI inputs (text, password, number, textarea, color, font, select, radio, multi-select checkbox, range slider, date/time, file) from within gulp tasks — with graceful CLI fallbacks when running without µGulp.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.mjs",
|
package/src/i18x.mjs
CHANGED
|
@@ -50,6 +50,7 @@ const LIDS_MAIN_CULTURES = {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
let dictionaryCache = new Map();
|
|
53
|
+
let formatDefinitionCache = new Map();
|
|
53
54
|
let availableLids = null;
|
|
54
55
|
let activeLid = null;
|
|
55
56
|
const timeZoneFormatters = {};
|
|
@@ -175,6 +176,7 @@ export function GetLid() {
|
|
|
175
176
|
*/
|
|
176
177
|
export function SetLid(_lid) {
|
|
177
178
|
activeLid = _lid ? _BestAvailableLid(_lid, _AvailableLids()) : null;
|
|
179
|
+
formatDefinitionCache.clear();
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
/** Loads and caches `<dir>/<lid>.json`; a missing/broken file yields source text. */
|
|
@@ -191,29 +193,104 @@ function _LoadDictionary(_lid) {
|
|
|
191
193
|
return dictionary;
|
|
192
194
|
}
|
|
193
195
|
|
|
194
|
-
/**
|
|
196
|
+
/** @param {Record<string, string>} _dictionary */
|
|
197
|
+
function _ExtractFormatDefinitions(_dictionary) {
|
|
198
|
+
let formats = {};
|
|
199
|
+
for (let [key, value] of Object.entries(_dictionary)) {
|
|
200
|
+
if (!key.startsWith('<format name=')) continue;
|
|
201
|
+
let match = key.match(/<format name="([^"]+)"/);
|
|
202
|
+
if (match) formats[match[1]] = value;
|
|
203
|
+
}
|
|
204
|
+
return formats;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Localized i18xe format XML keyed by format id (from i18x/gulp dictionaries). */
|
|
208
|
+
function _LoadFormatDefinitions(_lid) {
|
|
209
|
+
if (formatDefinitionCache.has(_lid)) return formatDefinitionCache.get(_lid);
|
|
210
|
+
let formats = { ..._ExtractFormatDefinitions(_LoadDictionary(DEFAULT_LID)) };
|
|
211
|
+
if (_lid !== DEFAULT_LID) {
|
|
212
|
+
Object.assign(formats, _ExtractFormatDefinitions(_LoadDictionary(_lid)));
|
|
213
|
+
}
|
|
214
|
+
formatDefinitionCache.set(_lid, formats);
|
|
215
|
+
return formats;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Formats milliseconds as M:SS or H:MM:SS (progress bar durations). */
|
|
219
|
+
function _FormatDuration(_ms) {
|
|
220
|
+
let ms = Math.max(0, Number(_ms) || 0);
|
|
221
|
+
let totalSeconds = Math.round(ms / 1000);
|
|
222
|
+
let seconds = totalSeconds % 60;
|
|
223
|
+
let minutes = Math.floor(totalSeconds / 60) % 60;
|
|
224
|
+
let hours = Math.floor(totalSeconds / 3600);
|
|
225
|
+
let pad = (_n) => String(_n).padStart(2, '0');
|
|
226
|
+
return hours > 0 ? hours + ':' + pad(minutes) + ':' + pad(seconds) : minutes + ':' + pad(seconds);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Formats a numeric value with a named i18xe format definition from the
|
|
231
|
+
* project's i18x/gulp dictionaries (synced from i18xe prod).
|
|
232
|
+
* @param {number|string} _value
|
|
233
|
+
* @param {string} _formatName e.g. 'int', 'floatFix2', 'byteSize'
|
|
234
|
+
* @param {string} [_lid]
|
|
235
|
+
* @returns {string}
|
|
236
|
+
*/
|
|
237
|
+
export function FormatValue(_value, _formatName, _lid) {
|
|
238
|
+
let lid = _lid ?? GetLid();
|
|
239
|
+
let tz = GetTimeZone();
|
|
240
|
+
if (_formatName === 'progressDuration') return _FormatDuration(Number(_value));
|
|
241
|
+
if (_formatName === 'stdTime') {
|
|
242
|
+
let date = new Date(Number(_value) || 0);
|
|
243
|
+
try {
|
|
244
|
+
return new Intl.DateTimeFormat(lid, { hour: '2-digit', minute: '2-digit', timeZone: tz }).format(date);
|
|
245
|
+
} catch {
|
|
246
|
+
return date.toISOString();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (_formatName === 'stdDateTime' || _formatName === 'fullDateTime') {
|
|
250
|
+
let date = new Date(Number(_value) || 0);
|
|
251
|
+
try {
|
|
252
|
+
return new Intl.DateTimeFormat(lid, {
|
|
253
|
+
year: 'numeric', month: '2-digit', day: '2-digit',
|
|
254
|
+
hour: '2-digit', minute: '2-digit', timeZone: tz,
|
|
255
|
+
}).format(date);
|
|
256
|
+
} catch {
|
|
257
|
+
return date.toISOString();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (_formatName === 'byteSize') {
|
|
261
|
+
let bytes = Number(_value);
|
|
262
|
+
if (!Number.isFinite(bytes)) return String(_value);
|
|
263
|
+
if (bytes >= 1073741824) return FormatValue(bytes / 1073741824, 'floatFix2', lid) + ' GB';
|
|
264
|
+
if (bytes >= 1048576) return FormatValue(bytes / 1048576, 'floatFix2', lid) + ' MB';
|
|
265
|
+
if (bytes >= 1024) return FormatValue(bytes / 1024, 'floatFix2', lid) + ' KB';
|
|
266
|
+
return FormatValue(bytes, 'int', lid) + ' Bytes';
|
|
267
|
+
}
|
|
268
|
+
let number = Number(_value);
|
|
269
|
+
if (!Number.isFinite(number)) return String(_value);
|
|
270
|
+
let definition = _LoadFormatDefinitions(lid)[_formatName];
|
|
271
|
+
if (!definition) return String(_value);
|
|
272
|
+
let groupSeparator = definition.match(/<xa if="1000\|1000\+">([^<]*)<\/xa>/)?.[1] ?? ',';
|
|
273
|
+
let decimalSeparator = definition.match(/if="0~">([^<]*)<m0\/>/)?.[1]
|
|
274
|
+
?? definition.match(/<\/xa>([^<]*)<f0\/>/)?.[1] ?? '.';
|
|
275
|
+
let fractionDigits = _formatName === 'floatFix1' ? 1
|
|
276
|
+
: (_formatName.startsWith('floatFix') ? 2 : 0);
|
|
277
|
+
if (_formatName === 'int') number = Math.round(number);
|
|
278
|
+
let negative = number < 0;
|
|
279
|
+
let absolute = Math.abs(number);
|
|
280
|
+
let integerPart = String(Math.trunc(fractionDigits ? absolute : Math.round(absolute)));
|
|
281
|
+
let grouped = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, groupSeparator);
|
|
282
|
+
let fraction = fractionDigits ? decimalSeparator + absolute.toFixed(fractionDigits).split('.')[1] : '';
|
|
283
|
+
return (negative ? '-' : '') + grouped + fraction;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Substitutes `<name/>` placeholders; honors `format="…"` via FormatValue. */
|
|
195
287
|
function _ApplyPlaceholders(_text, _values) {
|
|
196
288
|
let text = String(_text);
|
|
289
|
+
let lid = GetLid();
|
|
197
290
|
for (let key of Object.keys(_values ?? {})) {
|
|
198
291
|
let pattern = new RegExp('<' + key + '(?:\\s+format="([^"]*)")?\\s*/>', 'g');
|
|
199
|
-
text = text.replace(pattern, (_match, _formatName) =>
|
|
200
|
-
|
|
201
|
-
if (_formatName === 'stdtime' || _formatName === 'stddatetime') {
|
|
202
|
-
let date = new Date(Number(_values[key]) || 0);
|
|
203
|
-
let lid = GetLid();
|
|
204
|
-
try {
|
|
205
|
-
return new Intl.DateTimeFormat(lid, {
|
|
206
|
-
...( _formatName === 'stddatetime'
|
|
207
|
-
? { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }
|
|
208
|
-
: { hour: '2-digit', minute: '2-digit' }),
|
|
209
|
-
timeZone: GetTimeZone(),
|
|
210
|
-
}).format(date);
|
|
211
|
-
} catch {
|
|
212
|
-
return date.toISOString();
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return String(_values[key]);
|
|
216
|
-
});
|
|
292
|
+
text = text.replace(pattern, (_match, _formatName) =>
|
|
293
|
+
_formatName ? FormatValue(_values[key], _formatName, lid) : String(_values[key]));
|
|
217
294
|
}
|
|
218
295
|
return text.replace(/<[^>]*>/g, '');
|
|
219
296
|
}
|
|
@@ -302,5 +379,5 @@ InstallStringExtensions();
|
|
|
302
379
|
|
|
303
380
|
export default {
|
|
304
381
|
GetLid, SetLid, GetTimeZone, DateInTimeZone,
|
|
305
|
-
Translate, Log, Warn, LogError, InstallStringExtensions,
|
|
382
|
+
Translate, FormatValue, Log, Warn, LogError, InstallStringExtensions,
|
|
306
383
|
};
|
package/src/index.mjs
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
// Localized console output (i18x). Re-exported so tasks import everything
|
|
32
32
|
// from one place: `import { Log, Warn, ReportProgress } from 'gulp-mu-gulp-api'`.
|
|
33
|
-
export { Log, Warn, LogError, Translate, GetLid, SetLid, GetTimeZone, DateInTimeZone, InstallStringExtensions } from './i18x.mjs';
|
|
33
|
+
export { Log, Warn, LogError, Translate, FormatValue, GetLid, SetLid, GetTimeZone, DateInTimeZone, InstallStringExtensions } from './i18x.mjs';
|
|
34
34
|
import * as I18x from './i18x.mjs';
|
|
35
35
|
|
|
36
36
|
let uiRequestCounter = 0;
|
|
@@ -43,15 +43,12 @@ const pendingUiRequests = new Map();
|
|
|
43
43
|
* attached dashboard (progress and inputs are rendered in the webview)
|
|
44
44
|
*/
|
|
45
45
|
export function IsMicroGulp() {
|
|
46
|
-
return typeof process.send === 'function' && process.env.
|
|
46
|
+
return typeof process.send === 'function' && process.env.MICROGULP === '1';
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/** Brand-aligned alias for {@link IsMicroGulp}. */
|
|
50
50
|
export const IsµGulp = IsMicroGulp;
|
|
51
51
|
|
|
52
|
-
/** @deprecated Use {@link IsMicroGulp} or {@link IsµGulp}. */
|
|
53
|
-
export const IsUGulp = IsMicroGulp;
|
|
54
|
-
|
|
55
52
|
/*
|
|
56
53
|
* Task metadata (µDisplayName, µDescription, µTooltip, µGroup) is localized
|
|
57
54
|
* the same way as any other i18x phrase: with the context tag written *into*
|
|
@@ -520,7 +517,6 @@ function _AsciiGallery(_payload) {
|
|
|
520
517
|
export default {
|
|
521
518
|
IsMicroGulp,
|
|
522
519
|
IsµGulp,
|
|
523
|
-
IsUGulp,
|
|
524
520
|
ReportProgress,
|
|
525
521
|
CreateProgress,
|
|
526
522
|
PlaySignal,
|