fix(build): break Turbopack chunk-name hash collision from #1385's import edge (#1394)

Since the #1385 squash-merge every production build failed with 'Two or
more assets with different content were emitted to the same output path'
on [root-of-the-server]__1ge0sz5._.js: two distinct server chunk groups
(an AWS smithy helper chunk and the withRouteContext auth chunk) hash to
the same chunk name. The graph change that tipped the chunk layout into
the colliding state was entity-mapper.ts (arcim-migration extension
entry graph) importing lib/vat/supplier-invoice-line-checks, which
drags lib/money into the extension root.

Move normalizeVatRateToFraction into an import-free leaf module
(lib/vat/vat-rate-unit.ts), re-export it from
supplier-invoice-line-checks for all existing callers, and point
entity-mapper at the leaf. Behavior is unchanged (511 targeted tests
pass); the server chunk graph returns to the pre-#1385 shape that
builds cleanly. Verified: npm run build fails on ff864ad3d and passes
with this change.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-08-03 19:28:15 +02:00
committed by GitHub
parent ff864ad3db
commit 281cb3989d
3 changed files with 28 additions and 15 deletions
@@ -8,7 +8,7 @@
import type { SupabaseClient } from '@supabase/supabase-js'
import { fetchExchangeRate } from '@/lib/currency/riksbanken'
import { encryptCustomerPersonalNumber } from '@/lib/customers/protect-personal-number'
import { normalizeVatRateToFraction } from '@/lib/vat/supplier-invoice-line-checks'
import { normalizeVatRateToFraction } from '@/lib/vat/vat-rate-unit'
import type { Currency, CustomerType, ExchangeRate, SupplierType, VatTreatment } from '@/types'
import type {
CustomerDto,
+6 -14
View File
@@ -2,6 +2,7 @@
// Kept free of React so the rules can be unit-tested and reused.
import { roundOre } from '@/lib/money'
import { normalizeVatRateToFraction } from './vat-rate-unit'
// Only 25/12/6/0 % are legal Swedish VAT rates (ML 2023:200). The supplier
// invoice form stores rates as decimal fractions (0.25 = 25 %); this list is
@@ -17,20 +18,11 @@ export function isLegalVatRate(rate: number): boolean {
return LEGAL_VAT_RATES.includes(rate)
}
/**
* Convert a supplier-invoice VAT rate to the database's decimal-fraction
* unit without changing the underlying rate. Values above 1 are interpreted
* as percentages, while values already between 0 and 1 pass through. This is
* intentionally a unit normalizer, not a Swedish-rate validator: imported
* foreign VAT such as 19 % must remain 0.19 instead of being rewritten.
*/
export function normalizeVatRateToFraction(rate: unknown): number {
const n = Number(rate)
if (!Number.isFinite(n) || n < 0) return 0
const fraction = n > 1 ? n / 100 : n
return fraction <= 1 ? fraction : 0
}
// normalizeVatRateToFraction lives in the import-free leaf module
// vat-rate-unit.ts so extension entry graphs can use it without pulling in
// this file's lib/money dependency; re-exported here so existing callers
// keep their import path.
export { normalizeVatRateToFraction }
/**
* Normalize a VAT rate that may arrive percent-shaped (25, 12, 6: the AI
+21
View File
@@ -0,0 +1,21 @@
// Leaf module on purpose: imported by extension entry graphs (arcim-migration
// entity mapper) where pulling in supplier-invoice-line-checks' dependencies
// (lib/money) shifted the server chunk graph into a Turbopack chunk-name hash
// collision ("Two or more assets with different content were emitted to the
// same output path", first seen on the #1385 merge). Keep this file free of
// imports; supplier-invoice-line-checks re-exports it for all other callers.
/**
* Convert a supplier-invoice VAT rate to the database's decimal-fraction
* unit without changing the underlying rate. Values above 1 are interpreted
* as percentages, while values already between 0 and 1 pass through. This is
* intentionally a unit normalizer, not a Swedish-rate validator: imported
* foreign VAT such as 19 % must remain 0.19 instead of being rewritten.
*/
export function normalizeVatRateToFraction(rate: unknown): number {
const n = Number(rate)
if (!Number.isFinite(n) || n < 0) return 0
const fraction = n > 1 ? n / 100 : n
return fraction <= 1 ? fraction : 0
}