Skip to content

Svelte 5 rendering in Nx #403

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

Closed
DannyvanHolten opened this issue Oct 9, 2024 · 12 comments · Fixed by #404
Closed

Svelte 5 rendering in Nx #403

DannyvanHolten opened this issue Oct 9, 2024 · 12 comments · Fixed by #404
Labels

Comments

@DannyvanHolten
Copy link

DannyvanHolten commented Oct 9, 2024

Hey Team,

I have been spending some time already in trying to get svelte-testing-library to work in my monorepo.
However I am failing to do so. I will try to outline the details as best as I can below.

The error message.

When I run my test nx test ui-component (which is just a nx command for running the vitest command). I get the following error.

 FAIL  src/lib/Divider.spec.ts > should render correctly
Svelte error: rune_outside_svelte
The `$state` rune is only available inside `.svelte` and `.svelte.js/ts` files

When I remove this $state part from the render function inside modern.svelte.js (line 26( I get the following error:

 FAIL  src/lib/Divider.spec.ts > should render correctly
Svelte error: lifecycle_function_unavailable
`mount(...)` is not available on the server

To me this sounds like something is wrong with getting svelte to load correctly. I am in the myst about what I should do, and how to fix it. I have tried looking thoroughly at my vite configs, tsconfigs, etc. But I cannot copy the tutorials one on one as we're using Nx.

Below I will share some code / config

The test (nothing wrong with this, it works in other setups):

import { render, screen } from '@testing-library/svelte';
import { test } from 'vitest';

import '@testing-library/jest-dom/vitest';

import { Divider } from '../';

test('should render correctly', () => {
    render(Divider);
    expect(screen.queryByRole('button')).not.toBeInTheDocument();
});

The component itself (cannot be wrong I guess, works in Storybook)

<hr class="my-0 border-b border-t border-b-white/10 border-t-black/30" />

My vite.config.ts

/// <reference types='vitest' />
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { svelteTesting } from '@testing-library/svelte/vite';
import * as path from 'path';
import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';
import { viteStaticCopy } from 'vite-plugin-static-copy';

export default defineConfig({
  root: __dirname,
  cacheDir: '../../node_modules/.vite/ui/divider',

  plugins: [
    svelte(),
    svelteTesting(),
    nxViteTsPaths({ buildLibsFromSource: false }),
    dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') }),
    viteStaticCopy({
      targets: [
        {
          src: path.join(__dirname, '**/*.md'),
          dest: '.',
        },
        {
          src: path.join(__dirname, '**/index.ts'),
          dest: '.',
        },
        {
          src: path.join(__dirname, '**/*.svelte'),
          dest: './lib',
        },
      ],
    }),
  ],

  // Uncomment this if you are using workers.
  // worker: {
  //  plugins: [ nxViteTsPaths() ],
  // },

  // Configuration for building your library.
  // See: https://vitejs.dev/guide/build.html#library-mode
  build: {
    outDir: '../../dist/ui/divider',
    emptyOutDir: true,
    reportCompressedSize: true,
    lib: {
      // Could also be a dictionary or array of multiple entry points.
      entry: 'src/index.ts',
      name: 'ui-divider',
      fileName: 'index',
      // Change this to the formats you want to support.
      // Don't forget to update your package.json as well.
      formats: ['es'],
    },
    rollupOptions: {
      // External packages that should not be bundled into your library.
      external: ['svelte'],
    },
  },

  test: {
    watch: false,
    globals: true,
    environment: 'jsdom',
    include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],

    reporters: ['default'],
    coverage: {
      reportsDirectory: '../../coverage/ui/divider',
      provider: 'v8',
    },
  },
});

My tsconfig:

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
 "module": "esnext",
    "moduleResolution": "bundler",
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "target": "es2017",
    "verbatimModuleSyntax": true,
    "sourceMap": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "files": [],
  "include": [],
  "references": [
    {
      "path": "./tsconfig.lib.json"
    },
    {
      "path": "./tsconfig.spec.json"
    }
  ]
}

My packages:

   "svelte": "5.0.0-next.246",
   "@sveltejs/vite-plugin-svelte": "^4.0.0-next.7",
    "vitest": "^1.3.1",
    "@testing-library/svelte": "^5.2.3",
    "@testing-library/jest-dom": "^6.5.0"

Do you have any idea why this error is popping up? And how I can fix it.

@mcous
Copy link
Collaborator

mcous commented Oct 9, 2024

@DannyvanHolten I see that you're not using the Svelte testing plugin, as mentioned in the README. This plugin is required to ensure the browser version of Svelte is loaded into Vitest rather than its server-side-rendering bundle. The clue that you're loading the wrong Svelte bundle is in this log:

 FAIL  src/lib/Divider.spec.ts > should render correctly
