Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,51 @@
'use strict';
let internalTTy;
function lazyInternalTTY() {
internalTTy ??= require('internal/tty');
return internalTTy;
}
module.exports = {
blue: '',
green: '',
white: '',
red: '',
gray: '',
clear: '',
reset: '',
hasColors: false,
shouldColorize(stream) {
if (process.env.FORCE_COLOR !== undefined) {
return lazyInternalTTY().getColorDepth() > 2;
}
return stream?.isTTY && (
typeof stream.getColorDepth === 'function' ?
stream.getColorDepth() > 2 : true);
},
refresh() {
if (module.exports.shouldColorize(process.stderr)) {
module.exports.blue = '\u001b[34m';
module.exports.green = '\u001b[32m';
module.exports.white = '\u001b[39m';
module.exports.yellow = '\u001b[33m';
module.exports.red = '\u001b[31m';
module.exports.gray = '\u001b[90m';
module.exports.clear = '\u001bc';
module.exports.reset = '\u001b[0m';
module.exports.hasColors = true;
} else {
module.exports.blue = '';
module.exports.green = '';
module.exports.white = '';
module.exports.yellow = '';
module.exports.red = '';
module.exports.gray = '';
module.exports.clear = '';
module.exports.reset = '';
module.exports.hasColors = false;
}
},
};
module.exports.refresh();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,524 @@
'use strict';
const {
MathFloor,
Number,
NumberPrototypeToFixed,
ObjectDefineProperty,
RegExp,
RegExpPrototypeExec,
SafeArrayIterator,
SafeMap,
StringPrototypePadStart,
StringPrototypeSplit,
StringPrototypeToLowerCase,
StringPrototypeToUpperCase,
} = primordials;
const {
CHAR_LOWERCASE_B: kTraceBegin,
CHAR_LOWERCASE_E: kTraceEnd,
CHAR_LOWERCASE_N: kTraceInstant,
} = require('internal/constants');
const { inspect, format, formatWithOptions } = require('internal/util/inspect');
const { getCategoryEnabledBuffer, trace } = internalBinding('trace_events');
// `debugImpls` and `testEnabled` are deliberately not initialized so any call
// to `debuglog()` before `initializeDebugEnv()` is called will throw.
let debugImpls;
let testEnabled;
// `debugEnv` is initial value of process.env.NODE_DEBUG
function initializeDebugEnv(debugEnv) {
debugImpls = { __proto__: null };
if (debugEnv) {
// This is run before any user code, it's OK not to use primordials.
debugEnv = debugEnv.replace(/[|\\{}()[\]^$+?.]/g, '\\$&')
.replaceAll('*', '.*')
.replaceAll(',', '$|^');
const debugEnvRegex = new RegExp(`^${debugEnv}$`, 'i');
testEnabled = (str) => RegExpPrototypeExec(debugEnvRegex, str) !== null;
} else {
testEnabled = () => false;
}
}
// Emits warning when user sets
// NODE_DEBUG=http or NODE_DEBUG=http2.
function emitWarningIfNeeded(set) {
if ('HTTP' === set || 'HTTP2' === set) {
process.emitWarning('Setting the NODE_DEBUG environment variable ' +
'to \'' + StringPrototypeToLowerCase(set) + '\' can expose sensitive ' +
'data (such as passwords, tokens and authentication headers) ' +
'in the resulting log.');
}
}
const noop = () => {};
let utilColors;
function lazyUtilColors() {
utilColors ??= require('internal/util/colors');
return utilColors;
}
function debuglogImpl(enabled, set) {
if (debugImpls[set] === undefined) {
if (enabled) {
const pid = process.pid;
emitWarningIfNeeded(set);
debugImpls[set] = function debug(...args) {
const colors = lazyUtilColors().shouldColorize(process.stderr);
const msg = formatWithOptions({ colors }, ...args);
const coloredPID = inspect(pid, { colors });
process.stderr.write(format('%s %s: %s\n', set, coloredPID, msg));
};
} else {
debugImpls[set] = noop;
}
}
return debugImpls[set];
}
// debuglogImpl depends on process.pid and process.env.NODE_DEBUG,
// so it needs to be called lazily in top scopes of internal modules
// that may be loaded before these run time states are allowed to
// be accessed.
function debuglog(set, cb) {
function init() {
set = StringPrototypeToUpperCase(set);
enabled = testEnabled(set);
}
let debug = (...args) => {
init();
// Only invokes debuglogImpl() when the debug function is
// called for the first time.
debug = debuglogImpl(enabled, set);
if (typeof cb === 'function')
cb(debug);
switch (args.length) {
case 1: return debug(args[0]);
case 2: return debug(args[0], args[1]);
default: return debug(...new SafeArrayIterator(args));
}
};
let enabled;
let test = () => {
init();
test = () => enabled;
return enabled;
};
const logger = (...args) => {
// Improve performance when debug is disabled, avoid calling `new SafeArrayIterator(args)`
if (enabled === false) return;
switch (args.length) {
case 1: return debug(args[0]);
case 2: return debug(args[0], args[1]);
default: return debug(...new SafeArrayIterator(args));
}
};
ObjectDefineProperty(logger, 'enabled', {
__proto__: null,
get() {
return test();
},
configurable: true,
enumerable: true,
});
return logger;
}
function pad(value) {
return StringPrototypePadStart(`${value}`, 2, '0');
}
const kNone = 1 << 0;
const kSkipLog = 1 << 1;
const kSkipTrace = 1 << 2;
const kShouldSkipAll = kSkipLog | kSkipTrace;
const kSecond = 1000;
const kMinute = 60 * kSecond;
const kHour = 60 * kMinute;
function formatTime(ms) {
let hours = 0;
let minutes = 0;
let seconds = 0;
if (ms >= kSecond) {
if (ms >= kMinute) {
if (ms >= kHour) {
hours = MathFloor(ms / kHour);
ms = ms % kHour;
}
minutes = MathFloor(ms / kMinute);
ms = ms % kMinute;
}
seconds = ms / kSecond;
}
if (hours !== 0 || minutes !== 0) {
({ 0: seconds, 1: ms } = StringPrototypeSplit(
NumberPrototypeToFixed(seconds, 3),
'.',
2,
));
const res = hours !== 0 ? `${hours}:${pad(minutes)}` : minutes;
return `${res}:${pad(seconds)}.${ms} (${hours !== 0 ? 'h:m' : ''}m:ss.mmm)`;
}
if (seconds !== 0) {
return `${NumberPrototypeToFixed(seconds, 3)}s`;
}
return `${Number(NumberPrototypeToFixed(ms, 3))}ms`;
}
function safeTraceLabel(label) {
return label.replaceAll('\\', '\\\\').replaceAll('"', '\\"');
}
/**
* @typedef {(label: string, timeFormatted: string, args?: any[]) => void} LogImpl
*/
/**
* Returns true if label was found
* @param {string} timesStore
* @param {string} implementation
* @param {LogImpl} logImp
* @param {string} label
* @param {any} args
* @returns {void}
*/
function timeLogImpl(timesStore, implementation, logImp, label, args) {
const time = timesStore.get(label);
if (time === undefined) {
process.emitWarning(`No such label '${label}' for ${implementation}`);
return;
}
const duration = process.hrtime(time);
const ms = duration[0] * 1000 + duration[1] / 1e6;
const formatted = formatTime(ms);
if (args === undefined) {
logImp(label, formatted);
} else {
logImp(label, formatted, args);
}
}
/**
* @param {SafeMap} timesStore
* @param {string} traceCategory
* @param {string} implementation
* @param {number} timerFlags
* @param {string} logLabel
* @param {string} traceLabel
* @returns {void}
*/
function time(timesStore, traceCategory, implementation, timerFlags, logLabel = 'default', traceLabel = undefined) {
// Coerces everything other than Symbol to a string
logLabel = `${logLabel}`;
if (traceLabel !== undefined) {
traceLabel = `${traceLabel}`;
} else {
traceLabel = logLabel;
}
if (timesStore.has(logLabel)) {
process.emitWarning(`Label '${logLabel}' already exists for ${implementation}`);
return;
}
if ((timerFlags & kSkipTrace) === 0) {
traceLabel = safeTraceLabel(traceLabel);
trace(kTraceBegin, traceCategory, traceLabel, 0);
}
timesStore.set(logLabel, process.hrtime());
}
/**
* @param {SafeMap} timesStore
* @param {string} traceCategory
* @param {string} implementation
* @param {number} timerFlags
* @param {LogImpl} logImpl
* @param {string} logLabel
* @param {string} traceLabel
* @returns {void}
*/
function timeEnd(
timesStore,
traceCategory,
implementation,
timerFlags,
logImpl,
logLabel = 'default',
traceLabel = undefined,
) {
// Coerces everything other than Symbol to a string
logLabel = `${logLabel}`;
if (traceLabel !== undefined) {
traceLabel = `${traceLabel}`;
} else {
traceLabel = logLabel;
}
if ((timerFlags & kSkipLog) === 0) {
timeLogImpl(timesStore, implementation, logImpl, logLabel);
}
if ((timerFlags & kSkipTrace) === 0) {
traceLabel = safeTraceLabel(traceLabel);
trace(kTraceEnd, traceCategory, traceLabel, 0);
}
timesStore.delete(logLabel);
}
/**
* @param {SafeMap} timesStore
* @param {string} traceCategory
* @param {string} implementation
* @param {number} timerFlags
* @param {LogImpl} logImpl
* @param {string} logLabel
* @param {string} traceLabel
* @param {any[]} args
* @returns {void}
*/
function timeLog(
timesStore,
traceCategory,
implementation,
timerFlags,
logImpl,
logLabel = 'default',
traceLabel = undefined,
args,
) {
// Coerces everything other than Symbol to a string
logLabel = `${logLabel}`;
if (traceLabel !== undefined) {
traceLabel = `${traceLabel}`;
} else {
traceLabel = logLabel;
}
if ((timerFlags & kSkipLog) === 0) {
timeLogImpl(timesStore, implementation, logImpl, logLabel, args);
}
if ((timerFlags & kSkipTrace) === 0) {
traceLabel = safeTraceLabel(traceLabel);
trace(kTraceInstant, traceCategory, traceLabel, 0);
}
}
/**
* @type {Record<string, SafeMap>}
*/
let tracesStores;
/**
* @typedef {(logLabel: string, traceLabel?: string) => void} TimerStart
*/
/**
* @typedef {(logLabel: string, traceLabel?: string) => void} TimerEnd
*/
/**
* @typedef {(logLabel: string, traceLabel?: string, args?: any[]) => void} TimerLog
*/
/**
* Debuglog with time fns and support for trace
* @param {string} set
* @param {(startTimer: TimerStart, endTimer: TimerEnd, logTimer: TimerLog) => void} cb
* @returns {{startTimer: TimerStart, endTimer: TimerEnd, logTimer: TimerLog}}
*/
function debugWithTimer(set, cb) {
set = StringPrototypeToUpperCase(set);
if (tracesStores === undefined) {
tracesStores = { __proto__: null };
}
/**
* @type {LogImpl}
*/
function logImpl(label, timeFormatted, args) {
const pid = process.pid;
const colors = { colors: lazyUtilColors().shouldColorize(process.stderr) };
const coloredPID = inspect(pid, colors);
if (args === undefined)
process.stderr.write(format('%s %s %s: %s\n', set, coloredPID, label, timeFormatted));
else
process.stderr.write(
format(
'%s %s %s: %s\n',
set,
coloredPID,
label,
timeFormatted,
...new SafeArrayIterator(args),
),
);
}
const traceCategory = `node,node.${StringPrototypeToLowerCase(set)}`;
let traceCategoryBuffer;
let debugLogCategoryEnabled = false;
let timerFlags = kNone;
function ensureTimerFlagsAreUpdated() {
timerFlags &= ~kSkipTrace;
if (traceCategoryBuffer[0] === 0) {
timerFlags |= kSkipTrace;
}
}
/**
* @type {TimerStart}
*/
function internalStartTimer(logLabel, traceLabel) {
ensureTimerFlagsAreUpdated();
if ((timerFlags & kShouldSkipAll) === kShouldSkipAll) {
return;
}
time(
tracesStores[set],
traceCategory,
'debuglog.time',
timerFlags,
logLabel,
traceLabel,
);
}
/**
* @type {TimerEnd}
*/
function internalEndTimer(logLabel, traceLabel) {
ensureTimerFlagsAreUpdated();
if ((timerFlags & kShouldSkipAll) === kShouldSkipAll) {
return;
}
timeEnd(
tracesStores[set],
traceCategory,
'debuglog.timeEnd',
timerFlags,
logImpl,
logLabel,
traceLabel,
);
}
/**
* @type {TimerLog}
*/
function internalLogTimer(logLabel, traceLabel, args) {
ensureTimerFlagsAreUpdated();
if ((timerFlags & kShouldSkipAll) === kShouldSkipAll) {
return;
}
timeLog(
tracesStores[set],
traceCategory,
'debuglog.timeLog',
timerFlags,
logImpl,
logLabel,
traceLabel,
args,
);
}
function init() {
if (tracesStores[set] === undefined) {
tracesStores[set] = new SafeMap();
}
emitWarningIfNeeded(set);
debugLogCategoryEnabled = testEnabled(set);
traceCategoryBuffer = getCategoryEnabledBuffer(traceCategory);
timerFlags = kNone;
if (!debugLogCategoryEnabled) {
timerFlags |= kSkipLog;
}
if (traceCategoryBuffer[0] === 0) {
timerFlags |= kSkipTrace;
}
cb(internalStartTimer, internalEndTimer, internalLogTimer);
}
/**
* @type {TimerStart}
*/
const startTimer = (logLabel, traceLabel) => {
init();
if ((timerFlags & kShouldSkipAll) !== kShouldSkipAll)
internalStartTimer(logLabel, traceLabel);
};
/**
* @type {TimerEnd}
*/
const endTimer = (logLabel, traceLabel) => {
init();
if ((timerFlags & kShouldSkipAll) !== kShouldSkipAll)
internalEndTimer(logLabel, traceLabel);
};
/**
* @type {TimerLog}
*/
const logTimer = (logLabel, traceLabel, args) => {
init();
if ((timerFlags & kShouldSkipAll) !== kShouldSkipAll)
internalLogTimer(logLabel, traceLabel, args);
};
return {
startTimer,
endTimer,
logTimer,
};
}
module.exports = {
kNone,
kSkipLog,
kSkipTrace,
formatTime,
time,
timeEnd,
timeLog,
debuglog,
debugWithTimer,
initializeDebugEnv,
};

