Skip to content

Commit e1002a4

Browse files
committed
handle alias imports in typescript
1 parent bb8775c commit e1002a4

File tree

6 files changed

+91
-6
lines changed

6 files changed

+91
-6
lines changed

src/analyze/index.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,28 @@ async function analyzeDirectory(dirPath, customFunctions) {
2020

2121
const files = getAllFiles(dirPath);
2222
const tsFiles = files.filter(file => /\.(tsx?)$/.test(file));
23-
const tsProgram = ts.createProgram(tsFiles, {
23+
24+
// Attempt to reuse project tsconfig.json compiler options for proper module resolution (e.g., path aliases)
25+
let tsCompilerOptions = {
2426
target: ts.ScriptTarget.ESNext,
2527
module: ts.ModuleKind.CommonJS,
26-
});
28+
jsx: ts.JsxEmit.Preserve,
29+
allowJs: true,
30+
noEmit: true,
31+
};
32+
33+
const tsConfigPath = ts.findConfigFile(dirPath, ts.sys.fileExists, 'tsconfig.json');
34+
if (tsConfigPath) {
35+
const readResult = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
36+
if (!readResult.error && readResult.config) {
37+
const parsedConfig = ts.parseJsonConfigFileContent(readResult.config, ts.sys, path.dirname(tsConfigPath));
38+
if (!parsedConfig.errors || parsedConfig.errors.length === 0) {
39+
tsCompilerOptions = { ...tsCompilerOptions, ...parsedConfig.options };
40+
}
41+
}
42+
}
43+
44+
const tsProgram = ts.createProgram(tsFiles, tsCompilerOptions);
2745

2846
for (const file of files) {
2947
let events = [];

src/analyze/typescript/parser.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const ts = require('typescript');
77
const { detectAnalyticsSource } = require('./detectors');
88
const { extractEventData, processEventData } = require('./extractors');
99
const { findWrappingFunction } = require('./utils/function-finder');
10+
const path = require('path');
1011

1112
/**
1213
* Error thrown when TypeScript program cannot be created
@@ -44,16 +45,37 @@ function getProgram(filePath, existingProgram) {
4445
}
4546

4647
try {
47-
// Create a minimal program for single file analysis
48-
const options = {
48+
// Try to locate a tsconfig.json nearest to the file to inherit compiler options (important for path aliases)
49+
const searchPath = path.dirname(filePath);
50+
const configPath = ts.findConfigFile(searchPath, ts.sys.fileExists, 'tsconfig.json');
51+
52+
let compilerOptions = {
4953
target: ts.ScriptTarget.Latest,
5054
module: ts.ModuleKind.CommonJS,
5155
allowJs: true,
5256
checkJs: false,
53-
noEmit: true
57+
noEmit: true,
58+
jsx: ts.JsxEmit.Preserve
5459
};
60+
let rootNames = [filePath];
61+
62+
if (configPath) {
63+
// Read and parse the tsconfig.json
64+
const readResult = ts.readConfigFile(configPath, ts.sys.readFile);
65+
if (!readResult.error && readResult.config) {
66+
const parseResult = ts.parseJsonConfigFileContent(
67+
readResult.config,
68+
ts.sys,
69+
path.dirname(configPath)
70+
);
71+
if (!parseResult.errors || parseResult.errors.length === 0) {
72+
compilerOptions = { ...compilerOptions, ...parseResult.options };
73+
rootNames = parseResult.fileNames.length > 0 ? parseResult.fileNames : rootNames;
74+
}
75+
}
76+
}
5577

56-
const program = ts.createProgram([filePath], options);
78+
const program = ts.createProgram(rootNames, compilerOptions);
5779
return program;
5880
} catch (error) {
5981
throw new ProgramError(filePath, error);

tests/analyzeTypeScript.test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,4 +971,19 @@ test.describe('analyzeTsFile', () => {
971971
const builtInCount = events.filter(e => e.source !== 'custom').length;
972972
assert.ok(builtInCount >= 10, 'Should still include built-in provider events');
973973
});
974+
975+
test('should resolve constants imported via path alias from tsconfig', () => {
976+
const aliasFilePath = path.join(fixturesDir, 'typescript-alias', 'app', 'components', 'main.ts');
977+
// Pass null program to force internal program creation (tsconfig parsing)
978+
const events = analyzeTsFile(aliasFilePath, null, null);
979+
980+
// Expect one event detected with correct name
981+
assert.strictEqual(events.length, 1);
982+
const evt = events[0];
983+
assert.strictEqual(evt.eventName, 'ViewedPage');
984+
assert.strictEqual(evt.source, 'mixpanel');
985+
assert.deepStrictEqual(evt.properties, {
986+
foo: { type: 'string' }
987+
});
988+
});
974989
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Test file that imports constants via path alias and triggers tracking
2+
import { TELEMETRY_EVENTS } from 'lib/constants';
3+
4+
// Mock of a tracking library (simple function)
5+
const mixpanel: any = {
6+
track: (..._args: any[]) => {}
7+
};
8+
9+
// Event that should be detected via constant reference
10+
mixpanel.track(TELEMETRY_EVENTS.VIEWED_PAGE, {
11+
foo: 'bar'
12+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const TELEMETRY_EVENTS = Object.freeze({
2+
VIEWED_PAGE: 'ViewedPage',
3+
VIEWED_QUESTION: 'ViewedQuestion'
4+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2022",
4+
"module": "CommonJS",
5+
"strict": true,
6+
"baseUrl": "./",
7+
"paths": {
8+
"lib/*": ["lib/*"]
9+
},
10+
"jsx": "preserve"
11+
},
12+
"include": ["**/*"],
13+
"exclude": ["node_modules"]
14+
}

0 commit comments

Comments
 (0)