sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
79.83k stars 4.24k forks source link

Provide `print(ast: Ast): string` as compiler or svelte/printer #5972

Open mizchi opened 3 years ago

mizchi commented 3 years ago

Is your feature request related to a problem? Please describe.

Svelte compiler has parse(template: string): Ast but no print(code: Ast) => template printer.

TypeScript compiler (and some program languages) has this feature.

https://learning-notes.mistermicheels.com/javascript/typescript/compiler-api/#programmatically-creating-ast-nodes.

Describe the solution you'd like

Svelte compiler provides print(ast: Ast, optionalFormatter?: {js?: (code:string)=>string , ...} ): string.

import { parse, print } from "svelte/compiler";
const template = `<div {id}>foo</div>`
const parsed = parse(template);
const restored = print(parsed); // => same template

How important is this feature to you?

I'm creating code collaboration editor for non-programmer and programmer on svelte template.

Left side pane is auto generated forms by svelte template AST for non-programmer. By rewriting this, I try to reflect svelte template by ast changes. but svelte compiler does not have printer feature.

I think providing this feature helps svelte ecosystem.

Additional Context

https://github.com/sveltejs/prettier-plugin-svelte has code printer already. (fortunately for me, it works in browser!)

But that is format(template: string): string. I need formatter from ast.

So I hacked this for my workarround.

import prettier from "prettier/standalone";
import produce from "immer";
import { parse } from "svelte/compiler";
import type { Ast } from "svelte/types/compiler/interfaces";
// @ts-ignore
import * as sveltePlugin from "prettier-plugin-svelte";

function printTemplate(ast: Ast) {
  const overridePlugin = produce(sveltePlugin, (d: any) => {
    d.parsers.svelte.parse = () => {
      return { ...ast, __isRoot: true };
    };
  });
  return prettier.format(" dummy ", {
    parser: "svelte",
    plugins: [overridePlugin as any],
  });
}

const code = "<div {id}>text</div>";
const parsed = parse(code);
// override ast
// @ts-ignore
parsed.html.children[0].name = "span";
const out = printTemplate(parsed);
console.log(out); // <span {id}>text</span>

It depend on prettier but formatting js and css are optional feature for template block. Formatters for js and css already exist but svelte html template block formatter does not exists now.

I wrote simple printer (WIP) but I found prettier hack before I finished it.

https://gist.github.com/mizchi/8d51d239bccec98408cffde5ef5b855d

mizchi commented 3 years ago

I created fork for my usecase.

https://github.com/mizchi/prettier-plugin-svelte

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

benjaminpreiss commented 10 months ago

I would like to voice my interest in this topic as well... It would be great to have a printer for the svelte AST as it is much easier to do transforms with ASTs. The problem with hijacking prettier is that we do not get a sourcemap :(

I could imagine rewriting the code from the prettier-plugin-svelte to make it a custom generator that can be passed to astring is an undertaking that's manageable.

manuel3108 commented 10 months ago

This would really help svelte-add! See https://github.com/svelte-add/svelte-add/issues/193 in example. This happens because we currently cannot use the svelte AST for modifying and printing svelte files, and are required to use 3rd party tools that do not work in every case.

Original svelte This:

<script>
    import { fade } from "svelte/transition";
</script>

<main in:fade={{ duration: 200, delay: 200 }}>
    <slot />
</main>

will get converted into:

<script>
    import "../app.postcss"; // ignore this line, it was added by svelte-add
    import { fade } from "svelte/transition";
</script>

<main 200 in:fade="{{" duration: 200, delay: }}>
    <slot></slot>
</main>
benjamingwynn commented 9 months ago

I could imagine rewriting the code from the prettier-plugin-svelte to make it a custom generator that can be passed to astring is an undertaking that's manageable.

We were thinking of doing the same thing, I imagine this'd be much faster than prettier too

xeho91 commented 4 months ago

I've created an initial printer package - svelte-print-ast. It has support for Svelte v5.