diff --git a/CHANGELOG.md b/CHANGELOG.md index 65315946..d3c2b75e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ Semver applies from 1.0.0. A breaking change to a documented API needs a major In an app whose default locale is not `en`, every tab read `⟦dev.panel.mail.title⟧`. The framework catalog is registered under `en` only, and the shell translated through the app's ambient locale. +- **render:** `setStylesheetRoot` resolves a relative root, so `loadApp('.')` from `/app` classifies + sheets correctly. Found in review of #528. ## 22.2.1 - 2026-09-25 diff --git a/packages/render/src/module-loader.test.ts b/packages/render/src/module-loader.test.ts index af1c99e4..f888dfb4 100644 --- a/packages/render/src/module-loader.test.ts +++ b/packages/render/src/module-loader.test.ts @@ -265,6 +265,15 @@ describe('an app served from /app', () => { expect(site).not.toContain('color:blue'); }); + // `loadApp('.')` from `/app` names a RELATIVE root, and every path the plugin hands the loader is + // absolute: compared as written, the root matched nothing and the `/app/` segment won again. + test('a relative root is resolved against the working directory before it is compared', () => { + setStylesheetRoot('app'); + const sheet = join(process.cwd(), 'app', 'apps/web/site/page.module.scss'); + loadStylesheet(sheet, '.hero{color:red}'); + expect(registeredStylesheets()[0]?.surface).toBe('site'); + }); + // The order `x build` meets: a sheet can register before `loadApp` names the root, and a // classification frozen at registration would keep the answer the wrong root gave. test('naming the root reclassifies what registered before it, and moves the revision', () => { diff --git a/packages/render/src/module-loader.ts b/packages/render/src/module-loader.ts index c2ea54df..e444613e 100644 --- a/packages/render/src/module-loader.ts +++ b/packages/render/src/module-loader.ts @@ -4,6 +4,9 @@ * `bun test` all load a component the same way and there is no separate "bundled" behaviour. */ +// why: Bun ships no path API, and a relative app root has to be resolved against the working +// directory before it can be compared with the absolute paths the Bun plugin hands this loader. +import { resolve } from 'node:path'; import { renderThrowable } from '@ultimat3/core'; import { compileStylesheet, isGlobalStylesheet, stripCharset } from './css-modules'; import { PrerenderFailedError } from './errors'; @@ -115,10 +118,12 @@ const surfaceOfSheet = (path: string): Surface | null => * Every sheet already registered is classified again, because a sheet can load before the root is * named — and a classification frozen then would keep the answer the wrong root gave. The revision * moves only when an answer does, so naming the same root twice mints no new stylesheet URL. - * `undefined` returns to the working directory. + * A relative root is resolved against the working directory. `undefined` returns to the working + * directory. */ export function setStylesheetRoot(root: string | undefined): void { - stylesheetRoot = root; + // Resolved: `loadApp('.')` is a legal call, and `.` is a prefix of no absolute path. + stylesheetRoot = root === undefined ? undefined : resolve(root); for (const [path, sheet] of stylesheets) { const surface = surfaceOfSheet(path); if (surface === sheet.surface) continue;