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

feat: add a way to compile from an AST #8804

Closed

Conversation

benjamingwynn
Copy link

Allows the developer to compile directly from an AST, returned previously from svelte.parse.

There is currently no way to compile a modified AST to source code, this adds a function, compileAST, that provides that functionality.

Notes:

  • The second argument to new Component is currently set to null, this might break something, but for our purposes seems to be working.
  • @param {object} could be typed better.
  • compile could call compileAST underneath so we don't have to copy/paste code.

Hopefully someone has useful feedback!

Allows the developer to compile directly from an AST, returned previously from `svelte.parse`.

There is currently no way to compile a modified AST to source code, this adds a function, `compileAST`, that provides that functionality.

Notes:
- The second argument to `new Component` is currently set to null, this might break something, but for our purposes seems to be working.
- `@param {object}` could be typed better.
- `compile` could call `compileAST` underneath so we don't have to copy/paste code.

Hopefully someone has useful feedback!
Copy link
Member

@dummdidumm dummdidumm left a comment

Choose a reason for hiding this comment

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

As suggested it would be if compileAst would be called by compile if that doesn't result in more complex code.

Since there's no previous issue for this: What's the use case for this feature?

packages/svelte/src/compiler/compile/index.js Outdated Show resolved Hide resolved
packages/svelte/src/compiler/compile/index.js Outdated Show resolved Hide resolved
@benjamingwynn
Copy link
Author

Thank you for the quick response! Hopefully those patches address your review notes appropriately.

Since there's no previous issue for this: What's the use case for this feature?

We're implementing an internal GUI tool to modify Svelte files similar to #5972. This tool gets the AST with parse then mutates it, we're looking for a way to get that AST back into Svelte to compile .

Although the AST isn't a public API, because parse exists at all, we'd expect there to be a way to "unparse".

As suggested it would be if compileAst would be called by compile if that doesn't result in more complex code.

My only concern with this is that we'd be always passing null as the second argument to new Component, unless we refactored out into a common function. We haven't seen anything affected by using null here, but haven't done any testing outside of our tool.

@SystemParadox
Copy link

@Rich-Harris is there any chance this could be included in svelte 5?

@benjaminpreiss
Copy link

As suggested it would be if compileAst would be called by compile if that doesn't result in more complex code.

Since there's no previous issue for this: What's the use case for this feature?

The use case for this feature would be to be able to do AST transforms. It is much easier to do transforms on an AST than on strings using regexes. Here is a little example:

<script>
	import Component2 from './Component2.svelte'
	let name = 'world';
</script>

<h1>Hello {name}!</h1>
{#if name}
	{@const someVar = "literal"}
	<Component2 some-prop="literal" some-other-prop={someVar}>test</Component2>
{/if}

That I'd like to transform to:

<script>
	import Component2 from './Component2.svelte'
	import {importedVar} from './some/path.js'
	let name = 'world';
</script>

<h1>Hello {name}!</h1>
{#if name}
	{@const someVar = "literal"}
	<Component2 some-prop={importedVar} some-other-prop={someVar}>test</Component2>
{/if}

Now, that means I want to replace ONLY the attribute literal value "literal", but not the "literal" in the variable definition.

I could do that by finding the AST type="Attribute" node with a child of type="Text" and value="literal". Then I would replace the type="Text" node with the desired new AST.

If we need to do this with something like @Rich-Harris Magic-String library, it gets complicated very quickly.
One could argue, that the string positions found in the AST nodes would suffice to do accurate replacements.
But that cannot be done accurately, as the positions exclude the parenthesis surrounding the attribute literal (as you can see in the screenshot below)

Screenshot 2023-12-15 at 12 14 43

There are several use cases for wanting to transform the Svelte AST, e.g. we (@ivanhofer and me) were using AST transforms for inlangs i18n library.

@Rich-Harris what is your view on this?

@benjaminpreiss
Copy link

benjaminpreiss commented Dec 15, 2023

One way to achieve printing the AST would be to write code generators for all of these types:

https://github.com/sveltejs/svelte/blob/main/packages/svelte/src/compiler/types/template.d.ts

And then use these generators in https://github.com/davidbonnet/astring?tab=readme-ov-file#extending

Edit:

Maybe a relatively easy way to achieve svelte AST printing is to take the svelte prettier plugin and adapting the printer so that it can be passed to astring as a custom generator?

@benjamingwynn benjamingwynn closed this by deleting the head repository Jun 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler Changes relating to the compiler feature request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants