Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vite: Add partial SvelteKit support #19338

Merged
merged 20 commits into from
Oct 13, 2022
Merged

Vite: Add partial SvelteKit support #19338

merged 20 commits into from
Oct 13, 2022

Conversation

JReinhold
Copy link
Contributor

@JReinhold JReinhold commented Oct 3, 2022

Issue: #19280

This PR is a first stab at setting up a SvelteKit repro and getting it to work out of the box.

TODO

  • Test that base vite-svelte framework still works
  • Test that TS SvelteKit framework works.
  • Test that it works when published to verdaccio

What I did

  • Add repros via create-svelte-with-args
  • Removed the sveltePreprocess option from the template as that was causing ESM vs CJS issues and we decided it was an anti-pattern anyway since they should automatically be picked up by the Vite config
  • Remove vite-plugin-svelte-kit whenever it is recognised in a Vite config because it conflicts with Storybook's VIte server handling. This has some drawbacks that certain SvelteKit features aren't available in Storybook but for now we consider that a general issue that needs to be solved in SvelteKit.

How to test

  1. git checkout origin/jeppe/sb-544-sveltekit
  2. Use Node 16 instead of Node 14 (SvelteKit can't be built using v14) - eg. nvm use 16
  3. Generate repro with cd code && yarn generate-repros-next --template svelte-kit/skeleton-js
  4. cd ../sandbox/svelte-kit-skeleton-js && yarn install
  5. yarn storybook
  6. See that all basic stories work.
  7. Repeat for SvelteKit with TypeScript: svelte-kit/skeleton-ts

Out of scope

I want to include a few stories based on the Svelte CSF format in the sandbox so CI also ensures that they work. That's another PR.

@@ -24,8 +25,23 @@ export const viteFinal: StorybookConfig['viteFinal'] = async (config, { presets

plugins.push(svelteDocgen(config));

removeSvelteKitPlugin(plugins);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this should have been a plugin, but it doesn't seem to have an affect. The plugin correctly removes vite-plugin-svelte-kit from the config but it doesn't change any behavior

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes unfortunately there's no way to remove plugins from within a plugin, as far as I can tell.

Comment on lines -9 to -34
let extraMain;
// svelte.config.js ?
if (fse.existsSync('./svelte.config.js')) {
logger.info("Configuring preprocessor from 'svelte.config.js'");

extraMain = {
svelteOptions: { preprocess: '%%require("../svelte.config.js").preprocess%%' },
};
} else if (fse.existsSync('./svelte.config.cjs')) {
logger.info("Configuring preprocessor from 'svelte.config.cjs'");

extraMain = {
svelteOptions: { preprocess: '%%require("../svelte.config.cjs").preprocess%%' },
};
} else {
// svelte-preprocess dependencies ?
const packageJson = packageManager.retrievePackageJson();
if (packageJson.devDependencies && packageJson.devDependencies['svelte-preprocess']) {
logger.info("Configuring preprocessor with 'svelte-preprocess'");

extraMain = {
svelteOptions: { preprocess: '%%require("svelte-preprocess")()%%' },
};
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need any MIGRATION.md notes? @JReinhold

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, users needs to remove their svelteOptions property in main.cjs and rely on their Vite config to pick them up instead. Svelte+Webpack users probably don't want to remove anything.

I'll write it up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about pulling this change out to its own PR? I'd love to get it into the next release, since svelte apps are currently broken after bootstrapping with Storybook.

@JReinhold JReinhold self-assigned this Oct 3, 2022
Copy link
Member

@IanVS IanVS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this! It'll be great to have sveltekit repros, and getting it working out-of-the-box is an important step to better svelte support!

code/frameworks/svelte-vite/src/preset.ts Outdated Show resolved Hide resolved
code/lib/builder-vite/src/vite-config.ts Outdated Show resolved Hide resolved
@@ -24,8 +25,23 @@ export const viteFinal: StorybookConfig['viteFinal'] = async (config, { presets

plugins.push(svelteDocgen(config));

removeSvelteKitPlugin(plugins);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes unfortunately there's no way to remove plugins from within a plugin, as far as I can tell.

return {
...config,
plugins,
};
};

const removeSvelteKitPlugin = (plugins: PluginOption[]) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd still love to find a way to avoid having to do this, since we'll start getting complaints about sveltekit aliases not working (even though there seems to be some debate over whether they should be used at all inside components). But, this seems like a reasonable workaround for now, until we can figure it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, let's continue investigating the core issue and do a follow up to this.

code/lib/builder-vite/src/vite-server.ts Show resolved Hide resolved
@@ -24,7 +24,7 @@ export const viteFinal: StorybookConfig['viteFinal'] = async (config, { presets
const removeSvelteKitPlugin = (plugins: PluginOption[]) => {
plugins.forEach((plugin, index) => {
if (plugin && 'name' in plugin && plugin.name === 'vite-plugin-svelte-kit') {
// eslint-disable-next-line no-param-reassign -- we explicitly want to mutate the array as stated in Vite docs
// eslint-disable-next-line no-param-reassign -- we explicitly want to mutate the array as stated here: https://vitejs.dev/guide/api-plugin.html#config
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is not in a config hook, I think we don't necessarily need to follow that rule, but this seems like an okay approach anyway. I normally prefer map and filter, but since this is recursive, that might be a little trickier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Vite docs are a bit unclear on how it's handled when a partial config is returned - they say it is deeply merged into the existing config, which led me to believe it's not usable for removing any entries as we want to achieve here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but those docs are for a plugin. This code is not inside a plugin, it's directly modifying the config that we'll end up passing to vite.createServer() inside vite-server.ts. So there's no merging here except for the kind that we do ourselves.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah, I hadn't even noticed that.
I have a version that is more declarative, but I'm unsure if it's actually more readable. But I don't have any strong opinions so if you think it's better I'll happily push it.

export const viteFinal: StorybookConfig['viteFinal'] = async (config, { presets }) => {
  const { plugins = [] } = config;

  return {
    ...config,
    plugins: [
      // remove SvelteKit plugin
      ...plugins.map(withoutSvelteKitPlugin),
      // Add svelte plugin if not present
      !hasPlugin(plugins, 'vite-plugin-svelte') &&
        (await import('@sveltejs/vite-plugin-svelte')).svelte(),
      // Add docgen plugin
      svelteDocgen(config),
    ].filter(Boolean),
  };
};

const withoutSvelteKitPlugin = (plugin: PluginOption): PluginOption => {
  if (Array.isArray(plugin)) {
    // recursive - Vite plugins can be nested
    return plugin.map(withoutSvelteKitPlugin).filter(Boolean);
  }
  if (plugin && 'name' in plugin && plugin.name === 'vite-plugin-svelte-kit') {
    return undefined;
  }
  return plugin;
};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the way you've got it right now is fine I think, and hopefully this is temporary anyway, until we can figure out what's causing the app to load instead of storybook in 7.0.

code/lib/builder-vite/src/vite-config.ts Outdated Show resolved Hide resolved
name: 'storybook:allow-template-stories',
config(config) {
if (config?.server?.fs?.allow) {
config.server.fs.allow.push('template-stories');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could also just set server.fs.allow directly in the config, without needing to use a plugin here. That might even be a good test that such user-configured settings are properly honored, and that .storybook is being added correctly by the other plugin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I tried that but you actually can't because that won't pick up on other plugin's configs.
So if I set it directly on the config config?.server?.fs?.allow will be falsy, while doing it through this plugin logic config?.server?.fs?.allow will actually include vite-plugin-svelte-kit's allow list.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant without the check, you could just set the allow value directly, and then the rest of everything should still work. vite-plugin-svelte-kit should extend the user's config and the other plugin you created should as well, right? If we can't set custom allow paths in viteFinal, we're in trouble.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I don't think that I understand what you're saying, so bear with me.

My (verbose explanation of my) logic is:

  1. If allow is empty it will default to allow the whole workspace root, so if we add to an empty allow we're effectively denying the rest, which might break stuff for the user.
  2. So we have to check if allow is non-empty, before adding template-stories or other entries anywhere.
  3. checking the content in allow directly when building the config is inadequate because some plugins might add entries to allow, and they won't be checkable at "build config"-time.
  4. but we can check the content in allow by adding our own plugin, and from it read the config, and possibly add the entires to allow.

Which part of the above sounds wrong to you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the plugin is probably the safer approach because it can check whether the user has set server.fs.allow themselves or not. If we always pass server.fs.allow: ['.storybook'] then we enable that feature which will may break the user's project if they have not added the directories their project uses to the config.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this sandbox, we are the user. I'm not talking about setting it in all cases, just in the example sandbox, which is used for testing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah, that sounds like the better approach then

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, gotcha.
If we always set it in sandboxes then we might as well set it to '.' because we'd need to allow more than just 'template-stories'. I'm down with that.
Alternatively 'template-stories' + 'src' or similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've allowed 'src', 'template-stories', 'node_modules' which works, but only if sources are always in src, but at least they were in the react-vite/default-ts sandbox so I think it's safe to assume.
You're right that this also serves as a good way to test that we're adding .storybook correctly.

@JReinhold JReinhold marked this pull request as ready for review October 5, 2022 09:53
@JReinhold JReinhold added build Internal-facing build tooling & test updates svelte labels Oct 5, 2022
Copy link
Member

@IanVS IanVS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, but I see CI failures like:

Unable to find the Storybook folder in "/tmp/storybook/sandbox/svelte-kit-skeleton-js/.storybook". Are you sure it exists? Or maybe this folder uses a custom Storybook config directory?

Is that because the new repros have not yet been published?

@@ -24,7 +24,7 @@ export const viteFinal: StorybookConfig['viteFinal'] = async (config, { presets
const removeSvelteKitPlugin = (plugins: PluginOption[]) => {
plugins.forEach((plugin, index) => {
if (plugin && 'name' in plugin && plugin.name === 'vite-plugin-svelte-kit') {
// eslint-disable-next-line no-param-reassign -- we explicitly want to mutate the array as stated in Vite docs
// eslint-disable-next-line no-param-reassign -- we explicitly want to mutate the array as stated here: https://vitejs.dev/guide/api-plugin.html#config
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the way you've got it right now is fine I think, and hopefully this is temporary anyway, until we can figure out what's causing the app to load instead of storybook in 7.0.

@JReinhold
Copy link
Contributor Author

Is that because the new repros have not yet been published?

Yes I think that's probably it, they should work once we merge, or alternatively I can manually publish them - but I'll hold off until Node 16+ is ready in the repo.

@@ -58,7 +58,7 @@ export const webpack = async (webpackConfig: Configuration, options: PresetOptio
return webpackConfig;
}

if(angularOptions.enableNgcc !== false) {
if (angularOptions.enableNgcc !== false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unrelated to this PR, but I'm guessing it'll fall back out when next is updated and merged in.

@benmccann
Copy link
Contributor

@JReinhold just FYI, it looks like there's a merge conflict

@shilman shilman changed the title SvelteKit support for 7.0 Add SvelteKit sandbox testing Oct 6, 2022
@IanVS
Copy link
Member

IanVS commented Oct 12, 2022

Now that #19406 is merged, I guess this can be updated to leave the sveltekit plugin in the config, right?

@JReinhold
Copy link
Contributor Author

JReinhold commented Oct 13, 2022

I've disabled the SvelteKit repro for now because that was the only thing blocking this merge (waiting until we upgrade to node 16).

So this should be ready to merge now.

@IanVS I'm sorry for the timing, I should have thought of this sooner, before the Vite announcement.

@IanVS
Copy link
Member

IanVS commented Oct 13, 2022

This looks good, but as far as I know, build-storybook will still be broken when using svelteKit (see #19280 (comment)).

Maybe we can merge this in, but not advertise sveltekit too hard until we can take care of that part.

@JReinhold
Copy link
Contributor Author

Right, that looks gnarly.
It really seems like were struggling to incorporate a metaframework that (rightfully) wants to be it's own server and builder and isn't really built for others to hook into that (because why would it).

This will probably not be the last time facing these issues (hello SolidStart, Astro, Qwik City and Friends), so I wonder if we could think about this in a broader way..

But let's get this in and fix building the Storybook later.

@JReinhold JReinhold merged commit 5e87fd3 into next Oct 13, 2022
@JReinhold JReinhold deleted the jeppe/sb-544-sveltekit branch October 13, 2022 19:35
@IanVS
Copy link
Member

IanVS commented Oct 13, 2022

Awesome, thanks for slogging through and getting this in, @JReinhold!

@IanVS IanVS changed the title Add SvelteKit sandbox testing Vite: Add partial SvelteKit support Oct 13, 2022
@shilman shilman added maintenance User-facing maintenance tasks and removed build Internal-facing build tooling & test updates labels Oct 15, 2022
@shilman
Copy link
Member

shilman commented Oct 15, 2022

@JReinhold FWIW I would call this a maintenance PR because it touches user-facing code in the Vite builder, and in a significant way.

@JReinhold JReinhold mentioned this pull request Nov 30, 2022
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
builder-vite maintenance User-facing maintenance tasks svelte
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants