vite-react-ssg 0.0.2 → 0.1.0

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/README.md CHANGED
@@ -16,8 +16,9 @@ Static-site generation for React on Vite.
16
16
  // package.json
17
17
  {
18
18
  "scripts": {
19
- "dev": "vite",
19
+ - "dev": "vite",
20
20
  - "build": "vite build"
21
+ + "dev": "vite-react-ssg dev",
21
22
  + "build": "vite-react-ssg build"
22
23
 
23
24
  // OR if you want to use another vite config file
@@ -81,6 +82,94 @@ export const routes: RouteRecord[] = [
81
82
  ]
82
83
  ```
83
84
 
85
+ ## `<ClientOnly/>`
86
+
87
+ If you need to render some component in browser only, you can wrap your component with `<ClientOnly>`.
88
+
89
+ ```tsx
90
+ import { ClientOnly } from 'vite-react-ssg'
91
+
92
+ function MyComponent() {
93
+ return (
94
+ <ClientOnly>
95
+ {() => {
96
+ return <div>{window.location.href}</div>
97
+ }}
98
+ </ClientOnly>
99
+ )
100
+ }
101
+ ```
102
+
103
+ > It's important that the children of <ClientOnly> is not a JSX element, but a function that returns an element.
104
+ > Because React will try to render children, and may use the client's API on the server.
105
+
106
+ ## Document head
107
+
108
+ You can use `<Head/>` to manage all of your changes to the document head. It takes plain HTML tags and outputs plain HTML tags. It is a wrapper around [React Helmet](https://github.com/nfl/react-helmet).
109
+
110
+ ```tsx
111
+ import { Head } from 'vite-react-ssg'
112
+
113
+ const MyHead = () => (
114
+ <Head>
115
+ <meta property="og:description" content="My custom description" />
116
+ <meta charSet="utf-8" />
117
+ <title>My Title</title>
118
+ <link rel="canonical" href="http://mysite.com/example" />
119
+ </Head>
120
+ )
121
+ ```
122
+
123
+ Nested or latter components will override duplicate usages:
124
+
125
+ ```tsx
126
+ import { Head } from 'vite-react-ssg'
127
+
128
+ const MyHead = () => (
129
+ <parent>
130
+ <Head>
131
+ <title>My Title</title>
132
+ <meta name="description" content="Helmet application" />
133
+ </Head>
134
+ <child>
135
+ <Head>
136
+ <title>Nested Title</title>
137
+ <meta name="description" content="Nested component" />
138
+ </Head>
139
+ </child>
140
+ </parent>
141
+ )
142
+ ```
143
+
144
+ Outputs:
145
+ ```html
146
+ <head>
147
+ <title>Nested Title</title>
148
+ <meta name="description" content="Nested component" />
149
+ </head>
150
+ ```
151
+
152
+ ### Reactive head
153
+
154
+ ```tsx
155
+ import { useState } from 'react'
156
+ import { Head } from 'vite-react-ssg'
157
+
158
+ export default function MyHead() {
159
+ const [state, setState] = useState(false)
160
+
161
+ return (
162
+ <Head>
163
+ <meta charSet="UTF-8" />
164
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
165
+ <title>head test {state ? 'A' : 'B'}</title>
166
+ {/* You can also set the 'body' attributes here */}
167
+ <body className={`body-class-in-head-${state ? 'a' : 'b'}`} />
168
+ </Head>
169
+ )
170
+ }
171
+ ```
172
+
84
173
  ## Critical CSS
85
174
 
86
175
  Vite SSG has built-in support for generating [Critical CSS](https://web.dev/extract-critical-css/) inlined in the HTML via the [`critters`](https://github.com/GoogleChromeLabs/critters) package.
@@ -159,12 +248,44 @@ export default {
159
248
  }
160
249
  ```
161
250
 
251
+ ## Use CSR in development environment
252
+
253
+ If you want to use CSR during development, just:
254
+ ```ts
255
+ // src/main.ts
256
+ import { ViteReactSSG } from 'vite-react-ssg'
257
+ import routes from './App.tsx'
258
+
259
+ export const createRoot = ViteReactSSG(
260
+ { routes },
261
+ ({ router, routes, isClient, initialState }) => {
262
+ // do something.
263
+ },
264
+ {
265
+ // Default value is `true`
266
+ ssrWhenDev: false
267
+ }
268
+ )
269
+ ```
270
+
271
+ ```diff
272
+ // package.json
273
+ {
274
+ "scripts": {
275
+ - "dev": "vite-react-ssg dev",
276
+ + "dev": "vite",
277
+ "build": "vite-react-ssg build"
278
+ }
279
+ }
280
+ ```
281
+ Then, you can start the application with CSR in the development environment.
282
+
162
283
  ## Roadmap
163
284
 
164
285
  - [x] Preload assets
165
- - [ ] SSR under dev
286
+ - [x] Document head
287
+ - [x] SSR in dev environment
166
288
  - [ ] Initial State
167
- - [ ] Document head
168
289
  - [ ] More Client components, such as `<ClientOnly />`
169
290
 
170
291
  ## License
package/dist/index.cjs CHANGED
@@ -19,10 +19,34 @@ function documentReady(_passThrough) {
19
19
  return Promise.resolve(_passThrough);
20
20
  }
21
21
 
22
+ function useIsClient() {
23
+ const [isBrowser, setIsBrowser] = React.useState(false);
24
+ React.useEffect(() => {
25
+ setIsBrowser(true);
26
+ }, []);
27
+ return isBrowser;
28
+ }
29
+
30
+ function ClientOnly({
31
+ children,
32
+ fallback
33
+ }) {
34
+ const isBrowser = useIsClient();
35
+ if (isBrowser) {
36
+ if (typeof children !== "function" && process.env.NODE_ENV === "development") {
37
+ throw new Error(`vite-react-ssg error: The children of <ClientOnly> must be a "render function", e.g. <ClientOnly>{() => <span>{window.location.href}</span>}</ClientOnly>.
38
+ Current type: ${React.isValidElement(children) ? "React element" : typeof children}`);
39
+ }
40
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, children?.());
41
+ }
42
+ return fallback ?? null;
43
+ }
44
+
22
45
  function ViteReactSSG(routerOptions, fn, options = {}) {
23
46
  const {
24
47
  transformState,
25
- rootContainer = "#root"
48
+ rootContainer = "#root",
49
+ ssrWhenDev = true
26
50
  } = options;
27
51
  const isClient = typeof window !== "undefined";
28
52
  async function createRoot(client = false, routePath) {
@@ -64,11 +88,22 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
64
88
  (async () => {
65
89
  const container = typeof rootContainer === "string" ? document.querySelector(rootContainer) : rootContainer;
66
90
  const { router } = await createRoot(true);
67
- client.hydrateRoot(container, /* @__PURE__ */ React__default.createElement(reactHelmetAsync.HelmetProvider, null, /* @__PURE__ */ React__default.createElement(SiteMetadataDefaults.SiteMetadataDefaults, null), /* @__PURE__ */ React__default.createElement(reactRouterDom.RouterProvider, { router })));
91
+ const app = /* @__PURE__ */ React__default.createElement(reactHelmetAsync.HelmetProvider, null, /* @__PURE__ */ React__default.createElement(SiteMetadataDefaults.SiteMetadataDefaults, null), /* @__PURE__ */ React__default.createElement(reactRouterDom.RouterProvider, { router }));
92
+ if (!ssrWhenDev && undefined.DEV) {
93
+ const root = client.createRoot(container);
94
+ React__default.startTransition(() => {
95
+ root.render(app);
96
+ });
97
+ } else {
98
+ React__default.startTransition(() => {
99
+ client.hydrateRoot(container, app);
100
+ });
101
+ }
68
102
  })();
69
103
  }
70
104
  return createRoot;
71
105
  }
72
106
 
73
107
  exports.Head = SiteMetadataDefaults.Head;
108
+ exports.ClientOnly = ClientOnly;
74
109
  exports.ViteReactSSG = ViteReactSSG;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './types-82f34756.js';
2
- export { I as IndexRouteRecord, N as NonIndexRouteRecord, c as RouteRecord, b as ViteReactSSGOptions } from './types-82f34756.js';
1
+ import { R as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './types-25b96ed0.js';
2
+ export { I as IndexRouteRecord, N as NonIndexRouteRecord, c as RouteRecord, b as ViteReactSSGOptions } from './types-25b96ed0.js';
3
3
  import { ReactNode } from 'react';
4
4
  import { HelmetProps } from 'react-helmet-async';
5
5
  import 'critters';
@@ -10,6 +10,12 @@ type Props = HelmetProps & {
10
10
  };
11
11
  declare function Head(props: Props): JSX.Element;
12
12
 
13
+ interface ClientOnlyProps {
14
+ children?: () => JSX.Element;
15
+ fallback?: JSX.Element;
16
+ }
17
+ declare function ClientOnly({ children, fallback, }: ClientOnlyProps): JSX.Element | null;
18
+
13
19
  declare function ViteReactSSG(routerOptions: RouterOptions, fn?: (context: ViteReactSSGContext<true>) => Promise<void> | void, options?: ViteReactSSGClientOptions): (client?: boolean, routePath?: string) => Promise<ViteReactSSGContext<true>>;
14
20
 
15
- export { Head, RouterOptions, ViteReactSSG, ViteReactSSGClientOptions, ViteReactSSGContext };
21
+ export { ClientOnly, Head, RouterOptions, ViteReactSSG, ViteReactSSGClientOptions, ViteReactSSGContext };
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import React from 'react';
2
- import { hydrateRoot } from 'react-dom/client';
1
+ import React, { useState, useEffect, isValidElement } from 'react';
2
+ import { createRoot, hydrateRoot } from 'react-dom/client';
3
3
  import { HelmetProvider } from 'react-helmet-async';
4
- import { RouterProvider, createBrowserRouter } from 'react-router-dom';
5
- import { S as SiteMetadataDefaults, d as deserializeState } from './shared/vite-react-ssg.9d005d5e.mjs';
4
+ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
5
+ import { d as deserializeState, S as SiteMetadataDefaults } from './shared/vite-react-ssg.9d005d5e.mjs';
6
6
  export { H as Head } from './shared/vite-react-ssg.9d005d5e.mjs';
7
7
 
8
8
  function documentReady(_passThrough) {
@@ -14,13 +14,37 @@ function documentReady(_passThrough) {
14
14
  return Promise.resolve(_passThrough);
15
15
  }
16
16
 
17
+ function useIsClient() {
18
+ const [isBrowser, setIsBrowser] = useState(false);
19
+ useEffect(() => {
20
+ setIsBrowser(true);
21
+ }, []);
22
+ return isBrowser;
23
+ }
24
+
25
+ function ClientOnly({
26
+ children,
27
+ fallback
28
+ }) {
29
+ const isBrowser = useIsClient();
30
+ if (isBrowser) {
31
+ if (typeof children !== "function" && process.env.NODE_ENV === "development") {
32
+ throw new Error(`vite-react-ssg error: The children of <ClientOnly> must be a "render function", e.g. <ClientOnly>{() => <span>{window.location.href}</span>}</ClientOnly>.
33
+ Current type: ${isValidElement(children) ? "React element" : typeof children}`);
34
+ }
35
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, children?.());
36
+ }
37
+ return fallback ?? null;
38
+ }
39
+
17
40
  function ViteReactSSG(routerOptions, fn, options = {}) {
18
41
  const {
19
42
  transformState,
20
- rootContainer = "#root"
43
+ rootContainer = "#root",
44
+ ssrWhenDev = true
21
45
  } = options;
22
46
  const isClient = typeof window !== "undefined";
23
- async function createRoot(client = false, routePath) {
47
+ async function createRoot$1(client = false, routePath) {
24
48
  const browserRouter = client ? createBrowserRouter(routerOptions.routes) : void 0;
25
49
  const appRenderCallbacks = [];
26
50
  const onSSRAppRendered = client ? () => {
@@ -58,11 +82,21 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
58
82
  if (isClient) {
59
83
  (async () => {
60
84
  const container = typeof rootContainer === "string" ? document.querySelector(rootContainer) : rootContainer;
61
- const { router } = await createRoot(true);
62
- hydrateRoot(container, /* @__PURE__ */ React.createElement(HelmetProvider, null, /* @__PURE__ */ React.createElement(SiteMetadataDefaults, null), /* @__PURE__ */ React.createElement(RouterProvider, { router })));
85
+ const { router } = await createRoot$1(true);
86
+ const app = /* @__PURE__ */ React.createElement(HelmetProvider, null, /* @__PURE__ */ React.createElement(SiteMetadataDefaults, null), /* @__PURE__ */ React.createElement(RouterProvider, { router }));
87
+ if (!ssrWhenDev && import.meta.env.DEV) {
88
+ const root = createRoot(container);
89
+ React.startTransition(() => {
90
+ root.render(app);
91
+ });
92
+ } else {
93
+ React.startTransition(() => {
94
+ hydrateRoot(container, app);
95
+ });
96
+ }
63
97
  })();
64
98
  }
65
- return createRoot;
99
+ return createRoot$1;
66
100
  }
67
101
 
68
- export { ViteReactSSG };
102
+ export { ClientOnly, ViteReactSSG };
package/dist/node/cli.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  const kolorist = require('kolorist');
4
4
  const yargs = require('yargs');
5
5
  const helpers = require('yargs/helpers');
6
- const build = require('../shared/vite-react-ssg.0e1b881e.cjs');
6
+ const dev = require('../shared/vite-react-ssg.823b31c9.cjs');
7
7
  require('node:path');
8
8
  require('node:module');
9
9
  require('fs-extra');
@@ -12,6 +12,8 @@ require('jsdom');
12
12
  require('../shared/vite-react-ssg.4ca822c0.cjs');
13
13
  require('react');
14
14
  require('react-helmet-async');
15
+ require('express');
16
+ require('node:fs');
15
17
  require('react-router-dom/server.js');
16
18
  require('node:stream');
17
19
  require('react-dom/server');
@@ -20,7 +22,7 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
20
22
 
21
23
  const yargs__default = /*#__PURE__*/_interopDefaultCompat(yargs);
22
24
 
23
- yargs__default(helpers.hideBin(process.argv)).scriptName("vite-ssg").usage("$0 [args]").command(
25
+ yargs__default(helpers.hideBin(process.argv)).scriptName("vite-react-ssg").usage("$0 [args]").command(
24
26
  "build",
25
27
  "Build SSG",
26
28
  (args) => args.option("script", {
@@ -40,11 +42,33 @@ yargs__default(helpers.hideBin(process.argv)).scriptName("vite-ssg").usage("$0 [
40
42
  }),
41
43
  async (args) => {
42
44
  const { config: configFile = void 0, ...ssgOptions } = args;
43
- await build.build(ssgOptions, { configFile });
45
+ await dev.build(ssgOptions, { configFile });
46
+ }
47
+ ).command(
48
+ "dev",
49
+ "Dev SSG",
50
+ (args) => args.option("script", {
51
+ choices: ["sync", "async", "defer", "async defer"],
52
+ describe: "Rewrites script loading timing"
53
+ }).option("mock", {
54
+ type: "boolean",
55
+ describe: "Mock browser globals (window, document, etc.) for SSG"
56
+ }).option("config", {
57
+ alias: "c",
58
+ type: "string",
59
+ describe: "The vite config file to use"
60
+ }).option("base", {
61
+ alias: "b",
62
+ type: "string",
63
+ describe: "The base path to render"
64
+ }),
65
+ async (args) => {
66
+ const { config: configFile = void 0, ...ssgOptions } = args;
67
+ await dev.dev(ssgOptions, { configFile });
44
68
  }
45
69
  ).fail((msg, err, yargs2) => {
46
70
  console.error(`
47
- ${kolorist.gray("[vite-ssg]")} ${kolorist.bold(kolorist.red("An internal error occurred."))}`);
48
- console.error(`${kolorist.gray("[vite-ssg]")} ${kolorist.reset(`Please report an issue, if none already exists: ${kolorist.underline("https://github.com/antfu/vite-ssg/issues")}`)}`);
71
+ ${kolorist.gray("[vite-react-ssg]")} ${kolorist.bold(kolorist.red("An internal error occurred."))}`);
72
+ console.error(`${kolorist.gray("[vite-react-ssg]")} ${kolorist.reset(`Please report an issue, if none already exists: ${kolorist.underline("https://github.com/daydreamer-riri/vite-react-ssg/issues")}`)}`);
49
73
  yargs2.exit(1, err);
50
74
  }).showHelpOnFail(false).help().argv;
package/dist/node/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { gray, bold, red, reset, underline } from 'kolorist';
2
2
  import yargs from 'yargs';
3
3
  import { hideBin } from 'yargs/helpers';
4
- import { b as build } from '../shared/vite-react-ssg.f66e4ba5.mjs';
4
+ import { b as build, d as dev } from '../shared/vite-react-ssg.7ee042ba.mjs';
5
5
  import 'node:path';
6
6
  import 'node:module';
7
7
  import 'fs-extra';
@@ -10,11 +10,13 @@ import 'jsdom';
10
10
  import '../shared/vite-react-ssg.9d005d5e.mjs';
11
11
  import 'react';
12
12
  import 'react-helmet-async';
13
+ import 'express';
14
+ import 'node:fs';
13
15
  import 'react-router-dom/server.js';
14
16
  import 'node:stream';
15
17
  import 'react-dom/server';
16
18
 
17
- yargs(hideBin(process.argv)).scriptName("vite-ssg").usage("$0 [args]").command(
19
+ yargs(hideBin(process.argv)).scriptName("vite-react-ssg").usage("$0 [args]").command(
18
20
  "build",
19
21
  "Build SSG",
20
22
  (args) => args.option("script", {
@@ -36,9 +38,31 @@ yargs(hideBin(process.argv)).scriptName("vite-ssg").usage("$0 [args]").command(
36
38
  const { config: configFile = void 0, ...ssgOptions } = args;
37
39
  await build(ssgOptions, { configFile });
38
40
  }
41
+ ).command(
42
+ "dev",
43
+ "Dev SSG",
44
+ (args) => args.option("script", {
45
+ choices: ["sync", "async", "defer", "async defer"],
46
+ describe: "Rewrites script loading timing"
47
+ }).option("mock", {
48
+ type: "boolean",
49
+ describe: "Mock browser globals (window, document, etc.) for SSG"
50
+ }).option("config", {
51
+ alias: "c",
52
+ type: "string",
53
+ describe: "The vite config file to use"
54
+ }).option("base", {
55
+ alias: "b",
56
+ type: "string",
57
+ describe: "The base path to render"
58
+ }),
59
+ async (args) => {
60
+ const { config: configFile = void 0, ...ssgOptions } = args;
61
+ await dev(ssgOptions, { configFile });
62
+ }
39
63
  ).fail((msg, err, yargs2) => {
40
64
  console.error(`
41
- ${gray("[vite-ssg]")} ${bold(red("An internal error occurred."))}`);
42
- console.error(`${gray("[vite-ssg]")} ${reset(`Please report an issue, if none already exists: ${underline("https://github.com/antfu/vite-ssg/issues")}`)}`);
65
+ ${gray("[vite-react-ssg]")} ${bold(red("An internal error occurred."))}`);
66
+ console.error(`${gray("[vite-react-ssg]")} ${reset(`Please report an issue, if none already exists: ${underline("https://github.com/daydreamer-riri/vite-react-ssg/issues")}`)}`);
43
67
  yargs2.exit(1, err);
44
68
  }).showHelpOnFail(false).help().argv;
package/dist/node.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const build = require('./shared/vite-react-ssg.0e1b881e.cjs');
3
+ const dev = require('./shared/vite-react-ssg.823b31c9.cjs');
4
4
  require('node:path');
5
5
  require('node:module');
6
6
  require('kolorist');
@@ -10,10 +10,13 @@ require('jsdom');
10
10
  require('./shared/vite-react-ssg.4ca822c0.cjs');
11
11
  require('react');
12
12
  require('react-helmet-async');
13
+ require('express');
14
+ require('node:fs');
13
15
  require('react-router-dom/server.js');
14
16
  require('node:stream');
15
17
  require('react-dom/server');
16
18
 
17
19
 
18
20
 
19
- exports.build = build.build;
21
+ exports.build = dev.build;
22
+ exports.dev = dev.dev;
package/dist/node.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { InlineConfig } from 'vite';
2
- import { b as ViteReactSSGOptions } from './types-82f34756.js';
2
+ import { b as ViteReactSSGOptions } from './types-25b96ed0.js';
3
3
  import 'critters';
4
4
  import 'react-router-dom';
5
5
 
6
6
  declare function build(ssgOptions?: Partial<ViteReactSSGOptions>, viteConfig?: InlineConfig): Promise<void>;
7
7
 
8
- export { build };
8
+ declare function dev(ssgOptions?: Partial<ViteReactSSGOptions>, viteConfig?: InlineConfig): Promise<void>;
9
+
10
+ export { build, dev };
package/dist/node.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { b as build } from './shared/vite-react-ssg.f66e4ba5.mjs';
1
+ export { b as build, d as dev } from './shared/vite-react-ssg.7ee042ba.mjs';
2
2
  import 'node:path';
3
3
  import 'node:module';
4
4
  import 'kolorist';
@@ -8,6 +8,8 @@ import 'jsdom';
8
8
  import './shared/vite-react-ssg.9d005d5e.mjs';
9
9
  import 'react';
10
10
  import 'react-helmet-async';
11
+ import 'express';
12
+ import 'node:fs';
11
13
  import 'react-router-dom/server.js';
12
14
  import 'node:stream';
13
15
  import 'react-dom/server';