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