sv 1.0.0-next.5 → 1.0.0-next.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2306 @@
1
+ import { T as getSharedFiles, _ as defineAddon, dt as R, v as defineAddonOptions } from "./engine-w0Kq5hjN.mjs";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { Walker, coerceVersion, color, createPrinter, dedent, defineEnv, fileExists, isKit3, loadPackageJson, pnpm, resolveCommandArray, resolveLibPrefix, sanitizeName, svelteConfig, transforms } from "@sveltejs/sv-utils";
5
+ import V from "node:process";
6
+ import crypto from "node:crypto";
7
+ //#region src/addons/ai-tools.ts
8
+ const REGEX_MD = /\.md$/;
9
+ const CLIENTS = {
10
+ "claude-code": {
11
+ label: "Claude Code",
12
+ pluginSettings: {
13
+ path: ".claude/settings.json",
14
+ marketplace: "svelte",
15
+ repo: "sveltejs/ai-tools",
16
+ id: "svelte@svelte"
17
+ },
18
+ agentPath: "AGENTS.md",
19
+ agentLink: {
20
+ path: ".claude/CLAUDE.md",
21
+ contents: "@../AGENTS.md\n"
22
+ },
23
+ configPath: ".mcp.json",
24
+ skillsPath: ".claude/skills",
25
+ agentsPath: ".claude/agents",
26
+ mcpOptions: {
27
+ typeLocal: "stdio",
28
+ typeRemote: "http",
29
+ env: true
30
+ }
31
+ },
32
+ cursor: {
33
+ label: "Cursor",
34
+ agentPath: "AGENTS.md",
35
+ configPath: ".cursor/mcp.json",
36
+ skillsPath: ".cursor/skills",
37
+ agentsPath: ".cursor/agents",
38
+ mcpOptions: {}
39
+ },
40
+ gemini: {
41
+ label: "Gemini",
42
+ agentPath: "GEMINI.md",
43
+ configPath: ".gemini/settings.json",
44
+ skillsPath: ".gemini/skills",
45
+ agentsPath: ".gemini/agents",
46
+ schema: "https://raw.githubusercontent.com/google-gemini/gemini-cli/main/schemas/settings.schema.json",
47
+ mcpOptions: {}
48
+ },
49
+ opencode: {
50
+ label: "OpenCode",
51
+ pluginOnly: true,
52
+ agentPath: "AGENTS.md",
53
+ configPath: ".opencode/opencode.json",
54
+ schema: "https://opencode.ai/config.json",
55
+ customData: { plugin: ["@sveltejs/opencode"] },
56
+ extraFiles: [{
57
+ path: ".opencode/svelte.json",
58
+ data: { $schema: "https://svelte.dev/opencode/schema.json" }
59
+ }]
60
+ },
61
+ vscode: {
62
+ label: "VS Code",
63
+ agentPath: "AGENTS.md",
64
+ configPath: ".vscode/mcp.json",
65
+ skillsPath: ".github/skills",
66
+ agentsPath: ".github/agents",
67
+ agentExtension: ".agent.md",
68
+ mcpOptions: { serversKey: "servers" }
69
+ },
70
+ other: {
71
+ label: "Other",
72
+ agentPath: "AGENTS.md"
73
+ }
74
+ };
75
+ const acceptsTools = (client) => Boolean(client && !client.pluginOnly && (client.skillsPath || client.agentsPath || client.configPath));
76
+ const wantsLooseTools = (client, delivery) => acceptsTools(client) && (!client.pluginSettings || delivery !== "plugin");
77
+ const wantsMcpConfig = (client, delivery) => Boolean(client?.mcpOptions) && wantsLooseTools(client, delivery);
78
+ const TOOLS = {
79
+ mcp: {
80
+ label: "MCP server",
81
+ kind: "mcp"
82
+ },
83
+ "svelte-code-writer": {
84
+ label: "svelte-code-writer",
85
+ kind: "skill",
86
+ hint: "skill"
87
+ },
88
+ "svelte-core-bestpractices": {
89
+ label: "svelte-core-bestpractices",
90
+ kind: "skill",
91
+ hint: "skill"
92
+ },
93
+ "svelte-file-editor": {
94
+ label: "svelte-file-editor",
95
+ kind: "agent",
96
+ hint: "sub-agent"
97
+ }
98
+ };
99
+ var ai_tools_default = defineAddon({
100
+ id: "ai-tools",
101
+ shortDescription: "Svelte AI tools",
102
+ homepage: "https://svelte.dev/docs/ai",
103
+ options: defineAddonOptions().add("ide", {
104
+ question: "Which client would you like to use?",
105
+ type: "multiselect",
106
+ default: [],
107
+ options: Object.entries(CLIENTS).map(([value, client]) => ({
108
+ value,
109
+ label: client.label
110
+ })),
111
+ required: true
112
+ }).add("delivery", {
113
+ question: "How would you like to add the Svelte tools?",
114
+ type: "select",
115
+ default: "plugin",
116
+ options: [{
117
+ value: "plugin",
118
+ label: "Svelte plugin",
119
+ hint: "recommended, auto-installs & updates"
120
+ }, {
121
+ value: "tools",
122
+ label: "Individual tools",
123
+ hint: "choose exactly what to add"
124
+ }],
125
+ condition: ({ ide }) => ide.some((i) => CLIENTS[i]?.pluginSettings)
126
+ }).add("tools", {
127
+ question: "Which tools would you like to add?",
128
+ type: "multiselect",
129
+ default: Object.keys(TOOLS),
130
+ options: Object.entries(TOOLS).map(([value, t]) => ({
131
+ value,
132
+ label: t.label,
133
+ hint: t.hint
134
+ })),
135
+ required: false,
136
+ condition: ({ ide, delivery }) => ide.some((i) => wantsLooseTools(CLIENTS[i], delivery))
137
+ }).add("mcpSetup", {
138
+ question: "Which MCP setup would you like to use?",
139
+ type: "select",
140
+ default: "remote",
141
+ options: [{
142
+ value: "local",
143
+ label: "Local",
144
+ hint: "will use stdio"
145
+ }, {
146
+ value: "remote",
147
+ label: "Remote",
148
+ hint: "will use a remote endpoint"
149
+ }],
150
+ condition: ({ ide, delivery, tools }) => (tools === void 0 || tools.includes("mcp")) && ide.some((i) => wantsMcpConfig(CLIENTS[i], delivery))
151
+ }).build(),
152
+ run: ({ sv, options }) => {
153
+ const usePlugin = options.delivery === "plugin";
154
+ const selected = options.tools ?? Object.keys(TOOLS);
155
+ const selectedSkills = selected.filter((t) => TOOLS[t]?.kind === "skill");
156
+ const selectedAgents = selected.filter((t) => TOOLS[t]?.kind === "agent");
157
+ const wantsMcp = selected.includes("mcp");
158
+ const getLocalConfig = (o) => {
159
+ return {
160
+ ...o?.typeLocal ? { type: o.typeLocal } : {},
161
+ command: o?.command ?? "npx",
162
+ ...o?.env ? { env: {} } : {},
163
+ ...o?.args === null ? {} : { args: o?.args ?? ["-y", "@sveltejs/mcp"] }
164
+ };
165
+ };
166
+ const getRemoteConfig = (o) => {
167
+ return {
168
+ ...o?.typeRemote ? { type: o.typeRemote } : {},
169
+ url: "https://mcp.svelte.dev/mcp"
170
+ };
171
+ };
172
+ const filesAdded = [];
173
+ const filesExistingAlready = [];
174
+ const sharedFiles = getSharedFiles();
175
+ const mcpFiles = sharedFiles.filter((file) => file.include.includes("mcp"));
176
+ const skillFiles = sharedFiles.filter((file) => file.include.includes("skills"));
177
+ const agentFiles = sharedFiles.filter((file) => file.include.includes("agents"));
178
+ const agentFile = mcpFiles.find((file) => file.name === "AGENTS.md");
179
+ const addFile = (path, contents) => {
180
+ sv.file(path, (content) => {
181
+ if (content) {
182
+ filesExistingAlready.push(path);
183
+ return false;
184
+ }
185
+ if (!filesAdded.includes(path)) filesAdded.push(path);
186
+ return contents;
187
+ });
188
+ };
189
+ for (const ide of options.ide) {
190
+ const client = CLIENTS[ide];
191
+ if (!client) continue;
192
+ if (client.pluginSettings && usePlugin) {
193
+ const plugin = client.pluginSettings;
194
+ sv.file(plugin.path, transforms.json(({ data }) => {
195
+ data.extraKnownMarketplaces ??= {};
196
+ data.extraKnownMarketplaces[plugin.marketplace] ??= { source: {
197
+ source: "github",
198
+ repo: plugin.repo
199
+ } };
200
+ data.enabledPlugins ??= {};
201
+ data.enabledPlugins[plugin.id] = true;
202
+ }));
203
+ continue;
204
+ }
205
+ if (!client.agentPath) continue;
206
+ const { mcpOptions, agentPath, agentLink, configPath, skillsPath, agentsPath, agentExtension, schema, customData, extraFiles } = client;
207
+ if (!filesAdded.includes(agentPath)) sv.file(agentPath, (content) => {
208
+ if (content) {
209
+ if (!filesExistingAlready.includes(agentPath)) filesExistingAlready.push(agentPath);
210
+ return false;
211
+ }
212
+ filesAdded.push(agentPath);
213
+ return agentFile?.contents ?? "";
214
+ });
215
+ if (agentLink) addFile(agentLink.path, agentLink.contents);
216
+ const writeMcp = Boolean(mcpOptions) && wantsMcp;
217
+ if (configPath && (writeMcp || customData)) sv.file(configPath, transforms.json(({ data }) => {
218
+ if (schema) data["$schema"] = schema;
219
+ if (customData) for (const [key, value] of Object.entries(customData)) data[key] = value;
220
+ if (writeMcp) {
221
+ const key = mcpOptions.serversKey ?? "mcpServers";
222
+ data[key] ??= {};
223
+ data[key].svelte = options.mcpSetup === "local" ? getLocalConfig(mcpOptions) : getRemoteConfig(mcpOptions);
224
+ }
225
+ }));
226
+ if (extraFiles) for (const extra of extraFiles) sv.file(extra.path, transforms.json(({ data }) => {
227
+ for (const [key, value] of Object.entries(extra.data)) data[key] = value;
228
+ }));
229
+ if (skillsPath) for (const file of skillFiles) {
230
+ if (!selectedSkills.includes(file.name.split("/")[0])) continue;
231
+ addFile(`${skillsPath}/${file.name}`, file.contents);
232
+ }
233
+ if (agentsPath) for (const file of agentFiles) {
234
+ if (!selectedAgents.includes(file.name.replace(REGEX_MD, ""))) continue;
235
+ const ext = agentExtension ?? ".md";
236
+ addFile(`${agentsPath}/${file.name.replace(REGEX_MD, ext)}`, file.contents);
237
+ }
238
+ }
239
+ if (filesExistingAlready.length > 0) R.warn(`${filesExistingAlready.map((path) => color.path(path)).join(", ")} already exists, we didn't touch ${filesExistingAlready.length > 1 ? "them" : "it"}. See ${color.website("https://svelte.dev/docs/ai")} for manual setup.`);
240
+ },
241
+ nextSteps({ options, cwd }) {
242
+ const steps = [];
243
+ for (const ide of options.ide) {
244
+ const client = CLIENTS[ide];
245
+ if (!client?.pluginSettings) continue;
246
+ const plugin = client.pluginSettings;
247
+ const settingsPath = path.resolve(cwd, plugin.path);
248
+ if (!fs.existsSync(settingsPath)) continue;
249
+ let pluginEnabled = false;
250
+ try {
251
+ pluginEnabled = Boolean(JSON.parse(fs.readFileSync(settingsPath, "utf8")).enabledPlugins?.[plugin.id]);
252
+ } catch {}
253
+ if (!pluginEnabled) continue;
254
+ const looseDirs = [client.skillsPath, client.agentsPath].filter((dir) => Boolean(dir) && fs.existsSync(path.resolve(cwd, dir)) && fs.readdirSync(path.resolve(cwd, dir)).some((entry) => Object.keys(TOOLS).includes(entry.replace(REGEX_MD, ""))));
255
+ if (looseDirs.length === 0) continue;
256
+ steps.push(`${client.label}: the Svelte plugin is enabled and ${looseDirs.map((d) => color.path(d)).join(" + ")} also exist${looseDirs.length > 1 ? "" : "s"} - remove ${looseDirs.length > 1 ? "them" : "it"} (or disable the plugin) to avoid duplicate skills/sub-agents.`);
257
+ }
258
+ if (options.delivery === "plugin" && options.ide.includes("claude-code")) steps.push(`Open the project in Claude Code and trust the workspace - the Svelte plugin installs automatically.`);
259
+ if (options.ide.includes("other")) steps.push(`For other clients: ${color.website(`https://svelte.dev/docs/ai/${options.mcpSetup ?? "remote"}-setup#Other-clients`)}`);
260
+ return steps;
261
+ }
262
+ });
263
+ //#endregion
264
+ //#region src/addons/common.ts
265
+ const ESLINT_VERSION = "^10.4.1";
266
+ const addEslintConfigPrettier = transforms.script(({ ast, js }) => {
267
+ const sveltePluginImport = ast.body.filter((n) => n.type === "ImportDeclaration").find((n) => n.type === "ImportDeclaration" && n.source.value === "eslint-plugin-svelte" && n.specifiers?.some((n) => n.type === "ImportDefaultSpecifier"));
268
+ let svelteImportName;
269
+ for (const specifier of sveltePluginImport?.specifiers ?? []) if (specifier.type === "ImportDefaultSpecifier" && specifier.local?.name) svelteImportName = specifier.local.name;
270
+ svelteImportName ??= "svelte";
271
+ js.imports.addDefault(ast, {
272
+ from: "eslint-plugin-svelte",
273
+ as: svelteImportName
274
+ });
275
+ js.imports.addDefault(ast, {
276
+ from: "eslint-config-prettier",
277
+ as: "prettier"
278
+ });
279
+ const fallbackConfig = js.common.parseExpression("[]");
280
+ const eslintConfig = js.exports.createDefault(ast, { fallback: fallbackConfig }).value;
281
+ let elements = [];
282
+ if (eslintConfig.type === "ArrayExpression") elements = eslintConfig.elements;
283
+ else if (eslintConfig.type === "CallExpression") if (eslintConfig.arguments.length === 1 && eslintConfig.arguments[0].type === "ArrayExpression") elements = eslintConfig.arguments[0].elements;
284
+ else elements = eslintConfig.arguments;
285
+ else return false;
286
+ const prettier = js.common.parseExpression("prettier");
287
+ const sveltePrettierConfig = js.common.parseExpression(`${svelteImportName}.configs.prettier`);
288
+ const nodesToInsert = [];
289
+ if (!js.common.contains(eslintConfig, prettier)) nodesToInsert.push(prettier);
290
+ if (!js.common.contains(eslintConfig, sveltePrettierConfig)) nodesToInsert.push(sveltePrettierConfig);
291
+ const isSvelteConfig = (maybeSpread) => {
292
+ const el = maybeSpread?.type === "SpreadElement" ? maybeSpread?.argument : maybeSpread;
293
+ return el?.type === "MemberExpression" && el.object.type === "MemberExpression" && el.object.object.type === "Identifier" && el.object.object.name === svelteImportName && el.object.property.type === "Identifier" && el.object.property.name === "configs";
294
+ };
295
+ const idx = elements.findIndex(isSvelteConfig);
296
+ if (idx !== -1) elements.splice(idx + 1, 0, ...nodesToInsert);
297
+ else elements.push(...nodesToInsert);
298
+ });
299
+ /**
300
+ * Path of the prettier config file we generate. We always emit a `.js` file (with a JSDoc `@type`)
301
+ * rather than `.ts`, since the VSCode prettier extension can't load a `.ts` config without a recent
302
+ * enough Node in its extension host (see prettier/prettier-vscode#3989).
303
+ */
304
+ function prettierConfigPath() {
305
+ return "prettier.config.js";
306
+ }
307
+ /**
308
+ * Creates a `prettier.config.js` file with our defaults. Bails (leaving the file untouched)
309
+ * if a config is already defined.
310
+ */
311
+ const createPrettierConfig = (opts) => transforms.script(({ ast, comments, js }) => {
312
+ if (ast.body.some((node) => node.type === "ExportDefaultDeclaration")) {
313
+ R.warn(`A ${color.warning("prettier")} config already exists. Skipping initialization.`);
314
+ return false;
315
+ }
316
+ const plugins = ["prettier-plugin-svelte"];
317
+ if (opts.tailwind) plugins.push("prettier-plugin-tailwindcss");
318
+ const properties = [
319
+ "useTabs: true",
320
+ "singleQuote: true",
321
+ `trailingComma: 'none'`,
322
+ "printWidth: 100",
323
+ `plugins: [${plugins.map((p) => `'${p}'`).join(", ")}]`,
324
+ `overrides: [{ files: '*.svelte', options: { parser: 'svelte' } }]`
325
+ ];
326
+ if (opts.tailwind && opts.stylesheet) properties.push(`tailwindStylesheet: '${opts.stylesheet}'`);
327
+ js.common.appendFromString(ast, {
328
+ comments,
329
+ code: `
330
+ /** @type {import("prettier").Config} */
331
+ const config = { ${properties.join(", ")} };
332
+ export default config;
333
+ `
334
+ });
335
+ });
336
+ /**
337
+ * Augments an existing `prettier.config.js` config object with the tailwindcss plugin and
338
+ * stylesheet. Used when prettier was added before tailwindcss.
339
+ */
340
+ const addPrettierTailwind = (opts) => transforms.script(({ ast, js }) => {
341
+ if (!ast.body.some((node) => node.type === "ExportDefaultDeclaration")) return false;
342
+ const { value } = js.exports.createDefault(ast, { fallback: js.object.create({}) });
343
+ if (value.type !== "ObjectExpression") return false;
344
+ const plugins = js.object.property(value, {
345
+ name: "plugins",
346
+ fallback: js.array.create()
347
+ });
348
+ if (plugins.type === "ArrayExpression") js.array.append(plugins, "prettier-plugin-tailwindcss");
349
+ if (!value.properties.some((p) => p.type === "Property" && p.key.type === "Identifier" && p.key.name === "tailwindStylesheet")) js.object.property(value, {
350
+ name: "tailwindStylesheet",
351
+ fallback: js.common.createLiteral(opts.stylesheet)
352
+ });
353
+ });
354
+ const addToDemoPage = (path, language) => transforms.svelteScript({ language }, ({ ast, js, svelte }) => {
355
+ for (const node of ast.fragment.nodes) if (node.type === "RegularElement") {
356
+ const hrefAttribute = node.attributes.find((x) => x.type === "Attribute" && x.name === "href");
357
+ if (!hrefAttribute || !hrefAttribute.value) continue;
358
+ if (!Array.isArray(hrefAttribute.value)) continue;
359
+ if (hrefAttribute.value.some((x) => x.type === "Text" && x.data.includes(`/demo/${path}`))) return false;
360
+ }
361
+ js.imports.addNamed(ast.instance.content, {
362
+ imports: ["resolve"],
363
+ from: "$app/paths"
364
+ });
365
+ svelte.addFragment(ast, `<a href={resolve('/demo/${path}')}>${path}</a>`, { mode: "prepend" });
366
+ });
367
+ /**
368
+ * Returns the corresponding `@types/node` version for the version of Node.js running in the current process.
369
+ *
370
+ * If the installed version of Node.js is from a `Current` release, then the major is decremented to
371
+ * the nearest `LTS` release version.
372
+ */
373
+ function getNodeTypesVersion() {
374
+ const nodeVersion = V.versions.node;
375
+ const isDenoOrBun = Boolean(V.versions.deno ?? V.versions.bun);
376
+ const [major] = nodeVersion.split(".");
377
+ const majorNum = Number(major);
378
+ const isEvenMajor = majorNum % 2 === 0;
379
+ if (!!V.release.lts || isDenoOrBun && isEvenMajor) return `^${major}`;
380
+ return `^${isEvenMajor ? majorNum - 2 : majorNum - 1}`;
381
+ }
382
+ //#endregion
383
+ //#region \0@oxc-project+runtime@0.137.0/helpers/esm/taggedTemplateLiteral.js
384
+ function _taggedTemplateLiteral(e, t) {
385
+ return t || (t = e.slice(0)), Object.freeze(Object.defineProperties(e, { raw: { value: Object.freeze(t) } }));
386
+ }
387
+ //#endregion
388
+ //#region src/addons/better-auth.ts
389
+ var _templateObject$1;
390
+ var _templateObject2;
391
+ var better_auth_default = defineAddon({
392
+ id: "better-auth",
393
+ shortDescription: "auth library",
394
+ homepage: "https://www.better-auth.com",
395
+ options: defineAddonOptions().add("demo", {
396
+ question: "Which demo would you like to include?",
397
+ type: "multiselect",
398
+ default: ["password"],
399
+ options: [{
400
+ value: "password",
401
+ label: "Email & Password"
402
+ }, {
403
+ value: "github",
404
+ label: "GitHub OAuth"
405
+ }],
406
+ required: false
407
+ }).build(),
408
+ setup: ({ isKit, dependencyVersion, unsupported, dependsOn, runsAfter }) => {
409
+ if (!isKit) unsupported("Requires SvelteKit");
410
+ if (!dependencyVersion("drizzle-orm")) dependsOn("drizzle");
411
+ runsAfter("sveltekitAdapter");
412
+ runsAfter("tailwindcss");
413
+ runsAfter("experimental");
414
+ },
415
+ run: ({ sv, cwd, language, options, directory, dependencyVersion, file }) => {
416
+ const lib = resolveLibPrefix(dependencyVersion("@sveltejs/kit"));
417
+ const svelteVersion = dependencyVersion("svelte");
418
+ const svelte5 = !!svelteVersion && coerceVersion(svelteVersion).major === 5;
419
+ const [ts, s5] = createPrinter(language === "ts", svelte5);
420
+ const demoPassword = options.demo.includes("password");
421
+ const demoGithub = options.demo.includes("github");
422
+ const hasDemo = demoPassword || demoGithub;
423
+ let drizzleDialect;
424
+ let d1 = false;
425
+ sv.devDependency("auth", "^1.6.24");
426
+ sv.devDependency("better-auth", "^1.6.24");
427
+ sv.file(`drizzle.config.${language}`, transforms.script(({ ast }) => {
428
+ const isProp = (name, node) => node.key.type === "Identifier" && node.key.name === name;
429
+ Walker.walk(ast, null, { Property(node) {
430
+ if (isProp("dialect", node) && node.value.type === "Literal" && typeof node.value.value === "string") drizzleDialect = node.value.value;
431
+ if (isProp("driver", node) && node.value.type === "Literal" && node.value.value === "d1-http") d1 = true;
432
+ } });
433
+ if (!drizzleDialect) throw new Error("Failed to detect DB dialect in your `drizzle.config.[js|ts]` file");
434
+ return false;
435
+ }));
436
+ sv.file(".env", generateEnv$1(demoGithub, false));
437
+ sv.file(".env.example", generateEnv$1(demoGithub, true));
438
+ const env = defineEnv({
439
+ sv,
440
+ cwd,
441
+ dependencyVersion
442
+ });
443
+ env.define({
444
+ name: "ORIGIN",
445
+ description: "The app origin (base URL), e.g. `http://localhost:5173`."
446
+ });
447
+ env.define({
448
+ name: "BETTER_AUTH_SECRET",
449
+ description: "Secret used to sign tokens. For production use 32 characters generated with high entropy. See [Better Auth installation](https://www.better-auth.com/docs/installation)."
450
+ });
451
+ if (demoGithub) {
452
+ env.define({
453
+ name: "GITHUB_CLIENT_ID",
454
+ description: "GitHub OAuth client ID. See [Better Auth GitHub provider](https://www.better-auth.com/docs/authentication/github)."
455
+ });
456
+ env.define({
457
+ name: "GITHUB_CLIENT_SECRET",
458
+ description: "GitHub OAuth client secret. See [Better Auth GitHub provider](https://www.better-auth.com/docs/authentication/github)."
459
+ });
460
+ }
461
+ sv.file(`${directory.lib}/server/auth.${language}`, transforms.script(({ ast, comments, js }) => {
462
+ js.imports.addNamed(ast, {
463
+ from: `${lib}/server/db/index.${language}`,
464
+ imports: [d1 ? "getDb" : "db"]
465
+ });
466
+ js.imports.addNamed(ast, {
467
+ from: "$app/server",
468
+ imports: ["getRequestEvent"]
469
+ });
470
+ js.imports.addNamed(ast, {
471
+ from: "better-auth/svelte-kit",
472
+ imports: ["sveltekitCookies"]
473
+ });
474
+ js.imports.addNamed(ast, {
475
+ from: "better-auth/adapters/drizzle",
476
+ imports: ["drizzleAdapter"]
477
+ });
478
+ js.imports.addNamed(ast, {
479
+ from: "better-auth/minimal",
480
+ imports: ["betterAuth"]
481
+ });
482
+ const origin = env.reference(ast, js, { name: "ORIGIN" });
483
+ const secret = env.reference(ast, js, { name: "BETTER_AUTH_SECRET" });
484
+ const githubId = demoGithub ? env.reference(ast, js, { name: "GITHUB_CLIENT_ID" }) : "";
485
+ const githubSecret = demoGithub ? env.reference(ast, js, { name: "GITHUB_CLIENT_SECRET" }) : "";
486
+ const provider = {
487
+ mysql: "mysql",
488
+ postgresql: "pg",
489
+ sqlite: "sqlite",
490
+ turso: "sqlite"
491
+ }[drizzleDialect];
492
+ const githubProvider = demoGithub ? `
493
+ socialProviders: {
494
+ github: {
495
+ clientId: ${githubId},
496
+ clientSecret: ${githubSecret},
497
+ },
498
+ },` : "";
499
+ let authConfig = "";
500
+ if (d1) authConfig = dedent`
501
+ const authConfig = {
502
+ baseURL: ${origin},
503
+ secret: ${secret},
504
+ emailAndPassword: {
505
+ enabled: true
506
+ },${githubProvider}
507
+ plugins: [
508
+ sveltekitCookies(getRequestEvent) // make sure this is the last plugin in the array
509
+ ],
510
+ }${ts(" satisfies Omit<Parameters<typeof betterAuth>[0], \"database\">")};
511
+
512
+ export const createAuth = (d1${ts(": D1Database")}) => betterAuth({
513
+ ...authConfig,
514
+ database: drizzleAdapter(getDb(d1), { provider: '${provider}' }),
515
+ });
516
+
517
+ /**
518
+ * DO NOT USE!
519
+ *
520
+ * This instance is used by the \`auth\` CLI for schema generation ONLY.
521
+ * To access \`auth\` at runtime, use \`event.locals.auth\`.
522
+ */
523
+ export const auth = createAuth(null${ts("!")});`;
524
+ else authConfig = dedent`
525
+ export const auth = betterAuth({
526
+ baseURL: ${origin},
527
+ secret: ${secret},
528
+ database: drizzleAdapter(db, { provider: '${provider}' }),
529
+ emailAndPassword: {
530
+ enabled: true
531
+ },${githubProvider}
532
+ plugins: [
533
+ sveltekitCookies(getRequestEvent) // make sure this is the last plugin in the array
534
+ ],
535
+ });`;
536
+ js.common.appendFromString(ast, {
537
+ code: authConfig,
538
+ comments
539
+ });
540
+ }));
541
+ const authConfigPath = `${directory.lib}/server/auth.${language}`;
542
+ const authSchemaPath = `${directory.lib}/server/db/auth.schema.${language}`;
543
+ sv.file(file.package, transforms.json(({ data, json }) => {
544
+ json.packageScriptsUpsert(data, "auth:schema", `auth generate --config ${authConfigPath} --output ${authSchemaPath} --yes`);
545
+ }));
546
+ sv.file(`${directory.lib}/server/db/auth.schema.${language}`, transforms.text(({ content }) => {
547
+ if (content) return false;
548
+ return dedent`
549
+ // If you see this file, you have not run the auth:schema script yet, but you should!
550
+ export {};
551
+ `;
552
+ }));
553
+ sv.file(`${directory.lib}/server/db/schema.${language}`, transforms.script(({ ast, js }) => {
554
+ js.exports.addNamespace(ast, { from: "./auth.schema" });
555
+ }));
556
+ sv.file("src/app.d.ts", transforms.script(({ ast, comments, js }) => {
557
+ if (d1) js.imports.addNamed(ast, {
558
+ imports: ["createAuth"],
559
+ from: `${lib}/server/auth.${language}`
560
+ });
561
+ js.imports.addNamed(ast, {
562
+ imports: ["User", "Session"],
563
+ from: "better-auth",
564
+ isType: true
565
+ });
566
+ const locals = js.kit.addGlobalAppInterface(ast, { name: "Locals" });
567
+ if (!locals) throw new Error("Failed detecting `locals` interface in `src/app.d.ts`");
568
+ comments.remove((c) => c.type === "Line" && c.value.trim() === "interface Locals {}");
569
+ const user = locals.body.body.find((prop) => js.common.hasTypeProperty(prop, { name: "user" }));
570
+ const session = locals.body.body.find((prop) => js.common.hasTypeProperty(prop, { name: "session" }));
571
+ const auth = locals.body.body.find((prop) => js.common.hasTypeProperty(prop, { name: "auth" }));
572
+ if (!user) locals.body.body.push(js.common.createTypeProperty("user", "User", true));
573
+ if (!session) locals.body.body.push(js.common.createTypeProperty("session", "Session", true));
574
+ if (d1 && !auth) locals.body.body.push(js.common.createTypeProperty("auth", "ReturnType<typeof createAuth>", false));
575
+ }));
576
+ sv.file(`src/hooks.server.${language}`, transforms.script(({ ast, comments, js }) => {
577
+ js.imports.addNamed(ast, {
578
+ imports: ["svelteKitHandler"],
579
+ from: "better-auth/svelte-kit"
580
+ });
581
+ js.imports.addNamed(ast, {
582
+ imports: [d1 ? "createAuth" : "auth"],
583
+ from: `${lib}/server/auth.${language}`
584
+ });
585
+ env.importEnv(ast, js, ["building"]);
586
+ const handleContent = dedent`
587
+ async ({ event, resolve }) => {${d1 ? dedent`
588
+ if (!event.platform?.env?.DB) throw new Error('D1 binding "DB" not found - are you running with wrangler?');
589
+ event.locals.auth = createAuth(event.platform.env.DB);
590
+ const { auth } = event.locals;\n` : ""}
591
+ // Fetch current session from Better Auth
592
+ const session = await auth.api.getSession({
593
+ headers: event.request.headers
594
+ });
595
+ // Make session and user available on server
596
+ if (session) {
597
+ event.locals.session = session.session;
598
+ event.locals.user = session.user;
599
+ }
600
+ return svelteKitHandler({ event, resolve, auth, building });
601
+ };
602
+
603
+ export const handle = sequence(handleBetterAuth, handleSession);
604
+ `;
605
+ js.kit.addHooksHandle(ast, {
606
+ language,
607
+ newHandleName: "handleBetterAuth",
608
+ handleContent,
609
+ comments
610
+ });
611
+ }));
612
+ if (hasDemo) {
613
+ sv.file(`${directory.kitRoutes}/demo/+page.svelte`, addToDemoPage("better-auth", language));
614
+ sv.file(`${directory.kitRoutes}/demo/better-auth/login/+page.server.${language}`, (content) => {
615
+ if (content) {
616
+ const filePath = `${directory.kitRoutes}/demo/better-auth/login/+page.server.${language}`;
617
+ R.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
618
+ return false;
619
+ }
620
+ const d1AuthLine = d1 ? "\n const { auth } = event.locals;\n" : "";
621
+ const signInEmailAction = demoPassword ? `
622
+ signInEmail: async (event) => {${d1AuthLine}
623
+ const formData = await event.request.formData();
624
+ const email = formData.get('email')?.toString() ?? '';
625
+ const password = formData.get('password')?.toString() ?? '';
626
+
627
+ try {
628
+ await auth.api.signInEmail({
629
+ body: {
630
+ email,
631
+ password,
632
+ callbackURL: '/auth/verification-success'
633
+ }
634
+ });
635
+ } catch (error) {
636
+ if (error instanceof APIError) {
637
+ return fail(400, { message: error.message || 'Signin failed' });
638
+ }
639
+ return fail(500, { message: 'Unexpected error' });
640
+ }
641
+
642
+ return redirect(302, '/demo/better-auth');
643
+ },
644
+ signUpEmail: async (event) => {${d1AuthLine}
645
+ const formData = await event.request.formData();
646
+ const email = formData.get('email')?.toString() ?? '';
647
+ const password = formData.get('password')?.toString() ?? '';
648
+ const name = formData.get('name')?.toString() ?? '';
649
+
650
+ try {
651
+ await auth.api.signUpEmail({
652
+ body: {
653
+ email,
654
+ password,
655
+ name,
656
+ callbackURL: '/auth/verification-success'
657
+ }
658
+ });
659
+ } catch (error) {
660
+ if (error instanceof APIError) {
661
+ return fail(400, { message: error.message || 'Registration failed' });
662
+ }
663
+ return fail(500, { message: 'Unexpected error' });
664
+ }
665
+
666
+ return redirect(302, '/demo/better-auth');
667
+ },` : "";
668
+ const signInSocialAction = demoGithub ? `
669
+ signInSocial: async (event) => {${d1AuthLine}
670
+ const formData = await event.request.formData();
671
+ const provider = formData.get('provider')?.toString() ?? 'github';
672
+ const callbackURL = formData.get('callbackURL')?.toString() ?? '/demo/better-auth';
673
+
674
+ const result = await auth.api.signInSocial({
675
+ body: {
676
+ provider: provider${ts(" as \"github\"")},
677
+ callbackURL
678
+ }
679
+ });
680
+
681
+ if (result.url) {
682
+ return redirect(302, result.url);
683
+ }
684
+ return fail(400, { message: 'Social sign-in failed' });
685
+ },` : "";
686
+ const needsAPIError = demoPassword;
687
+ return dedent`
688
+ import { fail, redirect } from '@sveltejs/kit';
689
+ ${ts("import type { Actions } from './$types';")}
690
+ ${ts("import type { PageServerLoad } from './$types';")}
691
+ ${!d1 ? `import { auth } from '${lib}/server/auth.${language}';` : ""}
692
+ ${needsAPIError ? "import { APIError } from 'better-auth/api';" : ""}
693
+
694
+ export const load${ts(": PageServerLoad")} = (event) => {
695
+ if (event.locals.user) {
696
+ return redirect(302, '/demo/better-auth');
697
+ }
698
+ return {};
699
+ };
700
+
701
+ export const actions${ts(": Actions")} = {${signInEmailAction}${signInSocialAction}
702
+ };
703
+ `;
704
+ });
705
+ sv.file(`${directory.kitRoutes}/demo/better-auth/login/+page.svelte`, (content) => {
706
+ if (content) {
707
+ const filePath = `${directory.kitRoutes}/demo/better-auth/login/+page.svelte`;
708
+ R.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
709
+ return false;
710
+ }
711
+ const tailwind = dependencyVersion("@tailwindcss/vite") !== void 0;
712
+ const input = tailwind ? " class=\"mt-1 px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"" : "";
713
+ const btn = tailwind ? " class=\"bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition\"" : "";
714
+ const passwordForm = demoPassword ? `
715
+ <form method="post" action="?/signInEmail" use:enhance>
716
+ <label>
717
+ Email
718
+ <input type="email" name="email"${input} />
719
+ </label>
720
+ <label>
721
+ Password
722
+ <input type="password" name="password"${input} />
723
+ </label>
724
+ <label>
725
+ Name (for registration)
726
+ <input name="name"${input} />
727
+ </label>
728
+ <button${btn}>Login</button>
729
+ <button formaction="?/signUpEmail"${btn}>Register</button>
730
+ </form>
731
+ ${tailwind ? `<p class="text-red-500">{form?.message ?? ''}</p>` : `<p style="color: red">{form?.message ?? ''}</p>`}` : "";
732
+ const separator = demoPassword && demoGithub ? `\n\n\t\t\t\t\t<hr ${tailwind ? "class=\"my-4\"" : ""} />\n` : "";
733
+ const githubForm = demoGithub ? `
734
+ <form method="post" action="?/signInSocial" use:enhance>
735
+ <input type="hidden" name="provider" value="github" />
736
+ <input type="hidden" name="callbackURL" value="/demo/better-auth" />
737
+ <button${btn}>Sign in with GitHub</button>
738
+ </form>` : "";
739
+ return dedent(_templateObject$1 || (_templateObject$1 = _taggedTemplateLiteral([
740
+ "\n <script ",
741
+ ">\n import { enhance } from '$app/forms';\n ",
742
+ "\n ",
743
+ "\n <\/script>\n\n <h1>Login</h1>",
744
+ "",
745
+ "",
746
+ "\n "
747
+ ])), ts("lang='ts'"), ts("import type { ActionData } from './$types';\n"), s5(`let { form }${ts(": { form: ActionData }")} = $props();`, `export let form${ts(": ActionData")};`), passwordForm, separator, githubForm);
748
+ });
749
+ sv.file(`${directory.kitRoutes}/demo/better-auth/+page.server.${language}`, (content) => {
750
+ if (content) {
751
+ const filePath = `${directory.kitRoutes}/demo/better-auth/+page.server.${language}`;
752
+ R.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
753
+ return false;
754
+ }
755
+ const d1AuthLine = d1 ? "\n const { auth } = event.locals;\n" : "";
756
+ return dedent`
757
+ import { redirect } from '@sveltejs/kit';
758
+ ${ts("import type { Actions } from './$types';")}
759
+ ${ts("import type { PageServerLoad } from './$types';")}
760
+ ${!d1 ? `import { auth } from '${lib}/server/auth.${language}';` : ""}
761
+
762
+ export const load${ts(": PageServerLoad")} = (event) => {
763
+ if (!event.locals.user) {
764
+ return redirect(302, '/demo/better-auth/login');
765
+ }
766
+ return { user: event.locals.user };
767
+ };
768
+
769
+ export const actions${ts(": Actions")} = {
770
+ signOut: async (event) => {${d1AuthLine}
771
+ await auth.api.signOut({
772
+ headers: event.request.headers
773
+ });
774
+ return redirect(302, '/demo/better-auth/login');
775
+ }
776
+ };
777
+ `;
778
+ });
779
+ sv.file(`${directory.kitRoutes}/demo/better-auth/+page.svelte`, (content) => {
780
+ if (content) {
781
+ const filePath = `${directory.kitRoutes}/demo/better-auth/+page.svelte`;
782
+ R.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
783
+ return false;
784
+ }
785
+ const tailwind = dependencyVersion("@tailwindcss/vite") !== void 0;
786
+ return dedent(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral([
787
+ "\n <script ",
788
+ ">\n import { enhance } from '$app/forms';\n ",
789
+ "\n ",
790
+ "\n <\/script>\n\n <h1>Hi, {data.user.name}!</h1>\n <p>Your user ID is {data.user.id}.</p>\n <form method=\"post\" action=\"?/signOut\" use:enhance>\n <button ",
791
+ ">Sign out</button>\n </form>\n "
792
+ ])), ts("lang='ts'"), ts("import type { PageServerData } from './$types';\n"), s5(`let { data }${ts(": { data: PageServerData }")} = $props();`, `export let data${ts(": PageServerData")};`), tailwind ? "class=\"bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition\"" : "");
793
+ });
794
+ }
795
+ },
796
+ nextSteps: ({ options, packageManager }) => {
797
+ const steps = [
798
+ `Run ${color.command(resolveCommandArray(packageManager, "run", ["auth:schema"]))} to generate the auth schema`,
799
+ `Run ${color.command(resolveCommandArray(packageManager, "run", ["db:push"]))} to update your database`,
800
+ `Check ${color.env("ORIGIN")} & ${color.env("BETTER_AUTH_SECRET")} in ${color.path(".env")} and adjust it to your needs`
801
+ ];
802
+ if (options.demo.includes("github")) steps.push(`Set your ${color.env("GITHUB_CLIENT_ID")} and ${color.env("GITHUB_CLIENT_SECRET")} in ${color.path(".env")}`);
803
+ if (options.demo.length > 0) steps.push(`Visit ${color.route("/demo/better-auth")} route to view the demo`);
804
+ return steps;
805
+ }
806
+ });
807
+ const generateEnv$1 = (demoGithub, isExample) => transforms.text(({ content, text }) => {
808
+ content = text.upsert(content, "ORIGIN", {
809
+ value: isExample ? `""` : `"http://localhost:5173"`,
810
+ separator: true
811
+ });
812
+ content = text.upsert(content, "BETTER_AUTH_SECRET", {
813
+ value: isExample ? `""` : `"${crypto.randomUUID()}"`,
814
+ comment: [
815
+ "Better Auth",
816
+ "For production use 32 characters and generated with high entropy",
817
+ "https://www.better-auth.com/docs/installation"
818
+ ],
819
+ separator: true
820
+ });
821
+ if (demoGithub) {
822
+ content = text.upsert(content, "GITHUB_CLIENT_ID", {
823
+ value: `""`,
824
+ comment: "GitHub OAuth\n# https://www.better-auth.com/docs/authentication/github"
825
+ });
826
+ content = text.upsert(content, "GITHUB_CLIENT_SECRET", { value: `""` });
827
+ }
828
+ return content;
829
+ });
830
+ //#endregion
831
+ //#region src/addons/drizzle.ts
832
+ const PORTS = {
833
+ mysql: "3306",
834
+ postgresql: "5432",
835
+ sqlite: "",
836
+ d1: ""
837
+ };
838
+ var drizzle_default = defineAddon({
839
+ id: "drizzle",
840
+ shortDescription: "database orm",
841
+ homepage: "https://orm.drizzle.team",
842
+ options: defineAddonOptions().add("database", {
843
+ question: "Which database would you like to use?",
844
+ type: "select",
845
+ default: "sqlite",
846
+ options: [
847
+ {
848
+ value: "postgresql",
849
+ label: "PostgreSQL"
850
+ },
851
+ {
852
+ value: "mysql",
853
+ label: "MySQL"
854
+ },
855
+ {
856
+ value: "sqlite",
857
+ label: "SQLite"
858
+ },
859
+ {
860
+ value: "d1",
861
+ label: "Cloudflare D1"
862
+ }
863
+ ]
864
+ }).add("postgresql", {
865
+ question: "Which PostgreSQL client would you like to use?",
866
+ type: "select",
867
+ group: "client",
868
+ default: "postgres.js",
869
+ options: [{
870
+ value: "postgres.js",
871
+ label: "Postgres.JS",
872
+ hint: "recommended for most users"
873
+ }, {
874
+ value: "neon",
875
+ label: "Neon",
876
+ hint: "popular hosted platform"
877
+ }],
878
+ condition: ({ database }) => database === "postgresql"
879
+ }).add("mysql", {
880
+ question: "Which MySQL client would you like to use?",
881
+ type: "select",
882
+ group: "client",
883
+ default: "mysql2",
884
+ options: [{
885
+ value: "mysql2",
886
+ hint: "recommended for most users"
887
+ }, {
888
+ value: "planetscale",
889
+ label: "PlanetScale",
890
+ hint: "popular hosted platform"
891
+ }],
892
+ condition: ({ database }) => database === "mysql"
893
+ }).add("sqlite", {
894
+ question: "Which SQLite client would you like to use?",
895
+ type: "select",
896
+ group: "client",
897
+ default: "libsql",
898
+ options: [
899
+ {
900
+ value: "node-sqlite",
901
+ label: "node:sqlite",
902
+ hint: "built-in to Node.js and Deno"
903
+ },
904
+ {
905
+ value: "better-sqlite3",
906
+ hint: "for server environments"
907
+ },
908
+ {
909
+ value: "libsql",
910
+ label: "libSQL",
911
+ hint: "for serverless environments"
912
+ },
913
+ {
914
+ value: "turso",
915
+ label: "Turso",
916
+ hint: "popular hosted platform"
917
+ }
918
+ ],
919
+ condition: ({ database }) => database === "sqlite"
920
+ }).add("docker", {
921
+ question: "Do you want to run the database locally with docker-compose?",
922
+ default: false,
923
+ type: "boolean",
924
+ condition: ({ database, mysql, postgresql }) => database === "mysql" && mysql === "mysql2" || database === "postgresql" && postgresql === "postgres.js"
925
+ }).build(),
926
+ setup: ({ isKit, unsupported, runsAfter }) => {
927
+ runsAfter("prettier");
928
+ runsAfter("sveltekitAdapter");
929
+ runsAfter("experimental");
930
+ if (!isKit) return unsupported("Requires SvelteKit");
931
+ },
932
+ run: ({ sv, language, options, directory, dependencyVersion, cwd, cancel, file }) => {
933
+ const [ts] = createPrinter(language === "ts");
934
+ const baseDBPath = path.resolve(cwd, directory.lib, "server", "db");
935
+ const paths = {
936
+ "drizzle config": path.resolve(cwd, `drizzle.config.${language}`),
937
+ "database schema": path.resolve(baseDBPath, `schema.${language}`),
938
+ database: path.resolve(baseDBPath, `index.${language}`)
939
+ };
940
+ for (const [fileType, filePath] of Object.entries(paths)) if (fs.existsSync(filePath)) return cancel(`Preexisting ${fileType} file at '${filePath}'`);
941
+ sv.devDependency("drizzle-orm", "^0.45.2");
942
+ sv.devDependency("drizzle-kit", "^0.31.10");
943
+ sv.devDependency("@types/node", getNodeTypesVersion());
944
+ if (options.mysql === "mysql2") sv.devDependency("mysql2", "^3.22.4");
945
+ if (options.mysql === "planetscale") sv.devDependency("@planetscale/database", "^1.20.1");
946
+ if (options.postgresql === "neon") sv.devDependency("@neondatabase/serverless", "^1.1.0");
947
+ if (options.postgresql === "postgres.js") sv.devDependency("postgres", "^3.4.9");
948
+ if (options.sqlite === "better-sqlite3") {
949
+ sv.dependency("better-sqlite3", "^13.0.2");
950
+ sv.devDependency("@types/better-sqlite3", "^9.6.0");
951
+ }
952
+ if (options.sqlite === "libsql" || options.sqlite === "turso") sv.devDependency("@libsql/client", "^0.17.3");
953
+ sv.file(".env", generateEnv(options, false));
954
+ sv.file(".env.example", generateEnv(options, true));
955
+ if (options.docker && (options.mysql === "mysql2" || options.postgresql === "postgres.js")) {
956
+ const composeFile = [
957
+ "compose.yaml",
958
+ "compose.yml",
959
+ "docker-compose.yaml",
960
+ "docker-compose.yml"
961
+ ].find((option) => fs.existsSync(path.resolve(cwd, option))) ?? "compose.yaml";
962
+ sv.file(composeFile, (content) => {
963
+ if (content.length > 0) return false;
964
+ const imageName = options.database === "mysql" ? "mysql" : "postgres";
965
+ const port = PORTS[options.database];
966
+ const USER = "root";
967
+ const PASSWORD = "mysecretpassword";
968
+ const DB_NAME = "local";
969
+ let dbSpecificContent = "";
970
+ if (options.mysql === "mysql2") dbSpecificContent = `
971
+ MYSQL_ROOT_PASSWORD: ${PASSWORD}
972
+ MYSQL_DATABASE: ${DB_NAME}
973
+ volumes:
974
+ - mysqldata:/var/lib/mysql
975
+ volumes:
976
+ mysqldata:
977
+ `;
978
+ if (options.postgresql === "postgres.js") dbSpecificContent = `
979
+ POSTGRES_USER: ${USER}
980
+ POSTGRES_PASSWORD: ${PASSWORD}
981
+ POSTGRES_DB: ${DB_NAME}
982
+ volumes:
983
+ - pgdata:/var/lib/postgresql
984
+ volumes:
985
+ pgdata:
986
+ `;
987
+ return dedent`
988
+ services:
989
+ db:
990
+ image: ${imageName}
991
+ restart: always
992
+ ports:
993
+ - ${port}:${port}
994
+ environment: ${dbSpecificContent}
995
+ `;
996
+ });
997
+ }
998
+ sv.file(file.package, transforms.json(({ data, json }) => {
999
+ if (options.docker) json.packageScriptsUpsert(data, "db:start", "docker compose up");
1000
+ json.packageScriptsUpsert(data, "db:push", "drizzle-kit push");
1001
+ json.packageScriptsUpsert(data, "db:generate", "drizzle-kit generate");
1002
+ json.packageScriptsUpsert(data, "db:migrate", "drizzle-kit migrate");
1003
+ json.packageScriptsUpsert(data, "db:studio", "drizzle-kit studio");
1004
+ }));
1005
+ if (Boolean(dependencyVersion("prettier"))) sv.file(".prettierignore", transforms.text(({ content, text }) => text.upsert(content, "/drizzle/")));
1006
+ if (options.database === "sqlite") sv.file(file.gitignore, transforms.text(({ content, text }) => {
1007
+ if (content.length === 0) return false;
1008
+ return text.upsert(content, "*.db", { comment: "SQLite" });
1009
+ }));
1010
+ sv.file(paths["drizzle config"], transforms.script(({ ast, js }) => {
1011
+ const d1 = options.database === "d1";
1012
+ const turso = options.sqlite === "turso";
1013
+ js.imports.addNamed(ast, {
1014
+ from: "drizzle-kit",
1015
+ imports: { defineConfig: "defineConfig" }
1016
+ });
1017
+ if (d1) ast.body.push(js.common.parseStatement("if (!process.env.CLOUDFLARE_ACCOUNT_ID) throw new Error('CLOUDFLARE_ACCOUNT_ID is not set');"), js.common.parseStatement("if (!process.env.CLOUDFLARE_DATABASE_ID) throw new Error('CLOUDFLARE_DATABASE_ID is not set');"), js.common.parseStatement("if (!process.env.CLOUDFLARE_D1_TOKEN) throw new Error('CLOUDFLARE_D1_TOKEN is not set');"));
1018
+ else ast.body.push(js.common.parseStatement("if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');"));
1019
+ const getDialect = () => {
1020
+ if (d1) return "sqlite";
1021
+ if (turso) return "turso";
1022
+ return options.database;
1023
+ };
1024
+ const getCredentials = () => {
1025
+ const creds = [];
1026
+ if (d1) {
1027
+ creds.push("accountId: process.env.CLOUDFLARE_ACCOUNT_ID,");
1028
+ creds.push("databaseId: process.env.CLOUDFLARE_DATABASE_ID,");
1029
+ creds.push("token: process.env.CLOUDFLARE_D1_TOKEN,");
1030
+ }
1031
+ if (turso) creds.push("authToken: process.env.DATABASE_AUTH_TOKEN,");
1032
+ if (!d1) creds.push("url: process.env.DATABASE_URL,");
1033
+ return creds.join("\n");
1034
+ };
1035
+ js.exports.createDefault(ast, { fallback: js.common.parseExpression(`
1036
+ defineConfig({
1037
+ schema: "./${directory.lib}/server/db/schema.${language}",
1038
+ dialect: "${getDialect()}",
1039
+ ${d1 ? "driver: 'd1-http'," : ""}
1040
+ dbCredentials: {
1041
+ ${getCredentials()}
1042
+ },
1043
+ verbose: true,
1044
+ strict: true
1045
+ })
1046
+ `) });
1047
+ }));
1048
+ if (isKit3(dependencyVersion("@sveltejs/kit"))) {
1049
+ const configFile = language === "ts" ? "tsconfig.json" : "jsconfig.json";
1050
+ if (fileExists(cwd, configFile)) sv.file(configFile, transforms.json(({ data }) => {
1051
+ const include = data.include ??= ["src"];
1052
+ if (!include.includes(`drizzle.config.${language}`)) include.push(`drizzle.config.${language}`);
1053
+ }));
1054
+ } else svelteConfig.edit({
1055
+ sv,
1056
+ cwd
1057
+ }, ({ override, js }) => {
1058
+ override({ typescript: { config: js.common.parseExpression(`(config) => { config.include.push('../drizzle.config.${language}')}`) } });
1059
+ });
1060
+ sv.file(paths["database schema"], transforms.script(({ ast, js }) => {
1061
+ let taskSchemaExpression;
1062
+ if (options.database === "sqlite" || options.database === "d1") {
1063
+ js.imports.addNamed(ast, {
1064
+ from: "drizzle-orm/sqlite-core",
1065
+ imports: [
1066
+ "integer",
1067
+ "sqliteTable",
1068
+ "text"
1069
+ ]
1070
+ });
1071
+ taskSchemaExpression = js.common.parseExpression(`sqliteTable('task', {
1072
+ id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
1073
+ title: text('title').notNull(),
1074
+ priority: integer('priority').notNull().default(1)
1075
+ })`);
1076
+ }
1077
+ if (options.database === "mysql") {
1078
+ js.imports.addNamed(ast, {
1079
+ from: "drizzle-orm/mysql-core",
1080
+ imports: [
1081
+ "mysqlTable",
1082
+ "serial",
1083
+ "int",
1084
+ "text"
1085
+ ]
1086
+ });
1087
+ taskSchemaExpression = js.common.parseExpression(`mysqlTable('task', {
1088
+ id: serial('id').primaryKey(),
1089
+ title: text('title').notNull(),
1090
+ priority: int('priority').notNull().default(1)
1091
+ })`);
1092
+ }
1093
+ if (options.database === "postgresql") {
1094
+ js.imports.addNamed(ast, {
1095
+ from: "drizzle-orm/pg-core",
1096
+ imports: [
1097
+ "pgTable",
1098
+ "serial",
1099
+ "integer",
1100
+ "text"
1101
+ ]
1102
+ });
1103
+ taskSchemaExpression = js.common.parseExpression(`pgTable('task', {
1104
+ id: serial('id').primaryKey(),
1105
+ title: text('title').notNull(),
1106
+ priority: integer('priority').notNull().default(1)
1107
+ })`);
1108
+ }
1109
+ if (!taskSchemaExpression) throw new Error("unreachable state...");
1110
+ const taskIdentifier = js.variables.declaration(ast, {
1111
+ kind: "const",
1112
+ name: "task",
1113
+ value: taskSchemaExpression
1114
+ });
1115
+ js.exports.createNamed(ast, {
1116
+ name: "task",
1117
+ fallback: taskIdentifier
1118
+ });
1119
+ }));
1120
+ const env = defineEnv({
1121
+ sv,
1122
+ cwd,
1123
+ dependencyVersion
1124
+ });
1125
+ if (options.database !== "d1") {
1126
+ env.define({
1127
+ name: "DATABASE_URL",
1128
+ description: "The database connection string."
1129
+ });
1130
+ if (options.sqlite === "turso") env.define({
1131
+ name: "DATABASE_AUTH_TOKEN",
1132
+ description: "Auth token for the [Turso](https://turso.tech) database."
1133
+ });
1134
+ }
1135
+ sv.file(paths.database, transforms.script(({ ast, js }) => {
1136
+ if (options.database === "d1") {
1137
+ js.imports.addNamespace(ast, {
1138
+ from: "./schema",
1139
+ as: "schema"
1140
+ });
1141
+ js.imports.addNamed(ast, {
1142
+ from: "drizzle-orm/d1",
1143
+ imports: ["drizzle"]
1144
+ });
1145
+ const getDbFn = js.common.parseStatement(`export const getDb = (d1${ts(": D1Database")}) => drizzle(d1, { schema });`);
1146
+ ast.body.push(getDbFn);
1147
+ return;
1148
+ }
1149
+ const dbUrl = env.reference(ast, js, { name: "DATABASE_URL" });
1150
+ js.imports.addNamespace(ast, {
1151
+ from: "./schema",
1152
+ as: "schema"
1153
+ });
1154
+ ast.body.push(js.common.parseStatement(`if (!${dbUrl}) throw new Error('DATABASE_URL is not set');`));
1155
+ let clientExpression;
1156
+ if (options.sqlite === "better-sqlite3") {
1157
+ js.imports.addDefault(ast, {
1158
+ from: "better-sqlite3",
1159
+ as: "Database"
1160
+ });
1161
+ js.imports.addNamed(ast, {
1162
+ from: "drizzle-orm/better-sqlite3",
1163
+ imports: ["drizzle"]
1164
+ });
1165
+ clientExpression = js.common.parseExpression(`new Database(${dbUrl})`);
1166
+ }
1167
+ if (options.sqlite === "libsql" || options.sqlite === "turso") {
1168
+ js.imports.addNamed(ast, {
1169
+ from: "@libsql/client",
1170
+ imports: ["createClient"]
1171
+ });
1172
+ js.imports.addNamed(ast, {
1173
+ from: "drizzle-orm/libsql",
1174
+ imports: ["drizzle"]
1175
+ });
1176
+ if (options.sqlite === "turso") {
1177
+ const dbToken = env.reference(ast, js, { name: "DATABASE_AUTH_TOKEN" });
1178
+ ast.body.push(js.common.parseStatement(`if (!${dbToken}) throw new Error('DATABASE_AUTH_TOKEN is not set');`));
1179
+ clientExpression = js.common.parseExpression(`createClient({ url: ${dbUrl}, authToken: ${dbToken} })`);
1180
+ } else clientExpression = js.common.parseExpression(`createClient({ url: ${dbUrl} })`);
1181
+ }
1182
+ if (options.sqlite === "node-sqlite") {
1183
+ js.imports.addNamed(ast, {
1184
+ from: "node:sqlite",
1185
+ imports: ["DatabaseSync"]
1186
+ });
1187
+ js.imports.addNamed(ast, {
1188
+ from: "drizzle-orm/node-sqlite",
1189
+ imports: ["drizzle"]
1190
+ });
1191
+ clientExpression = js.common.parseExpression(`new DatabaseSync(${dbUrl})`);
1192
+ }
1193
+ if (options.mysql === "mysql2" || options.mysql === "planetscale") {
1194
+ js.imports.addDefault(ast, {
1195
+ from: "mysql2/promise",
1196
+ as: "mysql"
1197
+ });
1198
+ js.imports.addNamed(ast, {
1199
+ from: "drizzle-orm/mysql2",
1200
+ imports: ["drizzle"]
1201
+ });
1202
+ clientExpression = js.common.parseExpression(`mysql.createPool(${dbUrl})`);
1203
+ }
1204
+ if (options.postgresql === "neon") {
1205
+ js.imports.addNamed(ast, {
1206
+ from: "@neondatabase/serverless",
1207
+ imports: ["neon"]
1208
+ });
1209
+ js.imports.addNamed(ast, {
1210
+ from: "drizzle-orm/neon-http",
1211
+ imports: ["drizzle"]
1212
+ });
1213
+ clientExpression = js.common.parseExpression(`neon(${dbUrl})`);
1214
+ }
1215
+ if (options.postgresql === "postgres.js") {
1216
+ js.imports.addDefault(ast, {
1217
+ from: "postgres",
1218
+ as: "postgres"
1219
+ });
1220
+ js.imports.addNamed(ast, {
1221
+ from: "drizzle-orm/postgres-js",
1222
+ imports: ["drizzle"]
1223
+ });
1224
+ clientExpression = js.common.parseExpression(`postgres(${dbUrl})`);
1225
+ }
1226
+ if (!clientExpression) throw new Error("unreachable state...");
1227
+ ast.body.push(js.variables.declaration(ast, {
1228
+ kind: "const",
1229
+ name: "client",
1230
+ value: clientExpression
1231
+ }));
1232
+ const drizzleCall = js.functions.createCall({
1233
+ name: "drizzle",
1234
+ args: ["client"],
1235
+ useIdentifiers: true
1236
+ });
1237
+ const paramObject = js.object.create({ schema: js.variables.createIdentifier("schema") });
1238
+ if (options.database === "mysql") {
1239
+ const mode = options.mysql === "planetscale" ? "planetscale" : "default";
1240
+ js.object.property(paramObject, {
1241
+ name: "mode",
1242
+ fallback: js.common.createLiteral(mode)
1243
+ });
1244
+ }
1245
+ drizzleCall.arguments.push(paramObject);
1246
+ const db = js.variables.declaration(ast, {
1247
+ kind: "const",
1248
+ name: "db",
1249
+ value: drizzleCall
1250
+ });
1251
+ js.exports.createNamed(ast, {
1252
+ name: "db",
1253
+ fallback: db
1254
+ });
1255
+ }));
1256
+ },
1257
+ nextSteps: ({ options, packageManager, cwd, dependencyVersion }) => {
1258
+ const pm = (command, args) => color.command(resolveCommandArray(packageManager, command, args));
1259
+ const steps = [];
1260
+ if (options.database === "d1") {
1261
+ if (!dependencyVersion("@sveltejs/adapter-cloudflare")) steps.push(`Cloudflare D1 requires ${color.addon("@sveltejs/adapter-cloudflare")}. Run ${pm("execute", [
1262
+ "sv",
1263
+ "add",
1264
+ "sveltekit-adapter=adapter:cloudflare"
1265
+ ])} to add it`);
1266
+ steps.push(`Add your ${color.env("CLOUDFLARE_ACCOUNT_ID")}, ${color.env("CLOUDFLARE_DATABASE_ID")}, and ${color.env("CLOUDFLARE_D1_TOKEN")} to ${color.path(".env")}`);
1267
+ const ext = fileExists(cwd, "wrangler.toml") ? "toml" : "jsonc";
1268
+ steps.push(`Run ${pm("execute-local", [
1269
+ "wrangler",
1270
+ "d1",
1271
+ "create",
1272
+ "<DATABASE_NAME>"
1273
+ ])} to generate a D1 database ID for your ${color.path(`wrangler.${ext}`)}`);
1274
+ }
1275
+ if (options.docker) steps.push(`Run ${pm("run", ["db:start"])} to start the docker container`);
1276
+ else if (options.database !== "d1") steps.push(`Check ${color.env("DATABASE_URL")} in ${color.path(".env")} and adjust it to your needs`);
1277
+ steps.push(`Run ${pm("run", ["db:push"])} to update your database schema`);
1278
+ return steps;
1279
+ }
1280
+ });
1281
+ const generateEnv = (opts, isExample) => transforms.text(({ content, text }) => {
1282
+ const DB_URL_KEY = "DATABASE_URL";
1283
+ if (opts.database === "d1") {
1284
+ content = text.upsert(content, "CLOUDFLARE_ACCOUNT_ID", {
1285
+ value: "\"\"",
1286
+ comment: ["Cloudflare D1"],
1287
+ separator: true
1288
+ });
1289
+ content = text.upsert(content, "CLOUDFLARE_DATABASE_ID", { value: "\"\"" });
1290
+ content = text.upsert(content, "CLOUDFLARE_D1_TOKEN", { value: "\"\"" });
1291
+ return content;
1292
+ }
1293
+ let value;
1294
+ const comment = ["Drizzle"];
1295
+ if (opts.docker) value = `"${opts.database === "mysql" ? "mysql" : "postgres"}://root:mysecretpassword@localhost:${PORTS[opts.database]}/local"`;
1296
+ else if ([
1297
+ "better-sqlite3",
1298
+ "libsql",
1299
+ "node-sqlite"
1300
+ ].includes(opts.sqlite)) value = opts.sqlite === "libsql" ? "file:local.db" : "local.db";
1301
+ else if (opts.sqlite === "turso") if (isExample) {
1302
+ value = "\"libsql://db-name-user.turso.io\"";
1303
+ comment.push("Replace with your DB credentials!", {
1304
+ text: "A local DB can also be used in dev as well",
1305
+ mode: "append"
1306
+ }, {
1307
+ text: `${DB_URL_KEY}="file:local.db"`,
1308
+ mode: "append"
1309
+ });
1310
+ } else {
1311
+ value = "\"file:local.db\"";
1312
+ comment.push("Replace with your DB credentials!", `${DB_URL_KEY}="libsql://db-name-user.turso.io"`, "A local DB can also be used in dev as well");
1313
+ }
1314
+ else if (opts.database === "mysql") {
1315
+ value = "\"mysql://user:password@host:port/db-name\"";
1316
+ comment.push("Replace with your DB credentials!");
1317
+ } else if (opts.database === "postgresql") {
1318
+ value = "\"postgres://user:password@host:port/db-name\"";
1319
+ comment.push("Replace with your DB credentials!");
1320
+ } else value = "";
1321
+ content = text.upsert(content, DB_URL_KEY, {
1322
+ value,
1323
+ comment,
1324
+ separator: true
1325
+ });
1326
+ if (opts.sqlite === "turso") content = text.upsert(content, "DATABASE_AUTH_TOKEN", { value: isExample ? `""` : `"${crypto.randomUUID()}"` });
1327
+ return content;
1328
+ });
1329
+ //#endregion
1330
+ //#region src/addons/eslint.ts
1331
+ var eslint_default = defineAddon({
1332
+ id: "eslint",
1333
+ shortDescription: "linter",
1334
+ homepage: "https://eslint.org",
1335
+ options: {},
1336
+ run: ({ sv, language, dependencyVersion, file, cwd }) => {
1337
+ const typescript = language === "ts";
1338
+ const prettierInstalled = Boolean(dependencyVersion("prettier"));
1339
+ const configLocation = svelteConfig.find(cwd);
1340
+ const svelteConfigFile = configLocation?.kind === "svelte" ? configLocation.path : void 0;
1341
+ sv.devDependency("eslint", ESLINT_VERSION);
1342
+ sv.devDependency("eslint-plugin-svelte", "^3.19.0");
1343
+ sv.devDependency("globals", "^17.6.0");
1344
+ sv.devDependency("@eslint/js", "^10.0.1");
1345
+ sv.devDependency("@types/node", getNodeTypesVersion());
1346
+ if (typescript) sv.devDependency("typescript-eslint", "^8.60.1");
1347
+ if (prettierInstalled) sv.devDependency("eslint-config-prettier", "^10.1.8");
1348
+ sv.file(file.package, transforms.json(({ data, json }) => {
1349
+ json.packageScriptsUpsert(data, "lint", "eslint .");
1350
+ }));
1351
+ sv.file("eslint.config.js", transforms.script(({ ast, comments, js }) => {
1352
+ const eslintConfigs = [];
1353
+ if (svelteConfigFile) js.imports.addDefault(ast, {
1354
+ from: `./${svelteConfigFile}`,
1355
+ as: "svelteConfig"
1356
+ });
1357
+ const gitIgnorePathStatement = js.common.parseStatement("\nconst gitignorePath = path.resolve(import.meta.dirname, '.gitignore');");
1358
+ js.common.appendStatement(ast, { statement: gitIgnorePathStatement });
1359
+ const ignoresConfig = js.common.parseExpression("includeIgnoreFile(gitignorePath)");
1360
+ eslintConfigs.push(ignoresConfig);
1361
+ const jsConfig = js.common.parseExpression("js.configs.recommended");
1362
+ eslintConfigs.push(jsConfig);
1363
+ if (typescript) {
1364
+ const tsConfig = js.common.parseExpression("ts.configs.recommended");
1365
+ eslintConfigs.push(tsConfig);
1366
+ }
1367
+ const svelteConfig = js.common.parseExpression("svelte.configs.recommended");
1368
+ eslintConfigs.push(svelteConfig);
1369
+ const globalsBrowser = js.common.createSpread(js.common.parseExpression("globals.browser"));
1370
+ const globalsNode = js.common.createSpread(js.common.parseExpression("globals.node"));
1371
+ const globalsObjLiteral = js.object.create({});
1372
+ globalsObjLiteral.properties = [globalsBrowser, globalsNode];
1373
+ const rules = js.object.create({ "\"no-undef\"": "off" });
1374
+ if (rules.properties[0].type !== "Property") throw new Error("rules.properties[0].type !== \"Property\"");
1375
+ comments.add(rules.properties[0].key, {
1376
+ type: "Line",
1377
+ value: " typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects."
1378
+ });
1379
+ comments.add(rules.properties[0].key, {
1380
+ type: "Line",
1381
+ value: " see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors"
1382
+ });
1383
+ const globalsConfig = js.object.create({
1384
+ languageOptions: { globals: globalsObjLiteral },
1385
+ rules: typescript ? rules : void 0
1386
+ });
1387
+ eslintConfigs.push(globalsConfig);
1388
+ const svelteConfigProp = svelteConfigFile ? { svelteConfig: js.variables.createIdentifier("svelteConfig") } : {};
1389
+ if (typescript) {
1390
+ const svelteTSParserConfig = js.object.create({
1391
+ files: [
1392
+ "**/*.svelte",
1393
+ "**/*.svelte.ts",
1394
+ "**/*.svelte.js"
1395
+ ],
1396
+ languageOptions: { parserOptions: {
1397
+ projectService: true,
1398
+ extraFileExtensions: [".svelte"],
1399
+ parser: js.variables.createIdentifier("ts.parser"),
1400
+ ...svelteConfigProp
1401
+ } }
1402
+ });
1403
+ eslintConfigs.push(svelteTSParserConfig);
1404
+ } else {
1405
+ const svelteTSParserConfig = js.object.create({
1406
+ files: ["**/*.svelte", "**/*.svelte.js"],
1407
+ languageOptions: { parserOptions: { ...svelteConfigProp } }
1408
+ });
1409
+ eslintConfigs.push(svelteTSParserConfig);
1410
+ }
1411
+ const rulesOverride = js.object.create({ rules: {} });
1412
+ eslintConfigs.push(rulesOverride);
1413
+ if (rulesOverride.properties[0].type !== "Property") throw new Error("rulesOverride.properties[0].type !== \"Property\"");
1414
+ comments.add(rulesOverride.properties[0].key, {
1415
+ type: "Line",
1416
+ value: " Override or add rule settings here, such as:"
1417
+ });
1418
+ comments.add(rulesOverride.properties[0].key, {
1419
+ type: "Line",
1420
+ value: " 'svelte/button-has-type': 'error'"
1421
+ });
1422
+ const exportExpression = js.functions.createCall({
1423
+ name: "defineConfig",
1424
+ args: []
1425
+ });
1426
+ if (typescript) exportExpression.arguments.push(...eslintConfigs);
1427
+ else {
1428
+ const eslintArray = js.array.create();
1429
+ eslintConfigs.map((x) => js.array.append(eslintArray, x));
1430
+ exportExpression.arguments.push(eslintArray);
1431
+ }
1432
+ const { value: defaultExport } = js.exports.createDefault(ast, { fallback: exportExpression });
1433
+ if (defaultExport !== exportExpression) {
1434
+ R.warn("An eslint config is already defined. Skipping initialization.");
1435
+ return false;
1436
+ }
1437
+ if (typescript) js.imports.addDefault(ast, {
1438
+ from: "typescript-eslint",
1439
+ as: "ts"
1440
+ });
1441
+ js.imports.addDefault(ast, {
1442
+ from: "globals",
1443
+ as: "globals"
1444
+ });
1445
+ js.imports.addNamed(ast, {
1446
+ from: "eslint/config",
1447
+ imports: ["defineConfig"]
1448
+ });
1449
+ js.imports.addDefault(ast, {
1450
+ from: "eslint-plugin-svelte",
1451
+ as: "svelte"
1452
+ });
1453
+ js.imports.addDefault(ast, {
1454
+ from: "@eslint/js",
1455
+ as: "js"
1456
+ });
1457
+ js.imports.addNamed(ast, {
1458
+ from: "eslint/config",
1459
+ imports: ["includeIgnoreFile"]
1460
+ });
1461
+ js.imports.addDefault(ast, {
1462
+ from: "node:path",
1463
+ as: "path"
1464
+ });
1465
+ }));
1466
+ sv.file(".vscode/extensions.json", transforms.json(({ data, json }) => {
1467
+ json.arrayUpsert(data, "recommendations", "dbaeumer.vscode-eslint");
1468
+ }));
1469
+ if (prettierInstalled) sv.file("eslint.config.js", addEslintConfigPrettier);
1470
+ }
1471
+ });
1472
+ //#endregion
1473
+ //#region src/addons/experimental.ts
1474
+ const FEATURES = {
1475
+ async: {
1476
+ label: "async",
1477
+ hint: "await in components",
1478
+ path: "compilerOptions.experimental.async"
1479
+ },
1480
+ remoteFunctions: { label: "remote functions" },
1481
+ forkPreloads: {
1482
+ label: "forked preloading",
1483
+ off: true
1484
+ }
1485
+ };
1486
+ var experimental_default = defineAddon({
1487
+ id: "experimental",
1488
+ shortDescription: "svelte & kit experimental features",
1489
+ homepage: "https://svelte.dev/docs/kit/configuration#experimental",
1490
+ options: defineAddonOptions().add("features", {
1491
+ question: "Which experimental features do you want to enable?",
1492
+ type: "multiselect",
1493
+ default: Object.entries(FEATURES).filter(([, f]) => !f.off).map(([value]) => value),
1494
+ options: Object.entries(FEATURES).map(([value, { label, hint }]) => ({
1495
+ value,
1496
+ label,
1497
+ hint
1498
+ })),
1499
+ required: false
1500
+ }).build(),
1501
+ run: ({ sv, cwd, options }) => {
1502
+ const config = {};
1503
+ for (const [name, f] of Object.entries(FEATURES)) {
1504
+ if (!options.features.includes(name)) continue;
1505
+ const keys = (f.path ?? `experimental.${name}`).split(".");
1506
+ let target = config;
1507
+ for (const key of keys.slice(0, -1)) target = target[key] ??= {};
1508
+ target[keys.at(-1)] = true;
1509
+ }
1510
+ if (Object.keys(config).length) svelteConfig.edit({
1511
+ sv,
1512
+ cwd
1513
+ }, ({ override }) => override(config));
1514
+ }
1515
+ });
1516
+ //#endregion
1517
+ //#region src/addons/mdsvex.ts
1518
+ var mdsvex_default = defineAddon({
1519
+ id: "mdsvex",
1520
+ shortDescription: "svelte + markdown",
1521
+ homepage: "https://mdsvex.pngwn.io",
1522
+ options: {},
1523
+ run: ({ sv, cwd }) => {
1524
+ sv.devDependency("mdsvex", "^0.12.7");
1525
+ svelteConfig.edit({
1526
+ sv,
1527
+ cwd
1528
+ }, ({ ast, property, override, js }) => {
1529
+ js.imports.addNamed(ast, {
1530
+ from: "mdsvex",
1531
+ imports: ["mdsvex"]
1532
+ });
1533
+ let preprocessorArray = property("preprocess", { fallback: js.array.create() });
1534
+ if (!(preprocessorArray.type === "ArrayExpression")) {
1535
+ const previousElement = preprocessorArray;
1536
+ preprocessorArray = js.array.create();
1537
+ js.array.append(preprocessorArray, previousElement);
1538
+ override({ preprocess: preprocessorArray });
1539
+ }
1540
+ const mdsvexCall = js.functions.createCall({
1541
+ name: "mdsvex",
1542
+ args: []
1543
+ });
1544
+ mdsvexCall.arguments.push(js.object.create({ extensions: [".svx", ".md"] }));
1545
+ js.array.append(preprocessorArray, mdsvexCall);
1546
+ const extensionsArray = property("extensions", { fallback: js.array.create() });
1547
+ js.array.append(extensionsArray, ".svelte");
1548
+ js.array.append(extensionsArray, ".svx");
1549
+ js.array.append(extensionsArray, ".md");
1550
+ });
1551
+ }
1552
+ });
1553
+ //#endregion
1554
+ //#region src/addons/paraglide.ts
1555
+ const DEFAULT_INLANG_PROJECT = {
1556
+ $schema: "https://inlang.com/schema/project-settings",
1557
+ modules: ["https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"],
1558
+ "plugin.inlang.messageFormat": { pathPattern: "./messages/{locale}.json" }
1559
+ };
1560
+ const options$3 = defineAddonOptions().add("languageTags", {
1561
+ question: `Which languages would you like to support? ${color.optional("(e.g. en,de-ch)")}`,
1562
+ type: "string",
1563
+ default: "en, es",
1564
+ validate(input) {
1565
+ if (!input) return;
1566
+ const { invalidLanguageTags, validLanguageTags } = parseLanguageTagInput(input);
1567
+ if (invalidLanguageTags.length > 0) if (invalidLanguageTags.length === 1) return `The input "${invalidLanguageTags[0]}" is not a valid IETF BCP 47 language tag`;
1568
+ else return `The inputs ${new Intl.ListFormat("en", {
1569
+ style: "long",
1570
+ type: "conjunction"
1571
+ }).format(invalidLanguageTags.map((x) => `"${x}"`))} are not valid BCP47 language tags`;
1572
+ if (validLanguageTags.length === 0) return "Please enter at least one valid BCP47 language tag. Eg: en";
1573
+ }
1574
+ }).add("demo", {
1575
+ type: "boolean",
1576
+ default: true,
1577
+ question: "Do you want to include a demo?"
1578
+ }).build();
1579
+ var paraglide_default = defineAddon({
1580
+ id: "paraglide",
1581
+ shortDescription: "i18n",
1582
+ homepage: "https://inlang.com/m/gerre34r/library-inlang-paraglideJs",
1583
+ options: options$3,
1584
+ setup: ({ isKit, unsupported, runsAfter }) => {
1585
+ if (!isKit) unsupported("Requires SvelteKit");
1586
+ runsAfter("experimental");
1587
+ },
1588
+ run: ({ sv, options, file, language, directory, dependencyVersion }) => {
1589
+ const [ts] = createPrinter(language === "ts");
1590
+ const kitRange = dependencyVersion("@sveltejs/kit");
1591
+ const lib = resolveLibPrefix(kitRange);
1592
+ const pathType = isKit3(kitRange) ? "Path" : "Pathname";
1593
+ const paraglideOutDir = `${directory.lib}/paraglide`;
1594
+ sv.devDependency("@inlang/paraglide-js", "^2.18.2");
1595
+ sv.file(file.viteConfig, transforms.script(({ ast, js }) => {
1596
+ const vitePluginName = "paraglideVitePlugin";
1597
+ js.imports.addNamed(ast, {
1598
+ imports: [vitePluginName],
1599
+ from: "@inlang/paraglide-js"
1600
+ });
1601
+ js.vite.addPlugin(ast, { code: `${vitePluginName}({
1602
+ project: './project.inlang',
1603
+ outdir: './${paraglideOutDir}',
1604
+ emitTsDeclarations: true
1605
+ })` });
1606
+ }));
1607
+ sv.file(`src/hooks.${language}`, transforms.script(({ ast, comments, js }) => {
1608
+ js.imports.addNamed(ast, {
1609
+ from: `${lib}/paraglide/runtime.js`,
1610
+ imports: ["deLocalizeUrl"]
1611
+ });
1612
+ const expression = js.common.parseExpression("(request) => deLocalizeUrl(request.url).pathname");
1613
+ const rerouteIdentifier = js.variables.declaration(ast, {
1614
+ kind: "const",
1615
+ name: `reroute${language === "ts" ? ": Reroute" : ""}`,
1616
+ value: expression
1617
+ });
1618
+ const existingExport = js.exports.createNamed(ast, {
1619
+ name: "reroute",
1620
+ fallback: rerouteIdentifier
1621
+ });
1622
+ if (existingExport.declaration !== rerouteIdentifier) R.warn("Adding the reroute hook automatically failed. Add it manually");
1623
+ if (language === "ts") js.imports.addNamed(ast, {
1624
+ from: "@sveltejs/kit/hooks",
1625
+ imports: ["Reroute"],
1626
+ isType: true
1627
+ });
1628
+ else js.common.addJsDocTypeComment(existingExport, comments, { type: "import('@sveltejs/kit/hooks').Reroute" });
1629
+ }));
1630
+ sv.file(`src/hooks.server.${language}`, transforms.script(({ ast, comments, js }) => {
1631
+ js.imports.addNamed(ast, {
1632
+ from: `${lib}/paraglide/server.js`,
1633
+ imports: ["paraglideMiddleware"]
1634
+ });
1635
+ js.imports.addNamed(ast, {
1636
+ from: `${lib}/paraglide/runtime.js`,
1637
+ imports: ["getTextDirection"]
1638
+ });
1639
+ js.kit.addHooksHandle(ast, {
1640
+ language,
1641
+ newHandleName: "handleParaglide",
1642
+ handleContent: `({ event, resolve }) => paraglideMiddleware(event.request, ({ request, locale }) => {
1643
+ return resolve({ ...event, request }, {
1644
+ transformPageChunk: ({ html }) => html.replace('%paraglide.lang%', locale).replace('%paraglide.dir%', getTextDirection(locale))
1645
+ });
1646
+ });`,
1647
+ comments
1648
+ });
1649
+ }));
1650
+ sv.file("src/app.html", transforms.html(({ ast, html }) => {
1651
+ const htmlNode = ast.nodes.find((child) => child.type === "RegularElement" && child.name === "html");
1652
+ if (!htmlNode) {
1653
+ R.warn("Could not find <html> node in app.html. You'll need to add the language placeholder manually");
1654
+ return false;
1655
+ }
1656
+ html.addAttribute(htmlNode, "lang", "%paraglide.lang%");
1657
+ html.addAttribute(htmlNode, "dir", "%paraglide.dir%");
1658
+ }));
1659
+ sv.file(file.gitignore, transforms.text(({ content, text }) => {
1660
+ if (!content) return false;
1661
+ content = text.upsert(content, paraglideOutDir, { comment: "Paraglide" });
1662
+ content = text.upsert(content, "project.inlang/cache/");
1663
+ return content;
1664
+ }));
1665
+ sv.file("project.inlang/settings.json", transforms.json(({ data }) => {
1666
+ if (Object.keys(data).length > 0) return false;
1667
+ for (const key in DEFAULT_INLANG_PROJECT) data[key] = DEFAULT_INLANG_PROJECT[key];
1668
+ const { validLanguageTags } = parseLanguageTagInput(options.languageTags);
1669
+ data.baseLocale = validLanguageTags[0];
1670
+ data.locales = validLanguageTags;
1671
+ }));
1672
+ sv.file(`${directory.kitRoutes}/+layout.svelte`, transforms.svelteScript({ language }, ({ ast, svelte, js }) => {
1673
+ js.imports.addNamed(ast.instance.content, {
1674
+ imports: ["locales", "localizeHref"],
1675
+ from: `${lib}/paraglide/runtime.js`
1676
+ });
1677
+ js.imports.addNamed(ast.instance.content, {
1678
+ imports: ["page"],
1679
+ from: "$app/state"
1680
+ });
1681
+ js.imports.addNamed(ast.instance.content, {
1682
+ imports: ["resolve"],
1683
+ from: "$app/paths"
1684
+ });
1685
+ if (language === "ts") js.imports.addNamed(ast.instance.content, {
1686
+ imports: [pathType],
1687
+ from: "$app/types",
1688
+ isType: true
1689
+ });
1690
+ svelte.addFragment(ast, dedent`
1691
+ <div style="display:none">
1692
+ {#each locales as locale (locale)}
1693
+ <a href={resolve(localizeHref(page.url.pathname, { locale })${ts(` as ${pathType}`)})}>{locale}</a>
1694
+ {/each}
1695
+ </div>`, { language });
1696
+ }));
1697
+ if (options.demo) {
1698
+ sv.file(`${directory.kitRoutes}/demo/+page.svelte`, addToDemoPage("paraglide", language));
1699
+ sv.file(`${directory.kitRoutes}/demo/paraglide/+page.svelte`, transforms.svelteScript({ language }, ({ ast, svelte, js }) => {
1700
+ js.imports.addNamed(ast.instance.content, {
1701
+ imports: { m: "m" },
1702
+ from: `${lib}/paraglide/messages.js`
1703
+ });
1704
+ js.imports.addNamed(ast.instance.content, {
1705
+ imports: { setLocale: "setLocale" },
1706
+ from: `${lib}/paraglide/runtime.js`
1707
+ });
1708
+ let templateCode = "<h1>{m.hello_world({ name: 'SvelteKit User' })}</h1>";
1709
+ const { validLanguageTags } = parseLanguageTagInput(options.languageTags);
1710
+ const links = validLanguageTags.map((x) => `<button onclick={() => setLocale('${x}')}>${x}</button>`).join("");
1711
+ templateCode += `<div>${links}</div>`;
1712
+ templateCode += "<p>If you use VSCode, install the <a href=\"https://marketplace.visualstudio.com/items?itemName=inlang.vs-code-extension\" target=\"_blank\">Sherlock i18n extension</a> for a better i18n experience.</p>";
1713
+ svelte.addFragment(ast, templateCode);
1714
+ }));
1715
+ }
1716
+ const { validLanguageTags } = parseLanguageTagInput(options.languageTags);
1717
+ for (const languageTag of validLanguageTags) sv.file(`messages/${languageTag}.json`, transforms.json(({ data }) => {
1718
+ data["$schema"] = "https://inlang.com/schema/inlang-message-format";
1719
+ data.hello_world = `Hello, {name} from ${languageTag}!`;
1720
+ }));
1721
+ },
1722
+ nextSteps: () => [`Edit your messages in ${color.path("messages/en.json")}`, options$3.demo && `Visit ${color.route("/demo/paraglide")} route to view the demo`].filter((line) => Boolean(line))
1723
+ });
1724
+ const isValidLanguageTag = (languageTag) => RegExp("^((?<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?<language>([A-Za-z]{2,3}(-(?<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?))(-(?<script>[A-Za-z]{4}))?(-(?<region>[A-Za-z]{2}|[0-9]{3}))?(-(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*))$").test(languageTag);
1725
+ function parseLanguageTagInput(input) {
1726
+ const probablyLanguageTags = input.replace(/[,:\s]/g, " ").split(" ").filter(Boolean).map((tag) => tag.toLowerCase());
1727
+ const validLanguageTags = [];
1728
+ const invalidLanguageTags = [];
1729
+ for (const tag of probablyLanguageTags) if (isValidLanguageTag(tag)) validLanguageTags.push(tag);
1730
+ else invalidLanguageTags.push(tag);
1731
+ return {
1732
+ validLanguageTags,
1733
+ invalidLanguageTags
1734
+ };
1735
+ }
1736
+ //#endregion
1737
+ //#region src/addons/playwright.ts
1738
+ var playwright_default = defineAddon({
1739
+ id: "playwright",
1740
+ shortDescription: "browser testing",
1741
+ homepage: "https://playwright.dev",
1742
+ options: {},
1743
+ run: ({ sv, language, file, isKit, directory }) => {
1744
+ sv.devDependency("@playwright/test", "^1.60.0");
1745
+ sv.file(file.package, transforms.json(({ data, json }) => {
1746
+ json.packageScriptsUpsert(data, "test:e2e", "playwright test");
1747
+ json.packageScriptsUpsert(data, "test:e2e", "playwright install", { mode: "prepend" });
1748
+ json.packageScriptsUpsert(data, "test", "npm run test:e2e");
1749
+ }));
1750
+ sv.file(file.gitignore, transforms.text(({ content, text }) => {
1751
+ if (!content) return false;
1752
+ return text.upsert(content, "test-results", { comment: "Playwright" });
1753
+ }));
1754
+ const testDir = isKit ? `${directory.kitRoutes}/demo/playwright` : directory.src;
1755
+ const testRoute = isKit ? "/demo/playwright" : "/";
1756
+ if (isKit) {
1757
+ sv.file(`${directory.kitRoutes}/demo/+page.svelte`, addToDemoPage("playwright", language));
1758
+ sv.file(`${testDir}/+page.svelte`, transforms.text(({ content }) => {
1759
+ if (content) return false;
1760
+ return dedent`
1761
+ <h1>Playwright e2e test demo</h1>
1762
+ `;
1763
+ }));
1764
+ }
1765
+ sv.file(`${testDir}/${isKit ? "page" : "app"}.svelte.e2e.${language}`, transforms.text(({ content }) => {
1766
+ if (content) return false;
1767
+ return dedent`
1768
+ import { expect, test } from '@playwright/test';
1769
+
1770
+ test('has expected h1', async ({ page }) => {
1771
+ await page.goto('${testRoute}');
1772
+ await expect(page.locator('h1')).toBeVisible();
1773
+ });
1774
+ `;
1775
+ }));
1776
+ sv.file(`playwright.config.${language}`, transforms.script(({ ast, js }) => {
1777
+ const defineConfig = js.common.parseExpression("defineConfig({})");
1778
+ const { value: defaultExport } = js.exports.createDefault(ast, { fallback: defineConfig });
1779
+ const config = {
1780
+ webServer: {
1781
+ command: "npm run build && npm run preview",
1782
+ port: 4173
1783
+ },
1784
+ testMatch: "**/*.e2e.{ts,js}"
1785
+ };
1786
+ if (defaultExport.type === "CallExpression" && defaultExport.arguments[0]?.type === "ObjectExpression") {
1787
+ js.imports.addNamed(ast, {
1788
+ imports: ["defineConfig"],
1789
+ from: "@playwright/test"
1790
+ });
1791
+ js.object.overrideProperties(defaultExport.arguments[0], config);
1792
+ } else if (defaultExport.type === "ObjectExpression") js.object.overrideProperties(defaultExport, config);
1793
+ else R.warn("Unexpected playwright config for playwright add-on. Could not update.");
1794
+ }));
1795
+ },
1796
+ nextSteps: ({ isKit, packageManager }) => {
1797
+ const steps = [];
1798
+ steps.push(`Run ${color.command(resolveCommandArray(packageManager, "execute-local", ["playwright", "install"]))} to download browsers`);
1799
+ if (isKit) steps.push(`Visit ${color.route("/demo/playwright")} to see the demo page`);
1800
+ steps.push(`Run ${color.command(resolveCommandArray(packageManager, "run", ["test:e2e"]))} to execute the example tests`);
1801
+ return steps;
1802
+ }
1803
+ });
1804
+ //#endregion
1805
+ //#region src/addons/prettier.ts
1806
+ var prettier_default = defineAddon({
1807
+ id: "prettier",
1808
+ shortDescription: "formatter",
1809
+ homepage: "https://prettier.io",
1810
+ options: {},
1811
+ run: ({ sv, dependencyVersion, file }) => {
1812
+ const tailwindcssInstalled = Boolean(dependencyVersion("tailwindcss"));
1813
+ if (tailwindcssInstalled) sv.devDependency("prettier-plugin-tailwindcss", "^0.8.0");
1814
+ sv.devDependency("prettier", "^3.8.3");
1815
+ sv.devDependency("prettier-plugin-svelte", "^4.1.0");
1816
+ sv.file(".prettierignore", transforms.text(({ content }) => {
1817
+ if (content) return false;
1818
+ return dedent`
1819
+ # Package Managers
1820
+ package-lock.json
1821
+ pnpm-lock.yaml
1822
+ yarn.lock
1823
+ bun.lock
1824
+ bun.lockb
1825
+
1826
+ # Miscellaneous
1827
+ /static/
1828
+ `;
1829
+ }));
1830
+ sv.file(prettierConfigPath(), createPrettierConfig({
1831
+ tailwind: tailwindcssInstalled,
1832
+ stylesheet: tailwindcssInstalled ? file.getRelative({ to: file.stylesheet }) : void 0
1833
+ }));
1834
+ const eslintInfo = checkEslint(dependencyVersion("eslint"));
1835
+ sv.file(file.package, transforms.json(({ data, json }) => {
1836
+ json.packageScriptsUpsert(data, "lint", "prettier --check .", { mode: "prepend" });
1837
+ json.packageScriptsUpsert(data, "format", "prettier --write .");
1838
+ }));
1839
+ sv.file(".vscode/extensions.json", transforms.json(({ data, json }) => {
1840
+ json.arrayUpsert(data, "recommendations", "esbenp.prettier-vscode");
1841
+ }));
1842
+ if (eslintInfo.installed && !eslintInfo.supported) R.warn(`An unsupported major version of ${color.warning("eslint")} was detected. Skipping ${color.warning("eslint-config-prettier")} installation.`);
1843
+ if (eslintInfo.supported) {
1844
+ sv.devDependency("eslint-config-prettier", "^10.1.8");
1845
+ sv.file("eslint.config.js", addEslintConfigPrettier);
1846
+ }
1847
+ }
1848
+ });
1849
+ function checkEslint(version) {
1850
+ if (!version) return {
1851
+ installed: false,
1852
+ supported: false
1853
+ };
1854
+ const supportedMajor = coerceVersion(ESLINT_VERSION).major;
1855
+ return {
1856
+ installed: true,
1857
+ supported: coerceVersion(version).major === supportedMajor
1858
+ };
1859
+ }
1860
+ //#endregion
1861
+ //#region src/addons/storybook.ts
1862
+ var storybook_default = defineAddon({
1863
+ id: "storybook",
1864
+ shortDescription: "frontend workshop",
1865
+ homepage: "https://storybook.js.org",
1866
+ options: {},
1867
+ setup: ({ runsAfter }) => {
1868
+ runsAfter("vitest");
1869
+ runsAfter("eslint");
1870
+ },
1871
+ run: async ({ sv }) => {
1872
+ const args = [
1873
+ `create-storybook@latest`,
1874
+ "--skip-install",
1875
+ "--no-dev"
1876
+ ];
1877
+ if (V.env.NODE_ENV?.toLowerCase() === "test") args.push(...["--no-features", "--yes"]);
1878
+ await sv.execute(args, "inherit");
1879
+ sv.devDependency(`@types/node`, getNodeTypesVersion());
1880
+ }
1881
+ });
1882
+ //#endregion
1883
+ //#region src/addons/sveltekit-adapter.ts
1884
+ const adapters = [
1885
+ {
1886
+ id: "auto",
1887
+ package: "@sveltejs/adapter-auto",
1888
+ version: "^8.0.0-next.3"
1889
+ },
1890
+ {
1891
+ id: "node",
1892
+ package: "@sveltejs/adapter-node",
1893
+ version: "^6.0.0-next.10"
1894
+ },
1895
+ {
1896
+ id: "static",
1897
+ package: "@sveltejs/adapter-static",
1898
+ version: "^4.0.0-next.4"
1899
+ },
1900
+ {
1901
+ id: "vercel",
1902
+ package: "@sveltejs/adapter-vercel",
1903
+ version: "^7.0.0-next.6"
1904
+ },
1905
+ {
1906
+ id: "cloudflare",
1907
+ package: "@sveltejs/adapter-cloudflare",
1908
+ version: "^8.0.0-next.6"
1909
+ },
1910
+ {
1911
+ id: "netlify",
1912
+ package: "@sveltejs/adapter-netlify",
1913
+ version: "^7.0.0-next.8"
1914
+ }
1915
+ ];
1916
+ var sveltekit_adapter_default = defineAddon({
1917
+ id: "sveltekit-adapter",
1918
+ alias: "adapter",
1919
+ shortDescription: "deployment",
1920
+ homepage: "https://svelte.dev/docs/kit/adapters",
1921
+ options: defineAddonOptions().add("adapter", {
1922
+ type: "select",
1923
+ question: "Which SvelteKit adapter would you like to use?",
1924
+ default: "auto",
1925
+ options: adapters.map((p) => ({
1926
+ value: p.id,
1927
+ label: p.id,
1928
+ hint: p.package
1929
+ }))
1930
+ }).add("cfTarget", {
1931
+ condition: (options) => options.adapter === "cloudflare",
1932
+ type: "select",
1933
+ question: "Are you deploying to Workers (assets) or Pages?",
1934
+ default: "workers",
1935
+ options: [{
1936
+ value: "workers",
1937
+ label: "Workers",
1938
+ hint: "Recommended way to deploy to Cloudflare"
1939
+ }, {
1940
+ value: "pages",
1941
+ label: "Pages"
1942
+ }]
1943
+ }).build(),
1944
+ setup: ({ isKit, unsupported }) => {
1945
+ if (!isKit) unsupported("Requires SvelteKit");
1946
+ },
1947
+ run: ({ sv, options, packageManager, file, cwd }) => {
1948
+ const adapter = adapters.find((a) => a.id === options.adapter);
1949
+ sv.file(file.package, transforms.json(({ data }) => {
1950
+ const devDeps = data["devDependencies"];
1951
+ for (const pkg of Object.keys(devDeps)) if (pkg.startsWith("@sveltejs/adapter-")) delete devDeps[pkg];
1952
+ if (options.adapter === "cloudflare") {
1953
+ const preview = options.cfTarget === "workers" ? "wrangler dev .svelte-kit/cloudflare/_worker.js --port 4173" : "wrangler pages dev .svelte-kit/cloudflare --port 4173";
1954
+ data.scripts.preview = preview;
1955
+ }
1956
+ }));
1957
+ sv.devDependency(adapter.package, adapter.version);
1958
+ svelteConfig.edit({
1959
+ sv,
1960
+ cwd
1961
+ }, ({ ast, override, js }) => {
1962
+ const adapterImports = ast.body.filter((n) => n.type === "ImportDeclaration").find((importDecl) => typeof importDecl.source.value === "string" && importDecl.source.value.startsWith("@sveltejs/adapter-") && importDecl.importKind === "value");
1963
+ let adapterName = "adapter";
1964
+ if (adapterImports) {
1965
+ adapterImports.source.value = adapter.package;
1966
+ adapterImports.source.raw = void 0;
1967
+ adapterName = (adapterImports.specifiers?.find((s) => s.type === "ImportDefaultSpecifier")).local.name;
1968
+ } else js.imports.addDefault(ast, {
1969
+ from: adapter.package,
1970
+ as: adapterName
1971
+ });
1972
+ override({ adapter: js.functions.createCall({
1973
+ name: adapterName,
1974
+ args: [],
1975
+ useIdentifiers: true
1976
+ }) }, adapter.package === "@sveltejs/adapter-auto" ? void 0 : { dropLeadingComments: ["adapter"] });
1977
+ });
1978
+ if (adapter.package === "@sveltejs/adapter-cloudflare") {
1979
+ sv.devDependency("wrangler", "^4.97.0");
1980
+ if (packageManager === "pnpm") sv.file(file.findUp("pnpm-workspace.yaml"), pnpm.allowBuilds("workerd"));
1981
+ const ext = fileExists(cwd, "wrangler.toml") ? "toml" : "jsonc";
1982
+ const applyWranglerConfig = (data) => {
1983
+ if (ext === "jsonc") data.$schema ??= "./node_modules/wrangler/config-schema.json";
1984
+ if (!data.name) data.name = sanitizeName(loadPackageJson(cwd).data.name, "wrangler");
1985
+ data.compatibility_date ??= (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1986
+ data.compatibility_flags ??= [];
1987
+ if (!data.compatibility_flags.includes("nodejs_compat") && !data.compatibility_flags.includes("nodejs_als")) data.compatibility_flags.push("nodejs_als");
1988
+ switch (options.cfTarget) {
1989
+ case "workers":
1990
+ data.main = ".svelte-kit/cloudflare/_worker.js";
1991
+ data.assets ??= {};
1992
+ data.assets.binding = "ASSETS";
1993
+ data.assets.directory = ".svelte-kit/cloudflare";
1994
+ data.workers_dev = true;
1995
+ data.preview_urls = true;
1996
+ break;
1997
+ case "pages":
1998
+ data.pages_build_output_dir = ".svelte-kit/cloudflare";
1999
+ break;
2000
+ }
2001
+ };
2002
+ sv.file(`wrangler.${ext}`, ext === "toml" ? transforms.toml(({ data }) => applyWranglerConfig(data)) : transforms.json(({ data }) => applyWranglerConfig(data)));
2003
+ if (file.typeConfig) {
2004
+ sv.file(file.package, transforms.json(({ data, json }) => {
2005
+ json.packageScriptsUpsert(data, "gen", "wrangler types");
2006
+ json.packageScriptsUpsert(data, "check", "wrangler types --check", { mode: "prepend" });
2007
+ json.packageScriptsUpsert(data, "build", "wrangler types --check", { mode: "prepend" });
2008
+ }));
2009
+ sv.file(file.typeConfig, transforms.json(({ data }) => {
2010
+ data.compilerOptions ??= {};
2011
+ data.compilerOptions.types ??= [];
2012
+ data.compilerOptions.types.push("./worker-configuration.d.ts");
2013
+ }));
2014
+ sv.file("src/app.d.ts", transforms.script(({ ast, comments, js }) => {
2015
+ const platform = js.kit.addGlobalAppInterface(ast, { name: "Platform" });
2016
+ if (!platform) throw new Error("Failed detecting `platform` interface in `src/app.d.ts`");
2017
+ comments.remove((c) => c.type === "Line" && c.value.trim() === "interface Platform {}");
2018
+ platform.body.body.push(js.common.createTypeProperty("env", "Env"), js.common.createTypeProperty("ctx", "ExecutionContext"), js.common.createTypeProperty("caches", "CacheStorage"), js.common.createTypeProperty("cf", "IncomingRequestCfProperties", true));
2019
+ }));
2020
+ }
2021
+ }
2022
+ },
2023
+ nextSteps({ options, packageManager }) {
2024
+ const steps = [];
2025
+ if (options.adapter === "cloudflare") steps.push(`Run ${color.command(resolveCommandArray(packageManager, "run", ["gen"]))} to update ${color.addon("cloudflare")} types`);
2026
+ return steps;
2027
+ }
2028
+ });
2029
+ //#endregion
2030
+ //#region src/addons/tailwindcss.ts
2031
+ const plugins = [{
2032
+ id: "typography",
2033
+ package: "@tailwindcss/typography",
2034
+ version: "^0.5.19"
2035
+ }, {
2036
+ id: "forms",
2037
+ package: "@tailwindcss/forms",
2038
+ version: "^0.5.11"
2039
+ }];
2040
+ var tailwindcss_default = defineAddon({
2041
+ id: "tailwindcss",
2042
+ alias: "tailwind",
2043
+ shortDescription: "css framework",
2044
+ homepage: "https://tailwindcss.com",
2045
+ options: defineAddonOptions().add("plugins", {
2046
+ type: "multiselect",
2047
+ question: "Which plugins would you like to add?",
2048
+ options: plugins.map((p) => ({
2049
+ value: p.id,
2050
+ label: p.id,
2051
+ hint: p.package
2052
+ })),
2053
+ default: [],
2054
+ required: false
2055
+ }).build(),
2056
+ run: ({ sv, options, file, isKit, directory, dependencyVersion, language }) => {
2057
+ const prettierInstalled = Boolean(dependencyVersion("prettier"));
2058
+ sv.devDependency("tailwindcss", "^4.3.0");
2059
+ sv.devDependency("@tailwindcss/vite", "^4.3.0");
2060
+ if (prettierInstalled) sv.devDependency("prettier-plugin-tailwindcss", "^0.8.0");
2061
+ for (const plugin of plugins) {
2062
+ if (!options.plugins.includes(plugin.id)) continue;
2063
+ sv.devDependency(plugin.package, plugin.version);
2064
+ }
2065
+ sv.file(file.viteConfig, transforms.script(({ ast, js }) => {
2066
+ const vitePluginName = "tailwindcss";
2067
+ js.imports.addDefault(ast, {
2068
+ as: vitePluginName,
2069
+ from: "@tailwindcss/vite"
2070
+ });
2071
+ js.vite.addPlugin(ast, {
2072
+ code: `${vitePluginName}()`,
2073
+ mode: "prepend"
2074
+ });
2075
+ }));
2076
+ sv.file(file.stylesheet, transforms.css(({ ast, css }) => {
2077
+ for (const plugin of plugins) {
2078
+ if (!options.plugins.includes(plugin.id)) continue;
2079
+ css.addAtRule(ast, {
2080
+ name: "plugin",
2081
+ params: `'${plugin.package}'`,
2082
+ append: false
2083
+ });
2084
+ }
2085
+ css.addAtRule(ast, {
2086
+ name: "import",
2087
+ params: `'tailwindcss'`,
2088
+ append: false
2089
+ });
2090
+ }));
2091
+ if (isKit) {
2092
+ const layoutSvelte = `${directory.kitRoutes}/+layout.svelte`;
2093
+ const stylesheetRelative = file.getRelative({
2094
+ from: layoutSvelte,
2095
+ to: file.stylesheet
2096
+ });
2097
+ sv.file(layoutSvelte, transforms.svelteScript({ language }, ({ ast, svelte, js }) => {
2098
+ js.imports.addEmpty(ast.instance.content, { from: stylesheetRelative });
2099
+ if (ast.fragment.nodes.length === 0) {
2100
+ const svelteVersion = dependencyVersion("svelte");
2101
+ if (!svelteVersion) throw new Error("Failed to determine svelte version");
2102
+ svelte.addSlot(ast, { svelteVersion });
2103
+ }
2104
+ }));
2105
+ } else {
2106
+ const appSvelte = `${directory.src}/App.svelte`;
2107
+ const stylesheetRelative = file.getRelative({
2108
+ from: appSvelte,
2109
+ to: file.stylesheet
2110
+ });
2111
+ sv.file(appSvelte, transforms.svelteScript({ language }, ({ ast, js }) => {
2112
+ js.imports.addEmpty(ast.instance.content, { from: stylesheetRelative });
2113
+ }));
2114
+ }
2115
+ sv.file(".vscode/settings.json", transforms.json(({ data }) => {
2116
+ data["files.associations"] ??= {};
2117
+ data["files.associations"]["*.css"] = "tailwindcss";
2118
+ }));
2119
+ sv.file(".vscode/extensions.json", transforms.json(({ data, json }) => {
2120
+ json.arrayUpsert(data, "recommendations", "bradlc.vscode-tailwindcss");
2121
+ }));
2122
+ if (prettierInstalled) sv.file(prettierConfigPath(), addPrettierTailwind({ stylesheet: file.getRelative({ to: file.stylesheet }) }));
2123
+ }
2124
+ });
2125
+ //#endregion
2126
+ //#region src/addons/vitest-addon.ts
2127
+ var _templateObject;
2128
+ const options = defineAddonOptions().add("usages", {
2129
+ question: "What do you want to use vitest for?",
2130
+ type: "multiselect",
2131
+ default: ["unit", "component"],
2132
+ options: [{
2133
+ value: "unit",
2134
+ label: "unit testing"
2135
+ }, {
2136
+ value: "component",
2137
+ label: "component testing"
2138
+ }],
2139
+ required: true
2140
+ }).build();
2141
+ let vitestV3Installed = false;
2142
+ //#endregion
2143
+ //#region src/addons/index.ts
2144
+ const officialAddons = {
2145
+ prettier: prettier_default,
2146
+ eslint: eslint_default,
2147
+ vitest: defineAddon({
2148
+ id: "vitest",
2149
+ shortDescription: "unit testing",
2150
+ homepage: "https://vitest.dev",
2151
+ options,
2152
+ run: ({ sv, file, language, directory, options, dependencyVersion }) => {
2153
+ const unitTesting = options.usages.includes("unit");
2154
+ const componentTesting = options.usages.includes("component");
2155
+ const vitestVersion = dependencyVersion("vitest");
2156
+ if (vitestVersion) vitestV3Installed = coerceVersion(vitestVersion).major === 3;
2157
+ sv.devDependency("vitest", "^4.1.8");
2158
+ if (componentTesting) {
2159
+ sv.devDependency("@vitest/browser-playwright", "^4.1.8");
2160
+ sv.devDependency("vitest-browser-svelte", "^2.1.1");
2161
+ sv.devDependency("playwright", "^1.60.0");
2162
+ }
2163
+ sv.file(file.package, transforms.json(({ data, json }) => {
2164
+ json.packageScriptsUpsert(data, "test:unit", "vitest");
2165
+ json.packageScriptsUpsert(data, "test", "npm run test:unit -- --run", { mode: "prepend" });
2166
+ }));
2167
+ const examplesDir = `${directory.lib}/vitest-examples`;
2168
+ const [ts] = createPrinter(language === "ts");
2169
+ if (unitTesting || componentTesting) sv.file(`${examplesDir}/greet.${language}`, transforms.text(({ content }) => {
2170
+ if (content) return false;
2171
+ return dedent`
2172
+ export function greet(name${ts(": string")})${ts(": string")} {
2173
+ return 'Hello, ' + name + '!';
2174
+ }
2175
+ `;
2176
+ }));
2177
+ if (unitTesting) sv.file(`${examplesDir}/greet.spec.${language}`, transforms.text(({ content }) => {
2178
+ if (content) return false;
2179
+ return dedent`
2180
+ import { describe, it, expect } from 'vitest';
2181
+ import { greet } from './greet';
2182
+
2183
+ describe('greet', () => {
2184
+ it('returns a greeting', () => {
2185
+ expect(greet('Svelte')).toBe('Hello, Svelte!');
2186
+ });
2187
+ });
2188
+ `;
2189
+ }));
2190
+ if (componentTesting) {
2191
+ sv.file(`${examplesDir}/Welcome.svelte`, transforms.text(({ content }) => {
2192
+ if (content) return false;
2193
+ return dedent(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n <script>\n import { greet } from './greet';\n\n let { host = 'SvelteKit', guest = 'Vitest' } = $props();\n <\/script>\n\n <h1>{greet(host)}</h1>\n <p>{greet(guest)}</p>\n "])));
2194
+ }));
2195
+ sv.file(`${examplesDir}/Welcome.svelte.spec.${language}`, transforms.text(({ content }) => {
2196
+ if (content) return false;
2197
+ return dedent`
2198
+ import { page } from 'vitest/browser';
2199
+ import { describe, expect, it } from 'vitest';
2200
+ import { render } from 'vitest-browser-svelte';
2201
+ import Welcome from './Welcome.svelte';
2202
+
2203
+ describe('Welcome.svelte', () => {
2204
+ it('renders greetings for host and guest', async () => {
2205
+ render(Welcome, { host: 'SvelteKit', guest: 'Vitest' });
2206
+
2207
+ await expect.element(page.getByRole('heading', { level: 1 })).toHaveTextContent('Hello, SvelteKit!');
2208
+ await expect.element(page.getByText('Hello, Vitest!')).toBeInTheDocument();
2209
+ });
2210
+ });
2211
+ `;
2212
+ }));
2213
+ }
2214
+ sv.file(file.viteConfig, transforms.script(({ ast, js }) => {
2215
+ const clientObjectExpression = js.object.create({
2216
+ extends: `./${file.viteConfig}`,
2217
+ test: {
2218
+ name: "client",
2219
+ browser: {
2220
+ enabled: true,
2221
+ provider: js.functions.createCall({
2222
+ name: "playwright",
2223
+ args: []
2224
+ }),
2225
+ instances: [{
2226
+ browser: "chromium",
2227
+ headless: true
2228
+ }]
2229
+ },
2230
+ include: ["src/**/*.svelte.{test,spec}.{js,ts}"],
2231
+ exclude: ["src/lib/server/**"]
2232
+ }
2233
+ });
2234
+ const serverObjectExpression = js.object.create({
2235
+ extends: `./${file.viteConfig}`,
2236
+ test: {
2237
+ name: "server",
2238
+ environment: "node",
2239
+ include: ["src/**/*.{test,spec}.{js,ts}"],
2240
+ exclude: ["src/**/*.svelte.{test,spec}.{js,ts}"]
2241
+ }
2242
+ });
2243
+ const viteConfig = js.vite.getConfig(ast);
2244
+ const testObject = js.object.property(viteConfig, {
2245
+ name: "test",
2246
+ fallback: js.object.create({ expect: { requireAssertions: true } })
2247
+ });
2248
+ const workspaceArray = js.object.property(testObject, {
2249
+ name: "projects",
2250
+ fallback: js.array.create()
2251
+ });
2252
+ if (componentTesting) js.array.append(workspaceArray, clientObjectExpression);
2253
+ if (unitTesting) js.array.append(workspaceArray, serverObjectExpression);
2254
+ if (componentTesting) js.imports.addNamed(ast, {
2255
+ imports: ["playwright"],
2256
+ from: "@vitest/browser-playwright"
2257
+ });
2258
+ const importName = "defineConfig";
2259
+ const { statement, alias } = js.imports.find(ast, {
2260
+ name: importName,
2261
+ from: "vite"
2262
+ });
2263
+ if (statement) {
2264
+ js.imports.addNamed(ast, {
2265
+ imports: { defineConfig: alias },
2266
+ from: "vitest/config"
2267
+ });
2268
+ js.imports.remove(ast, {
2269
+ name: importName,
2270
+ from: "vite",
2271
+ statement
2272
+ });
2273
+ }
2274
+ }));
2275
+ },
2276
+ nextSteps: ({ language, options }) => {
2277
+ const toReturn = [];
2278
+ if (vitestV3Installed) {
2279
+ if (options.usages.includes("component")) {
2280
+ toReturn.push(`Uninstall ${color.command("@vitest/browser")} package`);
2281
+ toReturn.push(`Update usage from ${color.command("'@vitest/browser...'")} to ${color.command("'vitest/browser'")}`);
2282
+ }
2283
+ toReturn.push(`${color.optional("Optional")} Check ${color.path("./vite.config.ts")} and remove duplicate project definitions`);
2284
+ toReturn.push(`${color.optional("Optional")} Remove ${color.path(`./vitest-setup-client.${language}`)} file`);
2285
+ }
2286
+ return toReturn;
2287
+ }
2288
+ }),
2289
+ playwright: playwright_default,
2290
+ tailwindcss: tailwindcss_default,
2291
+ sveltekitAdapter: sveltekit_adapter_default,
2292
+ drizzle: drizzle_default,
2293
+ betterAuth: better_auth_default,
2294
+ mdsvex: mdsvex_default,
2295
+ paraglide: paraglide_default,
2296
+ storybook: storybook_default,
2297
+ aiTools: ai_tools_default,
2298
+ experimental: experimental_default
2299
+ };
2300
+ function getAddonDetails(id) {
2301
+ const details = Object.values(officialAddons).find((a) => a.id === id);
2302
+ if (!details) throw new Error(`Invalid add-on: ${id}`);
2303
+ return details;
2304
+ }
2305
+ //#endregion
2306
+ export { officialAddons as n, getAddonDetails as t };