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 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
+ };
@@ -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;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuby",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "description": "Zuby.js is framework for building SPA apps using Vite",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -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
  }
@@ -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 loadPreloadManifest();
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
- async function loadPreloadManifest() {
82
- if (preloadManifest)
83
- return;
84
- preloadManifest = {};
85
- const res = await fetch('/preload-manifest.json');
86
- preloadManifest = await res.json();
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,
@@ -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: {
@@ -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.
@@ -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 { srcDir, i18n, templateExtensions } = await getZubyInternalConfig();
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 templates = files
111
- .map(filename => {
112
- // Normalize the path and slashes to unix style (needed for windows)
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.
@@ -47,3 +47,8 @@ export declare const SYNC_TEMPLATES: {
47
47
  loader: string;
48
48
  };
49
49
  export type TemplateType = (typeof TEMPLATES)[keyof typeof TEMPLATES];
50
+ export interface TemplateFile {
51
+ path: string;
52
+ filename: string;
53
+ templateType: TemplateType;
54
+ }
@@ -1,4 +1,3 @@
1
- // Pages
2
1
  export const PATH_TYPES = {
3
2
  static: 'static',
4
3
  dynamic: 'dynamic',
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;