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

chore: explore parsers and printers #380

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@sveltejs/eslint-config": "^8.1.0",
"@svitejs/changesets-changelog-github-compact": "^1.2.0",
"@types/node": "^22.10.2",
"@vitest/ui": "^3.0.3",
"@vitest/ui": "^3.0.5",
"eslint": "^9.17.0",
"magic-string": "^0.30.15",
"prettier": "^3.4.2",
Expand All @@ -39,5 +39,5 @@
"unplugin-isolated-decl": "^0.8.3",
"vitest": "^3.0.5"
},
"packageManager": "pnpm@10.1.0"
"packageManager": "pnpm@10.2.0"
}
4 changes: 2 additions & 2 deletions packages/addons/drizzle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ export default defineAddon({
url: common.expressionFromString('process.env.DATABASE_URL'),
authToken
}),
verbose: { type: 'BooleanLiteral', value: true },
strict: { type: 'BooleanLiteral', value: true }
verbose: { type: 'Literal', value: true },
strict: { type: 'Literal', value: true }
});

const dialect = options.sqlite === 'turso' ? 'turso' : options.database;
Expand Down
3 changes: 1 addition & 2 deletions packages/addons/eslint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
functions,
imports,
object,
type AstKinds,
type AstTypes
} from '@sveltejs/cli-core/js';
import { parseJson, parseScript } from '@sveltejs/cli-core/parsers';
Expand Down Expand Up @@ -55,7 +54,7 @@ export default defineAddon({
const { ast, generateCode } = parseScript(content);

const eslintConfigs: Array<
AstKinds.ExpressionKind | AstTypes.SpreadElement | AstTypes.ObjectExpression
AstTypes.Expression | AstTypes.SpreadElement | AstTypes.ObjectExpression
> = [];

const gitIgnorePathStatement = common.statementFromString(
Expand Down
19 changes: 10 additions & 9 deletions packages/addons/lucia/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,17 @@

sv.file(`drizzle.config.${ext}`, (content) => {
const { ast, generateCode } = parseScript(content);
const isProp = (name: string, node: AstTypes.ObjectProperty) =>
const isProp = (name: string, node: AstTypes.Property) =>
node.key.type === 'Identifier' && node.key.name === name;

// prettier-ignore
Walker.walk(ast as AstTypes.ASTNode, {}, {
ObjectProperty(node) {
if (isProp('dialect', node) && node.value.type === 'StringLiteral') {
Walker.walk(ast as AstTypes.Node, {}, {
Property(node) {
if (isProp('dialect', node) && node.value.type === 'Literal' && typeof node.value.type === 'string') {
drizzleDialect = node.value.value as Dialect;
}
if (isProp('schema', node) && node.value.type === 'StringLiteral') {
schemaPath = node.value.value;
if (isProp('schema', node) && node.value.type === 'Literal' && typeof node.value.type === 'string') {
schemaPath = node.value.value as string;
}
}
})
Expand Down Expand Up @@ -335,7 +335,7 @@
});

if (typescript) {
sv.file('src/app.d.ts', (content) => {

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (windows-latest)

_tests/all-addons/test.ts > run all addons - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/all-addons/test.ts:23:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (windows-latest)

_tests/all-addons/test.ts > run all addons - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/all-addons/test.ts:23:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (windows-latest)

_tests/all-addons/test.ts > run all addons - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/all-addons/test.ts:23:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (windows-latest)

_tests/all-addons/test.ts > run all addons - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/all-addons/test.ts:23:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (windows-latest)

_tests/lucia/test.ts > core - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/lucia/test.ts:10:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (windows-latest)

_tests/lucia/test.ts > core - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/lucia/test.ts:10:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (windows-latest)

_tests/lucia/test.ts > core - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/lucia/test.ts:10:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (windows-latest)

_tests/lucia/test.ts > core - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/lucia/test.ts:10:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (macOS-latest)

_tests/all-addons/test.ts > run all addons - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/all-addons/test.ts:23:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (macOS-latest)

_tests/all-addons/test.ts > run all addons - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/all-addons/test.ts:23:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (macOS-latest)

_tests/all-addons/test.ts > run all addons - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/all-addons/test.ts:23:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (macOS-latest)

_tests/all-addons/test.ts > run all addons - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/all-addons/test.ts:23:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (macOS-latest)

_tests/lucia/test.ts > core - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/lucia/test.ts:10:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (macOS-latest)

_tests/lucia/test.ts > core - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/lucia/test.ts:10:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (macOS-latest)

_tests/lucia/test.ts > core - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/lucia/test.ts:10:14

Check failure on line 338 in packages/addons/lucia/index.ts

View workflow job for this annotation

GitHub Actions / test (macOS-latest)

_tests/lucia/test.ts > core - kit-ts

Error: Unable to process 'src/app.d.ts'. Reason: Not implemented TSModuleDeclaration ❯ Object.run lucia/index.ts:338:7 ❯ Object.ctx.run _tests/_setup/suite.ts:72:38 ❯ _tests/lucia/test.ts:10:14
const { ast, generateCode } = parseScript(content);

const locals = js.kit.addGlobalAppInterface(ast, 'Locals');
Expand Down Expand Up @@ -601,13 +601,14 @@
type: 'Identifier',
name
},
computed: false,
typeAnnotation: {
type: 'TSTypeAnnotation',
typeAnnotation: {
type: 'TSIndexedAccessType',
objectType: {
type: 'TSImportType',
argument: { type: 'StringLiteral', value: '$lib/server/auth' },
argument: { type: 'Literal', value: '$lib/server/auth' },
qualifier: {
type: 'Identifier',
name: 'SessionValidationResult'
Expand All @@ -616,7 +617,7 @@
indexType: {
type: 'TSLiteralType',
literal: {
type: 'StringLiteral',
type: 'Literal',
value: name
}
}
Expand Down Expand Up @@ -649,7 +650,7 @@
};`;
}

function getCallExpression(ast: AstTypes.ASTNode): AstTypes.CallExpression | undefined {
function getCallExpression(ast: AstTypes.Node): AstTypes.CallExpression | undefined {
let callExpression;

// prettier-ignore
Expand Down
12 changes: 7 additions & 5 deletions packages/addons/sveltekit-adapter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ export default defineAddon({
if (adapterImportDecl) {
// replaces the import's source with the new adapter
adapterImportDecl.source.value = adapter.package;
// reset raw value, so that the string is re-generated
adapterImportDecl.source.raw = undefined;

adapterName = adapterImportDecl.specifiers?.find((s) => s.type === 'ImportDefaultSpecifier')
?.local?.name as string;
} else {
Expand All @@ -78,16 +81,15 @@ export default defineAddon({

const { value: config } = exports.defaultExport(ast, object.createEmpty());
const kitConfig = config.properties.find(
(p) => p.type === 'ObjectProperty' && p.key.type === 'Identifier' && p.key.name === 'kit'
) as AstTypes.ObjectProperty | undefined;
(p) => p.type === 'Property' && p.key.type === 'Identifier' && p.key.name === 'kit'
) as AstTypes.Property | undefined;

if (kitConfig && kitConfig.value.type === 'ObjectExpression') {
const adapterProp = kitConfig.value.properties.find(
(p) =>
p.type === 'ObjectProperty' && p.key.type === 'Identifier' && p.key.name === 'adapter'
(p) => p.type === 'Property' && p.key.type === 'Identifier' && p.key.name === 'adapter'
);
if (adapterProp) {
adapterProp.comments = [];
adapterProp.leadingComments = [];
}

// only overrides the `adapter` property so we can reset it's args
Expand Down
115 changes: 61 additions & 54 deletions packages/ast-tooling/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { parse as tsParse } from 'recast/parsers/typescript.js';
import { parse as recastParse, print as recastPrint, type Options as RecastOptions } from 'recast';
import { Document, Element, type ChildNode } from 'domhandler';
import { ElementType, parseDocument } from 'htmlparser2';
import { removeElement, textContent } from 'domutils';
import serializeDom from 'dom-serializer';
import {
Root as CssAst,
Expand All @@ -15,8 +12,10 @@ import {
} from 'postcss';
import * as fleece from 'silver-fleece';
import * as Walker from 'zimmerframe';
import type { namedTypes as AstTypes } from 'ast-types';
import type * as AstKinds from 'ast-types/gen/kinds';
import type { TsEstree } from './ts-estree.ts';
import { print as esrapPrint } from 'esrap';
import * as acorn from 'acorn';
import { tsPlugin } from 'acorn-typescript';

/**
* Most of the AST tooling is pretty big in bundle size and bundling takes forever.
Expand Down Expand Up @@ -48,34 +47,72 @@ export type {
ChildNode as HtmlChildNode,

// js
AstTypes,
AstKinds,
TsEstree as AstTypes,

//css
CssChildNode
};

export function parseScript(content: string): AstTypes.Program {
const recastOutput: { program: AstTypes.Program } = recastParse(content, {
parser: {
parse: tsParse
export function parseScript(content: string): TsEstree.Program {
const comments: any[] = [];

// @ts-expect-error
const acornTs = acorn.Parser.extend(tsPlugin({ allowSatisfies: true }));

const ast = acornTs.parse(content, {
ecmaVersion: 'latest',
sourceType: 'module',
locations: true,
onComment: (block, value, start, end) => {
if (block && /\n/.test(value)) {
let a = start;
while (a > 0 && content[a - 1] !== '\n') a -= 1;

let b = a;
while (/[ \t]/.test(content[b])) b += 1;

const indentation = content.slice(a, b);
value = value.replace(new RegExp(`^${indentation}`, 'gm'), '');
}

comments.push({ type: block ? 'Block' : 'Line', value, start, end });
}
});

return recastOutput.program;
}
Walker.walk(ast, null, {
_(node, { next }) {
const commentNode /** @type {import('../../src/types').NodeWithComments} */ =
/** @type {any} */ node;
let comment;

export function serializeScript(ast: AstTypes.ASTNode, previousContent?: string): string {
let options: RecastOptions | undefined;
if (!previousContent) {
// provide sensible defaults if we generate a new file
options = {
quote: 'single',
useTabs: true
};
}
while (comments[0] && comments[0].start < node.start) {
comment = comments.shift();
// @ts-expect-error
(commentNode.leadingComments ||= []).push(comment);
}

next();

if (comments[0]) {
const slice = content.slice(node.end, comments[0].start);

if (/^[,) \t]*$/.test(slice)) {
// @ts-expect-error
commentNode.trailingComments = [comments.shift()];
}
}
}
});

return ast as TsEstree.Program;
}

return recastPrint(ast, options).code;
export function serializeScript(ast: TsEstree.Node): string {
const { code } = esrapPrint(ast, {
indent: '\t',
quotes: 'single'
});
return code;
}

export function parseCss(content: string): CssAst {
Expand Down Expand Up @@ -117,41 +154,11 @@ export function stripAst<T>(node: T, propToRemove: string): T {
}

export type SvelteAst = {
jsAst: AstTypes.Program;
jsAst: TsEstree.Program;
htmlAst: Document;
cssAst: CssAst;
};

export function parseSvelte(content: string): SvelteAst {
const htmlAst = parseHtml(content);

let scriptTag, styleTag;
for (const node of htmlAst.childNodes) {
if (node.type === ElementType.Script) {
scriptTag = node;
removeElement(scriptTag);
} else if (node.type === ElementType.Style) {
styleTag = node;
removeElement(styleTag);
}
}

if (!scriptTag) {
scriptTag = new Element('script', {}, undefined, ElementType.ElementType.Script);
}
if (!styleTag) {
styleTag = new Element('style', {}, undefined, ElementType.ElementType.Style);
}

const css = textContent(styleTag);
const cssAst = parseCss(css);

const scriptValue = textContent(scriptTag);
const jsAst = parseScript(scriptValue);

return { jsAst, htmlAst, cssAst };
}

export function parseJson(content: string): any {
// some of the files we need to process contain comments. The default
// node JSON.parse fails parsing those comments.
Expand Down
7 changes: 4 additions & 3 deletions packages/ast-tooling/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@
}
},
"devDependencies": {
"@babel/parser": "^7.26.3",
"ast-types": "^0.16.1",
"@types/estree": "^1.0.6",
"acorn": "^8.14.0",
"acorn-typescript": "^1.4.13",
"dom-serializer": "^2.0.0",
"domhandler": "^5.0.3",
"domutils": "^3.1.0",
"esrap": "^1.4.2",
"htmlparser2": "^9.1.0",
"postcss": "^8.4.49",
"recast": "^0.23.9",
"silver-fleece": "^1.2.1",
"zimmerframe": "^1.1.2"
}
Expand Down
Loading
Loading