@ecopages/ecopages-jsx 0.2.0-beta.36 → 0.2.0-beta.38
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/ecopages-jsx",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.38",
|
|
4
4
|
"description": "JSX integration plugin for Ecopages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"directory": "packages/integrations/ecopages-jsx"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ecopages/mdx": "0.2.0-beta.
|
|
34
|
+
"@ecopages/mdx": "0.2.0-beta.38",
|
|
35
35
|
"@mdx-js/mdx": "^3.1.1",
|
|
36
36
|
"vfile": "^6.0.3"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@ecopages/core": "0.2.0-beta.
|
|
39
|
+
"@ecopages/core": "0.2.0-beta.38",
|
|
40
40
|
"@ecopages/jsx": "0.3.0-beta.3",
|
|
41
41
|
"@ecopages/radiant": "0.3.0-beta.3"
|
|
42
42
|
}
|
|
@@ -30,7 +30,15 @@ export declare function getEjsxHmrOwnership(): EjsxHmrOwnershipState;
|
|
|
30
30
|
* state is replaced only when the resulting file set has a different hash, so
|
|
31
31
|
* the renderer's per-render hook is free in the steady state.
|
|
32
32
|
*/
|
|
33
|
-
export declare function updateEjsxHmrOwnership(components: ReadonlyArray<EcoComponent | undefined>): void;
|
|
33
|
+
export declare function updateEjsxHmrOwnership(components: ReadonlyArray<EcoComponent | Partial<EcoComponent> | undefined>): void;
|
|
34
|
+
/**
|
|
35
|
+
* Merges one render tree into a pending ownership set during an active render.
|
|
36
|
+
*/
|
|
37
|
+
export declare function mergeEjsxHmrOwnership(target: Set<string>, components: ReadonlyArray<EcoComponent | undefined>): void;
|
|
38
|
+
/**
|
|
39
|
+
* Publishes the merged ownership set for the completed render.
|
|
40
|
+
*/
|
|
41
|
+
export declare function publishEjsxHmrOwnership(files: ReadonlySet<string>): void;
|
|
34
42
|
/**
|
|
35
43
|
* Resets the state to empty. Used by tests and by HMR manager teardown.
|
|
36
44
|
*/
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* lifecycle anchor. A fresh state is installed only when the source-file set
|
|
13
13
|
* changes, so the renderer's per-render hook stays O(1) in the steady state.
|
|
14
14
|
*/
|
|
15
|
-
import path from 'node:path';
|
|
16
15
|
import { rapidhash } from '@ecopages/core/hash';
|
|
16
|
+
import { collectComponentConfigFilePaths } from '@ecopages/core/route-renderer/page-loading/file-scoped-dependency-components';
|
|
17
17
|
const EMPTY_STATE = {
|
|
18
18
|
fileOwners: new Set(),
|
|
19
19
|
hash: '',
|
|
@@ -35,13 +35,26 @@ export function getEjsxHmrOwnership() {
|
|
|
35
35
|
* the renderer's per-render hook is free in the steady state.
|
|
36
36
|
*/
|
|
37
37
|
export function updateEjsxHmrOwnership(components) {
|
|
38
|
-
|
|
38
|
+
publishEjsxHmrOwnership(collectFileOwners(components));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Merges one render tree into a pending ownership set during an active render.
|
|
42
|
+
*/
|
|
43
|
+
export function mergeEjsxHmrOwnership(target, components) {
|
|
44
|
+
for (const file of collectFileOwners(components)) {
|
|
45
|
+
target.add(file);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Publishes the merged ownership set for the completed render.
|
|
50
|
+
*/
|
|
51
|
+
export function publishEjsxHmrOwnership(files) {
|
|
39
52
|
const hash = hashFileSet(files);
|
|
40
53
|
if (hash === currentState.hash) {
|
|
41
54
|
return;
|
|
42
55
|
}
|
|
43
56
|
currentState = {
|
|
44
|
-
fileOwners: files,
|
|
57
|
+
fileOwners: new Set(files),
|
|
45
58
|
hash,
|
|
46
59
|
};
|
|
47
60
|
}
|
|
@@ -52,30 +65,7 @@ export function resetEjsxHmrOwnership() {
|
|
|
52
65
|
currentState = EMPTY_STATE;
|
|
53
66
|
}
|
|
54
67
|
function collectFileOwners(components) {
|
|
55
|
-
|
|
56
|
-
const visited = new Set();
|
|
57
|
-
const visit = (config) => {
|
|
58
|
-
const file = config?.__eco?.file;
|
|
59
|
-
if (!file) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const resolved = path.resolve(file);
|
|
63
|
-
if (visited.has(resolved)) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
visited.add(resolved);
|
|
67
|
-
files.add(resolved);
|
|
68
|
-
for (const dependency of config?.dependencies?.components ?? []) {
|
|
69
|
-
visit(dependency?.config);
|
|
70
|
-
}
|
|
71
|
-
for (const layout of config?.layouts ?? []) {
|
|
72
|
-
visit(layout?.config);
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
for (const component of components) {
|
|
76
|
-
visit(component?.config);
|
|
77
|
-
}
|
|
78
|
-
return files;
|
|
68
|
+
return collectComponentConfigFilePaths(components, { includeLayouts: true });
|
|
79
69
|
}
|
|
80
70
|
function hashFileSet(files) {
|
|
81
71
|
if (files.size === 0) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { EcoComponent } from '@ecopages/core';
|
|
1
2
|
import type { ProcessedAsset } from '@ecopages/core/services/asset-processing-service';
|
|
2
3
|
export declare const ECOPAGES_JSX_SSR_RENDER_STATE_KEY: unique symbol;
|
|
3
4
|
/**
|
|
@@ -21,11 +22,13 @@ export declare class EcopagesJsxRenderSession {
|
|
|
21
22
|
* mirrored back into the JSX SSR scope so nested `renderToString()` calls see
|
|
22
23
|
* the same asset frame stack. When no session exists yet, a new state is
|
|
23
24
|
* created and published through both the internal async-local store and the
|
|
24
|
-
* JSX SSR scope bridge.
|
|
25
|
+
* JSX SSR scope bridge. HMR ownership accumulated during the outer scope is
|
|
26
|
+
* published once when that scope completes.
|
|
25
27
|
*/
|
|
26
|
-
withActiveScope<T>(render: () => T): T
|
|
28
|
+
withActiveScope<T>(render: () => T | Promise<T>): Promise<T>;
|
|
27
29
|
beginCollectedAssetFrame(): ProcessedAsset[];
|
|
28
30
|
endCollectedAssetFrame(frame: ProcessedAsset[]): ProcessedAsset[];
|
|
31
|
+
mergeHmrOwnership(components: ReadonlyArray<EcoComponent | undefined>): void;
|
|
29
32
|
recordCollectedAssets(collectedAssets: ProcessedAsset[]): ProcessedAsset[];
|
|
30
33
|
/**
|
|
31
34
|
* Resolves the current render-session state from the local async scope first.
|
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
2
|
import { getActiveSsrScopeValue, withActiveSsrScopeValue } from '@ecopages/jsx/server';
|
|
3
|
+
import { mergeEjsxHmrOwnership, publishEjsxHmrOwnership } from './ecopages-jsx-hmr-ownership.js';
|
|
3
4
|
export const ECOPAGES_JSX_SSR_RENDER_STATE_KEY = Symbol.for('@ecopages/ecopages-jsx.ssr-render-state');
|
|
4
5
|
const renderStateStorage = new AsyncLocalStorage();
|
|
6
|
+
function ensurePendingHmrFileOwners(state) {
|
|
7
|
+
if (!state.pendingHmrFileOwners) {
|
|
8
|
+
state.pendingHmrFileOwners = new Set();
|
|
9
|
+
}
|
|
10
|
+
return state.pendingHmrFileOwners;
|
|
11
|
+
}
|
|
12
|
+
function commitPendingHmrOwnership(state) {
|
|
13
|
+
const pending = state.pendingHmrFileOwners;
|
|
14
|
+
if (!pending || pending.size === 0) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
publishEjsxHmrOwnership(pending);
|
|
18
|
+
pending.clear();
|
|
19
|
+
}
|
|
20
|
+
async function runWithCommittedHmrOwnership(state, render) {
|
|
21
|
+
try {
|
|
22
|
+
return await render();
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
commitPendingHmrOwnership(state);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
5
28
|
/**
|
|
6
29
|
* Tracks JSX SSR asset-collection state for one active render flow.
|
|
7
30
|
*
|
|
@@ -25,21 +48,24 @@ export class EcopagesJsxRenderSession {
|
|
|
25
48
|
* mirrored back into the JSX SSR scope so nested `renderToString()` calls see
|
|
26
49
|
* the same asset frame stack. When no session exists yet, a new state is
|
|
27
50
|
* created and published through both the internal async-local store and the
|
|
28
|
-
* JSX SSR scope bridge.
|
|
51
|
+
* JSX SSR scope bridge. HMR ownership accumulated during the outer scope is
|
|
52
|
+
* published once when that scope completes.
|
|
29
53
|
*/
|
|
30
|
-
withActiveScope(render) {
|
|
54
|
+
async withActiveScope(render) {
|
|
31
55
|
const activeState = renderStateStorage.getStore();
|
|
32
56
|
if (activeState) {
|
|
33
|
-
return withActiveSsrScopeValue(ECOPAGES_JSX_SSR_RENDER_STATE_KEY, activeState, render);
|
|
57
|
+
return await withActiveSsrScopeValue(ECOPAGES_JSX_SSR_RENDER_STATE_KEY, activeState, render);
|
|
34
58
|
}
|
|
35
59
|
const jsxScopeState = getActiveSsrScopeValue(ECOPAGES_JSX_SSR_RENDER_STATE_KEY);
|
|
36
60
|
if (jsxScopeState) {
|
|
37
|
-
|
|
61
|
+
ensurePendingHmrFileOwners(jsxScopeState);
|
|
62
|
+
return await runWithCommittedHmrOwnership(jsxScopeState, () => renderStateStorage.run(jsxScopeState, () => render()));
|
|
38
63
|
}
|
|
39
64
|
const state = {
|
|
40
65
|
collectedAssetFrames: [],
|
|
66
|
+
pendingHmrFileOwners: new Set(),
|
|
41
67
|
};
|
|
42
|
-
return renderStateStorage.run(state, () => withActiveSsrScopeValue(ECOPAGES_JSX_SSR_RENDER_STATE_KEY, state, render));
|
|
68
|
+
return await runWithCommittedHmrOwnership(state, () => renderStateStorage.run(state, () => withActiveSsrScopeValue(ECOPAGES_JSX_SSR_RENDER_STATE_KEY, state, render)));
|
|
43
69
|
}
|
|
44
70
|
beginCollectedAssetFrame() {
|
|
45
71
|
const state = this.getState();
|
|
@@ -54,6 +80,9 @@ export class EcopagesJsxRenderSession {
|
|
|
54
80
|
}
|
|
55
81
|
return this.dedupeProcessedAssets(activeFrame);
|
|
56
82
|
}
|
|
83
|
+
mergeHmrOwnership(components) {
|
|
84
|
+
mergeEjsxHmrOwnership(ensurePendingHmrFileOwners(this.getState()), components);
|
|
85
|
+
}
|
|
57
86
|
recordCollectedAssets(collectedAssets) {
|
|
58
87
|
const dedupedAssets = this.dedupeProcessedAssets(collectedAssets);
|
|
59
88
|
const state = this.getState();
|
|
@@ -6,7 +6,6 @@ import { ECOPAGES_JSX_PLUGIN_NAME } from './ecopages-jsx.constants.js';
|
|
|
6
6
|
import { isMdxFile, normalizeMdxPageModule, } from './ecopages-jsx-mdx.js';
|
|
7
7
|
import { EcopagesJsxRenderSession } from './ecopages-jsx-render-session.js';
|
|
8
8
|
import { EcopagesJsxRadiantSsrPolicy } from './ecopages-jsx-radiant-ssr-policy.js';
|
|
9
|
-
import { updateEjsxHmrOwnership } from './ecopages-jsx-hmr-ownership.js';
|
|
10
9
|
/**
|
|
11
10
|
* Local Ecopages renderer for JSX templates in the docs app.
|
|
12
11
|
*
|
|
@@ -136,7 +135,7 @@ export class EcopagesJsxRenderer extends IntegrationRenderer {
|
|
|
136
135
|
});
|
|
137
136
|
}
|
|
138
137
|
async render(options) {
|
|
139
|
-
return await this.withPreparedRadiantRuntime(() => this.renderSession.withActiveScope(async () => {
|
|
138
|
+
return await this.withPreparedRadiantRuntime(async () => await this.renderSession.withActiveScope(async () => {
|
|
140
139
|
try {
|
|
141
140
|
const result = await this.renderPageWithDocumentShell({
|
|
142
141
|
page: {
|
|
@@ -159,7 +158,12 @@ export class EcopagesJsxRenderer extends IntegrationRenderer {
|
|
|
159
158
|
metadata: options.metadata,
|
|
160
159
|
pageProps: options.pageProps ?? {},
|
|
161
160
|
});
|
|
162
|
-
this.recordHmrOwnership([
|
|
161
|
+
this.recordHmrOwnership([
|
|
162
|
+
options.Page,
|
|
163
|
+
options.Layout,
|
|
164
|
+
options.HtmlTemplate,
|
|
165
|
+
...(options.resolvedPageDependencyComponents ?? []),
|
|
166
|
+
]);
|
|
163
167
|
return result;
|
|
164
168
|
}
|
|
165
169
|
catch (error) {
|
|
@@ -168,7 +172,7 @@ export class EcopagesJsxRenderer extends IntegrationRenderer {
|
|
|
168
172
|
}));
|
|
169
173
|
}
|
|
170
174
|
async renderComponent(input) {
|
|
171
|
-
return await this.withPreparedRadiantRuntime(() => this.renderSession.withActiveScope(async () => {
|
|
175
|
+
return await this.withPreparedRadiantRuntime(async () => await this.renderSession.withActiveScope(async () => {
|
|
172
176
|
const assetFrame = this.renderSession.beginCollectedAssetFrame();
|
|
173
177
|
try {
|
|
174
178
|
if (typeof input.component !== 'function') {
|
|
@@ -227,7 +231,7 @@ export class EcopagesJsxRenderer extends IntegrationRenderer {
|
|
|
227
231
|
}));
|
|
228
232
|
}
|
|
229
233
|
async renderToResponse(view, props, ctx) {
|
|
230
|
-
return await this.withPreparedRadiantRuntime(() => this.renderSession.withActiveScope(async () => {
|
|
234
|
+
return await this.withPreparedRadiantRuntime(async () => await this.renderSession.withActiveScope(async () => {
|
|
231
235
|
try {
|
|
232
236
|
if (typeof view !== 'function') {
|
|
233
237
|
throw new TypeError('JSX renderer expected a callable view component.');
|
|
@@ -275,6 +279,6 @@ export class EcopagesJsxRenderer extends IntegrationRenderer {
|
|
|
275
279
|
* re-walking the component tree.
|
|
276
280
|
*/
|
|
277
281
|
recordHmrOwnership(components) {
|
|
278
|
-
|
|
282
|
+
this.renderSession.mergeHmrOwnership(components);
|
|
279
283
|
}
|
|
280
284
|
}
|