runem / lit-analyzer

Monorepository for tools that analyze lit-html templates

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Using Typescript's `compilerOptions.paths` causes false-positives from no-missing-import

jrencz opened this issue · comments

Initially I didn't use compilerOptions.paths, but I'm now adding that to the project.
Presence of that setting in tsconfig.json does not affect lit-analyser, but as I started gradually replacing imports in component modules I got some false positives from no-missing-import rule

I narrowed down the problem down a little bit, here's my investigation (collapsed)

(I'm fairly new to typescript as a user and I'm definitely not experienced in how it works, so the reasoning may have mistakes)

In a file I tested on I have 9 import statements:

  • 3 are from node_modules
  • one is styles import
  • 2 are legitimate relative imports I want to preserve
  • 3 are currently relative imports, but I'd like to make them aliased as those are imports of other components into my component

In DefaultLitAnalyzerContext#findDependenciesInFile helper parseDependencies shows 20 dependencies if I use relative imports in 3 relatively-imported deps which I mentioned, but only 2 if I switch to aliases. So - imports with aliased path is clearly omitted.

I wend into parseDependencies and inside, in parseAllIndirectImports call I get 163 and 53 elements respectively.

Next, down to visitDirectImports call in visitIndirectImportsFromSourceFile helper:

  • without aliases: 8
  • with aliases: 5

I also confirmed that all 9 import declarations in question regardless of whether they are aliased or not go through this line:

emitDirectModuleImportWithName(node.moduleSpecifier.text, node, context);

Import declarations with aliased module specifier make difference in those lines:

// Resolve the imported string
const result = context.project
? context.project.getResolvedModuleWithFailedLookupLocationsFromCache(moduleSpecifier, fromSourceFile.fileName)
: "getResolvedModuleWithFailedLookupLocationsFromCache" in context.program
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
(context.program as any)["getResolvedModuleWithFailedLookupLocationsFromCache"](moduleSpecifier, fromSourceFile.fileName)
: undefined;
if (result?.resolvedModule?.resolvedFileName != null) {

For all 9 there's result but aliased 3 don't have resolvedModule.

All 9 have no context.project LanguageServiceHost and for all 9 result comes from context.program.

If I'm not mistaken the problem is that analyzeCommand creates context with access to program, but not project (I assume that it's "project" level what knows about paths):

/**
* Executes the configuration and returns a boolean indicating if the command ran successfully.
* @param globs
* @param cliConfig
*/
export async function analyzeCommand(globs: string[], cliConfig: LitAnalyzerCliConfig): Promise<boolean> {
let program: Program | undefined = undefined;
const context = new DefaultLitAnalyzerContext({
getProgram() {
return program!;
}
});

The only context that has access to that is this one:

context = new LitPluginContext({
ts: typescript,
getProgram: () => {
return info.languageService.getProgram()!;
},
getProject: () => {
return info.project;
}
});

Additionally when I run lit-analyzer with just one file, instead of errors from no-missing-import I get errors from no-unknown-tag-name. Reason will be the same, but if analyser has access to other files it does know the tag name, but complains about it not being properly imported

commented

got the same issue with imports using tsconfig paths.