Skip to content

Commit e789d85

Browse files
author
zoedsoupe
committed
refactor: simplify modules
1 parent 4138dfb commit e789d85

12 files changed

Lines changed: 150 additions & 42 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
run: npm run lint
3737

3838
- name: Type check
39-
run: npm run type-check
39+
run: npm run type:check
4040

4141
test:
4242
runs-on: ubuntu-latest

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
run: npm run lint
3535

3636
- name: Type check
37-
run: npm run type-check
37+
run: npm run type:check
3838

3939
- name: Build project
4040
run: npm run build
File renamed without changes.

src/core/analyzer.ts

Whitespace-only changes.

src/core/extractor.ts

Whitespace-only changes.

src/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// src/index.ts
2-
export { extractSymbol, extractDocs, getSourcePath, formatOutput } from './extraction/typescript';
3-
export * from './core/types';
2+
export { extractSymbol, extractDocs, getSourcePath, formatOutput } from './resolution';
3+
export * from './core';
44

5-
import { extractDocs, getSourcePath, formatOutput } from './extraction/typescript';
5+
import { extractDocs, getSourcePath, formatOutput } from './resolution';
66

77
export const TidewaveExtractor = {
88
extractDocs,

src/interfaces/cli/commands.ts

Whitespace-only changes.

src/interfaces/cli/index.ts

Whitespace-only changes.
Lines changed: 100 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// src/extraction/typescript.ts
22
import ts from 'typescript';
33
import path from 'node:path';
4-
import type { ExtractionRequest, ExtractResult, ExtractorOptions, SymbolInfo } from '../core/types';
4+
import type { ExtractionRequest, ExtractResult, ExtractorOptions, SymbolInfo } from './core';
55

66
// Load TypeScript configuration
77
function loadTsConfig(tsConfigPath?: string): {
@@ -14,7 +14,7 @@ function loadTsConfig(tsConfigPath?: string): {
1414
let compilerOptions: ts.CompilerOptions = {
1515
target: ts.ScriptTarget.ES2020,
1616
module: ts.ModuleKind.CommonJS,
17-
moduleResolution: ts.ModuleResolutionKind.NodeJs,
17+
moduleResolution: ts.ModuleResolutionKind.Bundler,
1818
esModuleInterop: true,
1919
allowSyntheticDefaultImports: true,
2020
skipLibCheck: true,
@@ -71,18 +71,16 @@ function resolveModule(
7171
moduleName: string,
7272
compilerOptions: ts.CompilerOptions,
7373
): { sourceFile: ts.SourceFile; program: ts.Program } | null {
74-
// Use TypeScript's built-in module resolution
74+
// First try normal module resolution
7575
const moduleResolver = ts.resolveModuleName(
7676
moduleName,
77-
path.resolve('./index.ts'), // Use absolute path for containing file
77+
path.resolve('./index.ts'),
7878
compilerOptions,
7979
ts.sys,
8080
);
8181

8282
if (moduleResolver.resolvedModule) {
8383
const { resolvedFileName } = moduleResolver.resolvedModule;
84-
85-
// Create a dedicated program for this file like
8684
const dedicatedProgram = ts.createProgram([resolvedFileName], compilerOptions);
8785
const sourceFile = dedicatedProgram.getSourceFile(resolvedFileName);
8886

@@ -94,6 +92,53 @@ function resolveModule(
9492
return null;
9593
}
9694

95+
// Handle global symbols from lib.d.ts (like Math, console, etc.)
96+
function resolveGlobalSymbol(
97+
symbolName: string,
98+
compilerOptions: ts.CompilerOptions,
99+
): { sourceFile: ts.SourceFile; program: ts.Program; isGlobal: true } | null {
100+
try {
101+
// Create a minimal TypeScript file that references the global symbol
102+
const virtualFileName = 'virtual-globals.ts';
103+
const virtualContent = `// Global symbol reference\nconst _ref = ${symbolName};`;
104+
105+
// Create program with default host and proper lib files
106+
const program = ts.createProgram(
107+
[virtualFileName],
108+
{
109+
...compilerOptions,
110+
lib: compilerOptions.lib || ['lib.es2020.d.ts'],
111+
skipLibCheck: false,
112+
moduleResolution: ts.ModuleResolutionKind.NodeJs,
113+
},
114+
{
115+
...ts.createCompilerHost(compilerOptions),
116+
getSourceFile: (fileName: string, languageVersion: ts.ScriptTarget) => {
117+
if (fileName === virtualFileName) {
118+
return ts.createSourceFile(virtualFileName, virtualContent, languageVersion, true);
119+
}
120+
// Use the default compiler host for everything else (including lib.d.ts files)
121+
return ts.createCompilerHost(compilerOptions).getSourceFile(fileName, languageVersion);
122+
},
123+
fileExists: (fileName: string) => {
124+
if (fileName === virtualFileName) return true;
125+
return ts.createCompilerHost(compilerOptions).fileExists(fileName);
126+
},
127+
},
128+
);
129+
130+
const sourceFile = program.getSourceFile(virtualFileName);
131+
if (sourceFile) {
132+
return { sourceFile, program, isGlobal: true };
133+
}
134+
} catch (error) {
135+
// If global symbol resolution fails, return null
136+
console.debug(`Failed to resolve global symbol ${symbolName}:`, error);
137+
}
138+
139+
return null;
140+
}
141+
97142
// Find symbol in JavaScript file
98143
function findSymbolInJavaScriptFile(
99144
sourceFile: ts.SourceFile,
@@ -334,45 +379,73 @@ export async function extractDocs(
334379
): Promise<SymbolInfo | null> {
335380
try {
336381
const { module, symbol, member, isStatic } = parseModulePath(modulePath);
337-
338-
// Load config like
339382
const config = loadTsConfig(options.tsConfigPath);
340383

341-
// Resolve module with dedicated program like
342-
const resolvedModule = resolveModule(module, config.options);
384+
// Try to resolve as a regular module first
385+
let resolvedModule = resolveModule(module, config.options);
386+
let isGlobalModule = false;
387+
388+
// If regular module resolution fails, try as a global symbol
389+
if (!resolvedModule) {
390+
const globalResolution = resolveGlobalSymbol(module, config.options);
391+
if (globalResolution) {
392+
resolvedModule = globalResolution;
393+
isGlobalModule = true;
394+
}
395+
}
396+
343397
if (!resolvedModule) {
344398
throw new Error(`Module '${module}' not found`);
345399
}
346400

347401
const { sourceFile, program } = resolvedModule;
348402
const checker = program.getTypeChecker();
349-
350-
// Get the module symbol
351-
const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
352403
let targetSymbol: ts.Symbol | undefined;
353404

354-
if (moduleSymbol) {
355-
// TypeScript module with proper exports
356-
const exports = checker.getExportsOfModule(moduleSymbol);
357-
targetSymbol = exports.find(exp => exp.getName() === symbol);
405+
if (isGlobalModule) {
406+
// For global modules, get the symbol from the global scope
407+
const globalSymbols = checker.getSymbolsInScope(sourceFile, ts.SymbolFlags.Value);
408+
targetSymbol = globalSymbols.find(s => s.getName() === module);
358409

359410
if (!targetSymbol) {
360-
throw new Error(
361-
`Symbol '${symbol}' not found in module '${module}'. Available exports: ${exports
362-
.map(e => e.getName())
363-
.join(', ')}`,
364-
);
411+
// Try getting it from the AST node directly
412+
const [identifierNode] = sourceFile.statements;
413+
if (ts.isVariableStatement(identifierNode)) {
414+
const [declaration] = identifierNode.declarationList.declarations;
415+
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
416+
targetSymbol = checker.getSymbolAtLocation(declaration.initializer);
417+
}
418+
}
365419
}
366420
} else {
367-
// JavaScript file - look for symbols in statements
368-
targetSymbol = findSymbolInJavaScriptFile(sourceFile, checker, symbol);
421+
// Regular module handling
422+
const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
369423

370-
if (!targetSymbol) {
371-
throw new Error(`Symbol '${symbol}' not found in JavaScript module '${module}'`);
424+
if (moduleSymbol) {
425+
const exports = checker.getExportsOfModule(moduleSymbol);
426+
targetSymbol = exports.find(exp => exp.getName() === symbol);
427+
428+
if (!targetSymbol) {
429+
throw new Error(
430+
`Symbol '${symbol}' not found in module '${module}'. Available exports: ${exports
431+
.map(e => e.getName())
432+
.join(', ')}`,
433+
);
434+
}
435+
} else {
436+
// JavaScript file - look for symbols in statements
437+
targetSymbol = findSymbolInJavaScriptFile(sourceFile, checker, symbol);
438+
439+
if (!targetSymbol) {
440+
throw new Error(`Symbol '${symbol}' not found in JavaScript module '${module}'`);
441+
}
372442
}
373443
}
374444

375-
// Get symbol info like
445+
if (!targetSymbol) {
446+
throw new Error(`Symbol '${isGlobalModule ? module : symbol}' not found`);
447+
}
448+
376449
const result = getSymbolInfo(checker, targetSymbol, member, isStatic);
377450
return result;
378451
} catch (error) {

test-script.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* A simple test function for JavaScript support
3+
* @param {string} name - The name to greet
4+
* @returns {string} A greeting message
5+
*/
6+
function greetUser(name) {
7+
return `Hello, ${name}!`;
8+
}
9+
10+
/**
11+
* A test class with JSDoc
12+
*/
13+
class TestClass {
14+
/**
15+
* Constructor for TestClass
16+
* @param {number} value - Initial value
17+
*/
18+
constructor(value) {
19+
this.value = value;
20+
}
21+
22+
/**
23+
* Get the current value
24+
* @returns {number} The current value
25+
*/
26+
getValue() {
27+
return this.value;
28+
}
29+
30+
/**
31+
* Static method to create a new instance
32+
* @param {number} value - The value to use
33+
* @returns {TestClass} A new TestClass instance
34+
*/
35+
static create(value) {
36+
return new TestClass(value);
37+
}
38+
}
39+
40+
module.exports = { greetUser, TestClass };

0 commit comments

Comments
 (0)