node: fix test-util-parse-env.js (#17701)

This commit is contained in:
Meghan Denny
2025-02-28 20:15:18 -08:00
committed by GitHub
parent 7882418c5f
commit 2d74c0162a
5 changed files with 104 additions and 5 deletions

View File

@@ -7,6 +7,8 @@ const string = bun.string;
const Output = bun.Output;
const ZigString = JSC.ZigString;
const uv = bun.windows.libuv;
const validators = @import("./util/validators.zig");
const envloader = @import("./../../env_loader.zig");
pub fn internalErrorName(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue {
const arguments = callframe.arguments_old(1).slice();
@@ -212,3 +214,24 @@ pub fn normalizeEncoding(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFr
if (str.inMapCaseInsensitive(JSC.Node.Encoding.map)) |enc| return enc.toJS(globalThis);
return JSC.JSValue.jsUndefined();
}
pub fn parseEnv(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue {
const content = callframe.argument(0);
try validators.validateString(globalThis, content, "content", .{});
var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
defer arena.deinit();
const allocator = arena.allocator();
const str = content.asString().toSlice(globalThis, allocator);
var map = envloader.Map.init(allocator);
var p = envloader.Loader.init(&map, allocator);
p.loadFromString(str.slice(), true, false);
var obj = JSC.JSValue.createEmptyObject(globalThis, map.map.count());
for (map.map.keys(), map.map.values()) |k, v| {
obj.put(globalThis, JSC.ZigString.initUTF8(k), bun.String.createUTF8ForJS(globalThis, v.value));
}
return obj;
}

View File

@@ -538,9 +538,9 @@ pub const Loader = struct {
}
// mostly for tests
pub fn loadFromString(this: *Loader, str: string, comptime overwrite: bool) void {
pub fn loadFromString(this: *Loader, str: string, comptime overwrite: bool, comptime expand: bool) void {
var source = logger.Source.initPathString("test", str);
Parser.parse(&source, this.allocator, this.map, overwrite, false);
Parser.parse(&source, this.allocator, this.map, overwrite, false, expand);
std.mem.doNotOptimizeAway(&source);
}
@@ -803,6 +803,7 @@ pub const Loader = struct {
this.map,
override,
false,
true,
);
@field(this, base) = source;
@@ -873,6 +874,7 @@ pub const Loader = struct {
this.map,
override,
false,
true,
);
try this.custom_files_loaded.put(file_path, source);
@@ -1097,6 +1099,7 @@ const Parser = struct {
map: *Map,
comptime override: bool,
comptime is_process: bool,
comptime expand: bool,
) void {
var count = map.map.count();
while (this.pos < this.src.len) {
@@ -1120,7 +1123,7 @@ const Parser = struct {
.conditional = false,
};
}
if (comptime !is_process) {
if (comptime !is_process and expand) {
var it = map.iterator();
while (it.next()) |entry| {
if (count > 0) {
@@ -1142,9 +1145,10 @@ const Parser = struct {
map: *Map,
comptime override: bool,
comptime is_process: bool,
comptime expand: bool,
) void {
var parser = Parser{ .src = source.contents };
parser._parse(allocator, map, override, is_process);
parser._parse(allocator, map, override, is_process, expand);
}
};

View File

@@ -6,6 +6,7 @@ const { promisify } = require("internal/promisify");
const { validateString, validateOneOf } = require("internal/validators");
const internalErrorName = $newZigFunction("node_util_binding.zig", "internalErrorName", 1);
const parseEnv = $newZigFunction("node_util_binding.zig", "parseEnv", 1);
const NumberIsSafeInteger = Number.isSafeInteger;
const ObjectKeys = Object.keys;
@@ -347,7 +348,7 @@ cjs_exports = {
// transferableAbortController,
aborted,
types,
// parseEnv,
parseEnv,
parseArgs,
TextDecoder,
TextEncoder,

View File

@@ -38,6 +38,7 @@ RETAIN_INNER_QUOTES={"foo": "bar"}
RETAIN_INNER_QUOTES_AS_STRING='{"foo": "bar"}'
RETAIN_INNER_QUOTES_AS_BACKTICKS=`{"foo": "bar's"}`
TRIM_SPACE_FROM_UNQUOTED= some spaced out string
SPACE_BEFORE_DOUBLE_QUOTES= "space before double quotes"
EMAIL=therealnerdybeast@example.tld
SPACED_KEY = parsed
EDGE_CASE_INLINE_COMMENTS="VALUE1" # or "VALUE2" or "VALUE3"

View File

@@ -0,0 +1,70 @@
'use strict';
require('../common');
const fixtures = require('../../test/common/fixtures');
const assert = require('node:assert');
const util = require('node:util');
const fs = require('node:fs');
{
const validEnvFilePath = fixtures.path('dotenv/valid.env');
const validContent = fs.readFileSync(validEnvFilePath, 'utf8');
assert.deepStrictEqual(util.parseEnv(validContent), {
AFTER_LINE: 'after_line',
BACKTICKS: 'backticks',
BACKTICKS_INSIDE_DOUBLE: '`backticks` work inside double quotes',
BACKTICKS_INSIDE_SINGLE: '`backticks` work inside single quotes',
BACKTICKS_SPACED: ' backticks ',
BASIC: 'basic',
DONT_EXPAND_SQUOTED: 'dontexpand\\nnewlines',
DONT_EXPAND_UNQUOTED: 'dontexpand\\nnewlines',
DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS: "double \"quotes\" and single 'quotes' work inside backticks",
DOUBLE_QUOTES: 'double_quotes',
DOUBLE_QUOTES_INSIDE_BACKTICKS: 'double "quotes" work inside backticks',
DOUBLE_QUOTES_INSIDE_SINGLE: 'double "quotes" work inside single quotes',
DOUBLE_QUOTES_SPACED: ' double quotes ',
DOUBLE_QUOTES_WITH_NO_SPACE_BRACKET: '{ port: $MONGOLAB_PORT}',
EDGE_CASE_INLINE_COMMENTS: 'VALUE1',
EMAIL: 'therealnerdybeast@example.tld',
EMPTY: '',
EMPTY_BACKTICKS: '',
EMPTY_DOUBLE_QUOTES: '',
EMPTY_SINGLE_QUOTES: '',
EQUAL_SIGNS: 'equals==',
EXPORT_EXAMPLE: 'ignore export',
EXPAND_NEWLINES: 'expand\nnew\nlines',
INLINE_COMMENTS: 'inline comments',
INLINE_COMMENTS_BACKTICKS: 'inline comments outside of #backticks',
INLINE_COMMENTS_DOUBLE_QUOTES: 'inline comments outside of #doublequotes',
INLINE_COMMENTS_SINGLE_QUOTES: 'inline comments outside of #singlequotes',
INLINE_COMMENTS_SPACE: 'inline comments start with a',
MULTI_BACKTICKED: 'THIS\nIS\nA\n"MULTILINE\'S"\nSTRING',
MULTI_DOUBLE_QUOTED: 'THIS\nIS\nA\nMULTILINE\nSTRING',
MULTI_NOT_VALID: 'THIS',
MULTI_NOT_VALID_QUOTE: '"',
MULTI_SINGLE_QUOTED: 'THIS\nIS\nA\nMULTILINE\nSTRING',
RETAIN_INNER_QUOTES: '{"foo": "bar"}',
RETAIN_INNER_QUOTES_AS_BACKTICKS: '{"foo": "bar\'s"}',
RETAIN_INNER_QUOTES_AS_STRING: '{"foo": "bar"}',
SINGLE_QUOTES: 'single_quotes',
SINGLE_QUOTES_INSIDE_BACKTICKS: "single 'quotes' work inside backticks",
SINGLE_QUOTES_INSIDE_DOUBLE: "single 'quotes' work inside double quotes",
SINGLE_QUOTES_SPACED: ' single quotes ',
SPACED_KEY: 'parsed',
SPACE_BEFORE_DOUBLE_QUOTES: 'space before double quotes',
TRIM_SPACE_FROM_UNQUOTED: 'some spaced out string',
});
}
assert.deepStrictEqual(util.parseEnv(''), {});
assert.deepStrictEqual(util.parseEnv('FOO=bar\nFOO=baz\n'), { FOO: 'baz' });
// Test for invalid input.
assert.throws(() => {
for (const value of [null, undefined, {}, []]) {
util.parseEnv(value);
}
}, {
code: 'ERR_INVALID_ARG_TYPE',
});