1 | var __rest = (this && this.__rest) || function (s, e) {
|
2 | var t = {};
|
3 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
4 | t[p] = s[p];
|
5 | if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
6 | for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
7 | if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
8 | t[p[i]] = s[p[i]];
|
9 | }
|
10 | return t;
|
11 | };
|
12 | import { startServer } from "snowpack";
|
13 | import arg from "arg";
|
14 | import { join, resolve, extname } from "path";
|
15 | import { green, dim } from "kleur/colors";
|
16 | import polka from "polka";
|
17 | import { openInBrowser } from "../utils/open.js";
|
18 | import { readDir } from "../utils/fs.js";
|
19 | import { promises as fsp } from "fs";
|
20 | import { loadConfiguration } from "../utils/command.js";
|
21 | import { h } from "preact";
|
22 | import { generateStaticPropsContext } from "../utils/router.js";
|
23 | const noop = () => Promise.resolve();
|
24 | let devServer;
|
25 | let runtime;
|
26 | let renderToString;
|
27 | let csrSrc;
|
28 | let Document;
|
29 | let __HeadContext;
|
30 | let __InternalDocContext;
|
31 | let ErrorPage;
|
32 | let errorSrc;
|
33 | const loadErrorPage = async () => {
|
34 | if (!ErrorPage) {
|
35 | try {
|
36 | const { exports: { default: UserErrorPage } } = await runtime.importModule("/src/pages/_error.js");
|
37 | ErrorPage = UserErrorPage;
|
38 | errorSrc = "/src/pages/_error.js";
|
39 | }
|
40 | catch (e) {
|
41 | errorSrc = await devServer.getUrlForPackage('microsite/error');
|
42 | const { exports: { default: InternalErrorPage }, } = await runtime.importModule(errorSrc);
|
43 | ErrorPage = InternalErrorPage;
|
44 | }
|
45 | }
|
46 | return [ErrorPage, errorSrc];
|
47 | };
|
48 | const renderPage = async (page, initialProps) => {
|
49 | var _a, _b, _c, _d;
|
50 | if (!renderToString) {
|
51 | const preactRenderToStringSrc = await devServer.getUrlForPackage('preact-render-to-string');
|
52 | renderToString = await runtime
|
53 | .importModule(preactRenderToStringSrc)
|
54 | .then(({ exports: { default: mod } }) => mod);
|
55 | }
|
56 | if (!Document) {
|
57 | const [documentSrc, csrUrl] = await Promise.all([devServer.getUrlForPackage('microsite/document'), devServer.getUrlForPackage('microsite/client/csr')]);
|
58 | csrSrc = csrUrl;
|
59 | const { exports: { Document: InternalDocument, __HeadContext: __Head, __InternalDocContext: __Doc }, } = await runtime.importModule(documentSrc);
|
60 | __HeadContext = __Head;
|
61 | __InternalDocContext = __Doc;
|
62 | try {
|
63 | const { exports: { default: UserDocument }, } = await runtime.importModule("/src/pages/_document.js");
|
64 | Document = UserDocument;
|
65 | }
|
66 | catch (e) {
|
67 | Document = InternalDocument;
|
68 | }
|
69 | }
|
70 | try {
|
71 | let pathname = page.replace("/src/pages/", "");
|
72 | let Component = null;
|
73 | let getStaticProps = noop;
|
74 | let getStaticPaths = noop;
|
75 | let pageProps = initialProps !== null && initialProps !== void 0 ? initialProps : {};
|
76 | let paths = [];
|
77 | try {
|
78 | let { exports: { default: Page } } = await runtime.importModule(page);
|
79 | if (typeof Page === "function")
|
80 | Component = Page;
|
81 | if (Page.Component) {
|
82 | Component = Page.Component;
|
83 | getStaticProps = (_a = Page.getStaticProps) !== null && _a !== void 0 ? _a : noop;
|
84 | getStaticPaths = (_b = Page.getStaticPaths) !== null && _b !== void 0 ? _b : noop;
|
85 | }
|
86 | }
|
87 | catch (e) {
|
88 | const [Page, errorSrc] = await loadErrorPage();
|
89 | Component = ErrorPage;
|
90 | pageProps = (initialProps === null || initialProps === void 0 ? void 0 : initialProps.statusCode) ? initialProps : { statusCode: 404 };
|
91 | page = errorSrc;
|
92 | pathname = "/_error";
|
93 | if (typeof Page === "function")
|
94 | Component = Page;
|
95 | if (Page.Component) {
|
96 | Component = Page.Component;
|
97 | getStaticProps = (_c = Page.getStaticProps) !== null && _c !== void 0 ? _c : noop;
|
98 | getStaticPaths = (_d = Page.getStaticPaths) !== null && _d !== void 0 ? _d : noop;
|
99 | }
|
100 | }
|
101 | paths = await getStaticPaths({}).then((res) => res && res.paths);
|
102 | paths =
|
103 | paths &&
|
104 | paths.map((pathOrParams) => generateStaticPropsContext(pathname, pathOrParams));
|
105 | const match = paths &&
|
106 | paths.find((ctx) => ctx.path === pathname || ctx.path === `${pathname}/index`);
|
107 | if (paths && !match) {
|
108 | const [ErrorPage, errorSrc] = await loadErrorPage();
|
109 | Component = ErrorPage;
|
110 | pageProps = { statusCode: 404 };
|
111 | page = errorSrc;
|
112 | }
|
113 | else {
|
114 | let ctx = paths ? match : generateStaticPropsContext(pathname, pathname);
|
115 | pageProps = await getStaticProps(ctx).then((res) => res && res.props);
|
116 | if (!pageProps)
|
117 | pageProps = initialProps;
|
118 | }
|
119 | const headContext = {
|
120 | head: {
|
121 | current: [],
|
122 | },
|
123 | };
|
124 | const HeadProvider = ({ children }) => {
|
125 | return h(__HeadContext.Provider, Object.assign({ value: headContext }, { children }));
|
126 | };
|
127 | const _e = await Document.prepare({
|
128 | renderPage: async () => ({
|
129 | __renderPageResult: renderToString(h(HeadProvider, null,
|
130 | h(Component, Object.assign({}, pageProps)))),
|
131 | }),
|
132 | }), { __renderPageResult } = _e, docProps = __rest(_e, ["__renderPageResult"]);
|
133 | const docContext = {
|
134 | dev: page,
|
135 | devProps: pageProps !== null && pageProps !== void 0 ? pageProps : {},
|
136 | __csrUrl: csrSrc,
|
137 | __renderPageHead: headContext.head.current,
|
138 | __renderPageResult,
|
139 | };
|
140 | let contents = renderToString(h(__InternalDocContext.Provider, { value: docContext, children: h(Document, Object.assign({}, docProps)) }));
|
141 | return `<!DOCTYPE html>\n<!-- Generated by microsite -->\n${contents}`;
|
142 | }
|
143 | catch (e) {
|
144 | console.error(e);
|
145 | return;
|
146 | }
|
147 | };
|
148 | const EXTS = [".js", ".jsx", ".ts", ".tsx", ".mjs"];
|
149 | function parseArgs(argv) {
|
150 | return arg({
|
151 | "--port": Number,
|
152 | "--no-open": Boolean,
|
153 |
|
154 | "-p": "--port",
|
155 | }, { permissive: true, argv });
|
156 | }
|
157 | export default async function dev(argvOrParsedArgs) {
|
158 | var _a;
|
159 | const cwd = process.cwd();
|
160 | const args = Array.isArray(argvOrParsedArgs)
|
161 | ? parseArgs(argvOrParsedArgs)
|
162 | : argvOrParsedArgs;
|
163 | let PORT = (_a = args["--port"]) !== null && _a !== void 0 ? _a : 8888;
|
164 | const config = await loadConfiguration("dev");
|
165 | const snowpack = await startServer({
|
166 | config,
|
167 | lockfile: null,
|
168 | });
|
169 | devServer = snowpack;
|
170 | runtime = snowpack.getServerRuntime();
|
171 | snowpack.onFileChange(({ filePath }) => {
|
172 | const url = snowpack.getUrlForFile(filePath);
|
173 | if (url === '/src/pages/_document.js') {
|
174 | Document = null;
|
175 | }
|
176 | if (url === '/src/pages/_error.js') {
|
177 | ErrorPage = null;
|
178 | }
|
179 | });
|
180 | const sendErr = async (res, props) => {
|
181 | var _a;
|
182 | const contents = await renderPage(`/_error`, props);
|
183 | res.writeHead((_a = props === null || props === void 0 ? void 0 : props.statusCode) !== null && _a !== void 0 ? _a : 500, {
|
184 | "Content-Type": "text/html",
|
185 | });
|
186 | res.end(contents);
|
187 | };
|
188 | const server = polka()
|
189 | .use(async (req, res, next) => {
|
190 | var _a;
|
191 | if ((_a = req.url) === null || _a === void 0 ? void 0 : _a.endsWith(".js")) {
|
192 | res.setHeader("Content-Type", "application/javascript");
|
193 | }
|
194 | next();
|
195 | })
|
196 | .use(async (req, res, next) => {
|
197 | var _a, _b;
|
198 | if (req.url === "/")
|
199 | return next();
|
200 | const clean = /(\.html|index\.html|index|\/)$/;
|
201 | if (clean.test((_a = req.url) !== null && _a !== void 0 ? _a : "")) {
|
202 | res.writeHead(302, {
|
203 | Location: (_b = req.url) === null || _b === void 0 ? void 0 : _b.replace(clean, ""),
|
204 | });
|
205 | res.end();
|
206 | }
|
207 | next();
|
208 | })
|
209 | .use(async (req, res, next) => {
|
210 | if (req.url !== "/" &&
|
211 | !(req.url.endsWith(".html") || req.url.indexOf(".") === -1))
|
212 | return next();
|
213 | let base = req.url.slice(1);
|
214 | if (base.endsWith(".html"))
|
215 | base = base.slice(0, ".html".length * -1);
|
216 | if (base === "")
|
217 | base = "index";
|
218 | const loadAndSSR = async (base) => {
|
219 | try {
|
220 | const url = `/src/pages/${base}.js`;
|
221 | const result = await snowpack.loadUrl(url, { isSSR: true });
|
222 | if (!result)
|
223 | throw new Error();
|
224 | res.setHeader("Content-Type", "text/html");
|
225 | res.end(await renderPage(url));
|
226 | return true;
|
227 | }
|
228 | catch (err) { }
|
229 | return false;
|
230 | };
|
231 | const findPotentialMatch = async (base) => {
|
232 | const baseParts = [...base.split("/"), "index"];
|
233 | const pages = join(cwd, "src", "pages");
|
234 | let files = await readDir(pages);
|
235 | files = files
|
236 | .filter((file) => EXTS.includes(extname(file)))
|
237 | .map((file) => file.slice(pages.length, extname(file).length * -1))
|
238 | .filter((file) => {
|
239 | if (file.indexOf("[") === -1)
|
240 | return false;
|
241 | const parts = file.slice(1).split("/");
|
242 | if (parts.length === baseParts.length - 1)
|
243 | return parts.every((part, i) => part.indexOf("[") > -1 ? true : part === baseParts[i]);
|
244 | if (parts.length === baseParts.length)
|
245 | return parts.every((part, i) => part.indexOf("[") > -1 ? true : part === baseParts[i]);
|
246 | if (file.indexOf("[[") > -1)
|
247 | return parts.every((part, i) => {
|
248 | if (part.indexOf("[["))
|
249 | return i === parts.length - 1;
|
250 | if (part.indexOf("["))
|
251 | return true;
|
252 | return part === baseParts[i];
|
253 | });
|
254 | });
|
255 | if (files.length === 0)
|
256 | return null;
|
257 | if (files.length === 1)
|
258 | return files[0].slice(1);
|
259 | if (files.length > 1) {
|
260 |
|
261 |
|
262 | return files[0];
|
263 | }
|
264 | };
|
265 | const direct = await loadAndSSR(base);
|
266 | if (direct) {
|
267 | return next();
|
268 | }
|
269 | const index = await loadAndSSR(`${base}/index`);
|
270 | if (index) {
|
271 | return next();
|
272 | }
|
273 | const dynamic = await findPotentialMatch(base);
|
274 | if (dynamic) {
|
275 | await loadAndSSR(dynamic);
|
276 | }
|
277 | next();
|
278 | })
|
279 | .use(async (req, res, next) => {
|
280 | try {
|
281 |
|
282 | const result = await snowpack.loadUrl(req.url);
|
283 | if (result.contentType)
|
284 | res.setHeader("Content-Type", result.contentType);
|
285 | const MIME_EXCLUDE = ["image", "font"];
|
286 | if (req.url.indexOf("/_snowpack/pkg/microsite") === -1 &&
|
287 | result.contentType &&
|
288 | !MIME_EXCLUDE.includes(result.contentType.split("/")[0])) {
|
289 | result.contents = result.contents
|
290 | .toString()
|
291 | .replace(/preact\/hooks/, "microsite/client/hooks");
|
292 | }
|
293 | return res.end(result.contents);
|
294 | }
|
295 | catch (err) { }
|
296 | next();
|
297 | })
|
298 | .use(async (req, res, next) => {
|
299 | try {
|
300 | let localPath = resolve(cwd, `.${req.url}`);
|
301 | const stats = await fsp.stat(localPath);
|
302 | if (stats.isDirectory()) {
|
303 | let contents = await readDir(localPath);
|
304 | contents = contents.map((path) => path.slice(localPath.length));
|
305 | res.setHeader("Content-Type", "application/json");
|
306 | return res.end(JSON.stringify(contents));
|
307 | }
|
308 | }
|
309 | catch (err) { }
|
310 | next();
|
311 | })
|
312 | .get("*", (_req, res) => sendErr(res, { statusCode: 404 }));
|
313 | await new Promise((resolve) => server.listen(PORT, (err) => {
|
314 | if (err)
|
315 | throw err;
|
316 | resolve();
|
317 | }));
|
318 | let protocol = "http:";
|
319 | let hostname = "localhost";
|
320 | if (!args["--no-open"]) {
|
321 | await openInBrowser(protocol, hostname, PORT, "/", "chrome");
|
322 | }
|
323 | console.log(`${dim("[microsite]")} ${green("✔")} Microsite started on ${green(`${protocol}//${hostname}:${PORT}`)}\n`);
|
324 | }
|