42
lib/internal/util/diff.js Normal file
View File

@ -0,0 +1,42 @@
'use strict';
const {
ArrayIsArray,
ArrayPrototypeReverse,
} = primordials;
const { validateStringArray, validateString } = require('internal/validators');
const { myersDiff } = require('internal/assert/myers_diff');
function validateInput(value, name) {
if (!ArrayIsArray(value)) {
validateString(value, name);
return;
}
validateStringArray(value, name);
}
/**
* Generate a difference report between two values
* @param {Array | string} actual - The first value to compare
* @param {Array | string} expected - The second value to compare
* @returns {Array} - An array of differences between the two values.
* The returned data is an array of arrays, where each sub-array has two elements:
* 1. The operation to perform: -1 for delete, 0 for no-op, 1 for insert
* 2. The value to perform the operation on
*/
function diff(actual, expected) {
if (actual === expected) {
return [];
}
validateInput(actual, 'actual');
validateInput(expected, 'expected');
return ArrayPrototypeReverse(myersDiff(actual, expected));
}
module.exports = {
diff,
};

2525
lib/internal/util/inspect.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,115 @@
'use strict';
const {
ArrayPrototypePushApply,
ArrayPrototypeSome,
FunctionPrototypeBind,
ObjectDefineProperty,
ObjectKeys,
ObjectPrototypeHasOwnProperty,
RegExpPrototypeExec,
SafeWeakMap,
} = primordials;
const { validatePort } = require('internal/validators');
const permission = require('internal/process/permission');
const kMinPort = 1024;
const kMaxPort = 65535;
const kInspectArgRegex = /--inspect(?:-brk|-port)?|--debug-port/;
const kInspectMsgRegex = /Debugger listening on ws:\/\/\[?(.+?)\]?:(\d+)\/|For help, see: https:\/\/nodejs\.org\/en\/docs\/inspector|Debugger attached|Waiting for the debugger to disconnect\.\.\./;
const _isUsingInspector = new SafeWeakMap();
function isUsingInspector(execArgv = process.execArgv) {
if (!_isUsingInspector.has(execArgv)) {
_isUsingInspector.set(execArgv,
ArrayPrototypeSome(execArgv, (arg) => RegExpPrototypeExec(kInspectArgRegex, arg) !== null) ||
RegExpPrototypeExec(kInspectArgRegex, process.env.NODE_OPTIONS) !== null);
}
return _isUsingInspector.get(execArgv);
}
let debugPortOffset = 1;
function getInspectPort(inspectPort) {
if (typeof inspectPort === 'function') {
inspectPort = inspectPort();
} else if (inspectPort == null) {
inspectPort = process.debugPort + debugPortOffset;
if (inspectPort > kMaxPort)
inspectPort = inspectPort - kMaxPort + kMinPort - 1;
debugPortOffset++;
}
validatePort(inspectPort);
return inspectPort;
}
let session;
function sendInspectorCommand(cb, onError) {
const { hasInspector } = internalBinding('config');
if (!hasInspector) return onError();
// Do not preview when the permission model is enabled
// because this feature require access to the inspector,
// which is unavailable in this case.
if (permission.isEnabled()) return onError();
const inspector = require('inspector');
if (session === undefined) session = new inspector.Session();
session.connect();
try {
return cb(session);
} finally {
session.disconnect();
}
}
function isInspectorMessage(string) {
return isUsingInspector() && RegExpPrototypeExec(kInspectMsgRegex, string) !== null;
}
// Create a special require function for the inspector command line API
function installConsoleExtensions(commandLineApi) {
if (commandLineApi.require) { return; }
const { tryGetCwd } = require('internal/process/execution');
const CJSModule = require('internal/modules/cjs/loader').Module;
const { makeRequireFunction } = require('internal/modules/helpers');
const consoleAPIModule = new CJSModule('<inspector console>');
const cwd = tryGetCwd();
consoleAPIModule.paths = [];
ArrayPrototypePushApply(consoleAPIModule.paths, CJSModule._nodeModulePaths(cwd));
ArrayPrototypePushApply(consoleAPIModule.paths, CJSModule.globalPaths);
commandLineApi.require = makeRequireFunction(consoleAPIModule);
}
// Wrap a console implemented by Node.js with features from the VM inspector
function wrapConsole(consoleFromNode) {
const { consoleCall, console: consoleFromVM } = internalBinding('inspector');
for (const key of ObjectKeys(consoleFromVM)) {
// If global console has the same method as inspector console,
// then wrap these two methods into one. Native wrapper will preserve
// the original stack.
if (ObjectPrototypeHasOwnProperty(consoleFromNode, key)) {
consoleFromNode[key] = FunctionPrototypeBind(
consoleCall,
consoleFromNode,
consoleFromVM[key],
consoleFromNode[key],
);
ObjectDefineProperty(consoleFromNode[key], 'name', {
__proto__: null,
value: key,
});
} else {
// Add additional console APIs from the inspector
consoleFromNode[key] = consoleFromVM[key];
}
}
}
module.exports = {
getInspectPort,
installConsoleExtensions,
isInspectorMessage,
isUsingInspector,
sendInspectorCommand,
wrapConsole,
};

