From 324c0d1a39e0b3e291c6b98f2c36cdcd48ea4350 Mon Sep 17 00:00:00 2001 From: pfg Date: Tue, 14 Oct 2025 20:51:34 -0700 Subject: [PATCH] Eliminates special handling for bun:test in the transpiler (#22888) Eliminates special handling for bun:test in the transpiler --- src/ast/Macro.zig | 2 +- src/ast/Parser.zig | 119 ++++++++++++------ src/bake/production.zig | 2 +- src/bun.js/ModuleLoader.zig | 45 +++---- src/bun.js/VirtualMachine.zig | 4 +- src/bun.js/modules/BunTestModule.h | 22 ++++ src/bundler/LinkerContext.zig | 2 +- src/bundler/bundle_v2.zig | 24 +--- .../convertStmtsForChunkForDevServer.zig | 2 +- src/import_record.zig | 2 - src/js_printer.zig | 33 ----- src/linker.zig | 22 +--- src/options.zig | 2 +- src/resolver/resolver.zig | 6 +- test/cli/test/coverage.test.ts | 2 +- 15 files changed, 137 insertions(+), 152 deletions(-) diff --git a/src/ast/Macro.zig b/src/ast/Macro.zig index 97863921a4..129796a585 100644 --- a/src/ast/Macro.zig +++ b/src/ast/Macro.zig @@ -51,7 +51,7 @@ pub const MacroContext = struct { bun.assert(!isMacroPath(import_record_path_without_macro_prefix)); const input_specifier = brk: { - if (jsc.ModuleLoader.HardcodedModule.Alias.get(import_record_path, .bun)) |replacement| { + if (jsc.ModuleLoader.HardcodedModule.Alias.get(import_record_path, .bun, .{})) |replacement| { break :brk replacement.path; } diff --git a/src/ast/Parser.zig b/src/ast/Parser.zig index 8009c24b71..078a781792 100644 --- a/src/ast/Parser.zig +++ b/src/ast/Parser.zig @@ -1210,47 +1210,94 @@ pub const Parser = struct { if (items_count == 0) break :outer; - const import_record_id = p.addImportRecord(.stmt, logger.Loc.Empty, "bun:test"); - var import_record: *ImportRecord = &p.import_records.items[import_record_id]; - import_record.tag = .bun_test; - var declared_symbols = js_ast.DeclaredSymbol.List{}; try declared_symbols.ensureTotalCapacity(p.allocator, items_count); - var clauses: []js_ast.ClauseItem = p.allocator.alloc(js_ast.ClauseItem, items_count) catch unreachable; - var clause_i: usize = 0; - inline for (comptime std.meta.fieldNames(Jest)) |symbol_name| { - if (p.symbols.items[@field(jest, symbol_name).innerIndex()].use_count_estimate > 0) { - clauses[clause_i] = js_ast.ClauseItem{ - .name = .{ .ref = @field(jest, symbol_name), .loc = logger.Loc.Empty }, - .alias = symbol_name, - .alias_loc = logger.Loc.Empty, - .original_name = "", - }; - declared_symbols.appendAssumeCapacity(.{ .ref = @field(jest, symbol_name), .is_top_level = true }); - clause_i += 1; + + // For CommonJS modules, use require instead of import + if (exports_kind == .cjs) { + var import_record_indices = bun.handleOom(p.allocator.alloc(u32, 1)); + const import_record_id = p.addImportRecord(.require, logger.Loc.Empty, "bun:test"); + import_record_indices[0] = import_record_id; + + // Create object binding pattern for destructuring + var properties = p.allocator.alloc(B.Property, items_count) catch unreachable; + var prop_i: usize = 0; + inline for (comptime std.meta.fieldNames(Jest)) |symbol_name| { + if (p.symbols.items[@field(jest, symbol_name).innerIndex()].use_count_estimate > 0) { + properties[prop_i] = .{ + .key = p.newExpr(E.String{ + .data = symbol_name, + }, logger.Loc.Empty), + .value = p.b(B.Identifier{ .ref = @field(jest, symbol_name) }, logger.Loc.Empty), + }; + declared_symbols.appendAssumeCapacity(.{ .ref = @field(jest, symbol_name), .is_top_level = true }); + prop_i += 1; + } } + + // Create: const { test, expect, ... } = require("bun:test") + var decls = p.allocator.alloc(G.Decl, 1) catch unreachable; + decls[0] = .{ + .binding = p.b(B.Object{ + .properties = properties, + }, logger.Loc.Empty), + .value = p.newExpr(E.RequireString{ + .import_record_index = import_record_id, + }, logger.Loc.Empty), + }; + + var part_stmts = p.allocator.alloc(Stmt, 1) catch unreachable; + part_stmts[0] = p.s(S.Local{ + .kind = .k_const, + .decls = Decl.List.fromOwnedSlice(decls), + }, logger.Loc.Empty); + + before.append(js_ast.Part{ + .stmts = part_stmts, + .declared_symbols = declared_symbols, + .import_record_indices = bun.BabyList(u32).fromOwnedSlice(import_record_indices), + .tag = .bun_test, + }) catch unreachable; + } else { + var import_record_indices = bun.handleOom(p.allocator.alloc(u32, 1)); + const import_record_id = p.addImportRecord(.stmt, logger.Loc.Empty, "bun:test"); + import_record_indices[0] = import_record_id; + + // For ESM modules, use import statement + var clauses: []js_ast.ClauseItem = p.allocator.alloc(js_ast.ClauseItem, items_count) catch unreachable; + var clause_i: usize = 0; + inline for (comptime std.meta.fieldNames(Jest)) |symbol_name| { + if (p.symbols.items[@field(jest, symbol_name).innerIndex()].use_count_estimate > 0) { + clauses[clause_i] = js_ast.ClauseItem{ + .name = .{ .ref = @field(jest, symbol_name), .loc = logger.Loc.Empty }, + .alias = symbol_name, + .alias_loc = logger.Loc.Empty, + .original_name = "", + }; + declared_symbols.appendAssumeCapacity(.{ .ref = @field(jest, symbol_name), .is_top_level = true }); + clause_i += 1; + } + } + + const import_stmt = p.s( + S.Import{ + .namespace_ref = p.declareSymbol(.unbound, logger.Loc.Empty, "bun_test_import_namespace_for_internal_use_only") catch unreachable, + .items = clauses, + .import_record_index = import_record_id, + }, + logger.Loc.Empty, + ); + + var part_stmts = try p.allocator.alloc(Stmt, 1); + part_stmts[0] = import_stmt; + before.append(js_ast.Part{ + .stmts = part_stmts, + .declared_symbols = declared_symbols, + .import_record_indices = bun.BabyList(u32).fromOwnedSlice(import_record_indices), + .tag = .bun_test, + }) catch unreachable; } - const import_stmt = p.s( - S.Import{ - .namespace_ref = p.declareSymbol(.unbound, logger.Loc.Empty, "bun_test_import_namespace_for_internal_use_only") catch unreachable, - .items = clauses, - .import_record_index = import_record_id, - }, - logger.Loc.Empty, - ); - - var part_stmts = try p.allocator.alloc(Stmt, 1); - part_stmts[0] = import_stmt; - var import_record_indices = try p.allocator.alloc(u32, 1); - import_record_indices[0] = import_record_id; - before.append(js_ast.Part{ - .stmts = part_stmts, - .declared_symbols = declared_symbols, - .import_record_indices = bun.BabyList(u32).fromOwnedSlice(import_record_indices), - .tag = .bun_test, - }) catch unreachable; - // If we injected jest globals, we need to disable the runtime transpiler cache if (p.options.features.runtime_transpiler_cache) |cache| { cache.input_hash = null; diff --git a/src/bake/production.zig b/src/bake/production.zig index c787b5414a..69b0bac42f 100644 --- a/src/bake/production.zig +++ b/src/bake/production.zig @@ -813,7 +813,7 @@ pub export fn BakeProdResolve(global: *jsc.JSGlobalObject, a_str: bun.String, sp const specifier = specifier_str.toUTF8(alloc); defer specifier.deinit(); - if (jsc.ModuleLoader.HardcodedModule.Alias.get(specifier.slice(), .bun)) |alias| { + if (jsc.ModuleLoader.HardcodedModule.Alias.get(specifier.slice(), .bun, .{})) |alias| { return bun.String.static(alias.path); } diff --git a/src/bun.js/ModuleLoader.zig b/src/bun.js/ModuleLoader.zig index 9a291d5def..90e21151ca 100644 --- a/src/bun.js/ModuleLoader.zig +++ b/src/bun.js/ModuleLoader.zig @@ -2530,37 +2530,17 @@ pub const RuntimeTranspilerStore = struct { for (parse_result.ast.import_records.slice()) |*import_record_| { var import_record: *bun.ImportRecord = import_record_; - if (jsc.ModuleLoader.HardcodedModule.Alias.get(import_record.path.text, transpiler.options.target)) |replacement| { + if (jsc.ModuleLoader.HardcodedModule.Alias.get(import_record.path.text, transpiler.options.target, .{ .rewrite_jest_for_tests = transpiler.options.rewrite_jest_for_tests })) |replacement| { import_record.path.text = replacement.path; import_record.tag = replacement.tag; import_record.is_external_without_side_effects = true; continue; } - if (transpiler.options.rewrite_jest_for_tests) { - if (strings.eqlComptime( - import_record.path.text, - "@jest/globals", - ) or strings.eqlComptime( - import_record.path.text, - "vitest", - )) { - import_record.path.namespace = "bun"; - import_record.tag = .bun_test; - import_record.path.text = "test"; - import_record.is_external_without_side_effects = true; - continue; - } - } - if (strings.hasPrefixComptime(import_record.path.text, "bun:")) { import_record.path = Fs.Path.init(import_record.path.text["bun:".len..]); import_record.path.namespace = "bun"; import_record.is_external_without_side_effects = true; - - if (strings.eqlComptime(import_record.path.text, "test")) { - import_record.tag = .bun_test; - } } } @@ -2642,7 +2622,7 @@ pub const HardcodedModule = enum { @"bun:ffi", @"bun:jsc", @"bun:main", - @"bun:test", // usually replaced by the transpiler but `await import("bun:" + "test")` has to work + @"bun:test", @"bun:wrap", @"bun:sqlite", @"node:assert", @@ -2993,7 +2973,7 @@ pub const HardcodedModule = enum { const bun_extra_alias_kvs = [_]struct { string, Alias }{ .{ "bun", .{ .path = "bun", .tag = .bun } }, - .{ "bun:test", .{ .path = "bun:test", .tag = .bun_test } }, + .{ "bun:test", .{ .path = "bun:test" } }, .{ "bun:app", .{ .path = "bun:app" } }, .{ "bun:ffi", .{ .path = "bun:ffi" } }, .{ "bun:jsc", .{ .path = "bun:jsc" } }, @@ -3025,6 +3005,11 @@ pub const HardcodedModule = enum { .{ "next/dist/compiled/undici", .{ .path = "undici" } }, }; + const bun_test_extra_alias_kvs = [_]struct { string, Alias }{ + .{ "@jest/globals", .{ .path = "bun:test" } }, + .{ "vitest", .{ .path = "bun:test" } }, + }; + const node_extra_alias_kvs = [_]struct { string, Alias }{ nodeEntry("node:inspector/promises"), nodeEntry("inspector/promises"), @@ -3032,14 +3017,20 @@ pub const HardcodedModule = enum { const node_aliases = bun.ComptimeStringMap(Alias, common_alias_kvs ++ node_extra_alias_kvs); const bun_aliases = bun.ComptimeStringMap(Alias, common_alias_kvs ++ bun_extra_alias_kvs); + const bun_test_aliases = bun.ComptimeStringMap(Alias, common_alias_kvs ++ bun_extra_alias_kvs ++ bun_test_extra_alias_kvs); - pub fn has(name: []const u8, target: options.Target) bool { - return get(name, target) != null; + const Cfg = struct { rewrite_jest_for_tests: bool = false }; + pub fn has(name: []const u8, target: options.Target, cfg: Cfg) bool { + return get(name, target, cfg) != null; } - pub fn get(name: []const u8, target: options.Target) ?Alias { + pub fn get(name: []const u8, target: options.Target, cfg: Cfg) ?Alias { if (target.isBun()) { - return bun_aliases.get(name); + if (cfg.rewrite_jest_for_tests) { + return bun_test_aliases.get(name); + } else { + return bun_aliases.get(name); + } } else if (target.isNode()) { return node_aliases.get(name); } diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index 6a6737941b..ea0095dfea 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -1603,7 +1603,7 @@ fn _resolve( ret.result = null; ret.path = try bun.default_allocator.dupe(u8, specifier); return; - } else if (jsc.ModuleLoader.HardcodedModule.Alias.get(specifier, .bun)) |result| { + } else if (jsc.ModuleLoader.HardcodedModule.Alias.get(specifier, .bun, .{})) |result| { ret.result = null; ret.path = result.path; return; @@ -1774,7 +1774,7 @@ pub fn resolveMaybeNeedsTrailingSlash( } } - if (jsc.ModuleLoader.HardcodedModule.Alias.get(specifier_utf8.slice(), .bun)) |hardcoded| { + if (jsc.ModuleLoader.HardcodedModule.Alias.get(specifier_utf8.slice(), .bun, .{})) |hardcoded| { res.* = ErrorableString.ok( if (is_user_require_resolve and hardcoded.node_builtin) specifier diff --git a/src/bun.js/modules/BunTestModule.h b/src/bun.js/modules/BunTestModule.h index 442ef8b7d3..1aaf824701 100644 --- a/src/bun.js/modules/BunTestModule.h +++ b/src/bun.js/modules/BunTestModule.h @@ -8,11 +8,33 @@ void generateNativeModule_BunTest( { auto& vm = JSC::getVM(lexicalGlobalObject); auto globalObject = jsCast(lexicalGlobalObject); + auto catchScope = DECLARE_CATCH_SCOPE(vm); JSObject* object = globalObject->lazyTestModuleObject(); + // Export as default exportNames.append(vm.propertyNames->defaultKeyword); exportValues.append(object); + + // Also export all properties as named exports + JSC::PropertyNameArray properties(vm, JSC::PropertyNameMode::Strings, JSC::PrivateSymbolMode::Exclude); + object->methodTable()->getOwnPropertyNames(object, lexicalGlobalObject, properties, JSC::DontEnumPropertiesMode::Exclude); + if (catchScope.exception()) [[unlikely]] { + catchScope.clearException(); + return; + } + + for (auto& property : properties) { + JSC::PropertySlot slot(object, JSC::PropertySlot::InternalMethodType::Get); + auto ownPropertySlot = object->methodTable()->getOwnPropertySlot(object, lexicalGlobalObject, property, slot); + if (catchScope.exception()) [[unlikely]] { + catchScope.clearException(); + } + if (ownPropertySlot) { + exportNames.append(property); + exportValues.append(slot.getValue(lexicalGlobalObject, property)); + } + } } } // namespace Zig diff --git a/src/bundler/LinkerContext.zig b/src/bundler/LinkerContext.zig index 3bbb19ae29..59daa3f21b 100644 --- a/src/bundler/LinkerContext.zig +++ b/src/bundler/LinkerContext.zig @@ -1963,7 +1963,7 @@ pub const LinkerContext = struct { // "undefined" instead of emitting an error. symbol.import_item_status = .missing; - if (c.resolver.opts.target == .browser and jsc.ModuleLoader.HardcodedModule.Alias.has(next_source.path.pretty, .bun)) { + if (c.resolver.opts.target == .browser and jsc.ModuleLoader.HardcodedModule.Alias.has(next_source.path.pretty, .bun, .{})) { c.log.addRangeWarningFmtWithNote( source, r, diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index 1d380fc9cb..9b5d912dd5 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -1454,7 +1454,7 @@ pub const BundleV2 = struct { if (path.len > 0 and // Check for either node or bun builtins // We don't use the list from .bun because that includes third-party packages in some cases. - !jsc.ModuleLoader.HardcodedModule.Alias.has(path, .node) and + !jsc.ModuleLoader.HardcodedModule.Alias.has(path, .node, .{}) and !strings.hasPrefixComptime(path, "bun:") and !strings.eqlComptime(path, "bun")) { @@ -3142,7 +3142,7 @@ pub const BundleV2 = struct { } if (ast.target.isBun()) { - if (jsc.ModuleLoader.HardcodedModule.Alias.get(import_record.path.text, .bun)) |replacement| { + if (jsc.ModuleLoader.HardcodedModule.Alias.get(import_record.path.text, .bun, .{ .rewrite_jest_for_tests = this.transpiler.options.rewrite_jest_for_tests })) |replacement| { // When bundling node builtins, remove the "node:" prefix. // This supports special use cases where the bundle is put // into a non-node module resolver that doesn't support @@ -3157,32 +3157,12 @@ pub const BundleV2 = struct { continue; } - if (this.transpiler.options.rewrite_jest_for_tests) { - if (strings.eqlComptime( - import_record.path.text, - "@jest/globals", - ) or strings.eqlComptime( - import_record.path.text, - "vitest", - )) { - import_record.path.namespace = "bun"; - import_record.tag = .bun_test; - import_record.path.text = "test"; - import_record.is_external_without_side_effects = true; - continue; - } - } - if (strings.hasPrefixComptime(import_record.path.text, "bun:")) { import_record.path = Fs.Path.init(import_record.path.text["bun:".len..]); import_record.path.namespace = "bun"; import_record.source_index = Index.invalid; import_record.is_external_without_side_effects = true; - if (strings.eqlComptime(import_record.path.text, "test")) { - import_record.tag = .bun_test; - } - // don't link bun continue; } diff --git a/src/bundler/linker_context/convertStmtsForChunkForDevServer.zig b/src/bundler/linker_context/convertStmtsForChunkForDevServer.zig index 69010f0560..288c242869 100644 --- a/src/bundler/linker_context/convertStmtsForChunkForDevServer.zig +++ b/src/bundler/linker_context/convertStmtsForChunkForDevServer.zig @@ -60,7 +60,7 @@ pub fn convertStmtsForChunkForDevServer( const record = ast.import_records.mut(st.import_record_index); if (record.path.is_disabled) continue; - const is_builtin = record.tag == .builtin or record.tag == .bun_test or record.tag == .bun or record.tag == .runtime; + const is_builtin = record.tag == .builtin or record.tag == .bun or record.tag == .runtime; const is_bare_import = st.star_name_loc == null and st.items.len == 0 and st.default_name == null; if (is_builtin) { diff --git a/src/import_record.zig b/src/import_record.zig index 122c709d41..d6bc50ae68 100644 --- a/src/import_record.zig +++ b/src/import_record.zig @@ -173,8 +173,6 @@ pub const ImportRecord = struct { none, /// An import to 'bun' bun, - /// An import to 'bun:test' - bun_test, /// A builtin module, such as `node:fs` or `bun:sqlite` builtin, /// An import to the internal runtime diff --git a/src/js_printer.zig b/src/js_printer.zig index f1dcdb9882..b02bf0d7f6 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -929,19 +929,6 @@ fn NewPrinter( } } - fn printBunJestImportStatement(p: *Printer, import: S.Import) void { - comptime bun.assert(is_bun_platform); - - switch (p.options.module_type) { - .cjs => { - printInternalBunImport(p, import, @TypeOf("globalThis.Bun.jest(__filename)"), "globalThis.Bun.jest(__filename)"); - }, - else => { - printInternalBunImport(p, import, @TypeOf("globalThis.Bun.jest(import.meta.path)"), "globalThis.Bun.jest(import.meta.path)"); - }, - } - } - fn printGlobalBunImportStatement(p: *Printer, import: S.Import) void { if (comptime !is_bun_platform) unreachable; printInternalBunImport(p, import, @TypeOf("globalThis.Bun"), "globalThis.Bun"); @@ -1634,22 +1621,6 @@ fn NewPrinter( return; } }, - .bun_test => { - if (record.kind == .dynamic) { - if (module_type == .cjs) { - p.print("Promise.resolve(globalThis.Bun.jest(__filename))"); - } else { - p.print("Promise.resolve(globalThis.Bun.jest(import.meta.path))"); - } - } else if (record.kind == .require) { - if (module_type == .cjs) { - p.print("globalThis.Bun.jest(__filename)"); - } else { - p.print("globalThis.Bun.jest(import.meta.path)"); - } - } - return; - }, else => {}, } } @@ -4339,10 +4310,6 @@ fn NewPrinter( if (comptime is_bun_platform) { switch (record.tag) { - .bun_test => { - p.printBunJestImportStatement(s.*); - return; - }, .bun => { p.printGlobalBunImportStatement(s.*); return; diff --git a/src/linker.zig b/src/linker.zig index a71015523a..704f093283 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -142,7 +142,7 @@ pub const Linker = struct { } if (comptime is_bun) { - if (jsc.ModuleLoader.HardcodedModule.Alias.get(import_record.path.text, linker.options.target)) |replacement| { + if (jsc.ModuleLoader.HardcodedModule.Alias.get(import_record.path.text, linker.options.target, .{ .rewrite_jest_for_tests = linker.options.rewrite_jest_for_tests })) |replacement| { if (replacement.tag == .builtin and import_record.kind.isCommonJS()) continue; import_record.path.text = replacement.path; @@ -159,30 +159,10 @@ pub const Linker = struct { continue; } - // TODO: this is technical debt - if (linker.options.rewrite_jest_for_tests) { - if (strings.eqlComptime( - import_record.path.text, - "@jest/globals", - ) or strings.eqlComptime( - import_record.path.text, - "vitest", - )) { - import_record.path.namespace = "bun"; - import_record.tag = .bun_test; - import_record.path.text = "test"; - continue; - } - } - if (strings.hasPrefixComptime(import_record.path.text, "bun:")) { import_record.path = Fs.Path.init(import_record.path.text["bun:".len..]); import_record.path.namespace = "bun"; - if (strings.eqlComptime(import_record.path.text, "test")) { - import_record.tag = .bun_test; - } - // don't link bun continue; } diff --git a/src/options.zig b/src/options.zig index 6a32f61641..2e287751ad 100644 --- a/src/options.zig +++ b/src/options.zig @@ -61,7 +61,7 @@ pub const ExternalModules = struct { }; pub fn isNodeBuiltin(str: string) bool { - return bun.jsc.ModuleLoader.HardcodedModule.Alias.has(str, .node); + return bun.jsc.ModuleLoader.HardcodedModule.Alias.has(str, .node, .{}); } const default_wildcard_patterns = &[_]WildcardPattern{ diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index 38878c3764..eeb483a3e3 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -680,7 +680,7 @@ pub const Resolver = struct { if (r.opts.mark_builtins_as_external) { if (strings.hasPrefixComptime(import_path, "node:") or strings.hasPrefixComptime(import_path, "bun:") or - bun.jsc.ModuleLoader.HardcodedModule.Alias.has(import_path, r.opts.target)) + bun.jsc.ModuleLoader.HardcodedModule.Alias.has(import_path, r.opts.target, .{ .rewrite_jest_for_tests = r.opts.rewrite_jest_for_tests })) { return .{ .success = Result{ @@ -1222,7 +1222,7 @@ pub const Resolver = struct { if (had_node_prefix) { // Module resolution fails automatically for unknown node builtins - if (!bun.jsc.ModuleLoader.HardcodedModule.Alias.has(import_path_without_node_prefix, .node)) { + if (!bun.jsc.ModuleLoader.HardcodedModule.Alias.has(import_path_without_node_prefix, .node, .{})) { return .{ .not_found = {} }; } @@ -3147,7 +3147,7 @@ pub const Resolver = struct { // } // if (r.opts.mark_builtins_as_external or r.opts.target.isBun()) { - if (jsc.ModuleLoader.HardcodedModule.Alias.get(esm_resolution.path, r.opts.target)) |alias| { + if (jsc.ModuleLoader.HardcodedModule.Alias.get(esm_resolution.path, r.opts.target, .{})) |alias| { return .{ .success = .{ .path_pair = .{ .primary = bun.fs.Path.init(alias.path) }, diff --git a/test/cli/test/coverage.test.ts b/test/cli/test/coverage.test.ts index 87099ddc71..26a1ed10b3 100644 --- a/test/cli/test/coverage.test.ts +++ b/test/cli/test/coverage.test.ts @@ -394,7 +394,7 @@ TN: SF:test.test.ts FNF:1 FNH:1 -DA:2,60 +DA:2,40 DA:3,41 DA:4,39 DA:6,42