zuby 1.0.13 → 1.0.14

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.
@@ -1,6 +1,8 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
- import { Router as WouterRouter, Route as WouterRoute, } from "wouter-preact";
2
+ import { Router as WouterRouter, Route as WouterRoute, Switch as WouterSwitch, } from 'wouter-preact';
3
3
  import { Suspense, lazy } from 'preact/compat';
4
+ import { getContext } from '../../context/index.js';
5
+ let pages = undefined;
4
6
  /**
5
7
  * Zuby's Router component provides support for file-system based routing.
6
8
  */
@@ -9,13 +11,12 @@ export default function Router({ children, headerComponent, footerComponent, loa
9
11
  headerComponent = headerComponent || (_jsx(_Fragment, {}));
10
12
  footerComponent = footerComponent || (_jsx(_Fragment, {}));
11
13
  loaderComponent = loaderComponent || (_jsx(_Fragment, { children: "Loading..." }));
12
- const Zuby = globalThis?.Zuby || {};
13
- const currentPath = Zuby?.currentPath;
14
- // Retrieve the pages generated during the build process.
15
- const pages = (Zuby?.pages || [])
16
- .map((page) => ({
17
- ...page,
18
- component: lazy(page.component),
19
- }));
20
- return (_jsx("div", { children: _jsxs(WouterRouter, { ssrPath: currentPath, children: [headerComponent, children, _jsx(Suspense, { fallback: loaderComponent, children: pages.map((page) => (_jsx(WouterRoute, { path: page.path, component: page.component }))) }), footerComponent] }) }));
14
+ const zubyContext = getContext();
15
+ if (!pages) {
16
+ pages = zubyContext.pages?.map((page) => ({
17
+ ...page,
18
+ component: lazy(page.component),
19
+ })) || [];
20
+ }
21
+ return (_jsxs(WouterRouter, { ssrPath: zubyContext.currentPath, children: [headerComponent, children, _jsx(Suspense, { fallback: loaderComponent, children: _jsx(WouterSwitch, { children: pages.map((page) => (_jsx(WouterRoute, { path: page.path, component: page.component }))) }) }), footerComponent] }));
21
22
  }
package/config.d.ts CHANGED
@@ -1,5 +1,22 @@
1
1
  import { ZubyConfig } from './types.js';
2
2
  import { UserConfig as ViteUserConfig } from 'vite';
3
+ /**
4
+ * This function loads the config file and returns the config object with default values.
5
+ * It also caches the config object for future use.
6
+ * @param configFile
7
+ */
3
8
  export declare const getZubyConfig: (configFile?: string, cache?: boolean) => Promise<ZubyConfig>;
9
+ /**
10
+ * Adds default values to the config object.
11
+ * @param config
12
+ */
13
+ export declare const mergeDefaultConfig: (config?: ZubyConfig) => Promise<ZubyConfig>;
14
+ /**
15
+ * This function returns the array of built-in Zuby plugins.
16
+ * Check which framework components are relying on each plugin
17
+ * before removing any of them.
18
+ * @param config
19
+ */
20
+ export declare const getBuiltInPlugins: (config: ZubyConfig) => import("vite").PluginOption[];
4
21
  export declare const getViteConfig: (config: ZubyConfig) => ViteUserConfig;
5
22
  export declare const getZubyPackageJson: (cache?: boolean) => any;
package/config.js CHANGED
@@ -3,9 +3,20 @@ import { fileURLToPath } from 'url';
3
3
  import { existsSync, readFileSync } from 'fs';
4
4
  import { dirname, resolve } from 'path';
5
5
  import { bundleRequire } from 'bundle-require';
6
- import { getPages } from './pages.js';
6
+ import { getPages } from './pages/index.js';
7
+ import { createLogger } from './logger.js';
8
+ import { preact as preactPlugin } from '@preact/preset-vite';
9
+ import contextPlugin from './plugins/contextPlugin/index.js';
10
+ import compileTimePlugin from './plugins/compileTimePlugin/index.js';
11
+ import chunkNamingPlugin from './plugins/chunkNamingPlugin/index.js';
12
+ import prerenderPlugin from './plugins/prerenderPlugin/index.js';
7
13
  let zubyConfig;
8
14
  let packageJson;
15
+ /**
16
+ * This function loads the config file and returns the config object with default values.
17
+ * It also caches the config object for future use.
18
+ * @param configFile
19
+ */
9
20
  export const getZubyConfig = async (configFile = ZUBY_CONFIG_FILE, cache = true) => {
10
21
  // Return the cached config if it exists
11
22
  if (cache && zubyConfig)
@@ -30,9 +41,62 @@ export const getZubyConfig = async (configFile = ZUBY_CONFIG_FILE, cache = true)
30
41
  if (!zubyConfig) {
31
42
  throw new Error(`No valid default export found in config file: ${foundConfigFile}`);
32
43
  }
33
- zubyConfig.pages = await getPages();
44
+ zubyConfig = await mergeDefaultConfig(zubyConfig);
34
45
  return zubyConfig;
35
46
  };
47
+ /**
48
+ * Adds default values to the config object.
49
+ * @param config
50
+ */
51
+ export const mergeDefaultConfig = async (config = {}) => {
52
+ // Set default values
53
+ config.srcDir = config.srcDir ?? './';
54
+ config.publicDir = config.publicDir ?? 'public';
55
+ config.outDir = config.outDir ?? 'build';
56
+ config.output = config.output ?? 'static';
57
+ config.prerenderPaths = config.prerenderPaths ?? [];
58
+ config.pageExtensions = config.pageExtensions ?? ['tsx', 'jsx', 'ts', 'js', 'mjs'];
59
+ // Add minification
60
+ config.minifyCSS = config.minifyCSS ?? true;
61
+ config.minifyHTML = config.minifyHTML ?? true;
62
+ config.minifyJS = config.minifyJS ?? true;
63
+ // Add logger
64
+ config.customLogger =
65
+ config.customLogger ??
66
+ createLogger(config.logLevel, {
67
+ allowClearScreen: false,
68
+ prefix: '[Zuby]',
69
+ });
70
+ config.logLevel = config.logLevel ?? 'info';
71
+ // Transform to vite config
72
+ config.vite = config.vite ?? {};
73
+ config.vite.build = config.vite?.build ?? {};
74
+ config.vite.build.cssMinify = config.vite.build.cssMinify ?? config.minifyCSS;
75
+ config.vite.build.minify = config.vite.build.minify ?? config.minifyJS;
76
+ // Merge built-in plugins with user plugins
77
+ config.vite.plugins = [
78
+ ...getBuiltInPlugins(config),
79
+ ...(config.vite?.plugins ?? []),
80
+ ];
81
+ // Load pages
82
+ config.pages = await getPages();
83
+ return config;
84
+ };
85
+ /**
86
+ * This function returns the array of built-in Zuby plugins.
87
+ * Check which framework components are relying on each plugin
88
+ * before removing any of them.
89
+ * @param config
90
+ */
91
+ export const getBuiltInPlugins = (config) => {
92
+ return [
93
+ preactPlugin(),
94
+ contextPlugin(),
95
+ compileTimePlugin(),
96
+ chunkNamingPlugin(),
97
+ prerenderPlugin(),
98
+ ];
99
+ };
36
100
  export const getViteConfig = (config) => {
37
101
  // Generates valid vite build config from the zuby config
38
102
  return {
@@ -0,0 +1,12 @@
1
+ import { ZubyRawContext } from './types.js';
2
+ declare class ZubyContext {
3
+ readonly rawContext: ZubyRawContext;
4
+ constructor(rawContext: ZubyRawContext);
5
+ get title(): string | undefined;
6
+ set title(title: string | undefined);
7
+ get currentPath(): string | undefined;
8
+ get pages(): import("../pages/types.js").Page[] | undefined;
9
+ update: (newContext: ZubyRawContext) => this;
10
+ }
11
+ export declare const getContext: () => ZubyContext;
12
+ export {};
@@ -0,0 +1,37 @@
1
+ class ZubyContext {
2
+ constructor(rawContext) {
3
+ this.update = (newContext) => {
4
+ Object.assign(this.rawContext, newContext);
5
+ return this;
6
+ };
7
+ this.rawContext = rawContext;
8
+ }
9
+ get title() {
10
+ if (typeof document !== 'undefined' && document.title) {
11
+ this.rawContext.title = document.title;
12
+ return document.title;
13
+ }
14
+ return this.rawContext.title;
15
+ }
16
+ set title(title) {
17
+ if (typeof document !== 'undefined' && document.title) {
18
+ this.rawContext.title = document.title = title || '';
19
+ }
20
+ this.rawContext.title = title;
21
+ }
22
+ get currentPath() {
23
+ return this.rawContext.currentPath;
24
+ }
25
+ get pages() {
26
+ return this.rawContext.pages;
27
+ }
28
+ }
29
+ const getRawContext = () => {
30
+ return globalThis.ZubyRawContext;
31
+ };
32
+ export const getContext = () => {
33
+ if (!getRawContext()) {
34
+ globalThis.ZubyRawContext = {};
35
+ }
36
+ return new ZubyContext(getRawContext());
37
+ };
@@ -0,0 +1,15 @@
1
+ import { Page } from '../pages/types.js';
2
+ export interface ZubyRawContext {
3
+ /**
4
+ * The array with pages.
5
+ */
6
+ pages?: Page[];
7
+ /**
8
+ * The current path of the page in both client and server env.
9
+ */
10
+ currentPath?: string;
11
+ /**
12
+ * The current title of the page in both client and server env.
13
+ */
14
+ title?: string;
15
+ }
@@ -0,0 +1 @@
1
+ export {};
package/defineConfig.d.ts CHANGED
@@ -1,14 +1,13 @@
1
1
  import { ZubyConfig } from './types.js';
2
2
  /**
3
- * This function returns a ZubyConfig object with default values.
4
- * ZubyConfig is framework-specific config, which includes valid vite config.
3
+ * This helper function is used to define the Zuby config in zuby.config.mjs.
4
+ *
5
+ * It is not required, but it provides type safety and autocompletion in your IDE.
6
+ * See the full Zuby Configuration API Documentation
5
7
  * @param config
8
+ * @example defineConfig({
9
+ * outDir: 'build',
10
+ * srcDir: './',
11
+ * })
6
12
  */
7
13
  export declare const defineConfig: (config: ZubyConfig) => ZubyConfig;
8
- /**
9
- * This function adds all built-in Zuby plugins to the config.
10
- * Check which framework components are relying on each plugin
11
- * before removing any of them.
12
- * @param config
13
- */
14
- export declare const getBuiltInPlugins: (config: ZubyConfig) => import("vite").PluginOption[];
package/defineConfig.js CHANGED
@@ -1,58 +1,14 @@
1
- import { createLogger } from './logger.js';
2
- import { preact as preactPlugin } from '@preact/preset-vite';
3
- import zubyGlobalPlugin from './plugins/zubyGlobalPlugin/index.js';
4
- import compileTimePlugin from './plugins/compileTimePlugin/index.js';
5
- import chunkNamingPlugin from './plugins/chunkNamingPlugin/index.js';
6
- import prerenderPlugin from './plugins/prerenderPlugin/index.js';
7
1
  /**
8
- * This function returns a ZubyConfig object with default values.
9
- * ZubyConfig is framework-specific config, which includes valid vite config.
2
+ * This helper function is used to define the Zuby config in zuby.config.mjs.
3
+ *
4
+ * It is not required, but it provides type safety and autocompletion in your IDE.
5
+ * See the full Zuby Configuration API Documentation
10
6
  * @param config
7
+ * @example defineConfig({
8
+ * outDir: 'build',
9
+ * srcDir: './',
10
+ * })
11
11
  */
12
12
  export const defineConfig = (config) => {
13
- // Default values
14
- config.srcDir = config.srcDir ?? './';
15
- config.publicDir = config.publicDir ?? 'public';
16
- config.outDir = config.outDir ?? 'build';
17
- config.output = config.output ?? 'static';
18
- config.prerenderPaths = config.prerenderPaths ?? [];
19
- config.pageExtensions = config.pageExtensions ?? ['tsx', 'jsx', 'ts', 'js', 'mjs'];
20
- // Add minification
21
- config.minifyCSS = config.minifyCSS ?? true;
22
- config.minifyHTML = config.minifyHTML ?? true;
23
- config.minifyJS = config.minifyJS ?? true;
24
- // Add logger
25
- config.customLogger =
26
- config.customLogger ??
27
- createLogger(config.logLevel, {
28
- allowClearScreen: false,
29
- prefix: '[Zuby]',
30
- });
31
- config.logLevel = config.logLevel ?? 'info';
32
- // Transform to vite config
33
- config.vite = config.vite ?? {};
34
- config.vite.build = config.vite?.build ?? {};
35
- config.vite.build.cssMinify = config.vite.build.cssMinify ?? config.minifyCSS;
36
- config.vite.build.minify = config.vite.build.minify ?? config.minifyJS;
37
- // Merge built-in plugins with user plugins
38
- config.vite.plugins = [
39
- ...getBuiltInPlugins(config),
40
- ...(config.vite?.plugins ?? []),
41
- ];
42
13
  return config;
43
14
  };
44
- /**
45
- * This function adds all built-in Zuby plugins to the config.
46
- * Check which framework components are relying on each plugin
47
- * before removing any of them.
48
- * @param config
49
- */
50
- export const getBuiltInPlugins = (config) => {
51
- return [
52
- preactPlugin(),
53
- zubyGlobalPlugin(),
54
- compileTimePlugin(),
55
- chunkNamingPlugin(),
56
- prerenderPlugin(),
57
- ];
58
- };
@@ -0,0 +1,7 @@
1
+ export declare const useZuby: () => {
2
+ readonly rawContext: import("../context/types.js").ZubyRawContext;
3
+ title: string | undefined;
4
+ readonly currentPath: string | undefined;
5
+ readonly pages: import("../pages/types.js").Page[] | undefined;
6
+ update: (newContext: import("../context/types.js").ZubyRawContext) => any;
7
+ };
@@ -0,0 +1,4 @@
1
+ import { getContext } from '../context/index.js';
2
+ export const useZuby = () => {
3
+ return getContext();
4
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuby",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "Zuby.js is Preact Framework for building SPA apps using Vite",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -21,7 +21,6 @@
21
21
  "es-module-lexer": "^1.3.1",
22
22
  "html-minifier": "^4.0.0",
23
23
  "magic-string": "^0.30.5",
24
- "preact-iso": "^2.3.2",
25
24
  "preact-render-to-string": "^6.2.2",
26
25
  "preact-ssr-prepass": "^1.2.0",
27
26
  "rea": "^0.0.1",
@@ -2,11 +2,11 @@ import { Page } from './types.js';
2
2
  /**
3
3
  * Returns the array of pages with static path.
4
4
  */
5
- export declare function getStaticPages(): Promise<Page[]>;
5
+ export declare function getStaticPages(pages?: Page[]): Promise<Page[]>;
6
6
  /**
7
7
  * Returns the array of pages with dynamic path.
8
8
  */
9
- export declare function getDynamicPages(): Promise<Page[]>;
9
+ export declare function getDynamicPages(pages?: Page[]): Promise<Page[]>;
10
10
  /**
11
11
  * Returns the array of all pages
12
12
  * The pages are already sorted from the least dynamic path to the most dynamic.
@@ -32,3 +32,10 @@ export declare function sortPages(pages: Page[]): Page[];
32
32
  * @example /products/[id].tsx -> /products/:id
33
33
  */
34
34
  export declare function toPath(filename: string): string;
35
+ /**
36
+ * Returns the matching page for the given path.
37
+ */
38
+ export declare function findMatchingPage(pages: Page[], path: string): {
39
+ page: Page;
40
+ params: Record<string, string>;
41
+ } | undefined;
@@ -1,20 +1,21 @@
1
1
  import { PATH_TYPES } from './types.js';
2
- import { getZubyConfig } from './config.js';
2
+ import { getZubyConfig } from '../config.js';
3
3
  import { join, resolve } from 'path';
4
4
  import { glob } from 'glob';
5
- import { normalizePath } from './utils/pathUtils.js';
5
+ import { normalizePath } from '../utils/pathUtils.js';
6
+ import { pathToRegexp } from './pathUtils.js';
6
7
  /**
7
8
  * Returns the array of pages with static path.
8
9
  */
9
- export async function getStaticPages() {
10
- const pages = await getPages();
10
+ export async function getStaticPages(pages) {
11
+ pages = pages || await getPages();
11
12
  return pages.filter(page => page.pathType === PATH_TYPES.static);
12
13
  }
13
14
  /**
14
15
  * Returns the array of pages with dynamic path.
15
16
  */
16
- export async function getDynamicPages() {
17
- const pages = await getPages();
17
+ export async function getDynamicPages(pages) {
18
+ pages = pages || await getPages();
18
19
  return pages.filter(page => page.pathType === PATH_TYPES.dynamic);
19
20
  }
20
21
  /**
@@ -49,7 +50,11 @@ export async function getPages() {
49
50
  ...page,
50
51
  path: `/${locale}${page.path}`.replace(/(.+)\/+$/, '$1')
51
52
  }))
52
- ]);
53
+ ])
54
+ .map(page => ({
55
+ ...page,
56
+ ...pathToRegexp(page.path)
57
+ }));
53
58
  return sortPages(pages);
54
59
  }
55
60
  /**
@@ -110,3 +115,22 @@ export function toPath(filename) {
110
115
  // Param syntax
111
116
  .replace(/\[(\w+)]/, ':$1');
112
117
  }
118
+ /**
119
+ * Returns the matching page for the given path.
120
+ */
121
+ export function findMatchingPage(pages, path) {
122
+ for (const page of pages) {
123
+ const match = page.pathRegex.exec(path);
124
+ if (!match)
125
+ continue;
126
+ const matchedParams = page.pathParams.reduce((params, key, i) => {
127
+ params[key] = match[i + 1];
128
+ return params;
129
+ }, {});
130
+ return {
131
+ page,
132
+ params: matchedParams
133
+ };
134
+ }
135
+ return undefined;
136
+ }
@@ -0,0 +1,4 @@
1
+ export declare const pathToRegexp: (pathPattern: string) => {
2
+ pathParams: string[];
3
+ pathRegex: RegExp;
4
+ };
@@ -0,0 +1,34 @@
1
+ export const pathToRegexp = (pathPattern) => {
2
+ const groupRx = /:([A-Za-z0-9_]+)([?+*]?)/g;
3
+ let match = null, lastIndex = 0, keys = [], result = "";
4
+ while ((match = groupRx.exec(pathPattern)) !== null) {
5
+ const [_, segment, mod] = match;
6
+ // :foo [1] ( )
7
+ // :foo? [0 - 1] ( o)
8
+ // :foo+ [1 - ∞] (r )
9
+ // :foo* [0 - ∞] (ro)
10
+ const repeat = mod === "+" || mod === "*";
11
+ const optional = mod === "?" || mod === "*";
12
+ const prefix = optional && pathPattern[match.index - 1] === "/" ? 1 : 0;
13
+ const prev = pathPattern.substring(lastIndex, match.index - prefix);
14
+ keys.push(segment);
15
+ lastIndex = groupRx.lastIndex;
16
+ result += escapeRx(prev) + rxForSegment(repeat, optional, prefix);
17
+ }
18
+ result += escapeRx(pathPattern.substring(lastIndex));
19
+ return {
20
+ pathParams: keys,
21
+ pathRegex: new RegExp("^" + result + "(?:\\/)?$", "i")
22
+ };
23
+ };
24
+ // escapes a regexp string (borrowed from path-to-regexp sources)
25
+ // https://github.com/pillarjs/path-to-regexp/blob/v3.0.0/index.js#L202
26
+ const escapeRx = (str) => str.replace(/([.+*?=^!:${}()[\]|/\\])/g, "\\$1");
27
+ // returns a segment representation in RegExp based on flags
28
+ // adapted and simplified version from path-to-regexp sources
29
+ const rxForSegment = (repeat, optional, prefix) => {
30
+ let capture = repeat ? "((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*)" : "([^\\/]+?)";
31
+ if (optional && prefix)
32
+ capture = "(?:\\/" + capture + ")";
33
+ return capture + (optional ? "?" : "");
34
+ };
@@ -0,0 +1,13 @@
1
+ export declare const PATH_TYPES: {
2
+ static: string;
3
+ dynamic: string;
4
+ };
5
+ export type PathType = (typeof PATH_TYPES)[keyof typeof PATH_TYPES];
6
+ export interface Page {
7
+ path: string;
8
+ pathRegex: RegExp;
9
+ pathParams: string[];
10
+ pathType: PathType;
11
+ filename: string;
12
+ weight: number;
13
+ }
package/pages/types.js ADDED
@@ -0,0 +1,5 @@
1
+ // Pages
2
+ export const PATH_TYPES = {
3
+ static: 'static',
4
+ dynamic: 'dynamic',
5
+ };
@@ -1,4 +1,4 @@
1
1
  import type { PluginOption } from 'vite';
2
2
  export default function index(): PluginOption;
3
- export declare function generateGlobalCode(): Promise<string>;
3
+ export declare function generateCompileTimeContextCode(): Promise<string>;
4
4
  export declare function generatePagesCode(): Promise<string>;
@@ -1,22 +1,21 @@
1
1
  import { getZubyConfig } from '../../config.js';
2
2
  export default function index() {
3
3
  return {
4
- name: "zuby-global",
4
+ name: "compile-time-context",
5
5
  enforce: 'pre',
6
6
  async transform(code, id) {
7
7
  if ((id.includes("node_modules") && !id.includes("zuby")) ||
8
- !/\.(js|ts|jsx|tsx|mjs|cjs)(\?.+)?$/.test(id) ||
9
- !id.includes("app")) {
8
+ !/\.(js|ts|jsx|tsx|mjs|cjs)(\?.+)?$/.test(id)) {
10
9
  return;
11
10
  }
12
- const newCode = await generateGlobalCode();
13
- return newCode + code;
11
+ const contextCode = await generateCompileTimeContextCode();
12
+ return contextCode + code;
14
13
  }
15
14
  };
16
15
  }
17
- export async function generateGlobalCode() {
18
- return `globalThis.Zuby = {
19
- ...(globalThis.Zuby || {}),
16
+ export async function generateCompileTimeContextCode() {
17
+ return `globalThis.ZubyRawContext = {
18
+ ...(globalThis.ZubyRawContext || {}),
20
19
  pages: ${await generatePagesCode()},
21
20
  };`;
22
21
  }
@@ -1,9 +1,10 @@
1
1
  import { createServer } from 'vite';
2
- import { getStaticPages } from '../../pages.js';
2
+ import { findMatchingPage, getStaticPages } from '../../pages/index.js';
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
4
4
  import { dirname, join, resolve } from 'path';
5
- import { prerender } from './prerender.js';
6
5
  import { getZubyConfig } from '../../config.js';
6
+ import { getContext } from '../../context/index.js';
7
+ import { prerender } from './prerender.js';
7
8
  /**
8
9
  * This is internal plugin
9
10
  * that pre-renders the pages during the build.
@@ -17,7 +18,8 @@ export default function prerenderPlugin() {
17
18
  const zubyConfig = await getZubyConfig();
18
19
  const srcDir = zubyConfig.srcDir || '';
19
20
  const outDir = zubyConfig.outDir || '';
20
- const staticPages = await getStaticPages();
21
+ const pages = zubyConfig.pages || [];
22
+ const staticPages = await getStaticPages(pages);
21
23
  const prerenderPaths = [
22
24
  ...(zubyConfig.prerenderPaths || []),
23
25
  ...staticPages.map(page => page.path)
@@ -28,22 +30,29 @@ export default function prerenderPlugin() {
28
30
  ...(zubyConfig.vite?.server || {}),
29
31
  middlewareMode: true
30
32
  },
33
+ optimizeDeps: {
34
+ include: ["zuby"]
35
+ }
31
36
  });
32
37
  await viteServer.close();
33
- const appPath = resolve(srcDir, 'src', 'app.tsx');
38
+ const appPath = resolve(srcDir, 'src', 'main.tsx');
34
39
  const templatePath = resolve(outDir, 'index.html');
35
40
  const template = readFileSync(templatePath, 'utf-8');
41
+ const appModule = await viteServer.ssrLoadModule(appPath);
42
+ const app = appModule.default || appModule;
36
43
  for (const path of prerenderPaths) {
37
- const Zuby = globalThis.Zuby || {};
38
- Zuby.currentPath = path;
39
- globalThis.Zuby = Zuby;
40
- const appModule = await viteServer.ssrLoadModule(appPath);
41
- const app = appModule.default || appModule;
44
+ const page = findMatchingPage(pages, path);
45
+ const zubyContext = getContext().update({
46
+ currentPath: path,
47
+ title: undefined
48
+ });
42
49
  const { html } = await prerender(app(), {
43
- maxDepth: 100
50
+ maxDepth: 50,
44
51
  });
45
- //console.log("renderedPage", renderedPage)
46
- const renderedPageHtml = template.replace(/(<div id="app">)(<\/div>)/, `$1${html ?? ''}$2`);
52
+ const { title } = zubyContext;
53
+ const renderedPageHtml = template
54
+ .replace(/(<div id="app">)(<\/div>)/, `$1${html ?? ''}$2`)
55
+ .replace(/(<title>)(<\/title>)/, `$1${title ?? ''}$2`);
47
56
  const target = join(outDir, path, "index.html");
48
57
  const directory = dirname(target);
49
58
  if (!existsSync(directory)) {
@@ -10,6 +10,6 @@ interface PrerenderOptions {
10
10
  * @param {object} [options.props] Additional props to merge into the root JSX element
11
11
  */
12
12
  export declare function prerender(vnode: ReturnType<typeof h>, options?: PrerenderOptions): Promise<{
13
- html: any;
13
+ html: string;
14
14
  }>;
15
15
  export {};
@@ -20,32 +20,29 @@ export async function prerender(vnode, options) {
20
20
  const props = options.props;
21
21
  let tries = 0;
22
22
  if (typeof vnode === 'function') {
23
- // @ts-ignore
24
23
  vnode = h(vnode, props);
25
24
  }
26
25
  else if (props) {
27
26
  vnode = cloneElement(vnode, props);
28
27
  }
29
- const render = () => {
30
- if (++tries > maxDepth)
31
- return;
28
+ const render = async () => {
29
+ if (++tries > maxDepth) {
30
+ console.warn(`WARNING: Maximum depth of ${maxDepth} of lazy components exceeded while prerendering page.`);
31
+ console.warn(`This may indicate a circular dependency in your code.`);
32
+ return '';
33
+ }
32
34
  try {
33
35
  // @ts-ignore
34
36
  return renderToString(vnode);
35
37
  }
36
38
  catch (e) {
37
- // @ts-ignore
38
- if (e && e.then)
39
- return e.then(render);
39
+ if (e instanceof Promise) {
40
+ await e;
41
+ return render();
42
+ }
40
43
  throw e;
41
44
  }
42
45
  };
43
- try {
44
- let html = await render();
45
- return { html };
46
- }
47
- finally {
48
- // @ts-ignore
49
- vnodeHook = null;
50
- }
46
+ let html = await render();
47
+ return { html };
51
48
  }
package/types.d.ts CHANGED
@@ -1,17 +1,6 @@
1
1
  import { UserConfig as ViteUserConfig } from 'vite';
2
2
  import type { Plugin as VitePlugin } from "vite";
3
- export interface ZubyGlobalContext {
4
- /**
5
- * The array with pages.
6
- * @private
7
- */
8
- pages?: Page[];
9
- /**
10
- * The current path of the page in both client and server env.
11
- * @private
12
- */
13
- currentPath?: string;
14
- }
3
+ import { Page } from './pages/types.js';
15
4
  export interface ZubyConfig {
16
5
  /**
17
6
  * The relative path to directory were your zuby project is located in.
@@ -123,17 +112,6 @@ export declare const MODES: {
123
112
  production: string;
124
113
  };
125
114
  export type Mode = (typeof MODES)[keyof typeof MODES];
126
- export declare const PATH_TYPES: {
127
- static: string;
128
- dynamic: string;
129
- };
130
- export type PathType = (typeof PATH_TYPES)[keyof typeof PATH_TYPES];
131
- export interface Page {
132
- path: string;
133
- pathType: PathType;
134
- filename: string;
135
- weight: number;
136
- }
137
115
  export interface ZubyPlugin extends VitePlugin {
138
116
  }
139
117
  export type LogType = 'error' | 'warn' | 'info';
package/types.js CHANGED
@@ -3,11 +3,6 @@ export const MODES = {
3
3
  development: 'development',
4
4
  production: 'production',
5
5
  };
6
- // Pages
7
- export const PATH_TYPES = {
8
- static: 'static',
9
- dynamic: 'dynamic',
10
- };
11
6
  export const LogLevels = {
12
7
  silent: 0,
13
8
  error: 1,
@@ -1 +0,0 @@
1
- export default function Hello(): import("preact").JSX.Element;
@@ -1,6 +0,0 @@
1
- import { jsx as _jsx } from "preact/jsx-runtime";
2
- import { useState as Us2 } from 'preact/hooks';
3
- export default function Hello() {
4
- const [count, setCount] = Us2(0);
5
- return (_jsx("h1", { children: "Hello, world!" }));
6
- }
@@ -1 +0,0 @@
1
- export default function useTitle(): (string | ((title: string) => void) | undefined)[];
package/hooks/useTitle.js DELETED
@@ -1,19 +0,0 @@
1
- import { PAGE_TITLE_ENV_VAR } from "../constants.js";
2
- export default function useTitle() {
3
- const getTitle = () => {
4
- if (typeof document !== 'undefined' && document.title) {
5
- return document.title;
6
- }
7
- return process?.env[PAGE_TITLE_ENV_VAR];
8
- };
9
- const setTitle = (title) => {
10
- if (typeof document !== 'undefined' && document.title) {
11
- document.title = title;
12
- }
13
- process.env[PAGE_TITLE_ENV_VAR] = title;
14
- };
15
- return [
16
- getTitle(),
17
- setTitle
18
- ];
19
- }