View File

@ -0,0 +1,412 @@
'use strict';
const {
ArrayPrototypeForEach,
ArrayPrototypeIncludes,
ArrayPrototypeMap,
ArrayPrototypePush,
ArrayPrototypePushApply,
ArrayPrototypeShift,
ArrayPrototypeSlice,
ArrayPrototypeUnshiftApply,
ObjectEntries,
ObjectPrototypeHasOwnProperty: ObjectHasOwn,
StringPrototypeCharAt,
StringPrototypeIndexOf,
StringPrototypeSlice,
StringPrototypeStartsWith,
} = primordials;
const {
validateArray,
validateBoolean,
validateBooleanArray,
validateObject,
validateString,
validateStringArray,
validateUnion,
} = require('internal/validators');
const {
findLongOptionForShort,
isLoneLongOption,
isLoneShortOption,
isLongOptionAndValue,
isOptionValue,
isOptionLikeValue,
isShortOptionAndValue,
isShortOptionGroup,
useDefaultValueOption,
objectGetOwn,
optionsGetOwn,
} = require('internal/util/parse_args/utils');
const {
codes: {
ERR_INVALID_ARG_VALUE,
ERR_PARSE_ARGS_INVALID_OPTION_VALUE,
ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL,
ERR_PARSE_ARGS_UNKNOWN_OPTION,
},
} = require('internal/errors');
const {
kEmptyObject,
} = require('internal/util');
function getMainArgs() {
// Work out where to slice process.argv for user supplied arguments.
// Check node options for scenarios where user CLI args follow executable.
const execArgv = process.execArgv;
if (ArrayPrototypeIncludes(execArgv, '-e') ||
ArrayPrototypeIncludes(execArgv, '--eval') ||
ArrayPrototypeIncludes(execArgv, '-p') ||
ArrayPrototypeIncludes(execArgv, '--print')) {
return ArrayPrototypeSlice(process.argv, 1);
}
// Normally first two arguments are executable and script, then CLI arguments
return ArrayPrototypeSlice(process.argv, 2);
}
/**
* In strict mode, throw for possible usage errors like --foo --bar
* @param {object} token - from tokens as available from parseArgs
*/
function checkOptionLikeValue(token) {
if (!token.inlineValue && isOptionLikeValue(token.value)) {
// Only show short example if user used short option.
const example = StringPrototypeStartsWith(token.rawName, '--') ?
`'${token.rawName}=-XYZ'` :
`'--${token.name}=-XYZ' or '${token.rawName}-XYZ'`;
const errorMessage = `Option '${token.rawName}' argument is ambiguous.
Did you forget to specify the option argument for '${token.rawName}'?
To specify an option argument starting with a dash use ${example}.`;
throw new ERR_PARSE_ARGS_INVALID_OPTION_VALUE(errorMessage);
}
}
/**
* In strict mode, throw for usage errors.
* @param {object} config - from config passed to parseArgs
* @param {object} token - from tokens as available from parseArgs
*/
function checkOptionUsage(config, token) {
let tokenName = token.name;
if (!ObjectHasOwn(config.options, tokenName)) {
// Check for negated boolean option.
if (config.allowNegative && StringPrototypeStartsWith(tokenName, 'no-')) {
tokenName = StringPrototypeSlice(tokenName, 3);
if (!ObjectHasOwn(config.options, tokenName) || optionsGetOwn(config.options, tokenName, 'type') !== 'boolean') {
throw new ERR_PARSE_ARGS_UNKNOWN_OPTION(
token.rawName, config.allowPositionals);
}
} else {
throw new ERR_PARSE_ARGS_UNKNOWN_OPTION(
token.rawName, config.allowPositionals);
}
}
const short = optionsGetOwn(config.options, tokenName, 'short');
const shortAndLong = `${short ? `-${short}, ` : ''}--${tokenName}`;
const type = optionsGetOwn(config.options, tokenName, 'type');
if (type === 'string' && typeof token.value !== 'string') {
throw new ERR_PARSE_ARGS_INVALID_OPTION_VALUE(`Option '${shortAndLong} <value>' argument missing`);
}
// (Idiomatic test for undefined||null, expecting undefined.)
if (type === 'boolean' && token.value != null) {
throw new ERR_PARSE_ARGS_INVALID_OPTION_VALUE(`Option '${shortAndLong}' does not take an argument`);
}
}
/**
* Store the option value in `values`.
* @param {object} token - from tokens as available from parseArgs
* @param {object} options - option configs, from parseArgs({ options })
* @param {object} values - option values returned in `values` by parseArgs
* @param {boolean} allowNegative - allow negative optinons if true
*/
function storeOption(token, options, values, allowNegative) {
let longOption = token.name;
let optionValue = token.value;
if (longOption === '__proto__') {
return; // No. Just no.
}
if (allowNegative && StringPrototypeStartsWith(longOption, 'no-') && optionValue === undefined) {
// Boolean option negation: --no-foo
longOption = StringPrototypeSlice(longOption, 3);
token.name = longOption;
optionValue = false;
}
// We store based on the option value rather than option type,
// preserving the users intent for author to deal with.
const newValue = optionValue ?? true;
if (optionsGetOwn(options, longOption, 'multiple')) {
// Always store value in array, including for boolean.
// values[longOption] starts out not present,
// first value is added as new array [newValue],
// subsequent values are pushed to existing array.
// (note: values has null prototype, so simpler usage)
if (values[longOption]) {
ArrayPrototypePush(values[longOption], newValue);
} else {
values[longOption] = [newValue];
}
} else {
values[longOption] = newValue;
}
}
/**
* Store the default option value in `values`.
* @param {string} longOption - long option name e.g. 'foo'
* @param {string
* | boolean
* | string[]
* | boolean[]} optionValue - default value from option config
* @param {object} values - option values returned in `values` by parseArgs
*/
function storeDefaultOption(longOption, optionValue, values) {
if (longOption === '__proto__') {
return; // No. Just no.
}
values[longOption] = optionValue;
}
/**
* Process args and turn into identified tokens:
* - option (along with value, if any)
* - positional
* - option-terminator
* @param {string[]} args - from parseArgs({ args }) or mainArgs
* @param {object} options - option configs, from parseArgs({ options })
*/
function argsToTokens(args, options) {
const tokens = [];
let index = -1;
let groupCount = 0;
const remainingArgs = ArrayPrototypeSlice(args);
while (remainingArgs.length > 0) {
const arg = ArrayPrototypeShift(remainingArgs);
const nextArg = remainingArgs[0];
if (groupCount > 0)
groupCount--;
else
index++;
// Check if `arg` is an options terminator.
// Guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
if (arg === '--') {
// Everything after a bare '--' is considered a positional argument.
ArrayPrototypePush(tokens, { kind: 'option-terminator', index });
ArrayPrototypePushApply(
tokens, ArrayPrototypeMap(remainingArgs, (arg) => {
return { kind: 'positional', index: ++index, value: arg };
}),
);
break; // Finished processing args, leave while loop.
}
if (isLoneShortOption(arg)) {
// e.g. '-f'
const shortOption = StringPrototypeCharAt(arg, 1);
const longOption = findLongOptionForShort(shortOption, options);
let value;
let inlineValue;
if (optionsGetOwn(options, longOption, 'type') === 'string' &&
isOptionValue(nextArg)) {
// e.g. '-f', 'bar'
value = ArrayPrototypeShift(remainingArgs);
inlineValue = false;
}
ArrayPrototypePush(
tokens,
{ kind: 'option', name: longOption, rawName: arg,
index, value, inlineValue });
if (value != null) ++index;
continue;
}
if (isShortOptionGroup(arg, options)) {
// Expand -fXzy to -f -X -z -y
const expanded = [];
for (let index = 1; index < arg.length; index++) {
const shortOption = StringPrototypeCharAt(arg, index);
const longOption = findLongOptionForShort(shortOption, options);
if (optionsGetOwn(options, longOption, 'type') !== 'string' ||
index === arg.length - 1) {
// Boolean option, or last short in group. Well formed.
ArrayPrototypePush(expanded, `-${shortOption}`);
} else {
// String option in middle. Yuck.
// Expand -abfFILE to -a -b -fFILE
ArrayPrototypePush(expanded, `-${StringPrototypeSlice(arg, index)}`);
break; // finished short group
}
}
ArrayPrototypeUnshiftApply(remainingArgs, expanded);
groupCount = expanded.length;
continue;
}
if (isShortOptionAndValue(arg, options)) {
// e.g. -fFILE
const shortOption = StringPrototypeCharAt(arg, 1);
const longOption = findLongOptionForShort(shortOption, options);
const value = StringPrototypeSlice(arg, 2);
ArrayPrototypePush(
tokens,
{ kind: 'option', name: longOption, rawName: `-${shortOption}`,
index, value, inlineValue: true });
continue;
}
if (isLoneLongOption(arg)) {
// e.g. '--foo'
const longOption = StringPrototypeSlice(arg, 2);
let value;
let inlineValue;
if (optionsGetOwn(options, longOption, 'type') === 'string' &&
isOptionValue(nextArg)) {
// e.g. '--foo', 'bar'
value = ArrayPrototypeShift(remainingArgs);
inlineValue = false;
}
ArrayPrototypePush(
tokens,
{ kind: 'option', name: longOption, rawName: arg,
index, value, inlineValue });
if (value != null) ++index;
continue;
}
if (isLongOptionAndValue(arg)) {
// e.g. --foo=bar
const equalIndex = StringPrototypeIndexOf(arg, '=');
const longOption = StringPrototypeSlice(arg, 2, equalIndex);
const value = StringPrototypeSlice(arg, equalIndex + 1);
ArrayPrototypePush(
tokens,
{ kind: 'option', name: longOption, rawName: `--${longOption}`,
index, value, inlineValue: true });
continue;
}
ArrayPrototypePush(tokens, { kind: 'positional', index, value: arg });
}
return tokens;
}
const parseArgs = (config = kEmptyObject) => {
const args = objectGetOwn(config, 'args') ?? getMainArgs();
const strict = objectGetOwn(config, 'strict') ?? true;
const allowPositionals = objectGetOwn(config, 'allowPositionals') ?? !strict;
const returnTokens = objectGetOwn(config, 'tokens') ?? false;
const allowNegative = objectGetOwn(config, 'allowNegative') ?? false;
const options = objectGetOwn(config, 'options') ?? { __proto__: null };
// Bundle these up for passing to strict-mode checks.
const parseConfig = { args, strict, options, allowPositionals, allowNegative };
// Validate input configuration.
validateArray(args, 'args');
validateBoolean(strict, 'strict');
validateBoolean(allowPositionals, 'allowPositionals');
validateBoolean(returnTokens, 'tokens');
validateBoolean(allowNegative, 'allowNegative');
validateObject(options, 'options');
ArrayPrototypeForEach(
ObjectEntries(options),
({ 0: longOption, 1: optionConfig }) => {
validateObject(optionConfig, `options.${longOption}`);
// type is required
const optionType = objectGetOwn(optionConfig, 'type');
validateUnion(optionType, `options.${longOption}.type`, ['string', 'boolean']);
if (ObjectHasOwn(optionConfig, 'short')) {
const shortOption = optionConfig.short;
validateString(shortOption, `options.${longOption}.short`);
if (shortOption.length !== 1) {
throw new ERR_INVALID_ARG_VALUE(
`options.${longOption}.short`,
shortOption,
'must be a single character',
);
}
}
const multipleOption = objectGetOwn(optionConfig, 'multiple');
if (ObjectHasOwn(optionConfig, 'multiple')) {
validateBoolean(multipleOption, `options.${longOption}.multiple`);
}
const defaultValue = objectGetOwn(optionConfig, 'default');
if (defaultValue !== undefined) {
let validator;
switch (optionType) {
case 'string':
validator = multipleOption ? validateStringArray : validateString;
break;
case 'boolean':
validator = multipleOption ? validateBooleanArray : validateBoolean;
break;
}
validator(defaultValue, `options.${longOption}.default`);
}
},
);
// Phase 1: identify tokens
const tokens = argsToTokens(args, options);
// Phase 2: process tokens into parsed option values and positionals
const result = {
values: { __proto__: null },
positionals: [],
};
if (returnTokens) {
result.tokens = tokens;
}
ArrayPrototypeForEach(tokens, (token) => {
if (token.kind === 'option') {
if (strict) {
checkOptionUsage(parseConfig, token);
checkOptionLikeValue(token);
}
storeOption(token, options, result.values, parseConfig.allowNegative);
} else if (token.kind === 'positional') {
if (!allowPositionals) {
throw new ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL(token.value);
}
ArrayPrototypePush(result.positionals, token.value);
}
});
// Phase 3: fill in default values for missing args
ArrayPrototypeForEach(ObjectEntries(options), ({ 0: longOption,
1: optionConfig }) => {
const mustSetDefault = useDefaultValueOption(longOption,
optionConfig,
result.values);
if (mustSetDefault) {
storeDefaultOption(longOption,
objectGetOwn(optionConfig, 'default'),
result.values);
}
});
return result;
};
module.exports = {
parseArgs,
};

