@ultimat3/render 22.3.3 → 22.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/src/module-loader.ts +50 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/render",
|
|
3
|
-
"version": "22.3.
|
|
3
|
+
"version": "22.3.5",
|
|
4
4
|
"description": "The route primitive and the five render modes: static, isr, ssr, stream, spa.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"test": "bun test"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@ultimat3/cache": "22.3.
|
|
40
|
-
"@ultimat3/core": "22.3.
|
|
41
|
-
"@ultimat3/http": "22.3.
|
|
42
|
-
"@ultimat3/i18n": "22.3.
|
|
43
|
-
"@ultimat3/seo": "22.3.
|
|
39
|
+
"@ultimat3/cache": "22.3.5",
|
|
40
|
+
"@ultimat3/core": "22.3.5",
|
|
41
|
+
"@ultimat3/http": "22.3.5",
|
|
42
|
+
"@ultimat3/i18n": "22.3.5",
|
|
43
|
+
"@ultimat3/seo": "22.3.5",
|
|
44
44
|
"sass": "1.104.0"
|
|
45
45
|
}
|
|
46
46
|
}
|
package/src/module-loader.ts
CHANGED
|
@@ -78,6 +78,12 @@ export interface Stylesheet {
|
|
|
78
78
|
readonly surface: Surface | null;
|
|
79
79
|
/** A plain (non-module) stylesheet: the tokens and the reset, which the cascade needs first. */
|
|
80
80
|
readonly global: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Registered only by an ISLAND build, never by the server's module graph — what `x build --target
|
|
83
|
+
* docker` records in the island store. Not an ordering key: every sheet is ordered by
|
|
84
|
+
* `stylesheetOrder`, whoever registered it.
|
|
85
|
+
*/
|
|
86
|
+
readonly island: boolean;
|
|
81
87
|
readonly css: string;
|
|
82
88
|
}
|
|
83
89
|
|
|
@@ -139,18 +145,25 @@ export function clearStylesheets(): void {
|
|
|
139
145
|
}
|
|
140
146
|
|
|
141
147
|
/**
|
|
142
|
-
* The CSS a document on `surface` must carry
|
|
143
|
-
*
|
|
144
|
-
* browser parses, not just bytes it executes.
|
|
148
|
+
* The CSS a document on `surface` must carry. A `site/` page never receives `app/` CSS — that is
|
|
149
|
+
* axiom 6 applied to bytes the browser parses, not just bytes it executes.
|
|
145
150
|
*
|
|
146
151
|
* `shared/` is carried by both graphs by definition — it is the one directory both surfaces import
|
|
147
152
|
* from, and it is where an app's own global stylesheet lives. Filtering it out (which this did)
|
|
148
153
|
* meant an app could put its tokens in the one place the convention names and have every document
|
|
149
154
|
* silently drop them.
|
|
150
155
|
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
156
|
+
* THE ORDER IS A FUNCTION OF THE SHEETS, NEVER OF THEIR ARRIVAL (22.3.5). A sheet registers in Bun's
|
|
157
|
+
* `onLoad`, which runs as the loader fetches a module's dependencies — in parallel — and an island
|
|
158
|
+
* build's too: the arrival order differed on every boot of one image, and so did the joined bytes
|
|
159
|
+
* and the `/styles/<hash>.css` minted from them (notificado.co, 22.3.2–22.3.4: two pods, two URLs,
|
|
160
|
+
* each the other's 404). `stylesheetOrder` is the whole rule:
|
|
161
|
+
*
|
|
162
|
+
* | key | first | why |
|
|
163
|
+
* |---|---|---|
|
|
164
|
+
* | global | the global layer | the reset styles bare elements at the lowest specificity there is, so a reset after a module rule wins ties it must lose |
|
|
165
|
+
* | owner | a package's sheet, then `shared/`, then the surface's own | library before app: an app's rule wins a tie against the component it restyles |
|
|
166
|
+
* | path | app-root-relative, by code unit | a total order; the same on every machine |
|
|
154
167
|
*/
|
|
155
168
|
export function stylesFor(surface: Surface | null): string {
|
|
156
169
|
const carried = [...stylesheets.values()].filter(
|
|
@@ -158,11 +171,30 @@ export function stylesFor(surface: Surface | null): string {
|
|
|
158
171
|
);
|
|
159
172
|
// `stripCharset` on every sheet, not only the first: a `@charset` or a BOM is legal at byte 0 of
|
|
160
173
|
// a FILE and nowhere else, and this join is what turns seven files into one.
|
|
161
|
-
return
|
|
174
|
+
return carried
|
|
175
|
+
.sort(stylesheetOrder)
|
|
162
176
|
.map((sheet) => stripCharset(sheet.css))
|
|
163
177
|
.join('');
|
|
164
178
|
}
|
|
165
179
|
|
|
180
|
+
const OWNER_RANK = (sheet: Stylesheet): number =>
|
|
181
|
+
sheet.surface === null ? 0 : sheet.surface === 'shared' ? 1 : 2;
|
|
182
|
+
|
|
183
|
+
/** Relative to the app root, so a container at `/app` and a laptop order one app alike. */
|
|
184
|
+
const orderPath = (sheet: Stylesheet): string => {
|
|
185
|
+
const root = stylesheetRoot ?? process.cwd();
|
|
186
|
+
return sheet.file.startsWith(`${root}/`) ? sheet.file.slice(root.length + 1) : sheet.file;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/** See `stylesFor`: global layer, then owner, then path. Exported for the tests that pin it. */
|
|
190
|
+
export function stylesheetOrder(a: Stylesheet, b: Stylesheet): number {
|
|
191
|
+
if (a.global !== b.global) return a.global ? -1 : 1;
|
|
192
|
+
const owner = OWNER_RANK(a) - OWNER_RANK(b);
|
|
193
|
+
if (owner !== 0) return owner;
|
|
194
|
+
const [x, y] = [orderPath(a), orderPath(b)];
|
|
195
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
166
198
|
/**
|
|
167
199
|
* `.tsx` source → JS calling the server factory. Exported so the transform is testable as a pure
|
|
168
200
|
* function: the plugin below is the six lines of glue that hand it a file.
|
|
@@ -181,17 +213,26 @@ export function transformTsx(source: string): string {
|
|
|
181
213
|
* compilation — so a token file `@use`d by twenty `page.module.scss` would inline its `:root` block
|
|
182
214
|
* twenty times. One file, imported for its side effect, is the shape that cannot do that.
|
|
183
215
|
*/
|
|
184
|
-
export function loadStylesheet(
|
|
216
|
+
export function loadStylesheet(
|
|
217
|
+
path: string,
|
|
218
|
+
source: string,
|
|
219
|
+
origin: 'module' | 'island' = 'module',
|
|
220
|
+
): string {
|
|
185
221
|
const compiled = compileStylesheet(path, source);
|
|
186
222
|
// An EMPTY compile unregisters: under `x dev` an edit that deleted every rule left the old entry
|
|
187
223
|
// in place, serving rules the file no longer had until the process restarted.
|
|
188
224
|
if (compiled.css.length === 0 && stylesheets.delete(path)) revision += 1;
|
|
189
225
|
if (compiled.css.length > 0) {
|
|
190
|
-
|
|
226
|
+
const held = stylesheets.get(path);
|
|
227
|
+
// Island-only until the server graph loads it too; then it takes its place in load order.
|
|
228
|
+
const island = origin === 'island' && (held === undefined || held.island);
|
|
229
|
+
if (held?.css !== compiled.css || held.island !== island) revision += 1;
|
|
230
|
+
if (held?.island === true && !island) stylesheets.delete(path);
|
|
191
231
|
stylesheets.set(path, {
|
|
192
232
|
file: path,
|
|
193
233
|
surface: surfaceOfSheet(path),
|
|
194
234
|
global: isGlobalStylesheet(path),
|
|
235
|
+
island,
|
|
195
236
|
css: compiled.css,
|
|
196
237
|
});
|
|
197
238
|
}
|