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.
- package/components/Router/index.js +11 -10
- package/config.d.ts +17 -0
- package/config.js +66 -2
- package/context/index.d.ts +12 -0
- package/context/index.js +37 -0
- package/context/types.d.ts +15 -0
- package/context/types.js +1 -0
- package/defineConfig.d.ts +8 -9
- package/defineConfig.js +8 -52
- package/hooks/useZuby.d.ts +7 -0
- package/hooks/useZuby.js +4 -0
- package/package.json +1 -2
- package/{pages.d.ts → pages/index.d.ts} +9 -2
- package/{pages.js → pages/index.js} +31 -7
- package/pages/pathUtils.d.ts +4 -0
- package/pages/pathUtils.js +34 -0
- package/pages/types.d.ts +13 -0
- package/pages/types.js +5 -0
- package/plugins/{zubyGlobalPlugin → contextPlugin}/index.d.ts +1 -1
- package/plugins/{zubyGlobalPlugin → contextPlugin}/index.js +7 -8
- package/plugins/prerenderPlugin/index.js +21 -12
- package/plugins/prerenderPlugin/prerender.d.ts +1 -1
- package/plugins/prerenderPlugin/prerender.js +12 -15
- package/types.d.ts +1 -23
- package/types.js +0 -5
- package/components/Hello/index.d.ts +0 -1
- package/components/Hello/index.js +0 -6
- package/hooks/useTitle.d.ts +0 -1
- package/hooks/useTitle.js +0 -19
|
@@ -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
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
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 {};
|
package/context/index.js
ADDED
|
@@ -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
|
+
}
|
package/context/types.js
ADDED
|
@@ -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
|
|
4
|
-
*
|
|
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
|
|
9
|
-
*
|
|
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
|
+
};
|
package/hooks/useZuby.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zuby",
|
|
3
|
-
"version": "1.0.
|
|
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 '
|
|
2
|
+
import { getZubyConfig } from '../config.js';
|
|
3
3
|
import { join, resolve } from 'path';
|
|
4
4
|
import { glob } from 'glob';
|
|
5
|
-
import { normalizePath } from '
|
|
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
|
-
|
|
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
|
-
|
|
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,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
|
+
};
|
package/pages/types.d.ts
ADDED
|
@@ -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
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { PluginOption } from 'vite';
|
|
2
2
|
export default function index(): PluginOption;
|
|
3
|
-
export declare function
|
|
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: "
|
|
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
|
|
13
|
-
return
|
|
11
|
+
const contextCode = await generateCompileTimeContextCode();
|
|
12
|
+
return contextCode + code;
|
|
14
13
|
}
|
|
15
14
|
};
|
|
16
15
|
}
|
|
17
|
-
export async function
|
|
18
|
-
return `globalThis.
|
|
19
|
-
...(globalThis.
|
|
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
|
|
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', '
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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:
|
|
50
|
+
maxDepth: 50,
|
|
44
51
|
});
|
|
45
|
-
|
|
46
|
-
const renderedPageHtml = template
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
return
|
|
39
|
+
if (e instanceof Promise) {
|
|
40
|
+
await e;
|
|
41
|
+
return render();
|
|
42
|
+
}
|
|
40
43
|
throw e;
|
|
41
44
|
}
|
|
42
45
|
};
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function Hello(): import("preact").JSX.Element;
|
package/hooks/useTitle.d.ts
DELETED
|
@@ -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
|
-
}
|