View File

@ -0,0 +1,197 @@
'use strict';
const {
ArrayPrototypeFind,
ObjectEntries,
ObjectPrototypeHasOwnProperty: ObjectHasOwn,
StringPrototypeCharAt,
StringPrototypeIncludes,
StringPrototypeStartsWith,
} = primordials;
const {
validateObject,
} = require('internal/validators');
// These are internal utilities to make the parsing logic easier to read, and
// add lots of detail for the curious. They are in a separate file to allow
// unit testing, although that is not essential (this could be rolled into
// main file and just tested implicitly via API).
//
// These routines are for internal use, not for export to client.
/**
* Return the named property, but only if it is an own property.
*/
function objectGetOwn(obj, prop) {
if (ObjectHasOwn(obj, prop))
return obj[prop];
}
/**
* Return the named options property, but only if it is an own property.
*/
function optionsGetOwn(options, longOption, prop) {
if (ObjectHasOwn(options, longOption))
return objectGetOwn(options[longOption], prop);
}
/**
* Determines if the argument may be used as an option value.
* @example
* isOptionValue('V') // returns true
* isOptionValue('-v') // returns true (greedy)
* isOptionValue('--foo') // returns true (greedy)
* isOptionValue(undefined) // returns false
*/
function isOptionValue(value) {
if (value == null) return false;
// Open Group Utility Conventions are that an option-argument
// is the argument after the option, and may start with a dash.
return true; // greedy!
}
/**
* Detect whether there is possible confusion and user may have omitted
* the option argument, like `--port --verbose` when `port` of type:string.
* In strict mode we throw errors if value is option-like.
*/
function isOptionLikeValue(value) {
if (value == null) return false;
return value.length > 1 && StringPrototypeCharAt(value, 0) === '-';
}
/**
* Determines if `arg` is just a short option.
* @example '-f'
*/
function isLoneShortOption(arg) {
return arg.length === 2 &&
StringPrototypeCharAt(arg, 0) === '-' &&
StringPrototypeCharAt(arg, 1) !== '-';
}
/**
* Determines if `arg` is a lone long option.
* @example
* isLoneLongOption('a') // returns false
* isLoneLongOption('-a') // returns false
* isLoneLongOption('--foo') // returns true
* isLoneLongOption('--foo=bar') // returns false
*/
function isLoneLongOption(arg) {
return arg.length > 2 &&
StringPrototypeStartsWith(arg, '--') &&
!StringPrototypeIncludes(arg, '=', 3);
}
/**
* Determines if `arg` is a long option and value in the same argument.
* @example
* isLongOptionAndValue('--foo') // returns false
* isLongOptionAndValue('--foo=bar') // returns true
*/
function isLongOptionAndValue(arg) {
return arg.length > 2 &&
StringPrototypeStartsWith(arg, '--') &&
StringPrototypeIncludes(arg, '=', 3);
}
/**
* Determines if `arg` is a short option group.
*
* See Guideline 5 of the [Open Group Utility Conventions](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html).
* One or more options without option-arguments, followed by at most one
* option that takes an option-argument, should be accepted when grouped
* behind one '-' delimiter.
* @example
* isShortOptionGroup('-a', {}) // returns false
* isShortOptionGroup('-ab', {}) // returns true
* // -fb is an option and a value, not a short option group
* isShortOptionGroup('-fb', {
* options: { f: { type: 'string' } }
* }) // returns false
* isShortOptionGroup('-bf', {
* options: { f: { type: 'string' } }
* }) // returns true
* // -bfb is an edge case, return true and caller sorts it out
* isShortOptionGroup('-bfb', {
* options: { f: { type: 'string' } }
* }) // returns true
*/
function isShortOptionGroup(arg, options) {
if (arg.length <= 2) return false;
if (StringPrototypeCharAt(arg, 0) !== '-') return false;
if (StringPrototypeCharAt(arg, 1) === '-') return false;
const firstShort = StringPrototypeCharAt(arg, 1);
const longOption = findLongOptionForShort(firstShort, options);
return optionsGetOwn(options, longOption, 'type') !== 'string';
}
/**
* Determine if arg is a short string option followed by its value.
* @example
* isShortOptionAndValue('-a', {}); // returns false
* isShortOptionAndValue('-ab', {}); // returns false
* isShortOptionAndValue('-fFILE', {
* options: { foo: { short: 'f', type: 'string' }}
* }) // returns true
*/
function isShortOptionAndValue(arg, options) {
validateObject(options, 'options');
if (arg.length <= 2) return false;
if (StringPrototypeCharAt(arg, 0) !== '-') return false;
if (StringPrototypeCharAt(arg, 1) === '-') return false;
const shortOption = StringPrototypeCharAt(arg, 1);
const longOption = findLongOptionForShort(shortOption, options);
return optionsGetOwn(options, longOption, 'type') === 'string';
}
/**
* Find the long option associated with a short option. Looks for a configured
* `short` and returns the short option itself if a long option is not found.
* @example
* findLongOptionForShort('a', {}) // returns 'a'
* findLongOptionForShort('b', {
* options: { bar: { short: 'b' } }
* }) // returns 'bar'
*/
function findLongOptionForShort(shortOption, options) {
validateObject(options, 'options');
const longOptionEntry = ArrayPrototypeFind(
ObjectEntries(options),
({ 1: optionConfig }) => objectGetOwn(optionConfig, 'short') === shortOption,
);
return longOptionEntry?.[0] ?? shortOption;
}
/**
* Check if the given option includes a default value
* and that option has not been set by the input args.
* @param {string} longOption - long option name e.g. 'foo'
* @param {object} optionConfig - the option configuration properties
* @param {object} values - option values returned in `values` by parseArgs
*/
function useDefaultValueOption(longOption, optionConfig, values) {
return objectGetOwn(optionConfig, 'default') !== undefined &&
values[longOption] === undefined;
}
module.exports = {
findLongOptionForShort,
isLoneLongOption,
isLoneShortOption,
isLongOptionAndValue,
isOptionValue,
isOptionLikeValue,
isShortOptionAndValue,
isShortOptionGroup,
useDefaultValueOption,
objectGetOwn,
optionsGetOwn,
};

