2121#include < string_view>
2222#include < vector>
2323
24+ using v8::Array;
2425using v8::Boolean;
2526using v8::Context;
2627using v8::FunctionCallbackInfo;
@@ -1867,6 +1868,117 @@ void GetNamespaceOptionsInputType(const FunctionCallbackInfo<Value>& args) {
18671868 args.GetReturnValue ().Set (namespaces_map);
18681869}
18691870
1871+ // Return an array containing all currently active options as flag
1872+ // strings from all sources (command line, NODE_OPTIONS, config file)
1873+ void GetOptionsAsFlags (const FunctionCallbackInfo<Value>& args) {
1874+ Isolate* isolate = args.GetIsolate ();
1875+ Local<Context> context = isolate->GetCurrentContext ();
1876+ Environment* env = Environment::GetCurrent (context);
1877+
1878+ if (!env->has_run_bootstrapping_code ()) {
1879+ // No code because this is an assertion.
1880+ THROW_ERR_OPTIONS_BEFORE_BOOTSTRAPPING (
1881+ isolate, " Should not query options before bootstrapping is done" );
1882+ }
1883+ env->set_has_serialized_options (true );
1884+
1885+ Mutex::ScopedLock lock (per_process::cli_options_mutex);
1886+ IterateCLIOptionsScope s (env);
1887+
1888+ std::vector<std::string> flags;
1889+ PerProcessOptions* opts = per_process::cli_options.get ();
1890+
1891+ for (const auto & item : _ppop_instance.options_ ) {
1892+ const std::string& option_name = item.first ;
1893+ const auto & option_info = item.second ;
1894+ auto field = option_info.field ;
1895+
1896+ // TODO(pmarchini): Skip internal options for the moment as probably not
1897+ // required
1898+ if (option_name.empty () || option_name.starts_with (' [' )) {
1899+ continue ;
1900+ }
1901+
1902+ // Skip V8 options and NoOp options - only Node.js-specific options
1903+ if (option_info.type == kNoOp || option_info.type == kV8Option ) {
1904+ continue ;
1905+ }
1906+
1907+ switch (option_info.type ) {
1908+ case kBoolean : {
1909+ bool current_value = *_ppop_instance.Lookup <bool >(field, opts);
1910+ // For boolean options with default_is_true, we want the opposite logic
1911+ if (option_info.default_is_true ) {
1912+ if (!current_value) {
1913+ // If default is true and current is false, add --no-* flag
1914+ flags.push_back (" --no-" + option_name.substr (2 ));
1915+ }
1916+ } else {
1917+ if (current_value) {
1918+ // If default is false and current is true, add --flag
1919+ flags.push_back (option_name);
1920+ }
1921+ }
1922+ break ;
1923+ }
1924+ case kInteger : {
1925+ int64_t current_value = *_ppop_instance.Lookup <int64_t >(field, opts);
1926+ flags.push_back (option_name + " =" + std::to_string (current_value));
1927+ break ;
1928+ }
1929+ case kUInteger : {
1930+ uint64_t current_value = *_ppop_instance.Lookup <uint64_t >(field, opts);
1931+ flags.push_back (option_name + " =" + std::to_string (current_value));
1932+ break ;
1933+ }
1934+ case kString : {
1935+ const std::string& current_value =
1936+ *_ppop_instance.Lookup <std::string>(field, opts);
1937+ // Only include if not empty
1938+ if (!current_value.empty ()) {
1939+ flags.push_back (option_name + " =" + current_value);
1940+ }
1941+ break ;
1942+ }
1943+ case kStringList : {
1944+ const std::vector<std::string>& current_values =
1945+ *_ppop_instance.Lookup <StringVector>(field, opts);
1946+ // Add each string in the list as a separate flag
1947+ for (const std::string& value : current_values) {
1948+ flags.push_back (option_name + " =" + value);
1949+ }
1950+ break ;
1951+ }
1952+ case kHostPort : {
1953+ const HostPort& host_port =
1954+ *_ppop_instance.Lookup <HostPort>(field, opts);
1955+ // Only include if host is not empty or port is not default
1956+ if (!host_port.host ().empty () || host_port.port () != 0 ) {
1957+ std::string host_port_str = host_port.host ();
1958+ if (host_port.port () != 0 ) {
1959+ if (!host_port_str.empty ()) {
1960+ host_port_str += " :" ;
1961+ }
1962+ host_port_str += std::to_string (host_port.port ());
1963+ }
1964+ if (!host_port_str.empty ()) {
1965+ flags.push_back (option_name + " =" + host_port_str);
1966+ }
1967+ }
1968+ break ;
1969+ }
1970+ default :
1971+ // Skip unknown types
1972+ break ;
1973+ }
1974+ }
1975+
1976+ Local<Value> result;
1977+ CHECK (ToV8Value (context, flags).ToLocal (&result));
1978+
1979+ args.GetReturnValue ().Set (result);
1980+ }
1981+
18701982void Initialize (Local<Object> target,
18711983 Local<Value> unused,
18721984 Local<Context> context,
@@ -1877,6 +1989,8 @@ void Initialize(Local<Object> target,
18771989 context, target, " getCLIOptionsValues" , GetCLIOptionsValues);
18781990 SetMethodNoSideEffect (
18791991 context, target, " getCLIOptionsInfo" , GetCLIOptionsInfo);
1992+ SetMethodNoSideEffect (
1993+ context, target, " getOptionsAsFlags" , GetOptionsAsFlags);
18801994 SetMethodNoSideEffect (
18811995 context, target, " getEmbedderOptions" , GetEmbedderOptions);
18821996 SetMethodNoSideEffect (
@@ -1909,6 +2023,7 @@ void Initialize(Local<Object> target,
19092023void RegisterExternalReferences (ExternalReferenceRegistry* registry) {
19102024 registry->Register (GetCLIOptionsValues);
19112025 registry->Register (GetCLIOptionsInfo);
2026+ registry->Register (GetOptionsAsFlags);
19122027 registry->Register (GetEmbedderOptions);
19132028 registry->Register (GetEnvOptionsInputType);
19142029 registry->Register (GetNamespaceOptionsInputType);
0 commit comments