sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.19k stars 4.09k forks source link

Parametric compile options #12415

Open Rich-Harris opened 1 month ago

Rich-Harris commented 1 month ago

Describe the problem

It's often useful to have different compiler options for different files. For example, you might want to enforce that all your first-party components are in runes mode, while continuing to use non-runes components in node_modules, or you might want to programmatically define custom element names rather than manually specifying them with <svelte:options> in each component.

Today, this is possible using dynamicCompileOptions in vite-plugin-svelte, but it has drawbacks:

Describe the proposed solution

For the options where it makes sense, allow functions to be specified instead of values:

// svelte.config.js
export default {
  compilerOptions: {
    css: ({ filename }) => filename.includes('/og-cards/') ? 'injected' : 'external',
    runes: ({ filename }) => filename.includes('/node_modules/') ? undefined : true
  }
};

Importance

nice to have

dominikg commented 1 month ago

to avoid multiple options turning into possibly function, what about having compile(r)Options itself be a function. The tricky bit is filename here as thats currently part of options.

https://github.com/sveltejs/svelte/blob/36a6a6b789646c0561097d40c2281c5d96960a19/packages/svelte/src/compiler/index.js#L22

if it was compile(soure|{source,filename},options|({source,filename})=>options) it could work, but would require updates to all tooling that calls compile to pass filename along outside of options

dominikg commented 1 month ago

or introduce a new dynamicCompile wrapper that calls compile after resolving the options to avoid the overloaded signature of compile

dummdidumm commented 1 month ago

We discussed both these things and came to the conclusion that function per option is the best solution:

Rich-Harris commented 1 month ago

Another possibility is that we don't change the svelte.compile API at all, and this is just a svelte.config.js thing, since that's what gives equal footing to Vite, VSCode, svelte-check and all the rest of it. And that way — since you don't specify filename in your svelte.config.js — we can have a single function for everything, which would definitely make things more compact:

// svelte.config.js
export default {
  compilerOptions: ({ filename }) => ({
    css: filename.includes('/og-cards/') ? 'injected' : 'external',
    runes: filename.includes('/node_modules/') ? undefined : true
  })
};
dominikg commented 1 month ago

but then each tool would have to check if compilerOptions is a function and call it first before passing the output to svelte.compile. It would be a bit more convenient and reduce duplication if there was a way to just pass compilerOptions to svelte.compile and have that figure it out. Last idea to that i have is adding a dynamic: ({filename,content,options})=>CompilerOptions to CompilerOptions itself, that svelte.compile invokes if present and merges the output back on options

edit: passing source/content to the dynamic function can help too if you want to inspect svelte:options to selectively enable custom element output for example.