Skip to content

Commit fab5871

Browse files
committed
Extract vite.resolve.alias from Astro config AST (resolve #1692)
1 parent 2e8cf47 commit fab5871

14 files changed

Lines changed: 137 additions & 24 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import path from 'node:path';
2+
import { fileURLToPath } from 'node:url';
3+
4+
export default {
5+
vite: {
6+
resolve: {
7+
alias: {
8+
'@/lib/database': path.resolve('./src/lib/database-local.ts'),
9+
'@/lib/auth': path.resolve(__dirname, 'src/lib/auth-local.ts'),
10+
'@/lib/mailer': fileURLToPath(new URL('./src/lib/mailer-local.ts', import.meta.url)),
11+
},
12+
},
13+
},
14+
};

packages/knip/fixtures/plugins/astro2/node_modules/astro/index.js

Whitespace-only changes.

packages/knip/fixtures/plugins/astro2/node_modules/astro/package.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "@plugins/astro2",
3+
"type": "module",
4+
"scripts": {
5+
"dev": "astro dev",
6+
"build": "astro build"
7+
},
8+
"dependencies": {
9+
"astro": "*"
10+
}
11+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const login = () => ({ user: 'local' });
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const connect = () => ({ name: 'local' });
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const send = () => ({ status: 'ok' });
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
import { connect } from '@/lib/database';
3+
import { login } from '@/lib/auth';
4+
import { send } from '@/lib/mailer';
5+
6+
const db = connect();
7+
login();
8+
send();
9+
---
10+
<h1>{db.name}</h1>

packages/knip/src/plugins/astro/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import type { IsPluginEnabled, Plugin, RegisterCompilers, Resolve, ResolveFromAST } from '../../types/config.ts';
22
import { toDependency, toEntry, toProductionEntry } from '../../util/input.ts';
33
import { hasDependency } from '../../util/plugin.ts';
4+
import { getAliasInputs } from '../vitest/helpers.ts';
45
import compiler from './compiler.ts';
56
import mdxCompiler from './compiler-mdx.ts';
6-
import { getSrcDir, usesPassthroughImageService } from './resolveFromAST.ts';
7+
import { getSrcDir, getViteAliases, usesPassthroughImageService } from './resolveFromAST.ts';
78

89
// https://docs.astro.build/en/reference/configuration-reference/
910

@@ -26,12 +27,13 @@ const production = [
2627
'src/actions/index.{js,ts}',
2728
];
2829

29-
const resolveFromAST: ResolveFromAST = program => {
30+
const resolveFromAST: ResolveFromAST = (program, options) => {
3031
const srcDir = getSrcDir(program);
3132
const setSrcDir = (entry: string) => entry.replace(/^src\//, `${srcDir}/`);
3233
const inputs = [
3334
...entry.map(setSrcDir).map(path => toEntry(path)),
3435
...production.map(setSrcDir).map(path => toProductionEntry(path)),
36+
...getAliasInputs(getViteAliases(program), options.cwd),
3537
];
3638

3739
if (!usesPassthroughImageService(program)) inputs.push(toDependency('sharp', { optional: true }));
Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import type { Program } from 'oxc-parser';
2-
import { collectPropertyValues, hasImportSpecifier } from '../../typescript/ast-helpers.ts';
2+
import {
3+
collectPropertyValues,
4+
findProperty,
5+
getPropertyKey,
6+
getStringValue,
7+
hasImportSpecifier,
8+
} from '../../typescript/ast-helpers.ts';
39

410
export const getSrcDir = (program: Program): string => {
511
const values = collectPropertyValues(program, 'srcDir');
@@ -8,3 +14,43 @@ export const getSrcDir = (program: Program): string => {
814

915
export const usesPassthroughImageService = (program: Program) =>
1016
hasImportSpecifier(program, 'astro/config', 'passthroughImageService');
17+
18+
// First string literal reachable via CallExpression/NewExpression arguments. Handles common
19+
// config patterns: `'./src'`, `path.resolve('./src')`, `path.resolve(__dirname, 'src')`,
20+
// `fileURLToPath(new URL('./src', import.meta.url))`.
21+
const findFirstStringArg = (node: any): string | undefined => {
22+
const literal = getStringValue(node);
23+
if (literal) return literal;
24+
if (node?.type === 'CallExpression' || node?.type === 'NewExpression') {
25+
for (const arg of node.arguments ?? []) {
26+
const found = findFirstStringArg(arg);
27+
if (found) return found;
28+
}
29+
}
30+
};
31+
32+
// Extract `vite.resolve.alias` from the default-exported config object.
33+
// Supports `export default { ... }` and `export default defineConfig({ ... })`.
34+
export const getViteAliases = (program: Program): Record<string, string> => {
35+
const aliases: Record<string, string> = {};
36+
for (const node of (program as unknown as { body: any[] }).body ?? []) {
37+
if (node.type !== 'ExportDefaultDeclaration') continue;
38+
const decl = node.declaration;
39+
const root =
40+
decl?.type === 'ObjectExpression'
41+
? decl
42+
: decl?.type === 'CallExpression' && decl.arguments?.[0]?.type === 'ObjectExpression'
43+
? decl.arguments[0]
44+
: undefined;
45+
const aliasNode = findProperty(findProperty(findProperty(root, 'vite'), 'resolve'), 'alias');
46+
if (aliasNode?.type !== 'ObjectExpression') continue;
47+
for (const prop of aliasNode.properties ?? []) {
48+
if (prop.type !== 'Property') continue;
49+
const key = getPropertyKey(prop);
50+
if (!key) continue;
51+
const raw = findFirstStringArg(prop.value);
52+
if (raw) aliases[key] = raw;
53+
}
54+
}
55+
return aliases;
56+
};

0 commit comments

Comments
 (0)