Skip to content

Commit 4fb27d4

Browse files
committed
intl: Add more versions from ICU
* Adds process.versions.cldr, .tz, and .unicode * Changes how process.versions.icu is loaded * Lazy loads the process.versions.* values for these * add an exception to util.js to cause 'node -p process.versions' to still work * update process.version docs Fixes: #9237
1 parent 97dfced commit 4fb27d4

File tree

6 files changed

+111
-6
lines changed

6 files changed

+111
-6
lines changed

doc/api/process.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -1643,8 +1643,10 @@ Will generate output similar to:
16431643
ares: '1.10.0-DEV',
16441644
modules: '43',
16451645
icu: '55.1',
1646-
openssl: '1.0.1k'
1647-
}
1646+
openssl: '1.0.1k',
1647+
unicode: '8.0',
1648+
cldr: '29.0',
1649+
tz: '2016b' }
16481650
```
16491651

16501652
## Exit Codes

lib/internal/bootstrap_node.js

+32
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
// do this good and early, since it handles errors.
2727
setupProcessFatal();
2828

29+
setupProcessICUVersions();
30+
2931
setupGlobalVariables();
3032
if (!process._noBrowserGlobals) {
3133
setupGlobalTimeouts();
@@ -304,6 +306,36 @@
304306
};
305307
}
306308

309+
function setupProcessICUVersions() {
310+
const icu = process.binding('config').hasIntl ?
311+
process.binding('icu') : undefined;
312+
if (!icu) return; // no Intl/ICU: nothing to add here.
313+
// With no argument, getVersion() returns a comma separated list
314+
// of possible types.
315+
const versionTypes = icu.getVersion().split(',');
316+
versionTypes.forEach((name) => {
317+
// Copied from module.js:addBuiltinLibsToObject
318+
Object.defineProperty(process.versions, name, {
319+
configurable: true,
320+
enumerable: true,
321+
get: () => {
322+
// With an argument, getVersion(type) returns
323+
// the actual version string.
324+
const version = icu.getVersion(name);
325+
// Replace the current getter with a new
326+
// property.
327+
delete process.versions[name];
328+
Object.defineProperty(process.versions, name, {
329+
value: version,
330+
writable: false,
331+
enumerable: true
332+
});
333+
return version;
334+
}
335+
});
336+
});
337+
}
338+
307339
function tryGetCwd(path) {
308340
var threw = true;
309341
var cwd;

lib/util.js

+4
Original file line numberDiff line numberDiff line change
@@ -1049,3 +1049,7 @@ exports._exceptionWithHostPort = function(err,
10491049
}
10501050
return ex;
10511051
};
1052+
1053+
// process.versions needs a custom function as some values are lazy-evaluated.
1054+
process.versions[exports.inspect.custom] =
1055+
(depth) => exports.format(JSON.parse(JSON.stringify(process.versions)));

src/node.cc

+1-3
Original file line numberDiff line numberDiff line change
@@ -3032,9 +3032,7 @@ void SetupProcessObject(Environment* env,
30323032
FIXED_ONE_BYTE_STRING(env->isolate(), ARES_VERSION_STR));
30333033

30343034
#if defined(NODE_HAVE_I18N_SUPPORT) && defined(U_ICU_VERSION)
3035-
READONLY_PROPERTY(versions,
3036-
"icu",
3037-
OneByteString(env->isolate(), U_ICU_VERSION));
3035+
// ICU-related versions are now handled on the js side, see bootstrap_node.js
30383036

30393037
if (icu_data_dir != nullptr) {
30403038
// Did the user attempt (via env var or parameter) to set an ICU path?

src/node_i18n.cc

+67-1
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,18 @@
3131
#include "util-inl.h"
3232
#include "v8.h"
3333

34+
#include <unicode/utypes.h>
3435
#include <unicode/putil.h>
3536
#include <unicode/uchar.h>
3637
#include <unicode/udata.h>
3738
#include <unicode/uidna.h>
38-
#include <unicode/utypes.h>
3939
#include <unicode/ucnv.h>
4040
#include <unicode/utf8.h>
4141
#include <unicode/utf16.h>
42+
#include <unicode/timezone.h>
43+
#include <unicode/ulocdata.h>
44+
#include <unicode/uvernum.h>
45+
#include <unicode/uversion.h>
4246

4347
#ifdef NODE_HAVE_SMALL_ICU
4448
/* if this is defined, we have a 'secondary' entry point.
@@ -339,6 +343,67 @@ static void ICUErrorName(const FunctionCallbackInfo<Value>& args) {
339343
v8::NewStringType::kNormal).ToLocalChecked());
340344
}
341345

346+
#define TYPE_ICU "icu"
347+
#define TYPE_UNICODE "unicode"
348+
#define TYPE_CLDR "cldr"
349+
#define TYPE_TZ "tz"
350+
351+
/**
352+
* This is the workhorse function that deals with the actual version info.
353+
* Get an ICU version.
354+
* @param type the type of version to get. One of VERSION_TYPES
355+
* @param buf optional buffer for result
356+
* @param status ICU error status. If failure, assume result is undefined.
357+
* @return version number, or NULL. May or may not be buf.
358+
*/
359+
static const char* GetVersion(const char* type,
360+
char buf[U_MAX_VERSION_STRING_LENGTH],
361+
UErrorCode* status) {
362+
if (!strcmp(type, TYPE_ICU)) {
363+
return U_ICU_VERSION;
364+
} else if (!strcmp(type, TYPE_UNICODE)) {
365+
return U_UNICODE_VERSION;
366+
} else if (!strcmp(type, TYPE_TZ)) {
367+
return TimeZone::getTZDataVersion(*status);
368+
} else if (!strcmp(type, TYPE_CLDR)) {
369+
UVersionInfo versionArray;
370+
ulocdata_getCLDRVersion(versionArray, status);
371+
if (U_SUCCESS(*status)) {
372+
u_versionToString(versionArray, buf);
373+
return buf;
374+
}
375+
}
376+
// Fall through - unknown type or error case
377+
return nullptr;
378+
}
379+
380+
static void GetVersion(const FunctionCallbackInfo<Value>& args) {
381+
Environment* env = Environment::GetCurrent(args);
382+
if ( args.Length() == 0 ) {
383+
// With no args - return a comma-separated list of allowed values
384+
args.GetReturnValue().Set(
385+
String::NewFromUtf8(env->isolate(),
386+
TYPE_ICU ","
387+
TYPE_UNICODE ","
388+
TYPE_CLDR ","
389+
TYPE_TZ));
390+
} else {
391+
CHECK_GE(args.Length(), 1);
392+
CHECK(args[0]->IsString());
393+
Utf8Value val(env->isolate(), args[0]);
394+
UErrorCode status = U_ZERO_ERROR;
395+
char buf[U_MAX_VERSION_STRING_LENGTH] = ""; // Possible output buffer.
396+
const char* versionString = GetVersion(*val, buf, &status);
397+
398+
if (U_SUCCESS(status) && versionString) {
399+
// Success.
400+
args.GetReturnValue().Set(
401+
String::NewFromUtf8(env->isolate(),
402+
versionString));
403+
}
404+
}
405+
}
406+
342407
bool InitializeICUDirectory(const char* icu_data_path) {
343408
if (icu_data_path != nullptr) {
344409
flag_icu_data_dir = true;
@@ -558,6 +623,7 @@ void Init(Local<Object> target,
558623
env->SetMethod(target, "toUnicode", ToUnicode);
559624
env->SetMethod(target, "toASCII", ToASCII);
560625
env->SetMethod(target, "getStringWidth", GetStringWidth);
626+
env->SetMethod(target, "getVersion", GetVersion);
561627

562628
// One-shot converters
563629
env->SetMethod(target, "icuErrName", ICUErrorName);

test/parallel/test-process-versions.js

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ if (common.hasCrypto) {
1111

1212
if (common.hasIntl) {
1313
expected_keys.push('icu');
14+
expected_keys.push('cldr');
15+
expected_keys.push('tz');
16+
expected_keys.push('unicode');
1417
}
1518

1619
expected_keys.sort();

0 commit comments

Comments
 (0)