zuby 1.0.32 → 1.0.34

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/commands/build.js CHANGED
@@ -80,7 +80,8 @@ export default async function build(options) {
80
80
  }));
81
81
  logger?.info(`${chalk.bgYellow.bold.whiteBright(` Step 4/4 `)} ${chalk.gray(`tracing dependencies...`)}`);
82
82
  logger?.info(`Tracing production dependencies...`);
83
- const { fileList } = await nodeFileTrace([normalizePath(join(outDir, 'server', 'entry.js'))], {
83
+ const serverFiles = await glob(`${normalizePath(outDir)}/server/**/*.js`);
84
+ const { fileList } = await nodeFileTrace(serverFiles, {
84
85
  processCwd: process.cwd(),
85
86
  ignore: (path) => {
86
87
  return path.startsWith(outDir);
@@ -8,7 +8,7 @@ export declare class ZubyContext {
8
8
  errors?: import("../templates/types.js").LazyTemplate[] | undefined;
9
9
  layouts?: import("../templates/types.js").LazyTemplate[] | undefined;
10
10
  innerLayouts?: import("../templates/types.js").LazyTemplate[] | undefined;
11
- apis?: import("../templates/types.js").LazyTemplate[] | undefined;
11
+ handlers?: import("../templates/types.js").LazyTemplate[] | undefined;
12
12
  } | undefined;
13
13
  get site(): string | undefined;
14
14
  get generator(): string | undefined;
@@ -10,7 +10,7 @@ export interface ZubyRawContext {
10
10
  errors?: LazyTemplate[];
11
11
  layouts?: LazyTemplate[];
12
12
  innerLayouts?: LazyTemplate[];
13
- apis?: LazyTemplate[];
13
+ handlers?: LazyTemplate[];
14
14
  };
15
15
  /**
16
16
  * The render module from JsxProvider.
@@ -1,29 +1,8 @@
1
1
  {
2
+ "extends": "zuby/tsconfigs/default",
2
3
  "compilerOptions": {
3
- "target": "ES2020",
4
- "useDefineForClassFields": true,
5
- "module": "ESNext",
6
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
- "skipLibCheck": true,
8
- "types": ["vite/client", "<jsxProviderName>"],
9
-
10
- /* Bundler mode */
11
- "moduleResolution": "bundler",
12
- "allowImportingTsExtensions": true,
13
- "resolveJsonModule": true,
14
- "isolatedModules": true,
15
- "noEmit": true,
16
4
  "jsx": "react-jsx",
17
- "jsxImportSource": "<jsxProviderName>",
18
-
19
- /* Linting */
20
- "strict": true,
21
- "noUnusedLocals": true,
22
- "noUnusedParameters": true,
23
- "noFallthroughCasesInSwitch": true,
24
-
25
- "composite": true,
26
- "allowSyntheticDefaultImports": true
5
+ "jsxImportSource": "<jsxProviderName>"
27
6
  },
28
7
  "include": ["./**/*", "zuby.config.ts"]
29
8
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "zuby",
3
- "version": "1.0.32",
3
+ "version": "1.0.34",
4
4
  "description": "Zuby.js is framework for building SPA apps using Vite",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "scripts": {
8
8
  "release": "cd ./dist && npm publish --access public && cd ..",
9
9
  "build": "rm -rf dist/ && tsc && cp -rf package.json README.md src/examples dist/ && npm run bundle-server",
10
- "bundle-server": "esbuild src/server/index.ts --bundle --platform=node --format=esm --outfile=dist/server/index.js",
10
+ "bundle-server": "esbuild src/server/index.ts --bundle --minify=false --platform=node --format=esm --outfile=dist/server/index.js",
11
11
  "watch-build": "npm run build && tsc --watch",
12
12
  "push-build": "npm run build && cd dist && yalc push --force && cd ..",
13
13
  "test": "exit 0"
@@ -5,13 +5,17 @@ export declare class ZubyPageContext {
5
5
  protected _request?: Request;
6
6
  protected _params: Record<string, string>;
7
7
  protected _clientAddress?: string;
8
+ protected _statusCode: number;
9
+ protected _props: Record<string, any>;
8
10
  protected _zubyContext: ZubyContext;
9
11
  constructor(options: {
10
12
  url?: URL;
11
13
  title?: string;
12
14
  request?: Request;
13
- response?: Response;
14
15
  clientAddress?: string;
16
+ statusCode?: number;
17
+ props?: Record<string, any>;
18
+ zubyContext?: ZubyContext;
15
19
  });
16
20
  get url(): URL;
17
21
  get title(): string;
@@ -30,6 +34,10 @@ export declare class ZubyPageContext {
30
34
  errors?: import("../templates/types.js").LazyTemplate[] | undefined;
31
35
  layouts?: import("../templates/types.js").LazyTemplate[] | undefined;
32
36
  innerLayouts?: import("../templates/types.js").LazyTemplate[] | undefined;
33
- apis?: import("../templates/types.js").LazyTemplate[] | undefined;
37
+ handlers?: import("../templates/types.js").LazyTemplate[] | undefined;
34
38
  } | undefined;
39
+ get props(): Record<string, any>;
40
+ set props(value: Record<string, any>);
41
+ get statusCode(): number | undefined;
42
+ set statusCode(value: number | undefined);
35
43
  }
@@ -6,7 +6,9 @@ export class ZubyPageContext {
6
6
  this._request = options?.request;
7
7
  this._params = {};
8
8
  this._clientAddress = options?.clientAddress;
9
- this._zubyContext = getContext();
9
+ this._statusCode = options?.statusCode || 200;
10
+ this._props = options?.props || {};
11
+ this._zubyContext = options?.zubyContext || getContext();
10
12
  }
11
13
  get url() {
12
14
  if (this.isBrowserEnv) {
@@ -53,4 +55,19 @@ export class ZubyPageContext {
53
55
  get templates() {
54
56
  return this._zubyContext.templates;
55
57
  }
58
+ get props() {
59
+ return this._props;
60
+ }
61
+ set props(value) {
62
+ this._props = value || {};
63
+ }
64
+ get statusCode() {
65
+ return this._statusCode;
66
+ }
67
+ set statusCode(value) {
68
+ if (!value || !Number.isInteger(value) || value < 100 || value > 599) {
69
+ throw new Error(`Invalid status code: ${value}`);
70
+ }
71
+ this._statusCode = value;
72
+ }
56
73
  }
@@ -2,7 +2,7 @@ import { getZubyInternalConfig } from '../../config.js';
2
2
  import { relative } from 'path';
3
3
  import { normalizePath } from '../../utils/pathUtils.js';
4
4
  import { getZubyPackageConfig } from '../../packageConfig.js';
5
- import { getApis, getApps, getErrors, getInnerLayouts, getLayouts, getPages, getTemplates, } from '../../templates/index.js';
5
+ import { getApps, getErrors, getHandlers, getInnerLayouts, getLayouts, getPages, getTemplates, } from '../../templates/index.js';
6
6
  let viteConfig;
7
7
  export default function index() {
8
8
  return {
@@ -39,7 +39,7 @@ export async function generateTemplatesCode() {
39
39
  const layouts = await getLayouts(templates);
40
40
  const innerLayouts = await getInnerLayouts(templates);
41
41
  const errors = await getErrors(templates);
42
- const apis = await getApis(templates);
42
+ const handlers = await getHandlers(templates);
43
43
  const pagesCode = await Promise.all(pages.map(generateTemplateCode) || []);
44
44
  const appsCode = await Promise.all(apps.map(generateTemplateCode) || []);
45
45
  const errorsCode = await Promise.all(errors.map(generateTemplateCode) || []);
@@ -49,8 +49,8 @@ export async function generateTemplatesCode() {
49
49
  const innerLayoutsCode = viteConfig?.build.ssr
50
50
  ? await Promise.all(innerLayouts.map(generateTemplateCode) || [])
51
51
  : [];
52
- const apisCode = viteConfig?.build.ssr
53
- ? await Promise.all(apis.map(generateTemplateCode) || [])
52
+ const handlersCode = viteConfig?.build.ssr
53
+ ? await Promise.all(handlers.map(generateTemplateCode) || [])
54
54
  : [];
55
55
  return `{
56
56
  pages: [${pagesCode.join(',')}],
@@ -58,7 +58,7 @@ export async function generateTemplatesCode() {
58
58
  errors: [${errorsCode.join(',')}],
59
59
  layouts: [${layoutsCode.join(',')}],
60
60
  innerLayouts: [${innerLayoutsCode.join(',')}],
61
- apis: [${apisCode.join(',')}],
61
+ handlers: [${handlersCode.join(',')}],
62
62
  }`;
63
63
  }
64
64
  export async function generateTemplateCode(template) {
@@ -1,14 +1,10 @@
1
- import { findMatchingTemplate } from '../../templates/index.js';
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
3
- import { basename, dirname, join, resolve } from 'path';
1
+ import { existsSync, mkdirSync } from 'fs';
2
+ import { dirname, join } from 'path';
4
3
  import { getZubyInternalConfig } from '../../config.js';
5
- import { getContext } from '../../context/index.js';
6
4
  import chalk from 'chalk';
7
- import { normalizePath } from '../../utils/pathUtils.js';
8
- import { glob } from 'glob';
9
- import { ZubyPageContext } from '../../pageContext/index.js';
10
- import { PATH_TYPES } from '../../templates/types.js';
11
5
  import { performance } from 'node:perf_hooks';
6
+ import ZubyRenderer from '../../server/zubyRenderer.js';
7
+ import { writeFile } from 'fs/promises';
12
8
  /**
13
9
  * This is internal plugin
14
10
  * that pre-renders the pages during the build.
@@ -28,102 +24,37 @@ export default function prerenderPlugin() {
28
24
  return;
29
25
  }
30
26
  const startTime = performance.now();
31
- const { outDir, customLogger, prerenderPaths, jsx } = await getZubyInternalConfig();
27
+ const { outDir, customLogger, prerenderPaths, site } = await getZubyInternalConfig();
32
28
  customLogger?.info(`${chalk.bgYellow.bold.whiteBright(` Step 3/4 `)} ${chalk.gray(`pre-rendering pages...`)}`);
33
- const entryPath = normalizePath(resolve(outDir, 'server', 'entry.js'));
34
- const entryModule = await import(`file:///${entryPath}`);
35
- const entry = entryModule.default || entryModule;
36
- const entryClientJs = (await glob(`${outDir}/client/chunks/entry-*.js`)).pop();
37
- const entryClientCss = (await glob(`${outDir}/client/chunks/entry-*.css`)).pop();
38
- const ssrManifest = JSON.parse(readFileSync(resolve(outDir, 'server', 'ssr-manifest.json'), 'utf-8'));
39
- const zubyContext = getContext();
40
- const paths = [
41
- ...(prerenderPaths || []),
42
- ...(zubyContext.templates?.pages
43
- ?.filter(({ pathType }) => pathType === PATH_TYPES.static)
44
- .map(page => page.path) || []),
45
- ];
46
- for (const path of paths) {
47
- const zubyPageContext = new ZubyPageContext({
48
- url: new URL(path, zubyContext.site || 'http://localhost'),
49
- });
50
- if (!zubyContext.renderToString) {
51
- throw new Error(`zubyContext.renderToString is undefined`);
52
- }
53
- if (!zubyContext.renderToStream) {
54
- throw new Error(`zubyContext.renderToStream is undefined`);
55
- }
56
- // Page
57
- const { pages = [] } = zubyContext.templates || {};
58
- const { template: page } = findMatchingTemplate(pages, path);
59
- // App
60
- const { apps = [] } = zubyContext.templates || {};
61
- const { template: app } = findMatchingTemplate(apps, path);
62
- // Nothing to pre-render
63
- if (!page || !app)
64
- continue;
65
- // Load layout template component
66
- const { layouts = [] } = zubyContext.templates || {};
67
- const { template: layout } = findMatchingTemplate(layouts, path);
68
- if (!layout) {
69
- throw new Error(`No layout found for path: ${path}`);
70
- }
71
- const layoutModule = await layout?.component();
72
- const layoutComponent = layoutModule.default || layoutModule;
73
- // Load innerLayout template component
74
- const { innerLayouts = [] } = zubyContext.templates || {};
75
- const { template: innerLayout } = findMatchingTemplate(innerLayouts, path);
76
- if (!layout) {
77
- throw new Error(`No innerLayout found for path: ${path}`);
78
- }
79
- const innerLayoutModule = await innerLayout?.component();
80
- const innerLayoutComponent = innerLayoutModule.default || innerLayoutModule;
81
- // Render the app first
82
- let entryHtml = (await zubyContext.renderToString(entry({
83
- context: zubyPageContext,
84
- }))) || '';
85
- // Composition of the page and the layout
86
- const layoutChildren = layoutComponent({
87
- children: innerLayoutComponent({
88
- innerHtml: entryHtml,
89
- }),
90
- context: zubyPageContext,
91
- });
92
- // Render the layout
93
- let html = (await zubyContext.renderToString(layoutChildren)) || '';
94
- const staticImports = [
95
- ...(ssrManifest[app.filename] || []),
96
- ...(ssrManifest[page.filename] || []),
97
- ];
98
- const cssImports = staticImports.filter((imp) => imp.endsWith('.css'));
99
- const jsImports = [];
100
- // Entry css
101
- if (entryClientCss) {
102
- cssImports.unshift(`/chunks/${basename(entryClientCss)}`);
103
- }
104
- // Entry js
105
- if (entryClientJs) {
106
- jsImports.unshift(`/chunks/${basename(entryClientJs)}`);
107
- }
108
- // Insert document type
109
- html = '<!DOCTYPE html>' + html;
110
- // Insert client CSS imports
111
- cssImports.forEach((imp) => {
112
- html = html.replace(/(<\/head>)/, `<link rel='stylesheet' href="${imp}"/>$1`);
113
- });
114
- // Insert client JS imports
115
- jsImports.forEach((imp) => {
116
- html = html.replace(/(<\/head>)/, `<script type="module" src="${imp}" defer></script>$1`);
117
- });
118
- const target = join(outDir, 'client', path, 'index.html');
119
- const directory = dirname(target);
120
- if (!existsSync(directory)) {
121
- mkdirSync(directory, {
29
+ const zubyRenderer = new ZubyRenderer(outDir);
30
+ await zubyRenderer.init();
31
+ const reqBaseUrl = `http://${site || 'localhost'}`;
32
+ const reqOptions = {
33
+ headers: {
34
+ 'user-agent': 'zuby-prerender',
35
+ },
36
+ };
37
+ for (const path of prerenderPaths) {
38
+ const pageReq = new Request(new URL(path, reqBaseUrl), reqOptions);
39
+ const propsReq = new Request(new URL(`/_props${path}`, reqBaseUrl), reqOptions);
40
+ const [pageRes, propsRes] = await Promise.all([
41
+ zubyRenderer.render(pageReq),
42
+ zubyRenderer.render(propsReq),
43
+ ]);
44
+ const [page, props] = await Promise.all([pageRes.text(), propsRes.text()]);
45
+ const pageTarget = join(outDir, 'client', path, 'index.html');
46
+ const propsTarget = join(outDir, 'client', '_props', path, 'index.json');
47
+ const pageDir = dirname(pageTarget);
48
+ const propsDir = dirname(propsTarget);
49
+ [pageDir, propsDir].forEach(dir => {
50
+ if (existsSync(dir))
51
+ return;
52
+ mkdirSync(dir, {
122
53
  recursive: true,
123
54
  });
124
- }
125
- writeFileSync(target, html);
126
- console.log('[prerender] Pre-rendered page: ' + path);
55
+ });
56
+ await Promise.all([writeFile(pageTarget, page), writeFile(propsTarget, props)]);
57
+ console.log('[prerender] Prerendered page: ' + path);
127
58
  }
128
59
  const finishTime = performance.now();
129
60
  customLogger?.info(chalk.green(`✓ pre-rendered in ${Math.ceil(finishTime - startTime)}ms`));