diff --git a/.changeset/chatty-planes-bathe.md b/.changeset/chatty-planes-bathe.md new file mode 100644 index 000000000..726c6782f --- /dev/null +++ b/.changeset/chatty-planes-bathe.md @@ -0,0 +1,18 @@ +--- +'@astrojs/svelte': major +'astro': minor +--- + +The fallback Svelte preprocessor will only be applied if a custom `preprocess` option is not passed to the `svelte()` integration option, or in the `svelte.config.js` file. + +To support IDE autocompletion, or if you're migrating from `@astrojs/svelte` v1, you can create a `svelte.config.js` file with: + +```js +import { vitePreprocess } from '@astrojs/svelte'; + +export default { + preprocess: vitePreprocess(), +}; +``` + +This file will also be generated by `astro add svelte` by default. diff --git a/examples/framework-svelte/svelte.config.js b/examples/framework-svelte/svelte.config.js new file mode 100644 index 000000000..cbaee33df --- /dev/null +++ b/examples/framework-svelte/svelte.config.js @@ -0,0 +1,5 @@ +import { vitePreprocess } from '@astrojs/svelte'; + +export default { + preprocess: vitePreprocess(), +}; diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index fd7d66d15..02e49b754 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -53,6 +53,13 @@ module.exports = { }, plugins: [], }\n`; +const SVELTE_CONFIG_STUB = `\ +import { vitePreprocess } from '@astrojs/svelte'; + +export default { + preprocess: vitePreprocess(), +}; +`; const OFFICIAL_ADAPTER_TO_IMPORT_MAP: Record = { netlify: '@astrojs/netlify/functions', @@ -114,37 +121,30 @@ export default async function add(names: string[], { cwd, flags, logging, teleme switch (installResult) { case UpdateResult.updated: { if (integrations.find((integration) => integration.id === 'tailwind')) { - const possibleConfigFiles = [ - './tailwind.config.cjs', - './tailwind.config.mjs', - './tailwind.config.js', - ].map((p) => fileURLToPath(new URL(p, root))); - let alreadyConfigured = false; - for (const possibleConfigPath of possibleConfigFiles) { - if (existsSync(possibleConfigPath)) { - alreadyConfigured = true; - break; - } - } - if (!alreadyConfigured) { - info( - logging, - null, - `\n ${magenta( - `Astro will generate a minimal ${bold('./tailwind.config.cjs')} file.` - )}\n` - ); - if (await askToContinue({ flags })) { - await fs.writeFile( - fileURLToPath(new URL('./tailwind.config.cjs', root)), - TAILWIND_CONFIG_STUB, - { encoding: 'utf-8' } - ); - debug('add', `Generated default ./tailwind.config.cjs file`); - } - } else { - debug('add', `Using existing Tailwind configuration`); - } + await setupIntegrationConfig({ + root, + logging, + flags, + integrationName: 'Tailwind', + possibleConfigFiles: [ + './tailwind.config.cjs', + './tailwind.config.mjs', + './tailwind.config.js', + ], + defaultConfigFile: './tailwind.config.cjs', + defaultConfigContent: TAILWIND_CONFIG_STUB, + }); + } + if (integrations.find((integration) => integration.id === 'svelte')) { + await setupIntegrationConfig({ + root, + logging, + flags, + integrationName: 'Svelte', + possibleConfigFiles: ['./svelte.config.js', './svelte.config.cjs', './svelte.config.mjs'], + defaultConfigFile: './svelte.config.js', + defaultConfigContent: SVELTE_CONFIG_STUB, + }); } break; } @@ -886,3 +886,43 @@ function getDiffContent(input: string, output: string): string | null { return diffed; } + +async function setupIntegrationConfig(opts: { + root: URL; + logging: LogOptions; + flags: yargs.Arguments; + integrationName: string; + possibleConfigFiles: string[]; + defaultConfigFile: string; + defaultConfigContent: string; +}) { + const possibleConfigFiles = opts.possibleConfigFiles.map((p) => + fileURLToPath(new URL(p, opts.root)) + ); + let alreadyConfigured = false; + for (const possibleConfigPath of possibleConfigFiles) { + if (existsSync(possibleConfigPath)) { + alreadyConfigured = true; + break; + } + } + if (!alreadyConfigured) { + info( + opts.logging, + null, + `\n ${magenta(`Astro will generate a minimal ${bold(opts.defaultConfigFile)} file.`)}\n` + ); + if (await askToContinue({ flags: opts.flags })) { + await fs.writeFile( + fileURLToPath(new URL(opts.defaultConfigFile, opts.root)), + opts.defaultConfigContent, + { + encoding: 'utf-8', + } + ); + debug('add', `Generated default ${opts.defaultConfigFile} file`); + } + } else { + debug('add', `Using existing ${opts.integrationName} configuration`); + } +} diff --git a/packages/integrations/svelte/README.md b/packages/integrations/svelte/README.md index ab6f5d856..854bded21 100644 --- a/packages/integrations/svelte/README.md +++ b/packages/integrations/svelte/README.md @@ -84,18 +84,44 @@ A few of the default options passed to the Svelte compiler are required to build const defaultOptions = { emitCss: true, compilerOptions: { dev: isDev, hydratable: true }, - preprocess: [ - preprocess({ - less: true, - sass: { renderSync: true }, - scss: { renderSync: true }, - stylus: true, - typescript: true, - }), - ], + preprocess: vitePreprocess() }; ``` The `emitCss`, `compilerOptions.dev`, and `compilerOptions.hydratable` cannot be overridden. -Providing your own `preprocess` options **will** override the defaults - make sure to enable the preprocessor flags needed for your project. +Providing your own `preprocess` options **will** override the defaults - make sure to enable the preprocessor flags needed for your project. For example, + +```js +// astro.config.js +import svelte from '@astrojs/svelte'; + +export default { + integrations: [svelte({ preprocess: [] })], +}; +``` + +and + +```js +// svelte.config.js +export default { + preprocess: [], +}; +``` + +Will override the default `preprocess` option. You can read the [`vitePreprocess` docs](https://github.com/sveltejs/vite-plugin-svelte/blob/HEAD/docs/preprocess.md) for more information of how it works. + +## Intellisense for TypeScript + +If you're using a preprocessor like TypeScript or SCSS in your Svelte files, you can create a `svelte.config.js` file with: + +```js +import { vitePreprocess } from '@astrojs/svelte'; + +export default { + preprocess: vitePreprocess(), +}; +``` + +So the Svelte IDE extension can correctly parse the Svelte files. This config file is added by default when you run `astro add svelte`. diff --git a/packages/integrations/svelte/src/index.ts b/packages/integrations/svelte/src/index.ts index 6bf5bcc4e..7daa9bec5 100644 --- a/packages/integrations/svelte/src/index.ts +++ b/packages/integrations/svelte/src/index.ts @@ -2,6 +2,7 @@ import type { Options } from '@sveltejs/vite-plugin-svelte'; import { svelte, vitePreprocess } from '@sveltejs/vite-plugin-svelte'; import type { AstroIntegration, AstroRenderer } from 'astro'; import type { UserConfig } from 'vite'; +import { fileURLToPath } from 'url'; function getRenderer(): AstroRenderer { return { @@ -11,16 +12,27 @@ function getRenderer(): AstroRenderer { }; } +async function svelteConfigHasPreprocess(root: URL) { + const svelteConfigFiles = ['./svelte.config.js', './svelte.config.cjs', './svelte.config.mjs']; + for (const file of svelteConfigFiles) { + const filePath = fileURLToPath(new URL(file, root)); + try { + const config = (await import(filePath)).default; + return !!config.preprocess; + } catch {} + } +} + type ViteConfigurationArgs = { isDev: boolean; options?: Options | OptionsCallback; + root: URL; }; -function getViteConfiguration({ options, isDev }: ViteConfigurationArgs): UserConfig { +async function getViteConfiguration({ options, isDev, root }: ViteConfigurationArgs): Promise { const defaultOptions: Partial = { emitCss: true, compilerOptions: { dev: isDev, hydratable: true }, - preprocess: [vitePreprocess()], }; // Disable hot mode during the build @@ -43,11 +55,13 @@ function getViteConfiguration({ options, isDev }: ViteConfigurationArgs): UserCo // Always use dev and hydratable from defaults ...defaultOptions.compilerOptions, }, - // Ignore default preprocessor if the user provided their own - preprocess: options.preprocess ?? defaultOptions.preprocess, }; } + if (!resolvedOptions.preprocess && !(await svelteConfigHasPreprocess(root))) { + resolvedOptions.preprocess = vitePreprocess(); + } + return { optimizeDeps: { include: ['@astrojs/svelte/client.js'], @@ -63,15 +77,18 @@ export default function (options?: Options | OptionsCallback): AstroIntegration name: '@astrojs/svelte', hooks: { // Anything that gets returned here is merged into Astro Config - 'astro:config:setup': ({ command, updateConfig, addRenderer }) => { + 'astro:config:setup': async ({ command, updateConfig, addRenderer, config }) => { addRenderer(getRenderer()); updateConfig({ - vite: getViteConfiguration({ + vite: await getViteConfiguration({ options, isDev: command === 'dev', + root: config.root, }), }); }, }, }; } + +export { vitePreprocess };