115
lib/internal/util/types.js Normal file
View File

@ -0,0 +1,115 @@
'use strict';
const {
ArrayBufferIsView,
ObjectDefineProperties,
TypedArrayPrototypeGetSymbolToStringTag,
} = primordials;
function isTypedArray(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) !== undefined;
}
function isUint8Array(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'Uint8Array';
}
function isUint8ClampedArray(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'Uint8ClampedArray';
}
function isUint16Array(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'Uint16Array';
}
function isUint32Array(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'Uint32Array';
}
function isInt8Array(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'Int8Array';
}
function isInt16Array(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'Int16Array';
}
function isInt32Array(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'Int32Array';
}
function isFloat16Array(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'Float16Array';
}
function isFloat32Array(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'Float32Array';
}
function isFloat64Array(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'Float64Array';
}
function isBigInt64Array(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'BigInt64Array';
}
function isBigUint64Array(value) {
return TypedArrayPrototypeGetSymbolToStringTag(value) === 'BigUint64Array';
}
module.exports = {
...internalBinding('types'),
isArrayBufferView: ArrayBufferIsView,
isTypedArray,
isUint8Array,
isUint8ClampedArray,
isUint16Array,
isUint32Array,
isInt8Array,
isInt16Array,
isInt32Array,
isFloat16Array,
isFloat32Array,
isFloat64Array,
isBigInt64Array,
isBigUint64Array,
};
let isCryptoKey;
let isKeyObject;
ObjectDefineProperties(module.exports, {
isKeyObject: {
__proto__: null,
configurable: false,
enumerable: true,
value(obj) {
if (!process.versions.openssl) {
return false;
}
if (!isKeyObject) {
({ isKeyObject } = require('internal/crypto/keys'));
}
return isKeyObject(obj);
},
},
isCryptoKey: {
__proto__: null,
configurable: false,
enumerable: true,
value(obj) {
if (!process.versions.openssl) {
return false;
}
if (!isCryptoKey) {
({ isCryptoKey } = require('internal/crypto/keys'));
}
return isCryptoKey(obj);
},
},
});