Skip to content

Commit

Permalink
remove generics from script tag before calling svelte preprocessor (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
sastan committed Mar 5, 2024
1 parent 37829dd commit 2971ccf
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 0 deletions.
26 changes: 26 additions & 0 deletions .changeset/perfect-bags-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
'houdini-svelte': patch
---

Remove generics from script tag before calling svelte preprocessor.

Otherwise it will fail to parse the source if the generics attribute contains angle brackets.

For example this code failed to parse:

```html
<script lang="ts" generics="T extends Record<string, unknown>">
</script>
```

Now the `parseSvelte` function will remove the generics attribute before calling the svelte preprocessor
preserving the token positions in the source code.

The output for the above example will be:

```html
<script lang="ts" >
</script>
```
51 changes: 51 additions & 0 deletions packages/houdini-svelte/src/plugin/extract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,57 @@ describe('parser tests', () => {
`)
})

test('happy path - typescript with generics', async () => {
const doc = `
<script lang="ts" generics="T extends Record<string, unknown>">
export let x: T
</script>
`
// parse the string
const result = await parseSvelte(doc)

expect(result?.script).toMatchInlineSnapshot('export let x: T;')
})

test('happy path - typescript with generics over several lines', async () => {
const doc = `
<script lang="ts" context="module">
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { FormPath, SuperForm } from 'sveltekit-superforms';
type T = Record<string, unknown>;
type U = unknown;
</script>
<script
lang="ts"
generics="T extends Record<string, unknown>, U extends FormPath<T>"
>
import * as FormPrimitive from 'formsnap';
import { cn } from '$lib/utils.js';
type $$Props = FormPrimitive.FieldsetProps<T, U>;
export let form: SuperForm<T>;
export let name: U;
let className: $$Props['class'] = undefined;
export { className as class };
</script>
`
// parse the string
const result = await parseSvelte(doc)

expect(result?.script).toMatchInlineSnapshot(`
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils.js";
type $$Props = FormPrimitive.FieldsetProps<T, U>;
export let form: SuperForm<T>;
export let name: U;
let className: $$Props["class"] = undefined;
export { className as class };
`)
})

test('nested script block', async () => {
const doc = `
<div>
Expand Down
9 changes: 9 additions & 0 deletions packages/houdini-svelte/src/plugin/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ export async function parseSvelte(str: string): Promise<Maybe<EmbeddedScript>> {
// parsing a file happens in two steps:
// - first we use svelte's parser to find the bounds of the script tag
// - then we run the contents through babel to parse it

// remove generics from script tag — otherwise svelte preprocessor will fail to parse
// if the generics attribute contains angle brackets
// Input: <script lang="ts" generics="T extends Record<string, unknown>">
// Output: <script lang="ts" >
str = str.replace(/(<script[^>]*)(\s+)(generics="[^"]+?")/, (_, $1, $2, $3) => {
return $1 + $2 + ' '.repeat($3.length)
})

const preprocessed = await svelte.preprocess(str, [
{
script({ content: input }) {
Expand Down

0 comments on commit 2971ccf

Please sign in to comment.