Skip to content

Commit c1a7b22

Browse files
roli-lpciclaudeJPeer264Lms24
authored
ref(sveltekit): Replace recast + @babel/parser with acorn (#19533)
Replaces `recast` and `@babel/parser` with `acorn` and `@sveltejs/acorn-typescript` for AST parsing in the sveltekit auto-instrumentation plugin. - [x] If you've added code that should be tested, please add tests. - [x] Ensure your code lints and the test suite passes (`yarn lint`) & (`yarn test`). - [ ] Link an issue if there is one related to your pull request. Ref #19447 ## What this does The `autoInstrument` Vite plugin parses SvelteKit `+page.ts` / `+layout.server.ts` files to detect `export const load` or `export function load` declarations. It only **reads** the AST — never transforms or prints it — so recast's source-preserving round-trip feature is entirely unused. This replaces the full recast + @babel/parser pipeline with acorn (Node.js's own parser) and `@sveltejs/acorn-typescript` (the actively maintained TypeScript plugin used by SvelteKit itself). ### Changes - **Delete** `recastTypescriptParser.ts` — the 91-line Babel parser config with 20+ plugin declarations is no longer needed - **Replace** `recast.parse()` with `acorn.Parser.extend(tsPlugin()).parse()` in `autoInstrument.ts` - **Fix** AST node type: `StringLiteral` (Babel-specific) → `Literal` (ESTree standard) for string literal export detection - **Fix** AST structure: `ast.program` (recast's File wrapper) → direct `ast` (acorn returns Program) - **Improve** error handling: `try/catch` around parse (acorn throws SyntaxError) replaces dubious null check - **Improve** type safety: remove unsafe double-cast `(specifier.exported as unknown as t.StringLiteral)` in favor of proper discriminant narrowing ### Dependency reduction **Removed:** `recast`, `ast-types`, `esprima`, `source-map`, `tslib`, `tiny-invariant`, `@babel/parser`, `@babel/types`, `@babel/helper-string-parser`, `@babel/helper-validator-identifier` **Added:** `acorn` (already in dep tree via Vite/Rollup), `@sveltejs/acorn-typescript` (already in dep tree via `@sveltejs/kit`) Net effect: ~8 fewer transitive dependencies, zero new packages for end users. ### Why @sveltejs/acorn-typescript? The original `acorn-typescript` package by TyrealHu hasn't been updated since January 2024 and has known unpatched bugs. The Svelte team's fork (`@sveltejs/acorn-typescript`) is actively maintained and is already a transitive dependency of every SvelteKit project. All 69 existing tests pass without modification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: JPeer264 <jan.peer@sentry.io> Co-authored-by: Lukas Stracke <lukas.stracke@sentry.io>
1 parent ddca82a commit c1a7b22

File tree

4 files changed

+37
-135
lines changed

4 files changed

+37
-135
lines changed

packages/sveltekit/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,17 @@
4747
}
4848
},
4949
"dependencies": {
50-
"@babel/parser": "7.26.9",
5150
"@sentry/cloudflare": "10.45.0",
5251
"@sentry/core": "10.45.0",
5352
"@sentry/node": "10.45.0",
5453
"@sentry/svelte": "10.45.0",
5554
"@sentry/vite-plugin": "^5.1.0",
55+
"@sveltejs/acorn-typescript": "^1.0.9",
56+
"acorn": "^8.14.0",
5657
"magic-string": "~0.30.0",
57-
"recast": "0.23.11",
5858
"sorcery": "1.0.0"
5959
},
6060
"devDependencies": {
61-
"@babel/types": "^7.26.3",
6261
"@sveltejs/kit": "^2.53.3",
6362
"@sveltejs/vite-plugin-svelte": "^3.0.0",
6463
"svelte": "^4.2.8",

packages/sveltekit/src/vite/autoInstrument.ts

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import { tsPlugin } from '@sveltejs/acorn-typescript';
2+
import * as acorn from 'acorn';
13
import * as fs from 'fs';
24
import * as path from 'path';
3-
import * as recast from 'recast';
45
import type { Plugin } from 'vite';
56
import { WRAPPED_MODULE_SUFFIX } from '../common/utils';
6-
import { parser } from './recastTypescriptParser';
7-
import t = recast.types.namedTypes;
7+
8+
const AcornParser = acorn.Parser.extend(tsPlugin());
89

910
export type AutoInstrumentSelection = {
1011
/**
@@ -123,23 +124,21 @@ export async function canWrapLoad(id: string, debug: boolean): Promise<boolean>
123124

124125
const code = (await fs.promises.readFile(id, 'utf8')).toString();
125126

126-
const ast = recast.parse(code, {
127-
parser,
128-
});
129-
130-
const program = (ast as { program?: t.Program }).program;
131-
132-
if (!program) {
127+
let program: acorn.Program;
128+
try {
129+
program = AcornParser.parse(code, {
130+
sourceType: 'module',
131+
ecmaVersion: 'latest',
132+
locations: true,
133+
});
134+
} catch {
133135
// eslint-disable-next-line no-console
134136
debug && console.log(`Skipping wrapping ${id} because it doesn't contain valid JavaScript or TypeScript`);
135137
return false;
136138
}
137139

138140
const hasLoadDeclaration = program.body
139-
.filter(
140-
(statement): statement is recast.types.namedTypes.ExportNamedDeclaration =>
141-
statement.type === 'ExportNamedDeclaration',
142-
)
141+
.filter((statement): statement is acorn.ExportNamedDeclaration => statement.type === 'ExportNamedDeclaration')
143142
.find(exportDecl => {
144143
// find `export const load = ...`
145144
if (exportDecl.declaration?.type === 'VariableDeclaration') {
@@ -160,11 +159,8 @@ export async function canWrapLoad(id: string, debug: boolean): Promise<boolean>
160159
return exportDecl.specifiers.find(specifier => {
161160
return (
162161
(specifier.exported.type === 'Identifier' && specifier.exported.name === 'load') ||
163-
// Type casting here because somehow the 'exportExtensions' plugin isn't reflected in the possible types
164-
// This plugin adds support for exporting something as a string literal (see comment above)
165-
// Doing this to avoid adding another babel plugin dependency
166-
((specifier.exported.type as 'StringLiteral' | '') === 'StringLiteral' &&
167-
(specifier.exported as unknown as t.StringLiteral).value === 'load')
162+
// ESTree/acorn represents `export { x as "load" }` with a Literal node (not Babel's StringLiteral)
163+
(specifier.exported.type === 'Literal' && specifier.exported.value === 'load')
168164
);
169165
});
170166
}

packages/sveltekit/src/vite/recastTypescriptParser.ts

Lines changed: 0 additions & 91 deletions
This file was deleted.

yarn.lock

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,13 +1914,6 @@
19141914
"@babel/template" "^7.28.6"
19151915
"@babel/types" "^7.28.6"
19161916

1917-
"@babel/parser@7.26.9":
1918-
version "7.26.9"
1919-
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5"
1920-
integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==
1921-
dependencies:
1922-
"@babel/types" "^7.26.9"
1923-
19241917
"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.4", "@babel/parser@^7.18.10", "@babel/parser@^7.20.7", "@babel/parser@^7.22.10", "@babel/parser@^7.22.16", "@babel/parser@^7.23.5", "@babel/parser@^7.23.6", "@babel/parser@^7.25.4", "@babel/parser@^7.26.7", "@babel/parser@^7.27.7", "@babel/parser@^7.28.0", "@babel/parser@^7.28.4", "@babel/parser@^7.28.5", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0", "@babel/parser@^7.4.5", "@babel/parser@^7.7.0":
19251918
version "7.29.0"
19261919
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6"
@@ -2981,7 +2974,7 @@
29812974
"@babel/types" "^7.29.0"
29822975
debug "^4.3.1"
29832976

2984-
"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.17", "@babel/types@^7.22.19", "@babel/types@^7.23.6", "@babel/types@^7.24.7", "@babel/types@^7.25.4", "@babel/types@^7.26.3", "@babel/types@^7.26.8", "@babel/types@^7.26.9", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.27.7", "@babel/types@^7.28.2", "@babel/types@^7.28.5", "@babel/types@^7.28.6", "@babel/types@^7.29.0", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.2":
2977+
"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.17", "@babel/types@^7.22.19", "@babel/types@^7.23.6", "@babel/types@^7.24.7", "@babel/types@^7.25.4", "@babel/types@^7.26.8", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.27.7", "@babel/types@^7.28.2", "@babel/types@^7.28.5", "@babel/types@^7.28.6", "@babel/types@^7.29.0", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.2":
29852978
version "7.29.0"
29862979
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7"
29872980
integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==
@@ -8652,9 +8645,14 @@
86528645
"@supabase/storage-js" "2.7.1"
86538646

86548647
"@sveltejs/acorn-typescript@^1.0.5":
8655-
version "1.0.8"
8656-
resolved "https://registry.yarnpkg.com/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.8.tgz#69c746a7c232094c117c50dedbd1279fc64887b7"
8657-
integrity sha512-esgN+54+q0NjB0Y/4BomT9samII7jGwNy/2a3wNZbT2A2RpmXsXwUt24LvLhx6jUq2gVk4cWEvcRO6MFQbOfNA==
8648+
version "1.0.9"
8649+
resolved "https://registry.yarnpkg.com/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz#ac0bde368d6623727b0e0bc568cf6b4e5d5c4baa"
8650+
integrity sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==
8651+
8652+
"@sveltejs/acorn-typescript@^1.0.9":
8653+
version "1.0.9"
8654+
resolved "https://registry.yarnpkg.com/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz#ac0bde368d6623727b0e0bc568cf6b4e5d5c4baa"
8655+
integrity sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==
86588656

86598657
"@sveltejs/kit@^2.53.3":
86608658
version "2.53.3"
@@ -25831,17 +25829,6 @@ real-require@^0.2.0:
2583125829
resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78"
2583225830
integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==
2583325831

25834-
recast@0.23.11, recast@^0.23.4:
25835-
version "0.23.11"
25836-
resolved "https://registry.yarnpkg.com/recast/-/recast-0.23.11.tgz#8885570bb28cf773ba1dc600da7f502f7883f73f"
25837-
integrity sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==
25838-
dependencies:
25839-
ast-types "^0.16.1"
25840-
esprima "~4.0.0"
25841-
source-map "~0.6.1"
25842-
tiny-invariant "^1.3.3"
25843-
tslib "^2.0.1"
25844-
2584525832
recast@^0.18.1:
2584625833
version "0.18.10"
2584725834
resolved "https://registry.yarnpkg.com/recast/-/recast-0.18.10.tgz#605ebbe621511eb89b6356a7e224bff66ed91478"
@@ -25862,6 +25849,17 @@ recast@^0.20.5:
2586225849
source-map "~0.6.1"
2586325850
tslib "^2.0.1"
2586425851

25852+
recast@^0.23.4:
25853+
version "0.23.11"
25854+
resolved "https://registry.yarnpkg.com/recast/-/recast-0.23.11.tgz#8885570bb28cf773ba1dc600da7f502f7883f73f"
25855+
integrity sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==
25856+
dependencies:
25857+
ast-types "^0.16.1"
25858+
esprima "~4.0.0"
25859+
source-map "~0.6.1"
25860+
tiny-invariant "^1.3.3"
25861+
tslib "^2.0.1"
25862+
2586525863
rechoir@^0.6.2:
2586625864
version "0.6.2"
2586725865
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"

0 commit comments

Comments
 (0)