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 crypto from "crypto";
|
13 | import { resolve, join } from "path";
|
14 | import { writeFile, copyFile, fileExists } from "./fs.js";
|
15 | import module from "module";
|
16 | const { createRequire } = module;
|
17 | const require = createRequire(import.meta.url);
|
18 | import { Document as InternalDocument, __HeadContext, __InternalDocContext } from "../document.js";
|
19 | import { h } from "preact";
|
20 | import { renderToString } from "preact-render-to-string";
|
21 | import { generateStaticPropsContext } from "./router.js";
|
22 | import fetch from 'node-fetch';
|
23 |
|
24 | export const CACHE_DIR = ".microsite/cache";
|
25 | export const STAGING_DIR = ".microsite/staging";
|
26 | export const SSR_DIR = ".microsite/ssr";
|
27 | export const OUT_DIR_NO_BASE = "./dist";
|
28 | export let OUT_DIR = "./dist";
|
29 | export const setBasePath = (p) => OUT_DIR = p === '/' ? OUT_DIR : join(OUT_DIR, ...p.replace(/^\//, '').replace(/\/$/, '').split('/'));
|
30 | export const getFileNameFromPath = (path) => {
|
31 | return path
|
32 | .split(STAGING_DIR)[1]
|
33 | .replace(/^\/src\//, "")
|
34 | .replace(/([\[\]])/gi, "_")
|
35 | .replace(/\..*$/, "");
|
36 | };
|
37 | const PROXY_IMPORT_REGEX = /^.*\.(\w+)\.proxy\.js$/g;
|
38 | export const proxyImportTransformer = {
|
39 | filter: (source) => PROXY_IMPORT_REGEX.test(source),
|
40 | transform: (source) => source.replace(PROXY_IMPORT_REGEX, (fullMatch, originalExt) => {
|
41 | if (originalExt === "json") {
|
42 | return fullMatch;
|
43 | }
|
44 | return fullMatch.replace(".proxy.js", "");
|
45 | }),
|
46 | };
|
47 | const PREACT_IMPORT_REGEX = /^.*_snowpack\/pkg\/preact([\/\w]+)?\.js$/gm;
|
48 | export const preactImportTransformer = {
|
49 | filter: (source) => PREACT_IMPORT_REGEX.test(source),
|
50 | transform: (source) => source.replace(PREACT_IMPORT_REGEX, (_match, subpath) => {
|
51 | return subpath ? `preact${subpath}` : `preact`;
|
52 | }),
|
53 | };
|
54 | const PREACT_VERSION = require("preact/package.json").version;
|
55 | const PREACT_CDN_LOOKUP = {
|
56 | preact: `https://cdn.skypack.dev/preact@${PREACT_VERSION}`,
|
57 | "preact/hooks": `https://cdn.skypack.dev/preact@${PREACT_VERSION}/hooks`,
|
58 | };
|
59 | let PREACT_CDN_SOURCES = null;
|
60 | const resolvePreactCdnSources = async () => {
|
61 | if (PREACT_CDN_SOURCES)
|
62 | return;
|
63 | const mdls = Object.keys(PREACT_CDN_LOOKUP);
|
64 | const pinnedUrls = await Promise.all(mdls.map(mdl => {
|
65 | const lookupUrl = PREACT_CDN_LOOKUP[mdl];
|
66 | return fetch(lookupUrl).then(res => `https://cdn.skypack.dev${res.headers.get('x-pinned-url')}`);
|
67 | }));
|
68 | PREACT_CDN_SOURCES = mdls.reduce((acc, curr, i) => {
|
69 | return Object.assign(Object.assign({}, acc), { [curr]: pinnedUrls[i] });
|
70 | }, {});
|
71 | return;
|
72 | };
|
73 | const PREACT_REGEX = /['"]preact([\/\w]+)?['"]/gm;
|
74 | export const preactToCDN = async (code) => {
|
75 | if (!/preact/gm.test(code)) {
|
76 | return code;
|
77 | }
|
78 | await resolvePreactCdnSources();
|
79 | return code.replace(PREACT_REGEX, (fullMatch, subpath) => {
|
80 | if (subpath === "/hooks") {
|
81 | return fullMatch.replace("preact/hooks", PREACT_CDN_SOURCES["preact/hooks"]);
|
82 | }
|
83 | return fullMatch.replace("preact", PREACT_CDN_SOURCES["preact"]);
|
84 | });
|
85 | };
|
86 | const WITH_HYDRATE_REGEX = /import\s+\{\s*withHydrate\s*(?:as (\w+))?\}\s+from\s+['"]microsite\/hydrate(?:\.js)?['"];?/gim;
|
87 | export const withHydrateTransformer = {
|
88 | filter: (source) => WITH_HYDRATE_REGEX.test(source),
|
89 | transform: (source) => source.replace(WITH_HYDRATE_REGEX, (_match, name = "withHydrate") => {
|
90 | return `const ${name} = i=>i;`;
|
91 | }),
|
92 | };
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 | export const stripWithHydrate = (source) => {
|
99 | if (!withHydrateTransformer.filter(source))
|
100 | return source;
|
101 | return withHydrateTransformer.transform(source);
|
102 | };
|
103 | export const hashContentSync = (content, len) => {
|
104 | const hash = crypto.createHash("sha256");
|
105 | hash.update(Buffer.from(content));
|
106 | let res = hash.digest("hex");
|
107 | if (typeof len === "number")
|
108 | res = res.slice(0, len);
|
109 | return res;
|
110 | };
|
111 | export const emitFile = (filename, content) => writeFile(resolve(process.cwd(), join(SSR_DIR, filename)), content.toString());
|
112 | export const emitFinalAsset = (filename, content) => writeFile(resolve(process.cwd(), join(OUT_DIR, filename)), content.toString());
|
113 | export const copyAssetToFinal = async (path, transform) => copyFile(path, resolve(process.cwd(), join(OUT_DIR, path.slice(resolve(SSR_DIR).length))), { transform });
|
114 | const importPage = (filename) => import(resolve(process.cwd(), join(SSR_DIR, filename))).then((mod) => mod.default);
|
115 | let UserDocument = null;
|
116 | const getDocument = async () => {
|
117 | if (UserDocument)
|
118 | return UserDocument;
|
119 | else if (UserDocument === false)
|
120 | return InternalDocument;
|
121 | else if (await fileExists(resolve(process.cwd(), join(SSR_DIR, 'pages', '_document.js')))) {
|
122 | UserDocument = await importPage(join('pages', '_document.js'));
|
123 | return UserDocument;
|
124 | }
|
125 | UserDocument = false;
|
126 | return InternalDocument;
|
127 | };
|
128 | export const renderPage = async (data, manifest, { basePath = '/', debug = false, hasGlobalScript = false } = {}) => {
|
129 | let [Document, Page] = await Promise.all([getDocument(), importPage(manifest.name), resolvePreactCdnSources()]);
|
130 | Page = unwrapPage(Page);
|
131 | const pageProps = data.props;
|
132 | const headContext = {
|
133 | head: {
|
134 | current: [],
|
135 | },
|
136 | };
|
137 | const HeadProvider = ({ children }) => {
|
138 | return (h(__HeadContext.Provider, { value: headContext }, children));
|
139 | };
|
140 | const _a = await Document.prepare({
|
141 | renderPage: async () => ({
|
142 | __renderPageResult: renderToString(h(HeadProvider, null,
|
143 | h(Page, Object.assign({}, pageProps))))
|
144 | })
|
145 | }), { __renderPageResult } = _a, docProps = __rest(_a, ["__renderPageResult"]);
|
146 | const docContext = {
|
147 | dev: false,
|
148 | manifest,
|
149 | styles: manifest.hydrateStyleBindings,
|
150 | scripts: manifest.hydrateBindings,
|
151 | preload: manifest.hydrateBindings
|
152 | ? Object.values(PREACT_CDN_SOURCES)
|
153 | : [],
|
154 | preconnect: [],
|
155 | debug,
|
156 | hasGlobalScript,
|
157 | basePath,
|
158 | __renderPageResult,
|
159 | __renderPageHead: headContext.head.current
|
160 | };
|
161 | let contents = renderToString(h(__InternalDocContext.Provider, { value: docContext },
|
162 | h(Document, Object.assign({}, docProps))));
|
163 | contents = contents.replace(/<hydrate-marker>(\?h[\s\S]*?\?)<\/hydrate-marker>/g, '<$1>\n');
|
164 | contents = '<!DOCTYPE html>\n<!-- Generated by microsite -->\n' + contents;
|
165 | return {
|
166 | name: `${data.route.replace(/\.js$/, '')}.html`,
|
167 | contents,
|
168 | };
|
169 | };
|
170 | export const unwrapPage = (Page) => {
|
171 | return Page.Component ? Page.Component : Page;
|
172 | };
|
173 | let noop = () => Promise.resolve();
|
174 | export const importDataMethods = (path) => import(path).then((mod) => {
|
175 | var _a, _b;
|
176 | const Page = mod.default;
|
177 | let getStaticPaths = noop;
|
178 | let getStaticProps = noop;
|
179 | if (Page.Component) {
|
180 | getStaticPaths = (_a = Page.getStaticPaths) !== null && _a !== void 0 ? _a : noop;
|
181 | getStaticProps = (_b = Page.getStaticProps) !== null && _b !== void 0 ? _b : noop;
|
182 | }
|
183 | return { getStaticPaths, getStaticProps };
|
184 | });
|
185 |
|
186 | export async function applyDataMethods(fileName, handlers) {
|
187 | const { getStaticPaths, getStaticProps } = handlers;
|
188 |
|
189 | let staticPaths = [];
|
190 | staticPaths = await getStaticPaths({}).then((res) => { var _a; return (_a = res === null || res === void 0 ? void 0 : res.paths) !== null && _a !== void 0 ? _a : []; });
|
191 | if (staticPaths.length === 0) {
|
192 | const ctx = generateStaticPropsContext(fileName, fileName);
|
193 | const staticProps = await getStaticProps(ctx).then((res) => { var _a; return (_a = res === null || res === void 0 ? void 0 : res.props) !== null && _a !== void 0 ? _a : {}; });
|
194 | return [{ name: fileName, route: ctx.path, props: staticProps }];
|
195 | }
|
196 | return Promise.all(staticPaths.map((pathOrParams) => {
|
197 | const ctx = generateStaticPropsContext(fileName, pathOrParams);
|
198 | return getStaticProps(ctx).then((res) => {
|
199 | var _a;
|
200 | let staticProps = (_a = res === null || res === void 0 ? void 0 : res.props) !== null && _a !== void 0 ? _a : {};
|
201 | return { name: fileName, route: ctx.path, props: staticProps };
|
202 | });
|
203 | }));
|
204 | }
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|