diff --git a/apps/svelte.dev/.gitignore b/apps/svelte.dev/.gitignore
index a3079a3c21..b45f37b025 100644
--- a/apps/svelte.dev/.gitignore
+++ b/apps/svelte.dev/.gitignore
@@ -8,6 +8,7 @@
/src/routes/_home/Supporters/donors.js
/scripts/svelte-template
/static/svelte-template.json
+/static/svelte-tailwind-template.json
# git-repositories of synced docs go here
/repos/
diff --git a/apps/svelte.dev/scripts/get_svelte_tailwind_template.js b/apps/svelte.dev/scripts/get_svelte_tailwind_template.js
new file mode 100644
index 0000000000..5fec9acde7
--- /dev/null
+++ b/apps/svelte.dev/scripts/get_svelte_tailwind_template.js
@@ -0,0 +1,102 @@
+// @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 viteConfigTailwind =
+ "import { sveltekit } from '@sveltejs/kit/vite';\nimport { defineConfig } from 'vite';\nimport tailwindcss from '@tailwindcss/vite'\nexport default defineConfig({\n\tplugins: [sveltekit(),tailwindcss()]\n});\n";
+
+const force = process.env.FORCE_UPDATE === 'true';
+const output_file = fileURLToPath(
+ new URL('../static/svelte-tailwind-template.json', import.meta.url)
+);
+const output_dir = fileURLToPath(new URL('./svelte-tailwind-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];
+
+ // vite config to use along with Tailwind CSS
+ if (path.endsWith('vite.config.ts')) {
+ files.push({
+ path: 'vite.config.ts',
+ data: viteConfigTailwind
+ });
+ }
+
+ // add Tailwind CSS as devDependencies
+ if (path.endsWith('package.json')) {
+ try {
+ const pkg = JSON.parse(string);
+
+ pkg.devDependencies ||= {};
+ pkg.devDependencies['tailwindcss'] = '^4.1.8';
+ pkg.devDependencies['@tailwindcss/vite'] = '^4.1.8';
+
+ data = JSON.stringify(pkg, null, 2); // Pretty-print with 2 spaces
+ } catch (err) {
+ console.error('Failed to parse package.json:', err);
+ }
+ }
+
+ if (path.endsWith('routes/+page.svelte')) {
+ data = `\n\n\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
+
+ files.push({
+ path: 'src/app.css',
+ data: '@import "tailwindcss";'
+ });
+
+ writeFileSync(output_file, JSON.stringify(files));
+
+ // remove output dir afterwards to prevent it messing with Vite watcher
+ rmSync(output_dir, { force: true, recursive: true });
+}
diff --git a/apps/svelte.dev/scripts/get_svelte_template.js b/apps/svelte.dev/scripts/get_svelte_template.js
index d525eaa3df..7fc7eba0a3 100644
--- a/apps/svelte.dev/scripts/get_svelte_template.js
+++ b/apps/svelte.dev/scripts/get_svelte_template.js
@@ -66,7 +66,7 @@ try {
{ encoding: 'utf-8' }
);
const css = html
- .slice(html.indexOf(''))
+ .slice(html.indexOf(''))
.split('\n')
.map((line) =>
// remove leading \t
diff --git a/apps/svelte.dev/scripts/update.js b/apps/svelte.dev/scripts/update.js
index 9c25d958dc..7ec5ef95fe 100644
--- a/apps/svelte.dev/scripts/update.js
+++ b/apps/svelte.dev/scripts/update.js
@@ -9,4 +9,5 @@ const env = {
fork(`${dir}/get_contributors.js`, { env });
fork(`${dir}/get_donors.js`, { env });
+fork(`${dir}/get_svelte_tailwind_template.js`, { env });
fork(`${dir}/get_svelte_template.js`, { env });
diff --git a/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte b/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte
index 134402a78e..9761d10749 100644
--- a/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte
+++ b/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte
@@ -97,10 +97,12 @@
}
async function download() {
- const { files: components, imports } = repl.toJSON();
+ const { files: components, imports, tailwind } = repl.toJSON();
const files: Array<{ path: string; data: string }> = await (
- await fetch('/svelte-template.json')
+ tailwind
+ ? await fetch('/svelte-tailwind-template.json')
+ : await fetch('/svelte-template.json')
).json();
if (imports.length > 0) {