zuby 1.0.47 → 1.0.49
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/config.d.ts +4 -0
- package/config.js +66 -1
- package/context/index.d.ts +4 -0
- package/context/index.js +12 -0
- package/context/types.d.ts +21 -0
- package/package.json +1 -1
- package/pageContext/index.d.ts +37 -0
- package/pageContext/index.js +59 -0
- package/plugins/contextPlugin/index.js +5 -1
- package/preload/index.js +14 -7
- package/server/index.js +75 -0
- package/server/zubyRenderer.js +2 -0
- package/templates/index.d.ts +6 -1
- package/templates/index.js +38 -18
- package/templates/types.d.ts +5 -0
- package/templates/types.js +0 -1
- package/types.d.ts +47 -1
package/config.d.ts
CHANGED
|
@@ -48,3 +48,7 @@ export type ExecutePluginsParams = Omit<ZubyHookParams, 'command' | 'logger' | '
|
|
|
48
48
|
* @param plugins
|
|
49
49
|
*/
|
|
50
50
|
export declare const normalizePlugins: (plugins: (ZubyPlugin | ZubyPlugin[] | VitePluginOption | VitePluginOption[])[]) => Promise<(ZubyPlugin | VitePlugin)[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Returns random build ID.
|
|
53
|
+
*/
|
|
54
|
+
export declare const generateDefaultBuildId: () => string;
|
package/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PLUGIN_HOOKS, } from './types.js';
|
|
2
2
|
import { BUILD_CHUNKS_MANIFEST, ZUBY_CONFIG_FILE } from './constants.js';
|
|
3
3
|
import { existsSync } from 'fs';
|
|
4
|
+
import { randomBytes } from 'crypto';
|
|
4
5
|
import { bundleRequire } from 'bundle-require';
|
|
5
6
|
import { createLogger } from './logger/index.js';
|
|
6
7
|
// Plugins
|
|
@@ -11,6 +12,7 @@ import manifestPlugin from './plugins/manifestPlugin/index.js';
|
|
|
11
12
|
import prerenderPlugin from './plugins/prerenderPlugin/index.js';
|
|
12
13
|
import standaloneBuildPlugin from './plugins/dependenciesPlugin/index.js';
|
|
13
14
|
import preloadPlugin from './plugins/preloadPlugin/index.js';
|
|
15
|
+
import { TEMPLATES } from './templates/types.js';
|
|
14
16
|
let zubyInternalConfig;
|
|
15
17
|
/**
|
|
16
18
|
* Returns the path to the ZubyConfig file.
|
|
@@ -56,8 +58,57 @@ export const getZubyInternalConfig = async (configFilePath, cache = true) => {
|
|
|
56
58
|
return zubyInternalConfig;
|
|
57
59
|
const zubyConfig = await getZubyConfig(configFilePath);
|
|
58
60
|
zubyConfig.configFilePath = configFilePath;
|
|
61
|
+
zubyConfig.templateFiles = zubyConfig.templateFiles || [];
|
|
62
|
+
zubyConfig.headElements = zubyConfig.headElements || [];
|
|
59
63
|
// Run config setup hook
|
|
60
|
-
await executePlugins(zubyConfig, PLUGIN_HOOKS.ZubyConfigSetup
|
|
64
|
+
await executePlugins(zubyConfig, PLUGIN_HOOKS.ZubyConfigSetup, {
|
|
65
|
+
addEntryTemplate: (filename) => zubyConfig.templateFiles?.push({
|
|
66
|
+
filename,
|
|
67
|
+
path: '/:path*',
|
|
68
|
+
templateType: TEMPLATES.entry,
|
|
69
|
+
}),
|
|
70
|
+
addAppTemplate: (filename, path = '/:path*') => zubyConfig.templateFiles?.push({
|
|
71
|
+
filename,
|
|
72
|
+
path,
|
|
73
|
+
templateType: TEMPLATES.app,
|
|
74
|
+
}),
|
|
75
|
+
addLayoutTemplate: (filename, path = '/:path*') => zubyConfig.templateFiles?.push({
|
|
76
|
+
filename,
|
|
77
|
+
path,
|
|
78
|
+
templateType: TEMPLATES.layout,
|
|
79
|
+
}),
|
|
80
|
+
addInnerLayoutTemplate: (filename, path = '/:path*') => zubyConfig.templateFiles?.push({
|
|
81
|
+
filename,
|
|
82
|
+
path,
|
|
83
|
+
templateType: TEMPLATES.innerLayout,
|
|
84
|
+
}),
|
|
85
|
+
addPageTemplate: (filename, path = '/:path*') => zubyConfig.templateFiles?.push({
|
|
86
|
+
filename,
|
|
87
|
+
path,
|
|
88
|
+
templateType: TEMPLATES.page,
|
|
89
|
+
}),
|
|
90
|
+
addErrorTemplate: (filename, path = '/:path*') => zubyConfig.templateFiles?.push({
|
|
91
|
+
filename,
|
|
92
|
+
path,
|
|
93
|
+
templateType: TEMPLATES.error,
|
|
94
|
+
}),
|
|
95
|
+
addLoaderTemplate: (filename, path = '/:path*') => zubyConfig.templateFiles?.push({
|
|
96
|
+
filename,
|
|
97
|
+
path,
|
|
98
|
+
templateType: TEMPLATES.loader,
|
|
99
|
+
}),
|
|
100
|
+
addPage: (filename, path = '/:path*') => zubyConfig.templateFiles?.push({
|
|
101
|
+
filename,
|
|
102
|
+
path,
|
|
103
|
+
templateType: TEMPLATES.page,
|
|
104
|
+
}),
|
|
105
|
+
addHandler: (filename, path = '/:path*') => zubyConfig.templateFiles?.push({
|
|
106
|
+
filename,
|
|
107
|
+
path,
|
|
108
|
+
templateType: TEMPLATES.handler,
|
|
109
|
+
}),
|
|
110
|
+
addToHead: (element) => zubyConfig.headElements?.push(element),
|
|
111
|
+
});
|
|
61
112
|
validateConfig(zubyConfig);
|
|
62
113
|
zubyInternalConfig = await mergeDefaultConfig(zubyConfig);
|
|
63
114
|
// Run config done hook
|
|
@@ -103,6 +154,13 @@ export const mergeDefaultConfig = async (config) => {
|
|
|
103
154
|
config.minifyCSS = config.minifyCSS ?? true;
|
|
104
155
|
config.minifyHTML = config.minifyHTML ?? true;
|
|
105
156
|
config.minifyJS = config.minifyJS ?? true;
|
|
157
|
+
// Build ID generator
|
|
158
|
+
config.generateBuildId = config.generateBuildId ?? generateDefaultBuildId;
|
|
159
|
+
// Global props
|
|
160
|
+
config.props = config.props ?? {};
|
|
161
|
+
config.serverProps = config.serverProps ?? {};
|
|
162
|
+
// Head elements
|
|
163
|
+
config.headElements = config.headElements ?? [];
|
|
106
164
|
// Add logger
|
|
107
165
|
config.customLogger =
|
|
108
166
|
config.customLogger ??
|
|
@@ -134,6 +192,7 @@ export const mergeDefaultConfig = async (config) => {
|
|
|
134
192
|
return {
|
|
135
193
|
...config,
|
|
136
194
|
templateExtensions: ['js', 'jsx', 'ts', 'tsx'],
|
|
195
|
+
buildId: await config.generateBuildId(),
|
|
137
196
|
};
|
|
138
197
|
};
|
|
139
198
|
/**
|
|
@@ -206,3 +265,9 @@ export const normalizePlugins = async (plugins) => {
|
|
|
206
265
|
// Remove false, undefined, null values
|
|
207
266
|
.filter(plugin => !!plugin);
|
|
208
267
|
};
|
|
268
|
+
/**
|
|
269
|
+
* Returns random build ID.
|
|
270
|
+
*/
|
|
271
|
+
export const generateDefaultBuildId = () => {
|
|
272
|
+
return randomBytes(8).toString('hex');
|
|
273
|
+
};
|
package/context/index.d.ts
CHANGED
|
@@ -20,5 +20,9 @@ export declare class ZubyContext {
|
|
|
20
20
|
locales: string[];
|
|
21
21
|
defaultLocale: string;
|
|
22
22
|
} | undefined;
|
|
23
|
+
get buildId(): string | undefined;
|
|
24
|
+
get props(): Record<string, any> | undefined;
|
|
25
|
+
get serverProps(): Record<string, any> | undefined;
|
|
26
|
+
get headElements(): string[] | undefined;
|
|
23
27
|
}
|
|
24
28
|
export declare const getContext: () => ZubyContext;
|
package/context/index.js
CHANGED
|
@@ -23,6 +23,18 @@ export class ZubyContext {
|
|
|
23
23
|
get i18n() {
|
|
24
24
|
return this.rawContext.i18n;
|
|
25
25
|
}
|
|
26
|
+
get buildId() {
|
|
27
|
+
return this.rawContext.buildId;
|
|
28
|
+
}
|
|
29
|
+
get props() {
|
|
30
|
+
return this.rawContext.props;
|
|
31
|
+
}
|
|
32
|
+
get serverProps() {
|
|
33
|
+
return this.rawContext.serverProps;
|
|
34
|
+
}
|
|
35
|
+
get headElements() {
|
|
36
|
+
return this.rawContext.headElements;
|
|
37
|
+
}
|
|
26
38
|
}
|
|
27
39
|
const getRawContext = () => {
|
|
28
40
|
return globalThis.ZubyRawContext;
|
package/context/types.d.ts
CHANGED
|
@@ -35,6 +35,10 @@ export interface ZubyRawContext {
|
|
|
35
35
|
* @example 1.0.0
|
|
36
36
|
*/
|
|
37
37
|
version?: string;
|
|
38
|
+
/**
|
|
39
|
+
* The build ID of the site.
|
|
40
|
+
*/
|
|
41
|
+
buildId?: string;
|
|
38
42
|
/**
|
|
39
43
|
* The internalization config from ZubyConfig.
|
|
40
44
|
* @example {
|
|
@@ -46,4 +50,21 @@ export interface ZubyRawContext {
|
|
|
46
50
|
locales: string[];
|
|
47
51
|
defaultLocale: string;
|
|
48
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* The global props for the site
|
|
55
|
+
* that will be passed to all pages
|
|
56
|
+
* on both client and server side.
|
|
57
|
+
*/
|
|
58
|
+
props?: Record<string, any>;
|
|
59
|
+
/**
|
|
60
|
+
* The global server props for the site
|
|
61
|
+
* that will be passed to all pages
|
|
62
|
+
* only on server side.
|
|
63
|
+
*/
|
|
64
|
+
serverProps?: Record<string, any>;
|
|
65
|
+
/**
|
|
66
|
+
* Additional elements that should be added
|
|
67
|
+
* to the head of the page.
|
|
68
|
+
*/
|
|
69
|
+
headElements?: string[];
|
|
49
70
|
}
|
package/package.json
CHANGED
package/pageContext/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export declare class ZubyPageContext {
|
|
|
10
10
|
protected _headers: Headers;
|
|
11
11
|
protected _cache: number;
|
|
12
12
|
protected _props: Record<string, any>;
|
|
13
|
+
protected _serverProps: Record<string, any>;
|
|
14
|
+
protected _headElements: (string | (() => any))[];
|
|
13
15
|
protected _zubyContext: ZubyContext;
|
|
14
16
|
constructor(options: {
|
|
15
17
|
url?: URL;
|
|
@@ -94,6 +96,24 @@ export declare class ZubyPageContext {
|
|
|
94
96
|
*/
|
|
95
97
|
get props(): Record<string, any>;
|
|
96
98
|
set props(value: Record<string, any>);
|
|
99
|
+
/**
|
|
100
|
+
* The server props that are available only in server-side
|
|
101
|
+
* environment and are not shared with the client.
|
|
102
|
+
* You can use this property to pass data from handler to layout template.
|
|
103
|
+
* Hint: You should not read them in page because the hydration would fail,
|
|
104
|
+
* but you can write them on page and read them in layout.
|
|
105
|
+
* @example { my: 'secret-value' }
|
|
106
|
+
*/
|
|
107
|
+
get serverProps(): Record<string, any>;
|
|
108
|
+
set serverProps(value: Record<string, any>);
|
|
109
|
+
/**
|
|
110
|
+
* The global props from the ZubyConfig
|
|
111
|
+
*/
|
|
112
|
+
get globalProps(): Record<string, any> | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* The global server props from the ZubyConfig
|
|
115
|
+
*/
|
|
116
|
+
get globalServerProps(): Record<string, any> | undefined;
|
|
97
117
|
/**
|
|
98
118
|
* The current status code that will be returned to the client.
|
|
99
119
|
* This property has only effect in server-side.
|
|
@@ -172,4 +192,21 @@ export declare class ZubyPageContext {
|
|
|
172
192
|
* was made by the Zuby.js pre-render build step.
|
|
173
193
|
*/
|
|
174
194
|
get isPrerendering(): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* The current build ID of the site.
|
|
197
|
+
* @example ecdf1a94cc9b4f4c
|
|
198
|
+
*/
|
|
199
|
+
get buildId(): string | undefined;
|
|
200
|
+
/**
|
|
201
|
+
* Adds the given HTML string
|
|
202
|
+
* or Jsx Component to the head of the page
|
|
203
|
+
* when it's rendered on the server.
|
|
204
|
+
*/
|
|
205
|
+
addToHead(element: string | (() => any)): void;
|
|
206
|
+
/**
|
|
207
|
+
* Returns array of all elements as string
|
|
208
|
+
* that should be added to the head of the page.
|
|
209
|
+
* @private
|
|
210
|
+
*/
|
|
211
|
+
getHeadElements(): Promise<string[]>;
|
|
175
212
|
}
|
package/pageContext/index.js
CHANGED
|
@@ -9,9 +9,11 @@ export class ZubyPageContext {
|
|
|
9
9
|
this._clientAddress = options?.clientAddress;
|
|
10
10
|
this._statusCode = options?.statusCode || 200;
|
|
11
11
|
this._props = options?.props || {};
|
|
12
|
+
this._serverProps = {};
|
|
12
13
|
this._cache = 0;
|
|
13
14
|
this._headers = options?.headers || new Headers();
|
|
14
15
|
this._zubyContext = options?.zubyContext || getContext();
|
|
16
|
+
this._headElements = [...(this._zubyContext.headElements || [])];
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* The current URL of the page.
|
|
@@ -114,6 +116,35 @@ export class ZubyPageContext {
|
|
|
114
116
|
set props(value) {
|
|
115
117
|
this._props = value || {};
|
|
116
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* The server props that are available only in server-side
|
|
121
|
+
* environment and are not shared with the client.
|
|
122
|
+
* You can use this property to pass data from handler to layout template.
|
|
123
|
+
* Hint: You should not read them in page because the hydration would fail,
|
|
124
|
+
* but you can write them on page and read them in layout.
|
|
125
|
+
* @example { my: 'secret-value' }
|
|
126
|
+
*/
|
|
127
|
+
get serverProps() {
|
|
128
|
+
return {
|
|
129
|
+
...(this._zubyContext.serverProps || {}),
|
|
130
|
+
...(this._serverProps || {}),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
set serverProps(value) {
|
|
134
|
+
this._serverProps = value || {};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The global props from the ZubyConfig
|
|
138
|
+
*/
|
|
139
|
+
get globalProps() {
|
|
140
|
+
return this._zubyContext.props;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The global server props from the ZubyConfig
|
|
144
|
+
*/
|
|
145
|
+
get globalServerProps() {
|
|
146
|
+
return this._zubyContext.serverProps;
|
|
147
|
+
}
|
|
117
148
|
/**
|
|
118
149
|
* The current status code that will be returned to the client.
|
|
119
150
|
* This property has only effect in server-side.
|
|
@@ -225,4 +256,32 @@ export class ZubyPageContext {
|
|
|
225
256
|
get isPrerendering() {
|
|
226
257
|
return this._request?.headers?.get('user-agent') === 'zuby-prerender';
|
|
227
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* The current build ID of the site.
|
|
261
|
+
* @example ecdf1a94cc9b4f4c
|
|
262
|
+
*/
|
|
263
|
+
get buildId() {
|
|
264
|
+
return this._zubyContext.buildId;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Adds the given HTML string
|
|
268
|
+
* or Jsx Component to the head of the page
|
|
269
|
+
* when it's rendered on the server.
|
|
270
|
+
*/
|
|
271
|
+
addToHead(element) {
|
|
272
|
+
this._headElements.push(element);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Returns array of all elements as string
|
|
276
|
+
* that should be added to the head of the page.
|
|
277
|
+
* @private
|
|
278
|
+
*/
|
|
279
|
+
async getHeadElements() {
|
|
280
|
+
return Promise.all(this._headElements.map(element => {
|
|
281
|
+
if (typeof element === 'function') {
|
|
282
|
+
return this._zubyContext?.renderToString?.(element()) || '';
|
|
283
|
+
}
|
|
284
|
+
return element;
|
|
285
|
+
}));
|
|
286
|
+
}
|
|
228
287
|
}
|
|
@@ -25,7 +25,7 @@ export default function index() {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
export async function generateCompileTimeContextCode(ssr) {
|
|
28
|
-
const { site, i18n } = await getZubyInternalConfig();
|
|
28
|
+
const { site, i18n, buildId, props, serverProps, headElements } = await getZubyInternalConfig();
|
|
29
29
|
const { version } = await getZubyPackageConfig();
|
|
30
30
|
return `globalThis.ZubyRawContext = {
|
|
31
31
|
...(globalThis.ZubyRawContext || {}),
|
|
@@ -34,6 +34,10 @@ export async function generateCompileTimeContextCode(ssr) {
|
|
|
34
34
|
site: '${site || ''}',
|
|
35
35
|
generator: 'Zuby.js ${version}',
|
|
36
36
|
version: '${version}',
|
|
37
|
+
buildId: '${buildId}',
|
|
38
|
+
props: ${JSON.stringify(props)},
|
|
39
|
+
serverProps: ${JSON.stringify(ssr ? serverProps : {})},
|
|
40
|
+
headElements: ${JSON.stringify(ssr ? headElements : [])},
|
|
37
41
|
i18n: ${JSON.stringify(i18n)},
|
|
38
42
|
};`;
|
|
39
43
|
}
|
package/preload/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getContext } from '../context/index.js';
|
|
2
|
+
import { PREALOD_MANIFEST } from '../constants.js';
|
|
2
3
|
/**
|
|
3
4
|
* The set of links that were already preloaded.
|
|
4
5
|
*/
|
|
@@ -59,7 +60,7 @@ export function preloadPage(href, onHandle = () => { }) {
|
|
|
59
60
|
return;
|
|
60
61
|
}
|
|
61
62
|
window.requestIdleCallback(async () => {
|
|
62
|
-
await
|
|
63
|
+
const preloadManifest = await getPreloadManifest();
|
|
63
64
|
// Preload assets such as scripts and styles
|
|
64
65
|
const preloadAssets = preloadManifest?.[page.filename] || [];
|
|
65
66
|
preloadAssets.forEach(href => preload(href));
|
|
@@ -78,10 +79,16 @@ function addPreloadEntry({ href, as }) {
|
|
|
78
79
|
preloadLink.rel = 'preload';
|
|
79
80
|
document.head.appendChild(preloadLink);
|
|
80
81
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
preloadManifest =
|
|
82
|
+
/**
|
|
83
|
+
* Returns preload manifest the way
|
|
84
|
+
* that ensures it is loaded only once.
|
|
85
|
+
*/
|
|
86
|
+
async function getPreloadManifest() {
|
|
87
|
+
return (preloadManifest =
|
|
88
|
+
preloadManifest ||
|
|
89
|
+
(async () => {
|
|
90
|
+
const { buildId } = getContext();
|
|
91
|
+
const res = await fetch(`/${PREALOD_MANIFEST}?${buildId}`);
|
|
92
|
+
return res.json();
|
|
93
|
+
})());
|
|
87
94
|
}
|
package/server/index.js
CHANGED
|
@@ -2088,6 +2088,18 @@ var ZubyContext = class {
|
|
|
2088
2088
|
get i18n() {
|
|
2089
2089
|
return this.rawContext.i18n;
|
|
2090
2090
|
}
|
|
2091
|
+
get buildId() {
|
|
2092
|
+
return this.rawContext.buildId;
|
|
2093
|
+
}
|
|
2094
|
+
get props() {
|
|
2095
|
+
return this.rawContext.props;
|
|
2096
|
+
}
|
|
2097
|
+
get serverProps() {
|
|
2098
|
+
return this.rawContext.serverProps;
|
|
2099
|
+
}
|
|
2100
|
+
get headElements() {
|
|
2101
|
+
return this.rawContext.headElements;
|
|
2102
|
+
}
|
|
2091
2103
|
};
|
|
2092
2104
|
var getRawContext = () => {
|
|
2093
2105
|
return globalThis.ZubyRawContext;
|
|
@@ -2110,9 +2122,11 @@ var ZubyPageContext = class {
|
|
|
2110
2122
|
this._clientAddress = options?.clientAddress;
|
|
2111
2123
|
this._statusCode = options?.statusCode || 200;
|
|
2112
2124
|
this._props = options?.props || {};
|
|
2125
|
+
this._serverProps = {};
|
|
2113
2126
|
this._cache = 0;
|
|
2114
2127
|
this._headers = options?.headers || new Headers();
|
|
2115
2128
|
this._zubyContext = options?.zubyContext || getContext();
|
|
2129
|
+
this._headElements = [...this._zubyContext.headElements || []];
|
|
2116
2130
|
}
|
|
2117
2131
|
/**
|
|
2118
2132
|
* The current URL of the page.
|
|
@@ -2215,6 +2229,35 @@ var ZubyPageContext = class {
|
|
|
2215
2229
|
set props(value) {
|
|
2216
2230
|
this._props = value || {};
|
|
2217
2231
|
}
|
|
2232
|
+
/**
|
|
2233
|
+
* The server props that are available only in server-side
|
|
2234
|
+
* environment and are not shared with the client.
|
|
2235
|
+
* You can use this property to pass data from handler to layout template.
|
|
2236
|
+
* Hint: You should not read them in page because the hydration would fail,
|
|
2237
|
+
* but you can write them on page and read them in layout.
|
|
2238
|
+
* @example { my: 'secret-value' }
|
|
2239
|
+
*/
|
|
2240
|
+
get serverProps() {
|
|
2241
|
+
return {
|
|
2242
|
+
...this._zubyContext.serverProps || {},
|
|
2243
|
+
...this._serverProps || {}
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
set serverProps(value) {
|
|
2247
|
+
this._serverProps = value || {};
|
|
2248
|
+
}
|
|
2249
|
+
/**
|
|
2250
|
+
* The global props from the ZubyConfig
|
|
2251
|
+
*/
|
|
2252
|
+
get globalProps() {
|
|
2253
|
+
return this._zubyContext.props;
|
|
2254
|
+
}
|
|
2255
|
+
/**
|
|
2256
|
+
* The global server props from the ZubyConfig
|
|
2257
|
+
*/
|
|
2258
|
+
get globalServerProps() {
|
|
2259
|
+
return this._zubyContext.serverProps;
|
|
2260
|
+
}
|
|
2218
2261
|
/**
|
|
2219
2262
|
* The current status code that will be returned to the client.
|
|
2220
2263
|
* This property has only effect in server-side.
|
|
@@ -2326,6 +2369,36 @@ var ZubyPageContext = class {
|
|
|
2326
2369
|
get isPrerendering() {
|
|
2327
2370
|
return this._request?.headers?.get("user-agent") === "zuby-prerender";
|
|
2328
2371
|
}
|
|
2372
|
+
/**
|
|
2373
|
+
* The current build ID of the site.
|
|
2374
|
+
* @example ecdf1a94cc9b4f4c
|
|
2375
|
+
*/
|
|
2376
|
+
get buildId() {
|
|
2377
|
+
return this._zubyContext.buildId;
|
|
2378
|
+
}
|
|
2379
|
+
/**
|
|
2380
|
+
* Adds the given HTML string
|
|
2381
|
+
* or Jsx Component to the head of the page
|
|
2382
|
+
* when it's rendered on the server.
|
|
2383
|
+
*/
|
|
2384
|
+
addToHead(element) {
|
|
2385
|
+
this._headElements.push(element);
|
|
2386
|
+
}
|
|
2387
|
+
/**
|
|
2388
|
+
* Returns array of all elements as string
|
|
2389
|
+
* that should be added to the head of the page.
|
|
2390
|
+
* @private
|
|
2391
|
+
*/
|
|
2392
|
+
async getHeadElements() {
|
|
2393
|
+
return Promise.all(
|
|
2394
|
+
this._headElements.map((element) => {
|
|
2395
|
+
if (typeof element === "function") {
|
|
2396
|
+
return this._zubyContext?.renderToString?.(element()) || "";
|
|
2397
|
+
}
|
|
2398
|
+
return element;
|
|
2399
|
+
})
|
|
2400
|
+
);
|
|
2401
|
+
}
|
|
2329
2402
|
};
|
|
2330
2403
|
|
|
2331
2404
|
// src/server/zubyRenderer.ts
|
|
@@ -8711,6 +8784,8 @@ var ZubyRenderer = class {
|
|
|
8711
8784
|
jsImports.forEach((imp) => {
|
|
8712
8785
|
html = html.replace(/(<\/head>)/, `<script type="module" src="${imp}" defer></script>$1`);
|
|
8713
8786
|
});
|
|
8787
|
+
const headElements = await pageContext.getHeadElements();
|
|
8788
|
+
html = html.replace(/(<\/head>)/, headElements.join("") + "$1");
|
|
8714
8789
|
return this.injectHeaders(
|
|
8715
8790
|
new Response(html, {
|
|
8716
8791
|
status: pageContext.statusCode || 200,
|
package/server/zubyRenderer.js
CHANGED
|
@@ -149,6 +149,8 @@ export default class ZubyRenderer {
|
|
|
149
149
|
jsImports.forEach((imp) => {
|
|
150
150
|
html = html.replace(/(<\/head>)/, `<script type="module" src="${imp}" defer></script>$1`);
|
|
151
151
|
});
|
|
152
|
+
const headElements = await pageContext.getHeadElements();
|
|
153
|
+
html = html.replace(/(<\/head>)/, headElements.join('') + '$1');
|
|
152
154
|
return this.injectHeaders(new Response(html, {
|
|
153
155
|
status: pageContext.statusCode || 200,
|
|
154
156
|
headers: {
|
package/templates/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Template, TemplateType } from './types.js';
|
|
1
|
+
import { Template, TemplateFile, TemplateType } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Returns the array of pages with static path.
|
|
4
4
|
*/
|
|
@@ -42,6 +42,11 @@ export declare function getDefaultTemplate(filename: string, templateType: Templ
|
|
|
42
42
|
* If i18n config is defined, the pages will be localized and duplicated for each locale.
|
|
43
43
|
*/
|
|
44
44
|
export declare function getTemplates(cache?: boolean): Promise<Template[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Collects all template files from the pages directory and the templateFiles config
|
|
47
|
+
* and returns them as an array of TemplateFile objects.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getTemplateFiles(): Promise<TemplateFile[]>;
|
|
45
50
|
/**
|
|
46
51
|
* Calculates the weight of each template
|
|
47
52
|
* and sorts them from the least dynamic path to the most dynamic.
|
package/templates/index.js
CHANGED
|
@@ -103,26 +103,12 @@ export function getDefaultTemplate(filename, templateType) {
|
|
|
103
103
|
export async function getTemplates(cache = true) {
|
|
104
104
|
if (cache && templatesCache)
|
|
105
105
|
return templatesCache;
|
|
106
|
-
const {
|
|
107
|
-
const pagesDir = normalizePath(join(srcDir, 'pages'));
|
|
108
|
-
const files = await glob(`${pagesDir}/**/*.{${templateExtensions.join(',')}}`);
|
|
106
|
+
const { i18n } = await getZubyInternalConfig();
|
|
109
107
|
const locales = (i18n?.locales || []).filter(locale => locale !== i18n?.defaultLocale);
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
filename = normalizePath(filename);
|
|
108
|
+
const templateFiles = await getTemplateFiles();
|
|
109
|
+
const templates = templateFiles
|
|
110
|
+
.map(({ path, filename, templateType }) => {
|
|
114
111
|
const fileNameWithoutExtension = basename(filename).replace(/\.(.+)$/, '');
|
|
115
|
-
const templateType = getTemplateType(filename);
|
|
116
|
-
// Remove the pagesDir prefix from the filename
|
|
117
|
-
// and transform it to a valid wouter path
|
|
118
|
-
let path = toPath(filename.replace(pagesDir, ''));
|
|
119
|
-
// Create matching path for base templates
|
|
120
|
-
if (Object.values(BASE_TEMPLATES).includes(templateType)) {
|
|
121
|
-
path = `${path}/:path*`.replace(/\/+/g, '/');
|
|
122
|
-
}
|
|
123
|
-
// Calculate the relative path from
|
|
124
|
-
// the ZubyRouter component file to the page file
|
|
125
|
-
filename = normalizePath(resolve(filename));
|
|
126
112
|
return {
|
|
127
113
|
filename,
|
|
128
114
|
path,
|
|
@@ -148,6 +134,40 @@ export async function getTemplates(cache = true) {
|
|
|
148
134
|
}));
|
|
149
135
|
return (templatesCache = sortTemplates(templates));
|
|
150
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Collects all template files from the pages directory and the templateFiles config
|
|
139
|
+
* and returns them as an array of TemplateFile objects.
|
|
140
|
+
*/
|
|
141
|
+
export async function getTemplateFiles() {
|
|
142
|
+
const { srcDir, templateExtensions, templateFiles } = await getZubyInternalConfig();
|
|
143
|
+
const pagesDir = normalizePath(join(srcDir, 'pages'));
|
|
144
|
+
const files = await glob(`${pagesDir}/**/*.{${templateExtensions.join(',')}}`);
|
|
145
|
+
const configTemplateFiles = templateFiles.map(({ templateType, filename, path }) => {
|
|
146
|
+
return {
|
|
147
|
+
filename: normalizePath(resolve(filename)),
|
|
148
|
+
path: toPath(path),
|
|
149
|
+
templateType,
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
const folderTemplateFiles = files.map(filename => {
|
|
153
|
+
// Normalize the path and slashes to unix style (needed for windows)
|
|
154
|
+
filename = normalizePath(filename);
|
|
155
|
+
const templateType = getTemplateType(filename);
|
|
156
|
+
// Remove the pagesDir prefix from the filename
|
|
157
|
+
// and transform it to a valid wouter path
|
|
158
|
+
let path = toPath(filename.replace(pagesDir, ''));
|
|
159
|
+
// Create matching path for base templates
|
|
160
|
+
if (Object.values(BASE_TEMPLATES).includes(templateType)) {
|
|
161
|
+
path = `${path}/:path*`.replace(/\/+/g, '/');
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
filename: normalizePath(resolve(filename)),
|
|
165
|
+
path,
|
|
166
|
+
templateType,
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
return [...configTemplateFiles, ...folderTemplateFiles];
|
|
170
|
+
}
|
|
151
171
|
/**
|
|
152
172
|
* Calculates the weight of each template
|
|
153
173
|
* and sorts them from the least dynamic path to the most dynamic.
|
package/templates/types.d.ts
CHANGED
package/templates/types.js
CHANGED
package/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { UserConfig as ViteUserConfig, InlineConfig as ViteInlineConfig, PluginOption as VitePluginOption, Plugin as VitePlugin } from 'vite';
|
|
3
3
|
import { ZubyLogger } from './logger/types.js';
|
|
4
4
|
import ReadableStream = NodeJS.ReadableStream;
|
|
5
|
-
import { PathParamsType, Template } from './templates/types.js';
|
|
5
|
+
import { PathParamsType, Template, TemplateFile } from './templates/types.js';
|
|
6
6
|
import ZubyDevServer from './server/zubyDevServer.js';
|
|
7
7
|
export interface ZubyConfig {
|
|
8
8
|
/**
|
|
@@ -122,6 +122,39 @@ export interface ZubyConfig {
|
|
|
122
122
|
* @private
|
|
123
123
|
*/
|
|
124
124
|
configFilePath?: string;
|
|
125
|
+
/**
|
|
126
|
+
* If you're building in multiple environments,
|
|
127
|
+
* you can use this option to generate consistent build IDs.
|
|
128
|
+
*/
|
|
129
|
+
generateBuildId?: () => string | Promise<string>;
|
|
130
|
+
/**
|
|
131
|
+
* Template files that are used to
|
|
132
|
+
* create the templates during the build process.
|
|
133
|
+
* @private
|
|
134
|
+
*/
|
|
135
|
+
templateFiles?: TemplateFile[];
|
|
136
|
+
/**
|
|
137
|
+
* The global props for the site
|
|
138
|
+
* that will be passed to all pages, handlers etc...
|
|
139
|
+
* on both client and server side.
|
|
140
|
+
* They can be retrieved using PageContext.globalProps method.
|
|
141
|
+
* @default {}
|
|
142
|
+
*/
|
|
143
|
+
props?: Record<string, any>;
|
|
144
|
+
/**
|
|
145
|
+
* The global server props for the site
|
|
146
|
+
* that will be passed to all pages, handlers etc...
|
|
147
|
+
* only on server side.
|
|
148
|
+
* They can be retrieved using PageContext.globalServerProps method.
|
|
149
|
+
* @default {}
|
|
150
|
+
*/
|
|
151
|
+
serverProps?: Record<string, any>;
|
|
152
|
+
/**
|
|
153
|
+
* Additional HTML elements
|
|
154
|
+
* that will be injected into the head of the page.
|
|
155
|
+
* @default []
|
|
156
|
+
*/
|
|
157
|
+
headElements?: string[];
|
|
125
158
|
}
|
|
126
159
|
export interface ZubyInternalConfig extends Required<ZubyConfig> {
|
|
127
160
|
/**
|
|
@@ -136,6 +169,10 @@ export interface ZubyInternalConfig extends Required<ZubyConfig> {
|
|
|
136
169
|
* @default []
|
|
137
170
|
*/
|
|
138
171
|
plugins: ZubyPlugin[];
|
|
172
|
+
/**
|
|
173
|
+
* The current build ID
|
|
174
|
+
*/
|
|
175
|
+
buildId: string;
|
|
139
176
|
}
|
|
140
177
|
export interface BaseCommandOptions {
|
|
141
178
|
/**
|
|
@@ -304,6 +341,15 @@ export interface ZubyPlugin extends VitePlugin {
|
|
|
304
341
|
export interface ZubyConfigSetupHookParams {
|
|
305
342
|
config: ZubyConfig;
|
|
306
343
|
command: 'dev' | 'build';
|
|
344
|
+
addEntryTemplate: (entryFile: string) => void;
|
|
345
|
+
addAppTemplate: (appFile: string, path?: string) => void;
|
|
346
|
+
addLayoutTemplate: (layoutFile: string, path?: string) => void;
|
|
347
|
+
addInnerLayoutTemplate: (innerLayoutFile: string, path?: string) => void;
|
|
348
|
+
addErrorTemplate: (errorFile: string, path?: string) => void;
|
|
349
|
+
addLoaderTemplate: (loaderFile: string, path?: string) => void;
|
|
350
|
+
addPage: (pageFile: string, path?: string) => void;
|
|
351
|
+
addHandler: (handlerFile: string, path?: string) => void;
|
|
352
|
+
addToHead: (element: string) => void;
|
|
307
353
|
}
|
|
308
354
|
export interface ZubyConfigDoneHookParams extends ZubyConfigSetupHookParams {
|
|
309
355
|
config: ZubyInternalConfig;
|