Skip to content

Commit

Permalink
fix test-util-parse-env.js
Browse files Browse the repository at this point in the history
  • Loading branch information
nektro committed Feb 4, 2025
1 parent bfdc0b7 commit c677667
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 5 deletions.
23 changes: 23 additions & 0 deletions src/bun.js/node/node_util_binding.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
12 changes: 8 additions & 4 deletions src/env_loader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -803,6 +803,7 @@ pub const Loader = struct {
this.map,
override,
false,
true,
);

@field(this, base) = source;
Expand Down Expand Up @@ -873,6 +874,7 @@ pub const Loader = struct {
this.map,
override,
false,
true,
);

try this.custom_files_loaded.put(file_path, source);
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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);
}
};

Expand Down
3 changes: 2 additions & 1 deletion src/js/node/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const { validateString, validateOneOf, validateBoolean } = require("internal/val
const { isReadableStream, isWritableStream, isNodeStream } = require("internal/streams/utils");

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;
Expand Down Expand Up @@ -359,7 +360,7 @@ cjs_exports = {
// transferableAbortController,
aborted,
types,
// parseEnv,
parseEnv,
parseArgs,
TextDecoder,
TextEncoder,
Expand Down
1 change: 1 addition & 0 deletions test/js/node/test/fixtures/dotenv/valid.env
Original file line number Diff line number Diff line change
Expand Up @@ -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=[email protected]
SPACED_KEY = parsed
EDGE_CASE_INLINE_COMMENTS="VALUE1" # or "VALUE2" or "VALUE3"
Expand Down
70 changes: 70 additions & 0 deletions test/js/node/test/parallel/test-util-parse-env.js
Original file line number Diff line number Diff line change
@@ -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: '[email protected]',
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',
});

0 comments on commit c677667

Please sign in to comment.