Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,67 @@ function compareVersions(version1, version2, operator) {
}
}

/**
* Parse JSON string with support for large integers (beyond Number.MAX_SAFE_INTEGER).
* Large integers are automatically converted to strings to preserve precision.
*
* For Node.js >= 21.0.0, uses native JSON.parse() with context parameter.
* For older versions, preprocesses the JSON string with regex to convert unsafe integers.
*
* Replaces: json-bigint package
*
* @param {string} str - JSON string to parse
* @returns {any} Parsed JSON object with unsafe integers as strings
*
* @example
* parseJSONWithBigInt('{"value": 9007199254740992}')
* // Returns: { value: '9007199254740992' }
*
* parseJSONWithBigInt('{"value": 100}')
* // Returns: { value: 100 }
*/
function parseJSONWithBigInt(str) {
const contextSupportedVersion = '21.0.0.0';
const currentVersion = process.version;
const isContextSupported = compareVersions(
currentVersion.substring(1, currentVersion.length),
contextSupportedVersion,
'>='
);
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version comparison is executed on every call to parseJSONWithBigInt. Consider caching isContextSupported as a module-level constant to avoid repeated comparisons, similar to how it was implemented in the original code.

Copilot uses AI. Check for mistakes.

// For nodejs >= 21.0.0, we leverage context parameter to
// convert unsafe integer to string.
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
if (isContextSupported) {
return JSON.parse(str, (key, value, context) => {
if (
Number.isInteger(value) &&
!Number.isSafeInteger(Number(context.source))
) {
return context.source;
}
return value;
});
}

// For older Node.js versions, preprocess the JSON string to convert
// large integers (outside the safe integer range) to strings before parsing.
// This replaces the functionality of json-bigint package.
const processed = str.replace(
/:\s*(-?\d+)(?=\s*[,}\]])/g,
(match, number) => {
const num = Number(number);
// If the number is not safe, keep it as a string
if (!Number.isSafeInteger(num)) {
return `: "${number}"`;
}
return match;
}
);

return JSON.parse(processed);
Comment on lines 358 to 382
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern may incorrectly match numbers in JSON string values. For example, in {\"key\": \"value: 123\"}, the number inside the string value could be mistakenly converted. The pattern should account for proper JSON structure to avoid matching numbers within string literals.

Suggested change
const processed = str.replace(
/:\s*(-?\d+)(?=\s*[,}\]])/g,
(match, number) => {
const num = Number(number);
// If the number is not safe, keep it as a string
if (!Number.isSafeInteger(num)) {
return `: "${number}"`;
}
return match;
}
);
return JSON.parse(processed);
// Use a reviver to convert unsafe integers to strings
return JSON.parse(str, (key, value) => {
if (typeof value === 'number' && Number.isInteger(value) && !Number.isSafeInteger(value)) {
return value.toString();
}
return value;
});

Copilot uses AI. Check for mistakes.
}

module.exports = {
// General utilities
detectUbuntuCodename,
Expand All @@ -320,5 +381,7 @@ module.exports = {
removeSync,
readJsonSync,

// Version and JSON utilities
compareVersions,
parseJSONWithBigInt,
};
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@
"@rclnodejs/ref-struct-di": "^1.1.1",
"bindings": "^1.5.0",
"debug": "^4.4.0",
"json-bigint": "^1.0.0",
"node-addon-api": "^8.3.1",
"walk": "^2.3.15"
},
Expand Down
31 changes: 2 additions & 29 deletions rosidl_parser/rosidl_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,12 @@

'use strict';

const { compareVersions } = require('../lib/utils.js');
const { compareVersions, parseJSONWithBigInt } = require('../lib/utils');
const path = require('path');
const execFile = require('child_process').execFile;

const pythonExecutable = require('./py_utils').getPythonExecutable('python3');

const contextSupportedVersion = '21.0.0.0';
const currentVersion = process.version;
const isContextSupported = compareVersions(
currentVersion.substring(1, currentVersion.length),
contextSupportedVersion,
'>='
);

const rosidlParser = {
parseMessageFile(packageName, filePath) {
return this._parseFile('parse_message_file', packageName, filePath);
Expand All @@ -41,25 +33,6 @@ const rosidlParser = {
return this._parseFile('parse_action_file', packageName, filePath);
},

_parseJSONObject(str) {
// For nodejs >= `contextSupportedVersion`, we leverage context parameter to
// convert unsafe integer to string, otherwise, json-bigint is used.
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
if (isContextSupported) {
return JSON.parse(str, (key, value, context) => {
if (
Number.isInteger(value) &&
!Number.isSafeInteger(Number(context.source))
) {
return context.source;
}
return value;
});
}
const JSONbigString = require('json-bigint')({ storeAsString: true });
return JSONbigString.parse(str);
},

_parseFile(command, packageName, filePath) {
return new Promise((resolve, reject) => {
const args = [
Expand All @@ -82,7 +55,7 @@ const rosidlParser = {
)
);
} else {
resolve(this._parseJSONObject(stdout));
resolve(parseJSONWithBigInt(stdout));
}
}
);
Expand Down
Loading