mirror of
https://github.com/LukeHagar/sveltekit-electron-adapter.git
synced 2025-12-10 12:47:49 +00:00
[🚀 Release] v0.0.0
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<!-- all-shields/header-badges:START -->
|
<!-- all-shields/header-badges:START -->
|
||||||
|
|
||||||
[](https://github.com/ptkdev/sveltekit-electron-adapter/blob/main/CHANGELOG.md) [](https://www.npmjs.com/package/@ptkdev/sveltekit-electron-adapter) [](https://github.com/ptkdev/sveltekit-electron-adapter/blob/main/LICENSE.md) [](https://www.typescriptlang.org/) [](https://grammy.dev/) [](https://github.com/tc39/ecma262) [](https://discord.ptkdev.io)
|
[](https://github.com/ptkdev/sveltekit-electron-adapter/blob/main/CHANGELOG.md) [](https://www.npmjs.com/package/@ptkdev/sveltekit-electron-adapter) [](https://github.com/ptkdev/sveltekit-electron-adapter/blob/main/LICENSE.md) [](https://www.typescriptlang.org/) [](https://grammy.dev/) [](https://github.com/tc39/ecma262) [](https://discord.ptkdev.io)
|
||||||
|
|
||||||
<!-- all-shields/header-badges:END -->
|
<!-- all-shields/header-badges:END -->
|
||||||
|
|
||||||
|
|||||||
13
index.d.ts
vendored
Normal file
13
index.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Adapter } from "@sveltejs/kit";
|
||||||
|
|
||||||
|
export interface AdapterOptions {
|
||||||
|
pages?: string;
|
||||||
|
assets?: string;
|
||||||
|
fallback?: string;
|
||||||
|
precompress?: boolean;
|
||||||
|
strict?: boolean;
|
||||||
|
policy?: string;
|
||||||
|
viewport?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function plugin(options?: AdapterOptions): Adapter;
|
||||||
175
index.js
Normal file
175
index.js
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
|
import path from "path";
|
||||||
|
import glob from "tiny-glob";
|
||||||
|
import replace from "replace-in-file";
|
||||||
|
import { platforms } from "./platforms.js";
|
||||||
|
|
||||||
|
/** @type {import('.').default} */
|
||||||
|
export default function (options) {
|
||||||
|
return {
|
||||||
|
name: "@ptkdev/sveltekit-electron-adapter",
|
||||||
|
|
||||||
|
async adapt(builder) {
|
||||||
|
if (!options?.fallback) {
|
||||||
|
/** @type {string[]} */
|
||||||
|
const dynamic_routes = [];
|
||||||
|
|
||||||
|
// this is a bit of a hack — it allows us to know whether there are dynamic
|
||||||
|
// (i.e. prerender = false/'auto') routes without having dedicated API
|
||||||
|
// surface area for it
|
||||||
|
builder.createEntries((route) => {
|
||||||
|
dynamic_routes.push(route.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: "",
|
||||||
|
filter: () => false,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
complete: () => {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dynamic_routes.length > 0 && options?.strict !== false) {
|
||||||
|
const prefix = path.relative(".", builder.config.kit.files.routes);
|
||||||
|
const has_param_routes = dynamic_routes.some((route) => route.includes("["));
|
||||||
|
const config_option =
|
||||||
|
has_param_routes || JSON.stringify(builder.config.kit.prerender.entries) !== '["*"]'
|
||||||
|
? ` - adjust the \`prerender.entries\` config option ${
|
||||||
|
has_param_routes
|
||||||
|
? "(routes with parameters are not part of entry points by default)"
|
||||||
|
: ""
|
||||||
|
} — see https://kit.svelte.dev/docs/configuration#prerender for more info.`
|
||||||
|
: "";
|
||||||
|
|
||||||
|
builder.log.error(
|
||||||
|
`@ptkdev/sveltekit-electron-adapter: all routes must be fully prerenderable, but found the following routes that are dynamic:
|
||||||
|
${dynamic_routes.map((id) => ` - ${path.posix.join(prefix, id)}`).join("\n")}
|
||||||
|
|
||||||
|
You have the following options:
|
||||||
|
- set the \`fallback\` option — see https://github.com/sveltejs/kit/tree/master/packages/adapter-static#spa-mode for more info.
|
||||||
|
- add \`export const prerender = true\` to your root \`+layout.js/.ts\` or \`+layout.server.js/.ts\` file. This will try to prerender all pages.
|
||||||
|
- add \`export const prerender = true\` to any \`+server.js/ts\` files that are not fetched by page \`load\` functions.
|
||||||
|
${config_option}
|
||||||
|
- pass \`strict: false\` to \`adapter-static\` to ignore this error. Only do this if you are sure you don't need the routes in question in your final app, as they will be unavailable. See https://github.com/sveltejs/kit/tree/master/packages/adapter-static#strict for more info.
|
||||||
|
|
||||||
|
If this doesn't help, you may need to use a different adapter. @ptkdev/sveltekit-electron-adapter can only be used for sites that don't need a server for dynamic rendering, and can run on just a static file server.
|
||||||
|
See https://kit.svelte.dev/docs/page-options#prerender for more details`,
|
||||||
|
);
|
||||||
|
throw new Error("Encountered dynamic routes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const platform = platforms.find((platform) => platform.test());
|
||||||
|
|
||||||
|
if (platform) {
|
||||||
|
if (options) {
|
||||||
|
builder.log.warn(
|
||||||
|
`Detected ${platform.name}. Please remove adapter-static options to enable zero-config mode`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
builder.log.info(`Detected ${platform.name}, using zero-config mode`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
pages = "build",
|
||||||
|
assets = pages,
|
||||||
|
fallback,
|
||||||
|
precompress,
|
||||||
|
} = options ?? platform?.defaults ?? /** @type {import('./index').AdapterOptions} */ ({});
|
||||||
|
|
||||||
|
builder.rimraf(assets);
|
||||||
|
builder.rimraf(pages);
|
||||||
|
|
||||||
|
builder.writeClient(assets);
|
||||||
|
builder.writePrerendered(pages);
|
||||||
|
|
||||||
|
const HTML_pages = await glob("**/*.html", {
|
||||||
|
cwd: pages,
|
||||||
|
dot: true,
|
||||||
|
absolute: true,
|
||||||
|
filesOnly: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
HTML_pages.forEach(async (path) => {
|
||||||
|
let href = path.split("/").pop();
|
||||||
|
|
||||||
|
let regex_input = new RegExp(`href="/${href.replace(".html", "")}"`, "g");
|
||||||
|
let regex_replace = `href="${`./${href}`}"`;
|
||||||
|
|
||||||
|
if (href === "index.html") {
|
||||||
|
regex_input = new RegExp(`href="/"`, "g");
|
||||||
|
regex_replace = `href="./index.html"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await replace.sync({
|
||||||
|
files: [`${pages}/**/*.html`],
|
||||||
|
// @ts-ignore
|
||||||
|
processor: (input) => input.replace(regex_input, regex_replace),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let regex_input = new RegExp(`http-equiv="content-security-policy" content=""`, "g");
|
||||||
|
const policy = "default-src 'self'; script-src 'self'";
|
||||||
|
let regex_replace = `http-equiv="content-security-policy" content="${
|
||||||
|
options?.policy ? options.policy : policy
|
||||||
|
}"`;
|
||||||
|
|
||||||
|
await replace.sync({
|
||||||
|
files: [`${pages}/**/*.html`],
|
||||||
|
// @ts-ignore
|
||||||
|
processor: (input) => input.replace(regex_input, regex_replace),
|
||||||
|
});
|
||||||
|
|
||||||
|
const HTML_assets = await glob("_app/**/*", {
|
||||||
|
cwd: pages,
|
||||||
|
dot: true,
|
||||||
|
absolute: false,
|
||||||
|
filesOnly: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
HTML_assets.forEach(async () => {
|
||||||
|
let regex_input = new RegExp(`[^.](/_app/immutable)`, "g");
|
||||||
|
let regex_replace = `./_app/immutable`;
|
||||||
|
|
||||||
|
await replace.sync({
|
||||||
|
files: [`${pages}/**/*`],
|
||||||
|
// @ts-ignore
|
||||||
|
processor: (input) => input.replace(regex_input, regex_replace),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
regex_input = new RegExp(`name="viewport" content="width=device-width"`, "g");
|
||||||
|
const viewport = "width=device-width, initial-scale=1.0, viewport-fit=cover";
|
||||||
|
regex_replace = `name="viewport" content="${options?.viewport ? options.viewport : viewport}"`;
|
||||||
|
|
||||||
|
await replace.sync({
|
||||||
|
files: [`${pages}/**/*.html`],
|
||||||
|
// @ts-ignore
|
||||||
|
processor: (input) => input.replace(regex_input, regex_replace),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fallback) {
|
||||||
|
builder.generateFallback(path.join(pages, fallback));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (precompress) {
|
||||||
|
builder.log.minor("Compressing assets and pages");
|
||||||
|
if (pages === assets) {
|
||||||
|
await builder.compress(assets);
|
||||||
|
} else {
|
||||||
|
await Promise.all([builder.compress(assets), builder.compress(pages)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pages === assets) {
|
||||||
|
builder.log(`Wrote site to "${pages}"`);
|
||||||
|
} else {
|
||||||
|
builder.log(`Wrote pages to "${pages}" and assets to "${assets}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options) {
|
||||||
|
platform?.done(builder);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "@ptkdev/sveltekit-electron-adapter",
|
"name": "@ptkdev/sveltekit-electron-adapter",
|
||||||
"displayName": "SvelteKit Adapter for Desktop Apps with Electron",
|
"displayName": "SvelteKit Adapter for Desktop Apps with Electron",
|
||||||
"description": "Adapter for SvelteKit apps that prerenders your entire site as a collection of static files for use with Electron",
|
"description": "Adapter for SvelteKit apps that prerenders your entire site as a collection of static files for use with Electron",
|
||||||
"version": "0.2.1-nightly.0",
|
"version": "0.0.0",
|
||||||
"main": "./index.js",
|
"main": "./index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
75
platforms.js
Normal file
75
platforms.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* name: string;
|
||||||
|
* test: () => boolean;
|
||||||
|
* defaults: import('./index').AdapterOptions;
|
||||||
|
* done: (builder: import('@sveltejs/kit').Builder) => void;
|
||||||
|
* }}
|
||||||
|
* Platform */
|
||||||
|
|
||||||
|
// This function is duplicated in adapter-vercel
|
||||||
|
/** @param {import('@sveltejs/kit').Builder} builder */
|
||||||
|
function static_vercel_config(builder) {
|
||||||
|
/** @type {any[]} */
|
||||||
|
const prerendered_redirects = [];
|
||||||
|
|
||||||
|
/** @type {Record<string, { path: string }>} */
|
||||||
|
const overrides = {};
|
||||||
|
|
||||||
|
for (const [src, redirect] of builder.prerendered.redirects) {
|
||||||
|
prerendered_redirects.push({
|
||||||
|
src,
|
||||||
|
headers: {
|
||||||
|
Location: redirect.location,
|
||||||
|
},
|
||||||
|
status: redirect.status,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [path, page] of builder.prerendered.pages) {
|
||||||
|
if (path.endsWith("/") && path !== "/") {
|
||||||
|
prerendered_redirects.push(
|
||||||
|
{ src: path, dest: path.slice(0, -1) },
|
||||||
|
{ src: path.slice(0, -1), status: 308, headers: { Location: path } },
|
||||||
|
);
|
||||||
|
|
||||||
|
overrides[page.file] = { path: path.slice(1, -1) };
|
||||||
|
} else {
|
||||||
|
overrides[page.file] = { path: path.slice(1) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
version: 3,
|
||||||
|
routes: [
|
||||||
|
...prerendered_redirects,
|
||||||
|
{
|
||||||
|
src: `/${builder.getAppPath()}/immutable/.+`,
|
||||||
|
headers: {
|
||||||
|
"cache-control": "public, immutable, max-age=31536000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
handle: "filesystem",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Platform[]} */
|
||||||
|
export const platforms = [
|
||||||
|
{
|
||||||
|
name: "Vercel",
|
||||||
|
test: () => !!process.env.VERCEL,
|
||||||
|
defaults: {
|
||||||
|
pages: ".vercel/output/static",
|
||||||
|
},
|
||||||
|
done: (builder) => {
|
||||||
|
const config = static_vercel_config(builder);
|
||||||
|
fs.writeFileSync(".vercel/output/config.json", JSON.stringify(config, null, " "));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
Reference in New Issue
Block a user