From 439aa43bb0474dd5bb81611796d9376e8f0e242c Mon Sep 17 00:00:00 2001 From: jycouet Date: Tue, 18 Nov 2025 13:19:09 +0100 Subject: [PATCH 1/2] feat(cli): expend known files --- packages/addons/tailwindcss/index.ts | 26 ++++++++++---------------- packages/cli/commands/add/workspace.ts | 22 ++++++++++++++++++++-- packages/core/addon/workspace.ts | 8 ++++++-- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/packages/addons/tailwindcss/index.ts b/packages/addons/tailwindcss/index.ts index 792750b0e..c0776e15c 100644 --- a/packages/addons/tailwindcss/index.ts +++ b/packages/addons/tailwindcss/index.ts @@ -35,16 +35,6 @@ export default defineAddon({ run: ({ sv, options, files, typescript, kit, dependencyVersion }) => { const prettierInstalled = Boolean(dependencyVersion('prettier')); - const stylesheet = kit - ? ({ - rootPath: `${kit.routesDirectory}/layout.css`, - relativePath: './layout.css' - } as const) - : ({ - rootPath: 'src/app.css', - relativePath: './app.css' - } as const); - sv.devDependency('tailwindcss', '^4.1.14'); sv.devDependency('@tailwindcss/vite', '^4.1.14'); sv.pnpmBuildDependency('@tailwindcss/oxide'); @@ -68,7 +58,7 @@ export default defineAddon({ return generateCode(); }); - sv.file(stylesheet.rootPath, (content) => { + sv.file(files.stylesheet, (content) => { let atRules = parseCss(content).ast.nodes.filter((node) => node.type === 'atrule'); const findAtRule = (name: string, params: string) => @@ -104,15 +94,19 @@ export default defineAddon({ }); if (!kit) { - sv.file('src/App.svelte', (content) => { + const appSvelte = 'src/App.svelte'; + const stylesheetRelative = files.getRelative({ from: appSvelte, to: files.stylesheet }); + sv.file(appSvelte, (content) => { const { script, generateCode } = parseSvelte(content, { typescript }); - imports.addEmpty(script.ast, { from: stylesheet.relativePath }); + imports.addEmpty(script.ast, { from: stylesheetRelative }); return generateCode({ script: script.generateCode() }); }); } else { - sv.file(`${kit?.routesDirectory}/+layout.svelte`, (content) => { + const layoutSvelte = `${kit?.routesDirectory}/+layout.svelte`; + const stylesheetRelative = files.getRelative({ from: layoutSvelte, to: files.stylesheet }); + sv.file(layoutSvelte, (content) => { const { script, template, generateCode } = parseSvelte(content, { typescript }); - imports.addEmpty(script.ast, { from: stylesheet.relativePath }); + imports.addEmpty(script.ast, { from: stylesheetRelative }); if (content.length === 0) { const svelteVersion = dependencyVersion('svelte'); @@ -149,7 +143,7 @@ export default defineAddon({ if (!plugins.includes(PLUGIN_NAME)) plugins.push(PLUGIN_NAME); - data.tailwindStylesheet ??= stylesheet.rootPath; + data.tailwindStylesheet ??= files.getRelative({ to: files.stylesheet }); return generateCode(); }); diff --git a/packages/cli/commands/add/workspace.ts b/packages/cli/commands/add/workspace.ts index 385293794..509ca9ae3 100644 --- a/packages/cli/commands/add/workspace.ts +++ b/packages/cli/commands/add/workspace.ts @@ -61,13 +61,31 @@ export async function createWorkspace({ dependencies[key] = value.replaceAll(/[^\d|.]/g, ''); } + const kit = dependencies['@sveltejs/kit'] ? parseKitOptions(resolvedCwd) : undefined; + const stylesheet: `${string}/layout.css` | 'src/app.css' = kit + ? `${kit.routesDirectory}/layout.css` + : 'src/app.css'; + return { cwd: resolvedCwd, options, packageManager: packageManager ?? (await detect({ cwd }))?.name ?? getUserAgent() ?? 'npm', typescript: usesTypescript, - files: { viteConfig, svelteConfig }, - kit: dependencies['@sveltejs/kit'] ? parseKitOptions(resolvedCwd) : undefined, + files: { + viteConfig, + svelteConfig, + stylesheet, + getRelative({ from, to }) { + from = from ?? ''; + let relativePath = path.posix.relative(path.posix.dirname(from), to); + // Ensure relative paths start with ./ for proper relative path syntax + if (!relativePath.startsWith('.') && !relativePath.startsWith('/')) { + relativePath = `./${relativePath}`; + } + return relativePath; + } + }, + kit, dependencyVersion: (pkg) => dependencies[pkg] }; } diff --git a/packages/core/addon/workspace.ts b/packages/core/addon/workspace.ts index 824a34e45..3d8f3dead 100644 --- a/packages/core/addon/workspace.ts +++ b/packages/core/addon/workspace.ts @@ -14,8 +14,12 @@ export type Workspace = { dependencyVersion: (pkg: string) => string | undefined; typescript: boolean; files: { - viteConfig: string; - svelteConfig: string; + viteConfig: 'vite.config.js' | 'vite.config.ts'; + svelteConfig: 'svelte.config.js' | 'svelte.config.ts'; + /** `${kit.routesDirectory}/layout.css` or `src/app.css` */ + stylesheet: `${string}/layout.css` | 'src/app.css'; + /** Get the relative path between two files */ + getRelative: ({ from, to }: { from?: string; to: string }) => string; }; kit: { libDirectory: string; routesDirectory: string } | undefined; packageManager: PackageManager; From 028dde740b6edd3c47d346f936b63ca1f69083f0 Mon Sep 17 00:00:00 2001 From: jycouet Date: Tue, 18 Nov 2025 13:39:39 +0100 Subject: [PATCH 2/2] refactor where it make sense --- packages/addons/drizzle/index.ts | 8 ++++---- packages/addons/eslint/index.ts | 10 +++++----- packages/addons/paraglide/index.ts | 2 +- packages/addons/playwright/index.ts | 6 +++--- packages/addons/prettier/index.ts | 10 +++++----- packages/addons/sveltekit-adapter/index.ts | 2 +- packages/addons/tailwindcss/index.ts | 4 ++-- packages/addons/vitest-addon/index.ts | 2 +- packages/cli/commands/add/workspace.ts | 6 ++++++ packages/core/addon/workspace.ts | 9 +++++++++ 10 files changed, 37 insertions(+), 22 deletions(-) diff --git a/packages/addons/drizzle/index.ts b/packages/addons/drizzle/index.ts index b455218ed..0768f8b95 100644 --- a/packages/addons/drizzle/index.ts +++ b/packages/addons/drizzle/index.ts @@ -78,7 +78,7 @@ export default defineAddon({ if (!kit) return unsupported('Requires SvelteKit'); }, - run: ({ sv, typescript, options, kit, dependencyVersion, cwd, cancel }) => { + run: ({ sv, typescript, options, kit, dependencyVersion, cwd, cancel, files }) => { if (!kit) throw new Error('SvelteKit is required'); const ext = typescript ? 'ts' : 'js'; @@ -177,7 +177,7 @@ export default defineAddon({ }); } - sv.file('package.json', (content) => { + sv.file(files.package, (content) => { const { data, generateCode } = parseJson(content); data.scripts ??= {}; const scripts: Record = data.scripts; @@ -191,7 +191,7 @@ export default defineAddon({ const hasPrettier = Boolean(dependencyVersion('prettier')); if (hasPrettier) { - sv.file('.prettierignore', (content) => { + sv.file(files.prettierignore, (content) => { if (!content.includes(`/drizzle/`)) { return content.trimEnd() + '\n/drizzle/'; } @@ -200,7 +200,7 @@ export default defineAddon({ } if (options.database === 'sqlite') { - sv.file('.gitignore', (content) => { + sv.file(files.gitignore, (content) => { // Adds the db file to the gitignore if an ignore is present if (content.length === 0) return content; diff --git a/packages/addons/eslint/index.ts b/packages/addons/eslint/index.ts index c40406c4e..7b233322c 100644 --- a/packages/addons/eslint/index.ts +++ b/packages/addons/eslint/index.ts @@ -17,7 +17,7 @@ export default defineAddon({ shortDescription: 'linter', homepage: 'https://eslint.org', options: {}, - run: ({ sv, typescript, dependencyVersion }) => { + run: ({ sv, typescript, dependencyVersion, files }) => { const prettierInstalled = Boolean(dependencyVersion('prettier')); sv.devDependency('eslint', '^9.38.0'); @@ -31,7 +31,7 @@ export default defineAddon({ if (prettierInstalled) sv.devDependency('eslint-config-prettier', '^10.1.8'); - sv.file('package.json', (content) => { + sv.file(files.package, (content) => { const { data, generateCode } = parseJson(content); data.scripts ??= {}; const scripts: Record = data.scripts; @@ -41,7 +41,7 @@ export default defineAddon({ return generateCode(); }); - sv.file('.vscode/settings.json', (content) => { + sv.file(files.vscodeSettings, (content) => { if (!content) return content; const { data, generateCode } = parseJson(content); @@ -52,7 +52,7 @@ export default defineAddon({ return generateCode(); }); - sv.file('eslint.config.js', (content) => { + sv.file(files.eslintConfig, (content) => { const { ast, generateCode } = parseScript(content); const eslintConfigs: Array = []; @@ -170,7 +170,7 @@ export default defineAddon({ }); if (prettierInstalled) { - sv.file('eslint.config.js', addEslintConfigPrettier); + sv.file(files.eslintConfig, addEslintConfigPrettier); } } }); diff --git a/packages/addons/paraglide/index.ts b/packages/addons/paraglide/index.ts index bee25e136..f09befcf3 100644 --- a/packages/addons/paraglide/index.ts +++ b/packages/addons/paraglide/index.ts @@ -167,7 +167,7 @@ export default defineAddon({ return generateCode(); }); - sv.file('.gitignore', (content) => { + sv.file(files.gitignore, (content) => { if (!content) return content; if (!content.includes(`\n${paraglideOutDir}`)) { diff --git a/packages/addons/playwright/index.ts b/packages/addons/playwright/index.ts index 74b9f030f..ff7d2087d 100644 --- a/packages/addons/playwright/index.ts +++ b/packages/addons/playwright/index.ts @@ -7,12 +7,12 @@ export default defineAddon({ shortDescription: 'browser testing', homepage: 'https://playwright.dev', options: {}, - run: ({ sv, typescript }) => { + run: ({ sv, typescript, files }) => { const ext = typescript ? 'ts' : 'js'; sv.devDependency('@playwright/test', '^1.56.1'); - sv.file('package.json', (content) => { + sv.file(files.package, (content) => { const { data, generateCode } = parseJson(content); data.scripts ??= {}; const scripts: Record = data.scripts; @@ -24,7 +24,7 @@ export default defineAddon({ return generateCode(); }); - sv.file('.gitignore', (content) => { + sv.file(files.gitignore, (content) => { if (!content) return content; if (content.includes('test-results')) return content; return 'test-results\n' + content.trim(); diff --git a/packages/addons/prettier/index.ts b/packages/addons/prettier/index.ts index 692129784..813345c02 100644 --- a/packages/addons/prettier/index.ts +++ b/packages/addons/prettier/index.ts @@ -7,14 +7,14 @@ export default defineAddon({ shortDescription: 'formatter', homepage: 'https://prettier.io', options: {}, - run: ({ sv, dependencyVersion, kit }) => { + run: ({ sv, dependencyVersion, kit, files }) => { const tailwindcssInstalled = Boolean(dependencyVersion('tailwindcss')); if (tailwindcssInstalled) sv.devDependency('prettier-plugin-tailwindcss', '^0.7.1'); sv.devDependency('prettier', '^3.6.2'); sv.devDependency('prettier-plugin-svelte', '^3.4.0'); - sv.file('.prettierignore', (content) => { + sv.file(files.prettierignore, (content) => { if (content) return content; return dedent` # Package Managers @@ -29,7 +29,7 @@ export default defineAddon({ `; }); - sv.file('.prettierrc', (content) => { + sv.file(files.prettierrc, (content) => { let data, generateCode; try { ({ data, generateCode } = parseJson(content)); @@ -72,7 +72,7 @@ export default defineAddon({ const eslintVersion = dependencyVersion('eslint'); const eslintInstalled = hasEslint(eslintVersion); - sv.file('package.json', (content) => { + sv.file(files.package, (content) => { const { data, generateCode } = parseJson(content); data.scripts ??= {}; @@ -99,7 +99,7 @@ export default defineAddon({ if (eslintInstalled) { sv.devDependency('eslint-config-prettier', '^10.1.8'); - sv.file('eslint.config.js', addEslintConfigPrettier); + sv.file(files.eslintConfig, addEslintConfigPrettier); } } }); diff --git a/packages/addons/sveltekit-adapter/index.ts b/packages/addons/sveltekit-adapter/index.ts index f7f09fa68..c6e306fa9 100644 --- a/packages/addons/sveltekit-adapter/index.ts +++ b/packages/addons/sveltekit-adapter/index.ts @@ -33,7 +33,7 @@ export default defineAddon({ const adapter = adapters.find((a) => a.id === options.adapter)!; // removes previously installed adapters - sv.file('package.json', (content) => { + sv.file(files.package, (content) => { const { data, generateCode } = parseJson(content); const devDeps = data['devDependencies']; diff --git a/packages/addons/tailwindcss/index.ts b/packages/addons/tailwindcss/index.ts index c0776e15c..9f534a1e0 100644 --- a/packages/addons/tailwindcss/index.ts +++ b/packages/addons/tailwindcss/index.ts @@ -124,7 +124,7 @@ export default defineAddon({ }); } - sv.file('.vscode/settings.json', (content) => { + sv.file(files.vscodeSettings, (content) => { const { data, generateCode } = parseJson(content); data['files.associations'] ??= {}; @@ -134,7 +134,7 @@ export default defineAddon({ }); if (prettierInstalled) { - sv.file('.prettierrc', (content) => { + sv.file(files.prettierrc, (content) => { const { data, generateCode } = parseJson(content); const PLUGIN_NAME = 'prettier-plugin-tailwindcss'; diff --git a/packages/addons/vitest-addon/index.ts b/packages/addons/vitest-addon/index.ts index e963de423..ad3a956b2 100644 --- a/packages/addons/vitest-addon/index.ts +++ b/packages/addons/vitest-addon/index.ts @@ -33,7 +33,7 @@ export default defineAddon({ sv.devDependency('playwright', '^1.56.1'); } - sv.file('package.json', (content) => { + sv.file(files.package, (content) => { const { data, generateCode } = parseJson(content); data.scripts ??= {}; const scripts: Record = data.scripts; diff --git a/packages/cli/commands/add/workspace.ts b/packages/cli/commands/add/workspace.ts index 509ca9ae3..c8eae758a 100644 --- a/packages/cli/commands/add/workspace.ts +++ b/packages/cli/commands/add/workspace.ts @@ -75,6 +75,12 @@ export async function createWorkspace({ viteConfig, svelteConfig, stylesheet, + package: 'package.json', + gitignore: '.gitignore', + prettierignore: '.prettierignore', + prettierrc: '.prettierrc', + eslintConfig: 'eslint.config.js', + vscodeSettings: '.vscode/settings.json', getRelative({ from, to }) { from = from ?? ''; let relativePath = path.posix.relative(path.posix.dirname(from), to); diff --git a/packages/core/addon/workspace.ts b/packages/core/addon/workspace.ts index 3d8f3dead..037d909c8 100644 --- a/packages/core/addon/workspace.ts +++ b/packages/core/addon/workspace.ts @@ -18,6 +18,15 @@ export type Workspace = { svelteConfig: 'svelte.config.js' | 'svelte.config.ts'; /** `${kit.routesDirectory}/layout.css` or `src/app.css` */ stylesheet: `${string}/layout.css` | 'src/app.css'; + package: 'package.json'; + gitignore: '.gitignore'; + + prettierignore: '.prettierignore'; + prettierrc: '.prettierrc'; + eslintConfig: 'eslint.config.js'; + + vscodeSettings: '.vscode/settings.json'; + /** Get the relative path between two files */ getRelative: ({ from, to }: { from?: string; to: string }) => string; };