diff --git a/package.json b/package.json
index d2872bf..64888d8 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,8 @@
"@types/node": "^20.2.5",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
- "astring": "^1.8.5",
+ "@vardario/astring-ts-generator": "^1.1.0",
+ "astring": "^1.9.0",
"eslint": "^8.54.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-unused-imports": "^3.0.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1648fe2..9c8d047 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -30,9 +30,12 @@ importers:
'@typescript-eslint/parser':
specifier: ^6.11.0
version: 6.12.0(eslint@8.54.0)(typescript@5.0.4)
+ '@vardario/astring-ts-generator':
+ specifier: ^1.1.0
+ version: 1.1.0(astring@1.9.0)
astring:
- specifier: ^1.8.5
- version: 1.8.5
+ specifier: ^1.9.0
+ version: 1.9.0
eslint:
specifier: ^8.54.0
version: 8.54.0
@@ -574,6 +577,11 @@ packages:
'@ungap/structured-clone@1.2.0':
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+ '@vardario/astring-ts-generator@1.1.0':
+ resolution: {integrity: sha512-gLIXskiw5/79UjvXL8BC7ORYJ/5xYKwys2e248bhrm06QzEXnNSTuEaDW3ZxfYN+04nAuAbONdbHXNeD9GCGbg==}
+ peerDependencies:
+ astring: ^1.9.0
+
'@vitest/expect@2.1.3':
resolution: {integrity: sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==}
@@ -695,8 +703,8 @@ packages:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
- astring@1.8.5:
- resolution: {integrity: sha512-TuBbdn7jWVzf8dmFGTaRpW8qgANtWLi1qJLnkfGO5uVf6jf9f/F4B1H35tnOI+qVYZo3p3i8WZlbZOuPAE0wEA==}
+ astring@1.9.0:
+ resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
hasBin: true
author-regex@1.0.0:
@@ -2668,6 +2676,10 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
+ '@vardario/astring-ts-generator@1.1.0(astring@1.9.0)':
+ dependencies:
+ astring: 1.9.0
+
'@vitest/expect@2.1.3':
dependencies:
'@vitest/spy': 2.1.3
@@ -2784,7 +2796,7 @@ snapshots:
assertion-error@2.0.1: {}
- astring@1.8.5: {}
+ astring@1.9.0: {}
author-regex@1.0.0: {}
diff --git a/src/print-html.ts b/src/print-html.ts
index 129e02b..3510b20 100644
--- a/src/print-html.ts
+++ b/src/print-html.ts
@@ -3,49 +3,9 @@ import _ from 'lodash';
import { walk } from 'estree-walker';
import { AST } from 'svelte/compiler';
import { DefaultPrinterIdentOptions, PrinterIdentOptions } from './index.js';
-import { Node } from 'estree';
-export type Write = (text: string) => void;
+import { ElementLike, printAttributes, SvelteNode } from './utils.js';
-type ElementLike =
- | AST.Component
- | AST.TitleElement
- | AST.SlotElement
- | AST.RegularElement
- | AST.SvelteBody
- | AST.SvelteComponent
- | AST.SvelteDocument
- | AST.SvelteElement
- | AST.SvelteFragment
- | AST.SvelteHead
- | AST.SvelteOptionsRaw
- | AST.SvelteSelf
- | AST.SvelteWindow;
-
-type Directive =
- | AST.AnimateDirective
- | AST.BindDirective
- | AST.ClassDirective
- | AST.LetDirective
- | AST.OnDirective
- | AST.StyleDirective
- | AST.TransitionDirective
- | AST.UseDirective;
-
-type Tag = AST.ExpressionTag | AST.HtmlTag | AST.ConstTag | AST.DebugTag | AST.RenderTag;
-type Block = AST.EachBlock | AST.IfBlock | AST.AwaitBlock | AST.KeyBlock | AST.SnippetBlock;
-
-type TemplateNode =
- | AST.Root
- | AST.Text
- | Tag
- | ElementLike
- | AST.Attribute
- | AST.SpreadAttribute
- | Directive
- | AST.Comment
- | Block;
-
-type SvelteNode = Node | TemplateNode | AST.Fragment | any;
+export type Write = (text: string) => void;
const HTML_VOID_ELEMENTS = new Set([
'area',
@@ -97,93 +57,6 @@ class ExpressionTagPrinter extends BaseHtmlNodePrinter {
}
class ElementPrinter extends BaseHtmlNodePrinter {
- private printAttributes(attribute: AST.Attribute | AST.SpreadAttribute | Directive, context: PrinterContext) {
- const { write } = context;
-
- if (attribute.type === 'Attribute') {
- if (attribute.value === true) {
- return;
- }
-
- if (_.isArray(attribute.value)) {
- const [value] = attribute.value;
-
- if (value.type === 'Text') {
- write(` ${attribute.name}="${value.data}"`);
- }
-
- if (value.type === 'ExpressionTag') {
- write(` ${attribute.name}={${generate(value.expression, context.indent)}}`);
- }
- } else {
- write(` ${attribute.name}={${generate(attribute.value.expression, context.indent)}}`);
- }
- } else if (attribute.type === 'SpreadAttribute') {
- write(` {...${generate(attribute.expression, context.indent)}}`);
- } else if (attribute.type === 'AnimateDirective') {
- if (attribute.expression) {
- write(` animate:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
- } else {
- write(` animate:${attribute.name}`);
- }
- } else if (attribute.type === 'BindDirective') {
- write(` bind:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
- } else if (attribute.type === 'ClassDirective') {
- write(` class:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
- } else if (attribute.type === 'LetDirective') {
- if (attribute.expression) {
- write(` let:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
- } else {
- write(` let:${attribute.name}`);
- }
- } else if (attribute.type === 'OnDirective') {
- if (attribute.expression) {
- write(` on:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
- } else {
- write(` on:${attribute.name}`);
- }
- } else if (attribute.type === 'StyleDirective') {
- if (attribute.value === true) {
- write(` style:${attribute.name}`);
- } else {
- if (_.isArray(attribute.value)) {
- const [value] = attribute.value;
- if (value.type === 'Text') {
- write(` style:${attribute.name}="${value.data}"`);
- } else {
- write(` style:${attribute.name}={${generate(value, context.indent)}}`);
- }
- } else {
- write(` style:${attribute.name}={${generate(attribute.value.expression, context.indent)}}`);
- }
- }
- } else if (attribute.type === 'TransitionDirective') {
- const transition = () => {
- if (attribute.intro && !attribute.outro) {
- return 'in';
- }
-
- if (attribute.outro && !attribute.intro) {
- return 'out';
- }
-
- return 'transition';
- };
-
- if (attribute.expression) {
- write(` ${transition()}:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
- } else {
- write(` ${transition()}:${attribute.name}`);
- }
- } else if (attribute.type === 'UseDirective') {
- if (attribute.expression) {
- write(` use:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
- } else {
- write(` use:${attribute.name}`);
- }
- }
- }
-
enter(node: ElementLike, _: SvelteNode, context: PrinterContext) {
const { write } = context;
@@ -207,7 +80,7 @@ class ElementPrinter extends BaseHtmlNodePrinter {
});
}
- node.attributes.forEach(attribute => this.printAttributes(attribute, context));
+ node.attributes.forEach(attribute => printAttributes(attribute, context));
if (
(node.type === 'Component' ||
diff --git a/src/print-script.ts b/src/print-script.ts
index c3bbb28..aa68d3f 100644
--- a/src/print-script.ts
+++ b/src/print-script.ts
@@ -1,23 +1,26 @@
import { generate } from 'astring';
import type { AST } from 'svelte/compiler';
import { DefaultPrinterIdentOptions, PrinterIdentOptions } from './index.js';
+import generator from '@vardario/astring-ts-generator';
+import { attributeToString } from './utils.js';
export default function printScript(root: AST.Root, indent: PrinterIdentOptions = DefaultPrinterIdentOptions): string {
let result = '';
if (root.instance) {
result += '';
}
if (root.module) {
- result += '';
}
diff --git a/src/tests/print-script.test.ts b/src/tests/print-script.test.ts
index 6f18ce1..870a22f 100644
--- a/src/tests/print-script.test.ts
+++ b/src/tests/print-script.test.ts
@@ -3,12 +3,12 @@ import { describe, expect, test } from 'vitest';
import printScript from '../print-script';
function testScriptPrinter(code: string, expectedResult?: string) {
- const root = parse(code);
+ const root = parse(code, { modern: true });
const result = printScript(root, {
indent: '',
lineEnd: ''
});
- expect(result, expectedResult ?? code);
+ expect(result).toBe(expectedResult ?? code);
}
describe('');
});
+
+ test('typescript instance', () => {
+ testScriptPrinter('');
+ });
});
diff --git a/src/utils.ts b/src/utils.ts
index dd75fa8..5c45d5e 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,3 +1,155 @@
+import { AST } from 'svelte/compiler';
+import { PrinterContext } from './print-html';
+import { generate } from 'astring';
+import _ from 'lodash';
+
+export type ElementLike =
+ | AST.Component
+ | AST.TitleElement
+ | AST.SlotElement
+ | AST.RegularElement
+ | AST.SvelteBody
+ | AST.SvelteComponent
+ | AST.SvelteDocument
+ | AST.SvelteElement
+ | AST.SvelteFragment
+ | AST.SvelteHead
+ | AST.SvelteOptionsRaw
+ | AST.SvelteSelf
+ | AST.SvelteWindow;
+
+export type Directive =
+ | AST.AnimateDirective
+ | AST.BindDirective
+ | AST.ClassDirective
+ | AST.LetDirective
+ | AST.OnDirective
+ | AST.StyleDirective
+ | AST.TransitionDirective
+ | AST.UseDirective;
+
+export type Tag = AST.ExpressionTag | AST.HtmlTag | AST.ConstTag | AST.DebugTag | AST.RenderTag;
+export type Block = AST.EachBlock | AST.IfBlock | AST.AwaitBlock | AST.KeyBlock | AST.SnippetBlock;
+
+export type TemplateNode =
+ | AST.Root
+ | AST.Text
+ | Tag
+ | ElementLike
+ | AST.Attribute
+ | AST.SpreadAttribute
+ | Directive
+ | AST.Comment
+ | Block;
+
+export type SvelteNode = Node | TemplateNode | AST.Fragment | any;
+
export function identLiteral(level: number, ident: string) {
return new Array(level).join(ident);
}
+
+export function printAttributes(attribute: AST.Attribute | AST.SpreadAttribute | Directive, context: PrinterContext) {
+ const { write } = context;
+
+ if (attribute.type === 'Attribute') {
+ if (attribute.value === true) {
+ return;
+ }
+
+ if (_.isArray(attribute.value)) {
+ const [value] = attribute.value;
+
+ if (value.type === 'Text') {
+ write(` ${attribute.name}="${value.data}"`);
+ }
+
+ if (value.type === 'ExpressionTag') {
+ write(` ${attribute.name}={${generate(value.expression, context.indent)}}`);
+ }
+ } else {
+ write(` ${attribute.name}={${generate(attribute.value.expression, context.indent)}}`);
+ }
+ } else if (attribute.type === 'SpreadAttribute') {
+ write(` {...${generate(attribute.expression, context.indent)}}`);
+ } else if (attribute.type === 'AnimateDirective') {
+ if (attribute.expression) {
+ write(` animate:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
+ } else {
+ write(` animate:${attribute.name}`);
+ }
+ } else if (attribute.type === 'BindDirective') {
+ write(` bind:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
+ } else if (attribute.type === 'ClassDirective') {
+ write(` class:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
+ } else if (attribute.type === 'LetDirective') {
+ if (attribute.expression) {
+ write(` let:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
+ } else {
+ write(` let:${attribute.name}`);
+ }
+ } else if (attribute.type === 'OnDirective') {
+ if (attribute.expression) {
+ write(` on:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
+ } else {
+ write(` on:${attribute.name}`);
+ }
+ } else if (attribute.type === 'StyleDirective') {
+ if (attribute.value === true) {
+ write(` style:${attribute.name}`);
+ } else {
+ if (_.isArray(attribute.value)) {
+ const [value] = attribute.value;
+ if (value.type === 'Text') {
+ write(` style:${attribute.name}="${value.data}"`);
+ } else {
+ write(` style:${attribute.name}={${generate(value, context.indent)}}`);
+ }
+ } else {
+ write(` style:${attribute.name}={${generate(attribute.value.expression, context.indent)}}`);
+ }
+ }
+ } else if (attribute.type === 'TransitionDirective') {
+ const transition = () => {
+ if (attribute.intro && !attribute.outro) {
+ return 'in';
+ }
+
+ if (attribute.outro && !attribute.intro) {
+ return 'out';
+ }
+
+ return 'transition';
+ };
+
+ if (attribute.expression) {
+ write(` ${transition()}:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
+ } else {
+ write(` ${transition()}:${attribute.name}`);
+ }
+ } else if (attribute.type === 'UseDirective') {
+ if (attribute.expression) {
+ write(` use:${attribute.name}={${generate(attribute.expression, context.indent)}}`);
+ } else {
+ write(` use:${attribute.name}`);
+ }
+ }
+}
+
+export function attributeToString(attribute: AST.Attribute | AST.SpreadAttribute | Directive) {
+ let result = '';
+
+ const context: PrinterContext = {
+ _this: null,
+ write: (str: string) => {
+ return (result += str);
+ },
+ indent: {
+ indent: '',
+ lineEnd: ''
+ }
+ };
+
+ printAttributes(attribute, context);
+
+ return result;
+}