Svelte error: lifecycle_function_unavailable
`mount(...)` is not available on the server

Try adding in the Svelte testing plugin and let me know how that works:

// ...
import { svelteTesting } from '@testing-library/svelte/vite'
// ...
export default defineConfig({
  // ...
  plugins: [
    // ...
    svelteTesting(),
  ],
  // ...
});

@DannyvanHolten
Copy link
Author

DannyvanHolten commented Oct 10, 2024

Ah sorry, I had implemented this, and thought I was very careful :D. But after some tinkering I removed it in my example. In the actual code it was still present, and unfortunately is not the fix :( I edited my example to be correct

@mcous
Copy link
Collaborator

mcous commented Oct 10, 2024

Shoot, well that was the obvious idea. Are you able to provide a minimal reproduction repository (or stackblitz or whatever) demonstrating the issue? With that I could try to help diagnose

@DannyvanHolten
Copy link
Author

I will try so tomorrow :).

We will first try to get a complete install of everything to see if we can find the problem.

@playerfine
Copy link

Hey @mcous,

Hereby a minimal reproduction repository:

https://github.com/playerfine/svelte5unittestingexample/

If there are any questions lemme know 😄

@mcous
Copy link
Collaborator

mcous commented Oct 10, 2024

@playerfine there's a bit too much going on in that repo for me to figure out the issue. Could you pare it back to be a lot more minimal? e.g. ditch all the build stuff, the TypeScript stuff, so that all that remains is the problematic workspace and test setup itself

FWIW, I pulled libs/counter out into it's own directory and everything worked, so I have a hunch this is some sort of node_modules layout issue. Something about this setup is causing the Svelte plugin to miss that it needs to transform Svelte code inside @testing-library/svelte

@playerfine
Copy link

Sorry for the delay, @mcous. I removed as much of the Nx configuration as possible and completely removed TypeScript.

if you encounter anymore issues a simple nx reset normally fixes the problem.

If not feel free to reach out! Thanks for your help so far 😄

@mcous
Copy link
Collaborator

mcous commented Oct 11, 2024

@playerfine @DannyvanHolten looks like a potential bug in @sveltejs/vite-plugin-svelte that your monorepo setup is triggering. Nothing to do with nx!

See playerfine/svelte5unittestingexample#1 for more details:

I was also able to identify a workaround for the issue: add "@testing-library/svelte": "^5.2.3" to libs/counter/package.json:

  {
    "name": "@svelteunit/counter",
    "version": "0.0.1",
    "private": true,
-   "type": "module"
+   "type": "module",
+   "devDependencies": {
+     "@testing-library/svelte": "^5.2.3"
+   }
  }

This is the only change that is needed, you don't even have to re-run npm install. This, to me, suggests a potential bug in @sveltejs/vite-plugin-svelte's heuristics around when to apply the Svelte transform to .svelte.js files.

I'll dig a little more and file an issue with the Svelte team if I find something concrete. In the mean time, y'all may want to consider moving to a package manager with more robust monorepo support, like pnpm, which will allow you to define dev dependencies in the projects they're actually used in, but still maintain minimal installs and consistent versions

@mcous
Copy link
Collaborator

mcous commented Oct 11, 2024

Ok, traced it down specifically to the heuristics that the Svelte Vite plugin uses to add dependencies to ssr.noExternal. Better workaround for y'all: instead of adding @testing-library/svelte to package.json, you can add it to your vite.config.js:

export default defineConfig({
  // ...
  test: {
    server: {
      deps: {
        inline: ['@testing-library/svelte'],
      }
    }
  }
})

Since we have our own Vite plugin, I'm adding this same workaround in #404 just in case it can't be fixed in the Svelte-plugin proper

@playerfine
Copy link

This also seems to work fine thanks again!

Copy link

🎉 This issue has been resolved in version 5.2.4 🎉

The release is available on:

Your semantic-release bot 📦🚀

@mcous
Copy link
Collaborator

mcous commented Oct 18, 2024

@DannyvanHolten @playerfine thanks for y'all's feedback on this issue! I was able to track down and learn about a few relevant Vite and Svelte behaviors and improve this library a little bit as a result! Recap of issues you were experiencing:

  1. Internal runes inside @testing-library/svelte were not getting processed in your monorepo setup
  2. Setup file from @testing-library/svelte Vite plugin unable to load

Resolutions:

  1. Updated our Vite plugin to ensure that @testing-library/svelte is always compiled, allowing the runes to be processed - released in v5.2.4
  2. Monorepo misconfiguration on your end: if using npm, you need to add a workspaces field to your root package.json so that Vite understands it's allowed to load files from the root node_modules

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
3 participants