Skip to content

Commit

Permalink
Move unresolved module handling to resolveModuleName internally (close
Browse files Browse the repository at this point in the history
 #206, close #258)
  • Loading branch information
webpro committed Sep 26, 2023
1 parent c267827 commit 11f91f9
Show file tree
Hide file tree
Showing 13 changed files with 81 additions and 18 deletions.
6 changes: 6 additions & 0 deletions fixtures/dts/assets.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare module '*.svg';

declare module '*.html?raw' {
const value: string;
export default value;
}
1 change: 1 addition & 0 deletions fixtures/dts/block.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div>content</div>
1 change: 1 addition & 0 deletions fixtures/dts/image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions fixtures/dts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import b from './block.html?raw';
import s from './image.svg';
import n from './normal';

b;
s;
n;
1 change: 1 addition & 0 deletions fixtures/dts/normal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 1;
3 changes: 3 additions & 0 deletions fixtures/dts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "@fixtures/dts"
}
1 change: 1 addition & 0 deletions fixtures/dts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
5 changes: 2 additions & 3 deletions src/ProjectPrincipal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getJSDocTags, isInModuleBlock } from './typescript/ast-helpers.js';
import { createHosts } from './typescript/createHosts.js';
import { getImportsAndExports } from './typescript/getImportsAndExports.js';
import { SourceFileManager } from './typescript/SourceFileManager.js';
import { isMaybePackageName } from './util/modules.js';
import { isMaybePackageName, sanitizeSpecifier } from './util/modules.js';
import { dirname, extname, isInNodeModules, join } from './util/path.js';
import { timerify } from './util/Performance.js';
import type { SyncCompilers, AsyncCompilers } from './types/compilers.js';
Expand Down Expand Up @@ -198,8 +198,7 @@ export class ProjectPrincipal {
this.addEntryPath(resolvedModule.resolvedFileName, { skipExportsAnalysis: true });
}
} else {
// Strip special Webpack stuff (https://webpack.js.org/concepts/loaders/)
const sanitizedSpecifier = specifier.replace(/^([?!|-]+)?([^!?]+).*/, '$2');
const sanitizedSpecifier = sanitizeSpecifier(specifier);
if (isMaybePackageName(sanitizedSpecifier)) {
external.add(sanitizedSpecifier);
} else {
Expand Down
19 changes: 6 additions & 13 deletions src/typescript/getImportsAndExports.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { existsSync } from 'node:fs';
import { isBuiltin } from 'node:module';
// eslint-disable-next-line n/no-restricted-import
import { resolve, dirname } from 'node:path';
import ts from 'typescript';
import { getOrSet } from '../util/map.js';
import { isMaybePackageName } from '../util/modules.js';
import { isMaybePackageName, sanitizeSpecifier } from '../util/modules.js';
import { isInNodeModules } from '../util/path.js';
import {
isDeclarationFileExtension,
Expand Down Expand Up @@ -102,26 +99,22 @@ export const getImportsAndExports = (sourceFile: BoundSourceFile, options: GetIm
addInternalImport({ identifier, specifier, symbol, filePath, isReExport });
}

if (!isMaybePackageName(specifier)) return;
const sanitizedSpecifier = sanitizeSpecifier(specifier);
if (!isMaybePackageName(sanitizedSpecifier)) return;

if (isDeclarationFileExtension(module.resolvedModule.extension)) {
// We use TypeScript's module resolution, but it returns DTS references. In the rest of the program we want
// the package name based on the original specifier.
externalImports.add(specifier);
externalImports.add(sanitizedSpecifier);
} else {
externalImports.add(module.resolvedModule.packageId?.name ?? specifier);
externalImports.add(module.resolvedModule.packageId?.name ?? sanitizedSpecifier);
}
} else {
addInternalImport({ identifier, specifier, symbol, filePath, isReExport });
}
}
} else {
const filePath = resolve(dirname(sourceFile.fileName), specifier);
if (existsSync(filePath)) {
unresolvedImports.add(filePath); // TODO Optimize (it's not unresolved)
} else {
unresolvedImports.add(specifier);
}
unresolvedImports.add(specifier);
}
};

Expand Down
29 changes: 28 additions & 1 deletion src/typescript/resolveModuleNames.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { existsSync } from 'node:fs';
import ts from 'typescript';
import { sanitizeSpecifier } from '../util/modules.js';
import { dirname, extname, isInternal, join } from '../util/path.js';
import { ensureRealFilePath, isVirtualFilePath } from './utils.js';

export function createCustomModuleResolver(
Expand All @@ -11,7 +14,31 @@ export function createCustomModuleResolver(
}

function resolveModuleName(name: string, containingFile: string): ts.ResolvedModule | undefined {
const tsResolvedModule = ts.resolveModuleName(name, containingFile, compilerOptions, ts.sys).resolvedModule;
const sanitizedSpecifier = sanitizeSpecifier(name);

const tsResolvedModule = ts.resolveModuleName(
sanitizedSpecifier,
containingFile,
compilerOptions,
ts.sys
).resolvedModule;

// When TS does not resolve it, and it's not a registered virtual file ext, try `fs.existsSync`
if (!tsResolvedModule) {
const extension = extname(sanitizedSpecifier);
if (extension && isInternal(sanitizedSpecifier) && !virtualFileExtensions.includes(extension)) {
const resolvedFileName = join(dirname(containingFile), sanitizedSpecifier);
if (existsSync(resolvedFileName)) {
return {
resolvedFileName,
// @ts-expect-error Without this, TS throws for "unknown extension"
extension,
isExternalLibraryImport: false,
resolvedUsingTsExtension: false,
};
}
}
}

if (virtualFileExtensions.length === 0) return tsResolvedModule;

Expand Down
3 changes: 3 additions & 0 deletions src/util/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,6 @@ export const getEntryPathFromManifest = (cwd: string, dir: string, manifest: Pac
// Glob, as we only want source files that exist and not (generated) files that are .gitignore'd
return _glob({ cwd, workingDir: dir, patterns: Array.from(entryPaths) });
};

// Strip `?search` and other proprietary directives from the specifier (e.g. https://webpack.js.org/concepts/loaders/)
export const sanitizeSpecifier = (specifier: string) => specifier.replace(/^([?!|-]+)?([^!?]+).*/, '$2');
21 changes: 21 additions & 0 deletions tests/dts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { main } from '../src/index.js';
import { resolve } from '../src/util/path.js';
import baseArguments from './helpers/baseArguments.js';
import baseCounters from './helpers/baseCounters.js';

const cwd = resolve('fixtures/dts');

test('Include declaration files and allow unknown extensions', async () => {
const { counters } = await main({
...baseArguments,
cwd,
});

assert.deepEqual(counters, {
...baseCounters,
processed: 3,
total: 3,
});
});
2 changes: 1 addition & 1 deletion tests/unresolved-rtl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import baseCounters from './helpers/baseCounters.js';

const cwd = resolve('fixtures/מסמכים');

test('Report unresolved imports', async () => {
test('Report unresolved imports (rtl)', async () => {
const { counters } = await main({
...baseArguments,
cwd,
Expand Down

0 comments on commit 11f91f9

Please sign in to comment.