exta2 0.0.1-beta.29
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 +43 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +169 -0
- package/dist/client.mjs +155 -0
- package/dist/components.d.ts +23 -0
- package/dist/components.js +96 -0
- package/dist/components.mjs +72 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +851 -0
- package/dist/index.mjs +885 -0
- package/dist/router.d.ts +49 -0
- package/dist/router.js +270 -0
- package/dist/router.mjs +253 -0
- package/env.d.ts +138 -0
- package/package.json +45 -0
- package/ssr.d.ts +12 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Exta
|
|
2
|
+
|
|
3
|
+
SSG Framework for React
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **File-based Routing**: Generates routes based on file names.
|
|
8
|
+
|
|
9
|
+
- **Fast startup**: Runs with the speed of Vite and Esbuild.
|
|
10
|
+
|
|
11
|
+
- **Vite-based**: Leverages Vite's broad ecosystem within exta.
|
|
12
|
+
|
|
13
|
+
- **`.tsx`, `.jsx` support**: Build type-safe apps with TypeScript.
|
|
14
|
+
|
|
15
|
+
## Getting Started
|
|
16
|
+
|
|
17
|
+
- **Download Template**
|
|
18
|
+
|
|
19
|
+
You can download the current application template from [this repository](https://github.com/do4ng/exta-template).
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
git clone https://github.com/do4ng/exta-template my-app
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- **Start Manually**
|
|
26
|
+
|
|
27
|
+
exta is provided as a Vite plugin.
|
|
28
|
+
Install the exta core package and the Vite React plugin to apply exta to an existing project.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { defineConfig } from 'vite';
|
|
32
|
+
import react from '@vitejs/plugin-react';
|
|
33
|
+
|
|
34
|
+
import { exta } from 'exta';
|
|
35
|
+
|
|
36
|
+
export default defineConfig({
|
|
37
|
+
plugins: [react(), exta()],
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## LICENSE
|
|
42
|
+
|
|
43
|
+
MIT
|
package/dist/client.d.ts
ADDED
package/dist/client.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __copyProps = (to, from, except, desc) => {
|
|
8
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
9
|
+
for (let key of __getOwnPropNames(from))
|
|
10
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
11
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
12
|
+
}
|
|
13
|
+
return to;
|
|
14
|
+
};
|
|
15
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
16
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
17
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
18
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
19
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
20
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
21
|
+
mod
|
|
22
|
+
));
|
|
23
|
+
var import_react = __toESM(require("react"), 1);
|
|
24
|
+
var import_client = __toESM(require("react-dom/client"), 1);
|
|
25
|
+
var import_exta_router = require("$exta-router");
|
|
26
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
27
|
+
const import_meta = {};
|
|
28
|
+
function matchUrlToRoute(url, routeResult) {
|
|
29
|
+
let { regex, params } = routeResult, match = regex.exec(url);
|
|
30
|
+
if (!match)
|
|
31
|
+
return null;
|
|
32
|
+
let result = {};
|
|
33
|
+
for (let i = 0; i < params.length; i++) {
|
|
34
|
+
let paramName = params[i], capturedValue = match[i + 1];
|
|
35
|
+
if (paramName.startsWith("..."))
|
|
36
|
+
throw new Error(
|
|
37
|
+
"[... file names are not supported due to static data generation issues."
|
|
38
|
+
);
|
|
39
|
+
result[paramName] = capturedValue;
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
var overlayEl = null;
|
|
44
|
+
function ensureOverlay(style = "") {
|
|
45
|
+
overlayEl || (overlayEl = document.createElement("div"), overlayEl.className = "__exta_overlay", overlayEl.style.cssText = `
|
|
46
|
+
min-width: 100px;
|
|
47
|
+
position: fixed;
|
|
48
|
+
bottom: 16px;
|
|
49
|
+
left: 16px;
|
|
50
|
+
z-index: 99999;
|
|
51
|
+
font-family: Consolas, "Courier New", monospace;
|
|
52
|
+
background: rgba(0,0,0,0.78);
|
|
53
|
+
color: #fff;
|
|
54
|
+
padding: 10px 14px;
|
|
55
|
+
border-radius: 10px;
|
|
56
|
+
box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.24) 0px 1px 2px;
|
|
57
|
+
font-size: 14px;
|
|
58
|
+
line-height: 1.35;
|
|
59
|
+
max-width: 320px;
|
|
60
|
+
backdrop-filter: blur(4px);
|
|
61
|
+
transition: opacity 400ms ease, transform 400ms ease;
|
|
62
|
+
opacity: 0;
|
|
63
|
+
transform: translateY(-6px) scale(0.98);
|
|
64
|
+
display: none;
|
|
65
|
+
${style}
|
|
66
|
+
`, document.body.appendChild(overlayEl), overlayEl.addEventListener("transitionend", () => {
|
|
67
|
+
overlayEl.style.opacity === "0" && (overlayEl.style.display = "none");
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
function show(text, error) {
|
|
71
|
+
ensureOverlay(error === true ? "color: #ff3c3c" : ""), error === true && import_meta.env.PROD && (text = "An internal client error occurred. See the console for details."), text && (overlayEl.textContent = text), overlayEl.style.display = "block", requestAnimationFrame(() => {
|
|
72
|
+
overlayEl.style.opacity = "1", overlayEl.style.transform = "translateY(0) scale(1)";
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function setText(text) {
|
|
76
|
+
ensureOverlay(), overlayEl.textContent = text;
|
|
77
|
+
}
|
|
78
|
+
function hide() {
|
|
79
|
+
overlayEl && (overlayEl.style.opacity = "0", overlayEl.style.transform = "translateY(-6px) scale(0.98)");
|
|
80
|
+
}
|
|
81
|
+
typeof window < "u" && (window.__overlay__ = {
|
|
82
|
+
show,
|
|
83
|
+
setText,
|
|
84
|
+
hide
|
|
85
|
+
});
|
|
86
|
+
var ErrorBoundary = class extends import_react2.default.Component {
|
|
87
|
+
state = { hasError: false, error: null };
|
|
88
|
+
static getDerivedStateFromError(error) {
|
|
89
|
+
return { hasError: true, error };
|
|
90
|
+
}
|
|
91
|
+
componentDidCatch(error, info) {
|
|
92
|
+
this.props.onError ? this.props.onError(error, info) : console.error(error, info);
|
|
93
|
+
}
|
|
94
|
+
render() {
|
|
95
|
+
if (this.state.hasError) {
|
|
96
|
+
let ErrorComp = this.props.errorComponent;
|
|
97
|
+
return ErrorComp && this.state.error ? import_react2.default.createElement(ErrorComp, {
|
|
98
|
+
error: this.state.error,
|
|
99
|
+
status: "Client Error",
|
|
100
|
+
message: this.state.error.message
|
|
101
|
+
}) : import_react2.default.createElement("div", null, "Something went wrong.");
|
|
102
|
+
}
|
|
103
|
+
return import_react2.default.createElement(import_react2.default.Fragment, null, this.props.children);
|
|
104
|
+
}
|
|
105
|
+
}, error_default = ErrorBoundary;
|
|
106
|
+
function prettyURL(path) {
|
|
107
|
+
return path === "." && (path = ""), path.startsWith("/") || (path = `/${path}`), path.endsWith("/") && (path = path.slice(0, -1)), path;
|
|
108
|
+
}
|
|
109
|
+
function App() {
|
|
110
|
+
let isFirstRender = (0, import_react.useRef)(true), location = (0, import_exta_router.usePathname)(), search = (0, import_exta_router.useSearchQuery)(), url = decodeURIComponent(new URL(location, window.location.origin).pathname), props = import_exta_router.router.data.get(prettyURL(url).toLowerCase()), page = import_exta_router.router.findPage(url), Layout = import_exta_router.router.layout._page, rootElement = document.getElementById("_app");
|
|
111
|
+
(0, import_react.useEffect)(() => {
|
|
112
|
+
if (isFirstRender.current) {
|
|
113
|
+
isFirstRender.current = false;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
window.scrollTo({ top: 0, behavior: "instant" }), rootElement && rootElement.scrollTo({ top: 0, behavior: "instant" });
|
|
117
|
+
}, [location, search]);
|
|
118
|
+
let createError = (props2) => import_react.default.createElement(
|
|
119
|
+
Layout,
|
|
120
|
+
null,
|
|
121
|
+
import_react.default.createElement(import_exta_router.router.error._page, {
|
|
122
|
+
key: location,
|
|
123
|
+
...props2
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
if (!page || props?.status === 404)
|
|
127
|
+
return createError({
|
|
128
|
+
key: location,
|
|
129
|
+
status: 404,
|
|
130
|
+
message: "Page not found"
|
|
131
|
+
});
|
|
132
|
+
if (props?.status === 500)
|
|
133
|
+
return show(`ERROR: ${props.detail}`, true), createError({
|
|
134
|
+
key: location,
|
|
135
|
+
status: 500,
|
|
136
|
+
message: "Internal Server Error"
|
|
137
|
+
});
|
|
138
|
+
let Page = import_exta_router.router.modules[page.path]._page, params = matchUrlToRoute(url, { params: page.params, regex: page.regexp });
|
|
139
|
+
return import_react.default.createElement(
|
|
140
|
+
Layout,
|
|
141
|
+
null,
|
|
142
|
+
import_react.default.createElement(Page, { key: location, ...props, params })
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
var compilingInterval;
|
|
146
|
+
if (import_meta.env.DEV) {
|
|
147
|
+
let intervalIndex = 0;
|
|
148
|
+
compilingInterval = setInterval(() => {
|
|
149
|
+
if (intervalIndex * 250 >= 250) {
|
|
150
|
+
let num = intervalIndex % 3 + 1;
|
|
151
|
+
show(`compiling${".".repeat(num)}`);
|
|
152
|
+
}
|
|
153
|
+
intervalIndex += 1;
|
|
154
|
+
}, 250);
|
|
155
|
+
}
|
|
156
|
+
import_exta_router.router.goto(window.location.href).then(() => {
|
|
157
|
+
let mainComponent = import_react.default.createElement(
|
|
158
|
+
// Error Catcher
|
|
159
|
+
error_default,
|
|
160
|
+
{
|
|
161
|
+
errorComponent: import_exta_router.router.error._page,
|
|
162
|
+
onError(error) {
|
|
163
|
+
console.error(error), import_meta.env.PROD ? show("An internal client error occurred. See the console for details.", true) : show(`ERROR: ${error.message}`, true);
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
import_react.default.createElement(App, null)
|
|
167
|
+
);
|
|
168
|
+
import_meta.env.PROD ? import_client.default.hydrateRoot(document.getElementById("_app"), mainComponent) : (import_client.default.createRoot(document.getElementById("_app")).render(mainComponent), clearInterval(compilingInterval), hide());
|
|
169
|
+
});
|
package/dist/client.mjs
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// packages/exta/src/client/client.ts
|
|
2
|
+
import React2, { useEffect, useRef } from "react";
|
|
3
|
+
import ReactDOM from "react-dom/client";
|
|
4
|
+
import { router, usePathname, useSearchQuery } from "$exta-router";
|
|
5
|
+
|
|
6
|
+
// packages/exta/src/utils/params.ts
|
|
7
|
+
function matchUrlToRoute(url, routeResult) {
|
|
8
|
+
let { regex, params } = routeResult, match = regex.exec(url);
|
|
9
|
+
if (!match)
|
|
10
|
+
return null;
|
|
11
|
+
let result = {};
|
|
12
|
+
for (let i = 0; i < params.length; i++) {
|
|
13
|
+
let paramName = params[i], capturedValue = match[i + 1];
|
|
14
|
+
if (paramName.startsWith("..."))
|
|
15
|
+
throw new Error(
|
|
16
|
+
"[... file names are not supported due to static data generation issues."
|
|
17
|
+
);
|
|
18
|
+
result[paramName] = capturedValue;
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// packages/exta/src/client/overlay.ts
|
|
24
|
+
var overlayEl = null;
|
|
25
|
+
function ensureOverlay(style = "") {
|
|
26
|
+
overlayEl || (overlayEl = document.createElement("div"), overlayEl.className = "__exta_overlay", overlayEl.style.cssText = `
|
|
27
|
+
min-width: 100px;
|
|
28
|
+
position: fixed;
|
|
29
|
+
bottom: 16px;
|
|
30
|
+
left: 16px;
|
|
31
|
+
z-index: 99999;
|
|
32
|
+
font-family: Consolas, "Courier New", monospace;
|
|
33
|
+
background: rgba(0,0,0,0.78);
|
|
34
|
+
color: #fff;
|
|
35
|
+
padding: 10px 14px;
|
|
36
|
+
border-radius: 10px;
|
|
37
|
+
box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.24) 0px 1px 2px;
|
|
38
|
+
font-size: 14px;
|
|
39
|
+
line-height: 1.35;
|
|
40
|
+
max-width: 320px;
|
|
41
|
+
backdrop-filter: blur(4px);
|
|
42
|
+
transition: opacity 400ms ease, transform 400ms ease;
|
|
43
|
+
opacity: 0;
|
|
44
|
+
transform: translateY(-6px) scale(0.98);
|
|
45
|
+
display: none;
|
|
46
|
+
${style}
|
|
47
|
+
`, document.body.appendChild(overlayEl), overlayEl.addEventListener("transitionend", () => {
|
|
48
|
+
overlayEl.style.opacity === "0" && (overlayEl.style.display = "none");
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
function show(text, error) {
|
|
52
|
+
ensureOverlay(error === !0 ? "color: #ff3c3c" : ""), error === !0 && import.meta.env.PROD && (text = "An internal client error occurred. See the console for details."), text && (overlayEl.textContent = text), overlayEl.style.display = "block", requestAnimationFrame(() => {
|
|
53
|
+
overlayEl.style.opacity = "1", overlayEl.style.transform = "translateY(0) scale(1)";
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function setText(text) {
|
|
57
|
+
ensureOverlay(), overlayEl.textContent = text;
|
|
58
|
+
}
|
|
59
|
+
function hide() {
|
|
60
|
+
overlayEl && (overlayEl.style.opacity = "0", overlayEl.style.transform = "translateY(-6px) scale(0.98)");
|
|
61
|
+
}
|
|
62
|
+
typeof window < "u" && (window.__overlay__ = {
|
|
63
|
+
show,
|
|
64
|
+
setText,
|
|
65
|
+
hide
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// packages/exta/src/client/components/error.ts
|
|
69
|
+
import React from "react";
|
|
70
|
+
var ErrorBoundary = class extends React.Component {
|
|
71
|
+
state = { hasError: !1, error: null };
|
|
72
|
+
static getDerivedStateFromError(error) {
|
|
73
|
+
return { hasError: !0, error };
|
|
74
|
+
}
|
|
75
|
+
componentDidCatch(error, info) {
|
|
76
|
+
this.props.onError ? this.props.onError(error, info) : console.error(error, info);
|
|
77
|
+
}
|
|
78
|
+
render() {
|
|
79
|
+
if (this.state.hasError) {
|
|
80
|
+
let ErrorComp = this.props.errorComponent;
|
|
81
|
+
return ErrorComp && this.state.error ? React.createElement(ErrorComp, {
|
|
82
|
+
error: this.state.error,
|
|
83
|
+
status: "Client Error",
|
|
84
|
+
message: this.state.error.message
|
|
85
|
+
}) : React.createElement("div", null, "Something went wrong.");
|
|
86
|
+
}
|
|
87
|
+
return React.createElement(React.Fragment, null, this.props.children);
|
|
88
|
+
}
|
|
89
|
+
}, error_default = ErrorBoundary;
|
|
90
|
+
|
|
91
|
+
// packages/exta/src/client/client.ts
|
|
92
|
+
function prettyURL(path) {
|
|
93
|
+
return path === "." && (path = ""), path.startsWith("/") || (path = `/${path}`), path.endsWith("/") && (path = path.slice(0, -1)), path;
|
|
94
|
+
}
|
|
95
|
+
function App() {
|
|
96
|
+
let isFirstRender = useRef(!0), location = usePathname(), search = useSearchQuery(), url = decodeURIComponent(new URL(location, window.location.origin).pathname), props = router.data.get(prettyURL(url).toLowerCase()), page = router.findPage(url), Layout = router.layout._page, rootElement = document.getElementById("_app");
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (isFirstRender.current) {
|
|
99
|
+
isFirstRender.current = !1;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
window.scrollTo({ top: 0, behavior: "instant" }), rootElement && rootElement.scrollTo({ top: 0, behavior: "instant" });
|
|
103
|
+
}, [location, search]);
|
|
104
|
+
let createError = (props2) => React2.createElement(
|
|
105
|
+
Layout,
|
|
106
|
+
null,
|
|
107
|
+
React2.createElement(router.error._page, {
|
|
108
|
+
key: location,
|
|
109
|
+
...props2
|
|
110
|
+
})
|
|
111
|
+
);
|
|
112
|
+
if (!page || props?.status === 404)
|
|
113
|
+
return createError({
|
|
114
|
+
key: location,
|
|
115
|
+
status: 404,
|
|
116
|
+
message: "Page not found"
|
|
117
|
+
});
|
|
118
|
+
if (props?.status === 500)
|
|
119
|
+
return show(`ERROR: ${props.detail}`, !0), createError({
|
|
120
|
+
key: location,
|
|
121
|
+
status: 500,
|
|
122
|
+
message: "Internal Server Error"
|
|
123
|
+
});
|
|
124
|
+
let Page = router.modules[page.path]._page, params = matchUrlToRoute(url, { params: page.params, regex: page.regexp });
|
|
125
|
+
return React2.createElement(
|
|
126
|
+
Layout,
|
|
127
|
+
null,
|
|
128
|
+
React2.createElement(Page, { key: location, ...props, params })
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
var compilingInterval;
|
|
132
|
+
if (import.meta.env.DEV) {
|
|
133
|
+
let intervalIndex = 0;
|
|
134
|
+
compilingInterval = setInterval(() => {
|
|
135
|
+
if (intervalIndex * 250 >= 250) {
|
|
136
|
+
let num = intervalIndex % 3 + 1;
|
|
137
|
+
show(`compiling${".".repeat(num)}`);
|
|
138
|
+
}
|
|
139
|
+
intervalIndex += 1;
|
|
140
|
+
}, 250);
|
|
141
|
+
}
|
|
142
|
+
router.goto(window.location.href).then(() => {
|
|
143
|
+
let mainComponent = React2.createElement(
|
|
144
|
+
// Error Catcher
|
|
145
|
+
error_default,
|
|
146
|
+
{
|
|
147
|
+
errorComponent: router.error._page,
|
|
148
|
+
onError(error) {
|
|
149
|
+
console.error(error), import.meta.env.PROD ? show("An internal client error occurred. See the console for details.", !0) : show(`ERROR: ${error.message}`, !0);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
React2.createElement(App, null)
|
|
153
|
+
);
|
|
154
|
+
import.meta.env.PROD ? ReactDOM.hydrateRoot(document.getElementById("_app"), mainComponent) : (ReactDOM.createRoot(document.getElementById("_app")).render(mainComponent), clearInterval(compilingInterval), hide());
|
|
155
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React, { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
type AnchorBaseProps = Omit<React.ComponentPropsWithoutRef<'a'>, 'href' | 'onClick'> & {
|
|
4
|
+
href: string;
|
|
5
|
+
/**
|
|
6
|
+
* Download the data for that page in advance.
|
|
7
|
+
*/
|
|
8
|
+
prefetch?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Load page data from the SSR stage.
|
|
11
|
+
*/
|
|
12
|
+
preload?: boolean;
|
|
13
|
+
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void | Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
declare function Link({ href, onClick, prefetch, preload, ...props }: AnchorBaseProps): React.JSX.Element;
|
|
16
|
+
|
|
17
|
+
declare function Image({ src, ...props }: React.ComponentPropsWithoutRef<'img'>): React.JSX.Element;
|
|
18
|
+
|
|
19
|
+
declare function Head({ children }: {
|
|
20
|
+
children: ReactElement | ReactElement[];
|
|
21
|
+
}): any;
|
|
22
|
+
|
|
23
|
+
export { Head, Image, Link };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var components_exports = {};
|
|
29
|
+
__export(components_exports, {
|
|
30
|
+
Head: () => Head,
|
|
31
|
+
Image: () => Image,
|
|
32
|
+
Link: () => Link
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(components_exports);
|
|
35
|
+
var import_react = __toESM(require("react"), 1);
|
|
36
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
37
|
+
var import_react3 = require("react");
|
|
38
|
+
var import_server = require("react-dom/server");
|
|
39
|
+
function isExternal(url) {
|
|
40
|
+
try {
|
|
41
|
+
return new URL(url, window.location.href).origin !== window.location.origin;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function Link({ href, onClick, prefetch, preload, ...props }) {
|
|
47
|
+
if (typeof window > "u")
|
|
48
|
+
return preload && global.__EXTA_SSR_DATA__.head.push({ type: "preload-data-link", data: href }), /* @__PURE__ */ import_react.default.createElement("a", { ...props, href }, props.children);
|
|
49
|
+
let useRouter = window._exta_useRouter, extaRouter = window._exta_router, router = useRouter();
|
|
50
|
+
if (!isExternal(href) && prefetch !== false) {
|
|
51
|
+
let url = new URL(href, window.location.origin);
|
|
52
|
+
extaRouter.prefetch(url.pathname);
|
|
53
|
+
}
|
|
54
|
+
return /* @__PURE__ */ import_react.default.createElement("a", { ...props, href, onClick: async (e) => {
|
|
55
|
+
if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || props.target === "_blank")
|
|
56
|
+
return;
|
|
57
|
+
let current = window.location, target = new URL(href, current.href), isHashOnly = href.startsWith("#"), isSamePath = target.pathname === current.pathname, isHashChange = target.hash && target.hash !== current.hash;
|
|
58
|
+
if (!(isHashOnly || isSamePath && isHashChange) && (e.preventDefault(), e.stopPropagation(), !(onClick && (await onClick(e), e.defaultPrevented)))) {
|
|
59
|
+
if (isExternal(href)) {
|
|
60
|
+
window.location.href = href;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
await extaRouter.goto(target.pathname), router.push(target.pathname + target.search + target.hash);
|
|
64
|
+
}
|
|
65
|
+
} }, props.children);
|
|
66
|
+
}
|
|
67
|
+
function Image({ src, ...props }) {
|
|
68
|
+
return /* @__PURE__ */ import_react2.default.createElement("img", { ...props, src }, props.children);
|
|
69
|
+
}
|
|
70
|
+
function Head({ children }) {
|
|
71
|
+
if (typeof window > "u") {
|
|
72
|
+
if (global.__EXTA_SSR_DATA__) {
|
|
73
|
+
let wrapped = Array.isArray(children) ? children.map(
|
|
74
|
+
(c, i) => (0, import_react3.cloneElement)(c, { "data-ssr-head": "", key: i })
|
|
75
|
+
) : (0, import_react3.cloneElement)(children, {
|
|
76
|
+
"data-ssr-head": ""
|
|
77
|
+
});
|
|
78
|
+
global.__EXTA_SSR_DATA__.head.push({
|
|
79
|
+
type: "html",
|
|
80
|
+
data: (0, import_server.renderToStaticMarkup)(wrapped).replace(/data-ssr-head=""/g, "data-ssr-head")
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return (0, import_react3.useEffect)(() => {
|
|
86
|
+
document.head.querySelectorAll("[data-ssr-head]").forEach((el) => el.parentNode?.removeChild(el));
|
|
87
|
+
let html = (0, import_server.renderToStaticMarkup)(children), template = document.createElement("template");
|
|
88
|
+
template.innerHTML = html;
|
|
89
|
+
let nodes = Array.from(template.content.childNodes);
|
|
90
|
+
return nodes.forEach((node) => document.head.appendChild(node)), () => {
|
|
91
|
+
nodes.forEach((node) => {
|
|
92
|
+
document.head.contains(node) && document.head.removeChild(node);
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
}, [children]), null;
|
|
96
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// packages/exta/src/components/Link.tsx
|
|
2
|
+
import React from "react";
|
|
3
|
+
function isExternal(url) {
|
|
4
|
+
try {
|
|
5
|
+
return new URL(url, window.location.href).origin !== window.location.origin;
|
|
6
|
+
} catch {
|
|
7
|
+
return !1;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function Link({ href, onClick, prefetch, preload, ...props }) {
|
|
11
|
+
if (typeof window > "u")
|
|
12
|
+
return preload && global.__EXTA_SSR_DATA__.head.push({ type: "preload-data-link", data: href }), /* @__PURE__ */ React.createElement("a", { ...props, href }, props.children);
|
|
13
|
+
let useRouter = window._exta_useRouter, extaRouter = window._exta_router, router = useRouter();
|
|
14
|
+
if (!isExternal(href) && prefetch !== !1) {
|
|
15
|
+
let url = new URL(href, window.location.origin);
|
|
16
|
+
extaRouter.prefetch(url.pathname);
|
|
17
|
+
}
|
|
18
|
+
return /* @__PURE__ */ React.createElement("a", { ...props, href, onClick: async (e) => {
|
|
19
|
+
if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || props.target === "_blank")
|
|
20
|
+
return;
|
|
21
|
+
let current = window.location, target = new URL(href, current.href), isHashOnly = href.startsWith("#"), isSamePath = target.pathname === current.pathname, isHashChange = target.hash && target.hash !== current.hash;
|
|
22
|
+
if (!(isHashOnly || isSamePath && isHashChange) && (e.preventDefault(), e.stopPropagation(), !(onClick && (await onClick(e), e.defaultPrevented)))) {
|
|
23
|
+
if (isExternal(href)) {
|
|
24
|
+
window.location.href = href;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
await extaRouter.goto(target.pathname), router.push(target.pathname + target.search + target.hash);
|
|
28
|
+
}
|
|
29
|
+
} }, props.children);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// packages/exta/src/components/Image.tsx
|
|
33
|
+
import React2 from "react";
|
|
34
|
+
function Image({ src, ...props }) {
|
|
35
|
+
return /* @__PURE__ */ React2.createElement("img", { ...props, src }, props.children);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// packages/exta/src/components/Head.tsx
|
|
39
|
+
import { useEffect, cloneElement } from "react";
|
|
40
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
41
|
+
function Head({ children }) {
|
|
42
|
+
if (typeof window > "u") {
|
|
43
|
+
if (global.__EXTA_SSR_DATA__) {
|
|
44
|
+
let wrapped = Array.isArray(children) ? children.map(
|
|
45
|
+
(c, i) => cloneElement(c, { "data-ssr-head": "", key: i })
|
|
46
|
+
) : cloneElement(children, {
|
|
47
|
+
"data-ssr-head": ""
|
|
48
|
+
});
|
|
49
|
+
global.__EXTA_SSR_DATA__.head.push({
|
|
50
|
+
type: "html",
|
|
51
|
+
data: renderToStaticMarkup(wrapped).replace(/data-ssr-head=""/g, "data-ssr-head")
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return useEffect(() => {
|
|
57
|
+
document.head.querySelectorAll("[data-ssr-head]").forEach((el) => el.parentNode?.removeChild(el));
|
|
58
|
+
let html = renderToStaticMarkup(children), template = document.createElement("template");
|
|
59
|
+
template.innerHTML = html;
|
|
60
|
+
let nodes = Array.from(template.content.childNodes);
|
|
61
|
+
return nodes.forEach((node) => document.head.appendChild(node)), () => {
|
|
62
|
+
nodes.forEach((node) => {
|
|
63
|
+
document.head.contains(node) && document.head.removeChild(node);
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
}, [children]), null;
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
Head,
|
|
70
|
+
Image,
|
|
71
|
+
Link
|
|
72
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface BaseConfig {
|
|
4
|
+
compileOptions?: CompileOptions;
|
|
5
|
+
}
|
|
6
|
+
interface CompileOptions {
|
|
7
|
+
/** @default {true} */
|
|
8
|
+
ignoreSideEffects?: boolean;
|
|
9
|
+
preserveSideEffects?: string[];
|
|
10
|
+
outdir?: string;
|
|
11
|
+
assetsExtensions?: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare function exta(options?: BaseConfig): Plugin[];
|
|
15
|
+
|
|
16
|
+
interface ErrorProps {
|
|
17
|
+
status?: number | string;
|
|
18
|
+
message?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare const PAGE_STATIC_DATA_FUNCTION = "getStaticProps";
|
|
22
|
+
declare const PAGE_STATIC_PARAMS_FUNCTION = "getStaticParams";
|
|
23
|
+
|
|
24
|
+
interface StaticPropsContext {
|
|
25
|
+
params: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
type StaticParamsOutput<T> = Array<T>;
|
|
28
|
+
type StaticPropsFC<T = any> = (ctx: StaticPropsContext) => Promise<T> | T;
|
|
29
|
+
type StaticParamsFC<T = Record<string, string>> = () => Promise<StaticParamsOutput<T>> | StaticParamsOutput<T>;
|
|
30
|
+
type ServerModule = {
|
|
31
|
+
[PAGE_STATIC_DATA_FUNCTION]: StaticPropsFC;
|
|
32
|
+
[PAGE_STATIC_PARAMS_FUNCTION]: StaticParamsFC;
|
|
33
|
+
};
|
|
34
|
+
interface PageProps<PropsType = any, ParamsType = Record<string, string>> {
|
|
35
|
+
params: ParamsType;
|
|
36
|
+
props: PropsType;
|
|
37
|
+
}
|
|
38
|
+
type ExtaPage<PropsType = any, ParamsType = Record<string, string>> = (props: PageProps<PropsType, ParamsType>) => React.ReactNode;
|
|
39
|
+
type ExtaLayout = (props: {
|
|
40
|
+
children: React.ReactNode;
|
|
41
|
+
}) => React.ReactNode;
|
|
42
|
+
type ExtaErrorComponent = (props: ErrorProps) => React.ReactNode;
|
|
43
|
+
|
|
44
|
+
declare function compilePage(filename: string, options: CompileOptions, ignoreAssets: boolean): Promise<{
|
|
45
|
+
outfiles: {
|
|
46
|
+
client: string;
|
|
47
|
+
server: string;
|
|
48
|
+
};
|
|
49
|
+
}>;
|
|
50
|
+
|
|
51
|
+
export { compilePage, exta };
|
|
52
|
+
export type { ExtaErrorComponent, ExtaLayout, ExtaPage, PageProps, ServerModule, StaticParamsFC, StaticParamsOutput, StaticPropsContext, StaticPropsFC };
|