@sveltejs/kit
Version:
Here be dragons, etc etc.
277 lines (218 loc) • 7 kB
JavaScript
;
var _package = require('./package.js');
var path = require('path');
require('module');
var fs = require('fs');
var Url = require('url');
var utils = require('./utils.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
function clean_html(html) {
return html
.replace(/<!\[CDATA\[[\s\S]*?\]\]>/gm, '')
.replace(/(<script[\s\S]*?>)[\s\S]*?<\/script>/gm, '$1</' + 'script>')
.replace(/(<style[\s\S]*?>)[\s\S]*?<\/style>/gm, '$1</' + 'style>')
.replace(/<!--[\s\S]*?-->/gm, '');
}
function get_href(attrs) {
const match = /href\s*=\s*(?:"(.*?)"|'(.*?)'|([^\s>]*))/.exec(attrs);
return match && (match[1] || match[2] || match[3]);
}
function get_src(attrs) {
const match = /src\s*=\s*(?:"(.*?)"|'(.*?)'|([^\s>]*))/.exec(attrs);
return match && (match[1] || match[2] || match[3]);
}
function get_srcset_urls(attrs) {
const results = [];
// Note that the srcset allows any ASCII whitespace, including newlines.
const match = /srcset\s*=\s*(?:"(.*?)"|'(.*?)'|([^\s>]*))/s.exec(attrs);
if (match) {
const attr_content = match[1] || match[2] || match[3];
// Parse the content of the srcset attribute.
// The regexp is modelled after the srcset specs (https://html.spec.whatwg.org/multipage/images.html#srcset-attribute)
// and should cover most reasonable cases.
const regex = /\s*([^\s,]\S+[^\s,])\s*((?:\d+w)|(?:-?\d+(?:\.\d+)?(?:[eE]-?\d+)?x))?/gm;
let sub_matches;
while ((sub_matches = regex.exec(attr_content))) {
results.push(sub_matches[1]);
}
}
return results;
}
const OK = 2;
const REDIRECT = 3;
async function prerender({
dir,
out,
manifest,
log,
force
}) {
const seen = new Set();
const server_root = path.resolve(dir);
const app = require(`${server_root}/server/app.js`);
async function crawl(path$1) {
if (seen.has(path$1)) return;
seen.add(path$1);
const rendered = await app.render({
host: null, // TODO ???
method: 'GET',
headers: {},
path: path$1,
body: null,
query: new Url.URLSearchParams()
}, {
only_prerender: !force
});
if (rendered) {
const response_type = Math.floor(rendered.status / 100);
const headers = rendered.headers;
const type = headers && headers['content-type'];
const is_html = response_type === REDIRECT || type === 'text/html';
const parts = path$1.split('/');
if (is_html && parts[parts.length - 1] !== 'index.html') {
parts.push('index.html');
}
const file = `${out}${parts.join('/')}`;
utils.mkdirp(path.dirname(file));
if (response_type === REDIRECT) {
const location = headers['location'];
log.warn(`${rendered.status} ${path$1} -> ${location}`);
fs__default['default'].writeFileSync(
file,
`<script>window.location.href=${JSON.stringify(headers['location'])}</script>`
);
return;
}
if (response_type === OK) {
log.info(`${rendered.status} ${path$1}`);
fs__default['default'].writeFileSync(file, rendered.body); // TODO minify where possible?
} else {
// TODO should this fail the build?
log.error(`${rendered.status} ${path$1}`);
}
const { dependencies } = rendered;
if (dependencies) {
for (const path$1 in dependencies) {
const result = dependencies[path$1];
const response_type = Math.floor(result.status / 100);
const is_html = result.headers['content-type'] === 'text/html';
const parts = path$1.split('/');
if (is_html && parts[parts.length - 1] !== 'index.html') {
parts.push('index.html');
}
const file = `${out}${parts.join('/')}`;
utils.mkdirp(path.dirname(file));
fs__default['default'].writeFileSync(file, result.body);
if (response_type === OK) {
log.info(`${result.status} ${path$1}`);
} else {
log.error(`${result.status} ${path$1}`);
}
}
}
if (is_html) {
const cleaned = clean_html(rendered.body);
let match;
const pattern = /<(a|img|link|source)\s+([\s\S]+?)>/gm;
while ((match = pattern.exec(cleaned))) {
let hrefs = [];
const element = match[1];
const attrs = match[2];
if (element === 'a' || element === 'link') {
hrefs.push(get_href(attrs));
} else {
if (element === 'img') {
hrefs.push(get_src(attrs));
}
hrefs.push(...get_srcset_urls(attrs));
}
hrefs = hrefs.filter(Boolean);
for (const href of hrefs) {
const resolved = Url.resolve(path$1, href);
if (resolved[0] !== '/') continue;
const parsed = Url.parse(resolved);
const parts = parsed.pathname.slice(1).split('/').filter(Boolean);
if (parts[parts.length - 1] === 'index.html') parts.pop();
const file_exists =
(parsed.pathname.startsWith('/_app/') && fs__default['default'].existsSync(`${dir}/client/${parsed.pathname}`)) ||
fs__default['default'].existsSync(`${out}${parsed.pathname}`) ||
fs__default['default'].existsSync(`static${parsed.pathname}`) ||
fs__default['default'].existsSync(`static${parsed.pathname}/index.html`);
if (file_exists) continue;
if (parsed.query) ;
await crawl(parsed.pathname);
}
}
}
}
}
const entries = manifest.pages.map((page) => page.path).filter(Boolean);
for (const entry of entries) {
await crawl(entry);
}
}
class Builder {
#generated_files;
#static_files;
#manifest;
constructor({
generated_files,
static_files,
log,
manifest
}) {
this.#generated_files = generated_files;
this.#static_files = static_files;
this.#manifest = manifest;
this.log = log;
}
copy_client_files(dest) {
utils.copy(`${this.#generated_files}/client`, dest, (file) => file[0] !== '.');
}
copy_server_files(dest) {
utils.copy(`${this.#generated_files}/server`, dest, (file) => file[0] !== '.');
}
copy_static_files(dest) {
utils.copy(this.#static_files, dest);
}
async prerender({
force = false,
dest
}) {
await prerender({
out: dest,
force,
dir: this.#generated_files,
manifest: this.#manifest,
log: this.log
});
}
}
async function adapt(config) {
if (!config.adapter) {
throw new Error('No adapter specified');
}
if (typeof config.adapter !== 'string') {
// TODO
throw new Error('Adapter must be a string');
}
const log = utils.logger();
console.log(_package.$.bold().cyan(`\n> Using ${config.adapter}`));
const manifest_file = path.resolve('.svelte/build/manifest.cjs');
if (!fs.existsSync(manifest_file)) {
throw new Error('Could not find manifest file. Have you run `svelte-kit build`?');
}
const manifest = require(manifest_file);
const builder = new Builder({
generated_files: '.svelte/build/optimized',
static_files: config.paths.static,
manifest,
log
});
const adapter = _package.requireRelative_1(config.adapter);
await adapter(builder);
log.success('done');
}
exports.adapt = adapt;
//# sourceMappingURL=index5.js.map