mirror of
https://github.com/oven-sh/bun
synced 2026-02-03 07:28:53 +00:00
Compare commits
13 Commits
ciro/fix-a
...
nektro-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f88ba440a5 | ||
|
|
9f03331fca | ||
|
|
b17c400f85 | ||
|
|
b1b275fbb0 | ||
|
|
3234c79227 | ||
|
|
8e56d6a3b3 | ||
|
|
b69081532e | ||
|
|
284050c11f | ||
|
|
2320d69cf6 | ||
|
|
030083684e | ||
|
|
eb563fa3e4 | ||
|
|
e0b3ef0683 | ||
|
|
c953544881 |
@@ -1,18 +1,17 @@
|
||||
#include "root.h"
|
||||
#include "ZigGlobalObject.h"
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
#include <JavaScriptCore/JSObject.h>
|
||||
#include <JavaScriptCore/ObjectConstructor.h>
|
||||
#include <JavaScriptCore/JSArray.h>
|
||||
#include <JavaScriptCore/JSArrayInlines.h>
|
||||
#include <JavaScriptCore/JSString.h>
|
||||
#include <JavaScriptCore/JSStringInlines.h>
|
||||
|
||||
#include "BunClientData.h"
|
||||
#include "wtf/Compiler.h"
|
||||
#include "wtf/Forward.h"
|
||||
#include "JSEnvironmentVariableMap.h"
|
||||
#include "ErrorCode.h"
|
||||
|
||||
using namespace JSC;
|
||||
|
||||
@@ -267,7 +266,6 @@ JSC_DEFINE_HOST_FUNCTION(jsEditWindowsEnvVar, (JSGlobalObject * global, JSC::Cal
|
||||
WTF::String string1 = callFrame->uncheckedArgument(0).toWTFString(global);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
JSValue arg2 = callFrame->uncheckedArgument(1);
|
||||
ASSERT(arg2.isNull() || arg2.isString());
|
||||
if (arg2.isCell()) {
|
||||
WTF::String string2 = arg2.toWTFString(global);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
@@ -279,6 +277,55 @@ JSC_DEFINE_HOST_FUNCTION(jsEditWindowsEnvVar, (JSGlobalObject * global, JSC::Cal
|
||||
}
|
||||
#endif
|
||||
|
||||
const JSC::ClassInfo JSEnvironmentVariableMap::s_info = { "EnvironmentVariableMap"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSEnvironmentVariableMap) };
|
||||
|
||||
void JSEnvironmentVariableMap::finishCreation(JSC::VM& vm)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
ASSERT(inherits(info()));
|
||||
}
|
||||
|
||||
bool JSEnvironmentVariableMap::defineOwnProperty(JSC::JSObject* object, JSC::JSGlobalObject* globalObject, JSC::PropertyName propertyName, const JSC::PropertyDescriptor& descriptor, bool shouldThrow)
|
||||
{
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
if (propertyName.isSymbol()) {
|
||||
throwTypeError(globalObject, scope, "Cannot convert a symbol to a string"_s);
|
||||
return false;
|
||||
}
|
||||
if (descriptor.getterPresent() || descriptor.setterPresent()) {
|
||||
scope.throwException(globalObject, createError(globalObject, Bun::ErrorCode::ERR_INVALID_OBJECT_DEFINE_PROPERTY, "'process.env' does not accept an accessor(getter/setter) descriptor"_s));
|
||||
return false;
|
||||
}
|
||||
if (!descriptor.configurable() || !descriptor.writable() || !descriptor.enumerable()) {
|
||||
scope.throwException(globalObject, createError(globalObject, Bun::ErrorCode::ERR_INVALID_OBJECT_DEFINE_PROPERTY, "'process.env' only accepts a configurable, writable, and enumerable data descriptor"_s));
|
||||
return false;
|
||||
}
|
||||
auto value = descriptor.value();
|
||||
value = value ? value : jsUndefined();
|
||||
if (propertyName.publicName()->length() == 0) return false;
|
||||
auto string = value.toStringOrNull(globalObject);
|
||||
EXCEPTION_ASSERT(!!string == !scope.exception());
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
descriptor.value() = string;
|
||||
RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, globalObject, propertyName, descriptor, shouldThrow));
|
||||
}
|
||||
|
||||
bool JSEnvironmentVariableMap::put(JSC::JSCell* cell, JSC::JSGlobalObject* globalObject, JSC::PropertyName propertyName, JSC::JSValue value, JSC::PutPropertySlot& slot)
|
||||
{
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
if (propertyName.isSymbol()) {
|
||||
throwTypeError(globalObject, scope, "Cannot convert a symbol to a string"_s);
|
||||
return false;
|
||||
}
|
||||
if (propertyName.publicName()->length() == 0) return false;
|
||||
auto string = value.toStringOrNull(globalObject);
|
||||
EXCEPTION_ASSERT(!!string == !scope.exception());
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
RELEASE_AND_RETURN(scope, Base::put(cell, globalObject, propertyName, string, slot));
|
||||
}
|
||||
|
||||
JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
@@ -286,12 +333,11 @@ JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject)
|
||||
|
||||
void* list;
|
||||
size_t count = Bun__getEnvCount(globalObject, &list);
|
||||
JSC::JSObject* object = nullptr;
|
||||
if (count < 63) {
|
||||
object = constructEmptyObject(globalObject, globalObject->objectPrototype(), count);
|
||||
} else {
|
||||
object = constructEmptyObject(globalObject, globalObject->objectPrototype());
|
||||
}
|
||||
#if OS(WINDOWS)
|
||||
auto* object = constructEmptyObject(globalObject, globalObject->objectPrototype());
|
||||
#else
|
||||
auto* object = JSEnvironmentVariableMap::create(vm, globalObject, JSEnvironmentVariableMap::createStructure(vm, globalObject, globalObject->objectPrototype()));
|
||||
#endif
|
||||
|
||||
#if OS(WINDOWS)
|
||||
JSArray* keyArray = constructEmptyArray(globalObject, nullptr, count);
|
||||
@@ -362,25 +408,19 @@ JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject)
|
||||
if (!hasTZ) {
|
||||
TZAttrs |= JSC::PropertyAttribute::DontEnum;
|
||||
}
|
||||
object->putDirectCustomAccessor(
|
||||
vm,
|
||||
Identifier::fromString(vm, TZ), JSC::CustomGetterSetter::create(vm, jsTimeZoneEnvironmentVariableGetter, jsTimeZoneEnvironmentVariableSetter), TZAttrs);
|
||||
object->putDirectCustomAccessor(vm, Identifier::fromString(vm, TZ), JSC::CustomGetterSetter::create(vm, jsTimeZoneEnvironmentVariableGetter, jsTimeZoneEnvironmentVariableSetter), TZAttrs);
|
||||
|
||||
unsigned int NODE_TLS_REJECT_UNAUTHORIZED_Attrs = JSC::PropertyAttribute::CustomAccessor | 0;
|
||||
if (!hasNodeTLSRejectUnauthorized) {
|
||||
NODE_TLS_REJECT_UNAUTHORIZED_Attrs |= JSC::PropertyAttribute::DontEnum;
|
||||
}
|
||||
object->putDirectCustomAccessor(
|
||||
vm,
|
||||
Identifier::fromString(vm, NODE_TLS_REJECT_UNAUTHORIZED), JSC::CustomGetterSetter::create(vm, jsNodeTLSRejectUnauthorizedGetter, jsNodeTLSRejectUnauthorizedSetter), NODE_TLS_REJECT_UNAUTHORIZED_Attrs);
|
||||
object->putDirectCustomAccessor(vm, Identifier::fromString(vm, NODE_TLS_REJECT_UNAUTHORIZED), JSC::CustomGetterSetter::create(vm, jsNodeTLSRejectUnauthorizedGetter, jsNodeTLSRejectUnauthorizedSetter), NODE_TLS_REJECT_UNAUTHORIZED_Attrs);
|
||||
|
||||
unsigned int BUN_CONFIG_VERBOSE_FETCH_Attrs = JSC::PropertyAttribute::CustomAccessor | 0;
|
||||
if (!hasBunConfigVerboseFetch) {
|
||||
BUN_CONFIG_VERBOSE_FETCH_Attrs |= JSC::PropertyAttribute::DontEnum;
|
||||
}
|
||||
object->putDirectCustomAccessor(
|
||||
vm,
|
||||
Identifier::fromString(vm, BUN_CONFIG_VERBOSE_FETCH), JSC::CustomGetterSetter::create(vm, jsBunConfigVerboseFetchGetter, jsBunConfigVerboseFetchSetter), BUN_CONFIG_VERBOSE_FETCH_Attrs);
|
||||
object->putDirectCustomAccessor(vm, Identifier::fromString(vm, BUN_CONFIG_VERBOSE_FETCH), JSC::CustomGetterSetter::create(vm, jsBunConfigVerboseFetchGetter, jsBunConfigVerboseFetchSetter), BUN_CONFIG_VERBOSE_FETCH_Attrs);
|
||||
|
||||
#if OS(WINDOWS)
|
||||
auto editWindowsEnvVar = JSC::JSFunction::create(vm, globalObject, 0, String("editWindowsEnvVar"_s), jsEditWindowsEnvVar, ImplementationVisibility::Public);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
#include "root.h"
|
||||
#include "BunClientData.h"
|
||||
|
||||
namespace Zig {
|
||||
class GlobalObject;
|
||||
@@ -12,4 +14,43 @@ namespace Bun {
|
||||
|
||||
JSC::JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject);
|
||||
|
||||
class JSEnvironmentVariableMap : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags | JSC::OverridesPut;
|
||||
~JSEnvironmentVariableMap();
|
||||
|
||||
static JSEnvironmentVariableMap* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSEnvironmentVariableMap* ptr = new (NotNull, JSC::allocateCell<JSEnvironmentVariableMap>(vm)) JSEnvironmentVariableMap(vm, structure);
|
||||
ptr->finishCreation(vm);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
protected:
|
||||
static bool defineOwnProperty(JSC::JSObject*, JSC::JSGlobalObject*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);
|
||||
static bool put(JSC::JSCell*, JSC::JSGlobalObject*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);
|
||||
|
||||
private:
|
||||
JSEnvironmentVariableMap(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
#include "JSPublicKeyObject.h"
|
||||
#include "JSPrivateKeyObject.h"
|
||||
#include "CryptoKeyType.h"
|
||||
#include "JSEnvironmentVariableMap.h"
|
||||
#include "JSNodePerformanceHooksHistogram.h"
|
||||
|
||||
#if USE(CG)
|
||||
@@ -2656,7 +2657,11 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in)
|
||||
// objects have been handled. If we reach this point and
|
||||
// the input is not an Object object then we should throw
|
||||
// a DataCloneError.
|
||||
if (inObject->classInfo() != JSFinalObject::info())
|
||||
auto* inInfo = inObject->classInfo();
|
||||
if (
|
||||
inInfo != JSFinalObject::info()
|
||||
&& inInfo != JSEnvironmentVariableMap::info()
|
||||
&& true)
|
||||
return SerializationReturnCode::DataCloneError;
|
||||
inputObjectStack.append(inObject);
|
||||
indexStack.append(0);
|
||||
|
||||
@@ -374,58 +374,115 @@ export function windowsEnv(
|
||||
//
|
||||
// it throws "Cannot convert a Symbol value to a string"
|
||||
|
||||
// maps uppercase->original case
|
||||
const keyStore = new Map<string, string>();
|
||||
for (const key in internalEnv) {
|
||||
keyStore.set(key.toUpperCase(), key);
|
||||
}
|
||||
for (const key of [
|
||||
"hasOwnProperty",
|
||||
"isPrototypeOf",
|
||||
"propertyIsEnumerable",
|
||||
"toLocaleString",
|
||||
"toString",
|
||||
"valueOf",
|
||||
]) {
|
||||
const k = key.toUpperCase();
|
||||
if (!keyStore.has(k)) keyStore.set(k, key);
|
||||
}
|
||||
|
||||
(internalEnv as any)[Bun.inspect.custom] = () => {
|
||||
let o = {};
|
||||
for (let k of envMapList) {
|
||||
o[k] = internalEnv[k.toUpperCase()];
|
||||
o[k] = internalEnv[keyStore.get(k.toUpperCase())!];
|
||||
}
|
||||
return o;
|
||||
};
|
||||
|
||||
(internalEnv as any).toJSON = () => {
|
||||
return { ...internalEnv };
|
||||
};
|
||||
|
||||
return new Proxy(internalEnv, {
|
||||
get(_, p) {
|
||||
return typeof p === "string" ? internalEnv[p.toUpperCase()] : undefined;
|
||||
if (typeof p !== "string") return undefined;
|
||||
const k = p.toUpperCase();
|
||||
if (!keyStore.has(k)) return undefined;
|
||||
return internalEnv[keyStore.get(k)!];
|
||||
},
|
||||
set(_, p, value) {
|
||||
const k = String(p).toUpperCase();
|
||||
if (typeof p === "symbol") throw new TypeError("Cannot convert a symbol to a string");
|
||||
if (typeof value === "symbol") throw new TypeError("Cannot convert a symbol to a string");
|
||||
$assert(typeof p === "string"); // proxy is only string and symbol. the symbol would have thrown by now
|
||||
if (p.length === 0) return true;
|
||||
value = String(value); // If toString() throws, we want to avoid it existing in the envMapList
|
||||
if (!(k in internalEnv) && !envMapList.includes(p)) {
|
||||
if (keyStore.has(k)) {
|
||||
p = keyStore.get(k)!;
|
||||
} else {
|
||||
keyStore.set(k, p);
|
||||
}
|
||||
if (!envMapList.includes(p)) {
|
||||
envMapList.push(p);
|
||||
}
|
||||
if (internalEnv[k] !== value) {
|
||||
editWindowsEnvVar(k, value);
|
||||
internalEnv[k] = value;
|
||||
if (internalEnv[p] !== value) {
|
||||
editWindowsEnvVar(p, value);
|
||||
internalEnv[p] = value;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
has(_, p) {
|
||||
return typeof p !== "symbol" ? String(p).toUpperCase() in internalEnv : false;
|
||||
return typeof p !== "symbol" ? keyStore.has(String(p).toUpperCase()) : false;
|
||||
},
|
||||
deleteProperty(_, p) {
|
||||
if (typeof p === "symbol") return true;
|
||||
const k = String(p).toUpperCase();
|
||||
const i = envMapList.findIndex(x => x.toUpperCase() === k);
|
||||
if (keyStore.has(k)) {
|
||||
p = keyStore.get(k)!;
|
||||
keyStore.delete(k);
|
||||
}
|
||||
const i = envMapList.findIndex(x => x === p);
|
||||
if (i !== -1) {
|
||||
envMapList.splice(i, 1);
|
||||
}
|
||||
editWindowsEnvVar(k, null);
|
||||
return typeof p !== "symbol" ? delete internalEnv[k] : false;
|
||||
editWindowsEnvVar(p, null);
|
||||
return typeof p !== "symbol" ? delete internalEnv[p] : false;
|
||||
},
|
||||
defineProperty(_, p, attributes) {
|
||||
if (attributes.get)
|
||||
throw $ERR_INVALID_OBJECT_DEFINE_PROPERTY(
|
||||
`'process.env' does not accept an accessor(getter/setter) descriptor`,
|
||||
);
|
||||
if (attributes.set)
|
||||
throw $ERR_INVALID_OBJECT_DEFINE_PROPERTY(
|
||||
`'process.env' does not accept an accessor(getter/setter) descriptor`,
|
||||
);
|
||||
if (!attributes.writable)
|
||||
throw $ERR_INVALID_OBJECT_DEFINE_PROPERTY(
|
||||
`'process.env' only accepts a configurable, writable, and enumerable data descriptor`,
|
||||
);
|
||||
if (!attributes.enumerable)
|
||||
throw $ERR_INVALID_OBJECT_DEFINE_PROPERTY(
|
||||
`'process.env' only accepts a configurable, writable, and enumerable data descriptor`,
|
||||
);
|
||||
if (!attributes.configurable)
|
||||
throw $ERR_INVALID_OBJECT_DEFINE_PROPERTY(
|
||||
`'process.env' only accepts a configurable, writable, and enumerable data descriptor`,
|
||||
);
|
||||
const k = String(p).toUpperCase();
|
||||
$assert(typeof p === "string"); // proxy is only string and symbol. the symbol would have thrown by now
|
||||
if (!(k in internalEnv) && !envMapList.includes(p)) {
|
||||
if (keyStore.has(k)) {
|
||||
p = keyStore.get(k)!;
|
||||
} else {
|
||||
keyStore.set(k, p);
|
||||
}
|
||||
if (!envMapList.includes(p)) {
|
||||
envMapList.push(p);
|
||||
}
|
||||
editWindowsEnvVar(k, internalEnv[k]);
|
||||
return $Object.$defineProperty(internalEnv, k, attributes);
|
||||
editWindowsEnvVar(p, attributes.value);
|
||||
return $Object.$defineProperty(internalEnv, p, attributes);
|
||||
},
|
||||
getOwnPropertyDescriptor(target, p) {
|
||||
return typeof p === "string" ? Reflect.getOwnPropertyDescriptor(target, p.toUpperCase()) : undefined;
|
||||
if (typeof p !== "string") return undefined;
|
||||
const k = keyStore.get(p.toUpperCase());
|
||||
if (!k) return undefined;
|
||||
return Reflect.getOwnPropertyDescriptor(target, k);
|
||||
},
|
||||
ownKeys() {
|
||||
// .slice() because paranoia that there is a way to call this without the engine cloning it for us
|
||||
|
||||
13
test/js/node/test/parallel/test-process-env-delete.js
Normal file
13
test/js/node/test/parallel/test-process-env-delete.js
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
process.env.foo = 'foo';
|
||||
assert.strictEqual(process.env.foo, 'foo');
|
||||
process.env.foo = undefined;
|
||||
assert.strictEqual(process.env.foo, 'undefined');
|
||||
|
||||
process.env.foo = 'foo';
|
||||
assert.strictEqual(process.env.foo, 'foo');
|
||||
delete process.env.foo;
|
||||
assert.strictEqual(process.env.foo, undefined);
|
||||
@@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
Object.defineProperty(process.env, 'foo', {
|
||||
value: 'foo1'
|
||||
});
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_OBJECT_DEFINE_PROPERTY',
|
||||
name: 'TypeError',
|
||||
message: '\'process.env\' only accepts a ' +
|
||||
'configurable, writable,' +
|
||||
' and enumerable data descriptor'
|
||||
}
|
||||
);
|
||||
|
||||
assert.strictEqual(process.env.foo, undefined);
|
||||
process.env.foo = 'foo2';
|
||||
assert.strictEqual(process.env.foo, 'foo2');
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
Object.defineProperty(process.env, 'goo', {
|
||||
get() {
|
||||
return 'goo';
|
||||
},
|
||||
set() {}
|
||||
});
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_OBJECT_DEFINE_PROPERTY',
|
||||
name: 'TypeError',
|
||||
message: '\'process.env\' does not accept an ' +
|
||||
'accessor(getter/setter) descriptor'
|
||||
}
|
||||
);
|
||||
|
||||
const attributes = ['configurable', 'writable', 'enumerable'];
|
||||
|
||||
for (const attribute of attributes) {
|
||||
assert.throws(
|
||||
() => {
|
||||
Object.defineProperty(process.env, 'goo', {
|
||||
[attribute]: false
|
||||
});
|
||||
},
|
||||
{
|
||||
code: 'ERR_INVALID_OBJECT_DEFINE_PROPERTY',
|
||||
name: 'TypeError',
|
||||
message: '\'process.env\' only accepts a ' +
|
||||
'configurable, writable,' +
|
||||
' and enumerable data descriptor'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
assert.strictEqual(process.env.goo, undefined);
|
||||
Object.defineProperty(process.env, 'goo', {
|
||||
value: 'goo',
|
||||
configurable: true,
|
||||
writable: true,
|
||||
enumerable: true
|
||||
});
|
||||
assert.strictEqual(process.env.goo, 'goo');
|
||||
31
test/js/node/test/parallel/test-process-env-symbols.js
Normal file
31
test/js/node/test/parallel/test-process-env-symbols.js
Normal file
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const symbol = Symbol('sym');
|
||||
|
||||
// Verify that getting via a symbol key returns undefined.
|
||||
assert.strictEqual(process.env[symbol], undefined);
|
||||
|
||||
// Verify that assigning via a symbol key throws.
|
||||
// The message depends on the JavaScript engine and so will be different between
|
||||
// different JavaScript engines. Confirm that the `Error` is a `TypeError` only.
|
||||
assert.throws(() => {
|
||||
process.env[symbol] = 42;
|
||||
}, TypeError);
|
||||
|
||||
// Verify that assigning a symbol value throws.
|
||||
// The message depends on the JavaScript engine and so will be different between
|
||||
// different JavaScript engines. Confirm that the `Error` is a `TypeError` only.
|
||||
assert.throws(() => {
|
||||
process.env.foo = symbol;
|
||||
}, TypeError);
|
||||
|
||||
// Verify that using a symbol with the in operator returns false.
|
||||
assert.strictEqual(symbol in process.env, false);
|
||||
|
||||
// Verify that deleting a symbol key returns true.
|
||||
assert.strictEqual(delete process.env[symbol], true);
|
||||
|
||||
// Checks that well-known symbols like `Symbol.toStringTag` won’t throw.
|
||||
Object.prototype.toString.call(process.env);
|
||||
123
test/js/node/test/parallel/test-process-env.js
Normal file
123
test/js/node/test/parallel/test-process-env.js
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
// Changes in environment should be visible to child processes
|
||||
if (process.argv[2] === 'you-are-the-child') {
|
||||
assert.strictEqual('NODE_PROCESS_ENV_DELETED' in process.env, false);
|
||||
assert.strictEqual(process.env.NODE_PROCESS_ENV, '42');
|
||||
assert.strictEqual(process.env.hasOwnProperty, 'asdf');
|
||||
const has = Object.hasOwn(process.env, 'hasOwnProperty');
|
||||
assert.strictEqual(has, true);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
{
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
assert.strictEqual(Object.prototype.hasOwnProperty,
|
||||
process.env.hasOwnProperty);
|
||||
const has = Object.hasOwn(process.env, 'hasOwnProperty');
|
||||
assert.strictEqual(has, false);
|
||||
|
||||
process.env.hasOwnProperty = 'asdf';
|
||||
|
||||
process.env.NODE_PROCESS_ENV = 42;
|
||||
assert.strictEqual(process.env.NODE_PROCESS_ENV, '42');
|
||||
|
||||
process.env.NODE_PROCESS_ENV_DELETED = 42;
|
||||
assert.strictEqual('NODE_PROCESS_ENV_DELETED' in process.env, true);
|
||||
|
||||
delete process.env.NODE_PROCESS_ENV_DELETED;
|
||||
assert.strictEqual('NODE_PROCESS_ENV_DELETED' in process.env, false);
|
||||
|
||||
const child = spawn(process.argv[0], [process.argv[1], 'you-are-the-child']);
|
||||
child.stdout.on('data', function(data) { console.log(data.toString()); });
|
||||
child.stderr.on('data', function(data) { console.log(data.toString()); });
|
||||
child.on('exit', function(statusCode) {
|
||||
if (statusCode !== 0) {
|
||||
process.exit(statusCode); // Failed assertion in child process
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Delete should return true except for non-configurable properties
|
||||
// https://github.com/nodejs/node/issues/7960
|
||||
delete process.env.NON_EXISTING_VARIABLE;
|
||||
assert(delete process.env.NON_EXISTING_VARIABLE);
|
||||
|
||||
// For the moment we are not going to support setting the timezone via the
|
||||
// environment variables. The problem is that various V8 platform backends
|
||||
// deal with timezone in different ways. The Windows platform backend caches
|
||||
// the timezone value while the Linux one hits libc for every query.
|
||||
//
|
||||
// https://github.com/joyent/node/blob/08782931205bc4f6d28102ebc29fd806e8ccdf1f/deps/v8/src/platform-linux.cc#L339-345
|
||||
// https://github.com/joyent/node/blob/08782931205bc4f6d28102ebc29fd806e8ccdf1f/deps/v8/src/platform-win32.cc#L590-596
|
||||
//
|
||||
// // set the timezone; see tzset(3)
|
||||
// process.env.TZ = 'Europe/Amsterdam';
|
||||
//
|
||||
// // time difference between Greenwich and Amsterdam is +2 hours in the summer
|
||||
// date = new Date('Fri, 10 Sep 1982 03:15:00 GMT');
|
||||
// assert.strictEqual(3, date.getUTCHours());
|
||||
// assert.strictEqual(5, date.getHours());
|
||||
|
||||
// Environment variables should be case-insensitive on Windows, and
|
||||
// case-sensitive on other platforms.
|
||||
process.env.TEST = 'test';
|
||||
assert.strictEqual(process.env.TEST, 'test');
|
||||
|
||||
// Check both mixed case and lower case, to avoid any regressions that might
|
||||
// simply convert input to lower case.
|
||||
if (common.isWindows) {
|
||||
assert.strictEqual(process.env.test, 'test');
|
||||
assert.strictEqual(process.env.teST, 'test');
|
||||
} else {
|
||||
assert.strictEqual(process.env.test, undefined);
|
||||
assert.strictEqual(process.env.teST, undefined);
|
||||
}
|
||||
|
||||
{
|
||||
const keys = Object.keys(process.env);
|
||||
assert.ok(keys.length > 0);
|
||||
}
|
||||
|
||||
// https://github.com/nodejs/node/issues/45380
|
||||
// BUN: TODO: on windows
|
||||
if (!common.isWindows) {
|
||||
const env = structuredClone(process.env);
|
||||
// deepEqual(), not deepStrictEqual(), because of different prototypes.
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
assert.deepEqual(env, process.env);
|
||||
}
|
||||
|
||||
// Setting environment variables on Windows with empty names should not cause
|
||||
// an assertion failure.
|
||||
// https://github.com/nodejs/node/issues/32920
|
||||
{
|
||||
process.env[''] = '';
|
||||
assert.strictEqual(process.env[''], undefined);
|
||||
}
|
||||
Reference in New Issue
Block a user