Skip to content

Commit

Permalink
feat: "download app" possibility (#1051)
Browse files Browse the repository at this point in the history
* feat: "download app" possibility

closes #827

* lint

* maybe this?

* use SvelteKit instead

* integrate into menu

* move here

* remove outdated comment

* add css from playground
  • Loading branch information
dummdidumm authored Dec 23, 2024
1 parent a940812 commit 6e4cbb3
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 3 deletions.
2 changes: 2 additions & 0 deletions apps/svelte.dev/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
/src/routes/_home/Supporters/contributors.js
/src/routes/_home/Supporters/donors.jpg
/src/routes/_home/Supporters/donors.js
/scripts/svelte-template
/static/svelte-template.json

# git-repositories of synced docs go here
/repos/
Expand Down
2 changes: 2 additions & 0 deletions apps/svelte.dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"ansi-to-html": "^0.7.2",
"base64-js": "^1.5.1",
"cookie": "^0.7.0",
"do-not-zip": "^1.0.0",
"d3-geo": "^3.1.0",
"d3-geo-projection": "^4.0.0",
"editor": "workspace:*",
Expand Down Expand Up @@ -72,6 +73,7 @@
"prettier-plugin-svelte": "^3.3.2",
"satori": "^0.10.13",
"satori-html": "^0.3.2",
"sv": "^0.6.8",
"svelte": "5.14.0",
"svelte-check": "^4.1.1",
"svelte-preprocess": "^6.0.3",
Expand Down
86 changes: 86 additions & 0 deletions apps/svelte.dev/scripts/get_svelte_template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// @ts-check
import { readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { create } from 'sv';

// This download the currente Vite template from Github, adjusts it to our needs, and saves it to static/svelte-template.json
// This is used by the Svelte REPL as part of the "download project" feature

const force = process.env.FORCE_UPDATE === 'true';
const output_file = fileURLToPath(new URL('../static/svelte-template.json', import.meta.url));
const output_dir = fileURLToPath(new URL('./svelte-template', import.meta.url));

try {
if (!force && statSync(output_file)) {
console.info(`[update/template] ${output_file} exists. Skipping`);
process.exit(0);
}
} catch {
// create Svelte-Kit skelton app
create(output_dir, { template: 'minimal', types: 'typescript', name: 'your-app' });

function get_all_files(dir) {
const files = [];
const items = readdirSync(dir, { withFileTypes: true });

for (const item of items) {
const full_path = join(dir, item.name);
if (item.isDirectory()) {
files.push(...get_all_files(full_path));
} else {
files.push(full_path.replaceAll('\\', '/'));
}
}

return files;
}

const all_files = get_all_files(output_dir);
const files = [];

for (let path of all_files) {
const bytes = readFileSync(path);
const string = bytes.toString();
let data = bytes.compare(Buffer.from(string)) === 0 ? string : [...bytes];

if (path.endsWith('routes/+page.svelte')) {
data = `<script>\n\timport '../app.css';\n\timport App from './App.svelte';\n</script>\n\n<App />\n`;
}

files.push({ path: path.slice(output_dir.length + 1), data });
}

files.push({
path: 'src/routes/+page.js',
data:
"// Because we don't know whether or not your playground app can run in a server environment, we disable server-side rendering.\n" +
'// Make sure to test whether or not you can re-enable it, as SSR improves perceived performance and site accessibility.\n' +
'// Read more about this option here: https://svelte.dev/docs/kit/page-options#ssr\n' +
'export const ssr = false;\n'
});

// add CSS styles from playground to the project
const html = readFileSync(
join(output_dir, '../../../../packages/repl/src/lib/Output/srcdoc/index.html'),
{ encoding: 'utf-8' }
);
const css = html
.slice(html.indexOf('<style>') + 7, html.indexOf('</style>'))
.split('\n')
.map((line) =>
// remove leading \t
line.slice(3)
)
.join('\n')
.trimStart();
files.push({
path: 'src/app.css',
data: css
});

writeFileSync(output_file, JSON.stringify(files));

// remove output dir afterwards to prevent it messing with Vite watcher
rmSync(output_dir, { force: true, recursive: true });
}
1 change: 1 addition & 0 deletions apps/svelte.dev/scripts/update.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ const env = {

fork(`${dir}/get_contributors.js`, { env });
fork(`${dir}/get_donors.js`, { env });
fork(`${dir}/get_svelte_template.js`, { env });
40 changes: 40 additions & 0 deletions apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script lang="ts">
// @ts-expect-error no types
import * as doNotZip from 'do-not-zip';
import { browser } from '$app/environment';
import { afterNavigate, goto, replaceState } from '$app/navigation';
import type { Gist } from '$lib/db/types';
Expand Down Expand Up @@ -113,6 +115,43 @@
}
}
async function download() {
const { files: components, imports } = repl.toJSON();
const files: Array<{ path: string; data: string }> = await (
await fetch('/svelte-template.json')
).json();
if (imports.length > 0) {
const idx = files.findIndex(({ path }) => path === 'package.json');
const pkg = JSON.parse(files[idx].data);
const { devDependencies } = pkg;
imports.forEach((mod) => {
const match = /^(@[^/]+\/)?[^@/]+/.exec(mod)!;
devDependencies[match[0]] = 'latest';
});
pkg.devDependencies = devDependencies;
files[idx].data = JSON.stringify(pkg, null, ' ');
}
files.push(
...components.map((component) => ({
path: `src/routes/${component.name}`,
data: (component as File).contents
}))
);
const url = URL.createObjectURL(doNotZip.toBlob(files));
const link = document.createElement('a');
link.href = url;
link.download = 'svelte-app.zip';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(url);
link.remove();
}
async function update_hash() {
// Only change hash when necessary to avoid polluting everyone's browser history
if (modified) {
Expand Down Expand Up @@ -200,6 +239,7 @@
{can_escape}
injectedJS={mapbox_setup}
{onchange}
{download}
previewTheme={$theme.current}
/>
</div>
Expand Down
7 changes: 6 additions & 1 deletion packages/repl/src/lib/Input/ComponentSelector.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
workspace: Workspace;
can_migrate: boolean;
migrate: () => void;
download?: () => void;
}
let { runes, onchange, workspace, can_migrate, migrate }: Props = $props();
let { runes, onchange, workspace, can_migrate, migrate, download }: Props = $props();
let input = $state() as HTMLInputElement;
let input_value = $state(workspace.current.name);
Expand Down Expand Up @@ -170,6 +171,10 @@
</label>

<button disabled={!can_migrate} onclick={migrate}>Migrate to Svelte 5, if possible</button>

{#if download}
<button onclick={download}>Download app</button>
{/if}
</Toolbox>
</div>
</div>
Expand Down
6 changes: 4 additions & 2 deletions packages/repl/src/lib/Repl.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
injectedCSS?: string;
previewTheme?: 'light' | 'dark';
onchange?: () => void;
download?: () => void;
}
let {
Expand All @@ -37,7 +38,8 @@
injectedJS = '',
injectedCSS = '',
previewTheme = 'light',
onchange = () => {}
onchange = () => {},
download
}: Props = $props();
// TODO pass in real data
Expand Down Expand Up @@ -174,7 +176,7 @@
>
{#snippet a()}
<section>
<ComponentSelector {runes} {onchange} {workspace} {can_migrate} {migrate} />
<ComponentSelector {runes} {onchange} {workspace} {can_migrate} {migrate} {download} />

<Editor {workspace} />
</section>
Expand Down
17 changes: 17 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 6e4cbb3

Please sign in to comment.