UNPKG

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