Skip to content

Commit

Permalink
fix(bug): fix issues with directory imports (implicit /index files)
Browse files Browse the repository at this point in the history
  • Loading branch information
wessberg committed Oct 19, 2019
1 parent 9ed7924 commit 5d329cd
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 60 deletions.
32 changes: 1 addition & 31 deletions src/service/language-service/incremental-language-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {IFile, IFileInput} from "./i-file";
import {getScriptKindFromPath} from "../../util/get-script-kind-from-path/get-script-kind-from-path";
import {sync} from "find-up";
import {DEFAULT_LIB_NAMES} from "../../constant/constant";
import {ensureAbsolute, isInternalFile, setExtension} from "../../util/path/path-util";
import {ensureAbsolute, isInternalFile} from "../../util/path/path-util";
import {CustomTransformersFunction} from "../../util/merge-transformers/i-custom-transformer-options";
import {IExtendedDiagnostic} from "../../diagnostic/i-extended-diagnostic";

Expand Down Expand Up @@ -87,36 +87,6 @@ export class IncrementalLanguageService implements LanguageServiceHost, Compiler
return program.getSourceFile(fileName);
}

/**
* Returns true if the LanguageServiceHost has the given file
* @param {string} fileName
* @returns {boolean}
*/
public hasFile(fileName: string): boolean {
return this.files.has(fileName);
}

/**
* Gets the fileName, if any, that is has been added to the LanguageServiceHost and looks like the given one.
* For example, if a file without any file extension is provided as input, any file that has an extension but the
* same base name will be returned
* @param {string} fileName
* @returns {string | undefined}
*/
public getClosestFileName(fileName: string): string | undefined {
const absolute = ensureAbsolute(this.options.cwd, fileName);

if (this.hasFile(fileName)) return fileName;
if (this.hasFile(absolute)) return absolute;
for (const extension of this.options.supportedExtensions) {
const withExtension = setExtension(fileName, extension);
const absoluteWithExtension = ensureAbsolute(this.options.cwd, withExtension);
if (this.hasFile(withExtension)) return withExtension;
if (this.hasFile(absoluteWithExtension)) return absoluteWithExtension;
}
return undefined;
}

/**
* Gets all diagnostics reported of transformers for the given filename
* @param {string} fileName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {visitEnumDeclaration} from "./visitor/visit-enum-declaration";
import {visitInterfaceDeclaration} from "./visitor/visit-interface-declaration";
import {visitModuleDeclaration} from "./visitor/visit-module-declaration";
import {ensureHasLeadingDotAndPosix, setExtension} from "../../../../util/path/path-util";
import {extname, normalize} from "path";
import {join, normalize} from "path";

export function updateExports({usedExports, ...rest}: IDeclarationBundlerOptions): TransformerFactory<SourceFile> {
const parsedExportedSymbolsMap: Map<string, Map<string, Statement>> = new Map();
Expand Down Expand Up @@ -84,14 +84,17 @@ export function updateExports({usedExports, ...rest}: IDeclarationBundlerOptions
let matched: Map<string, Statement> | undefined;
let matchedModuleName: string = moduleName;

const extensions = extname(moduleName) !== "" ? [extname(moduleName)] : rest.supportedExtensions;
for (const extension of extensions) {
const tryPath = setExtension(moduleName, extension);
matched = parsedExportedSymbolsMap.get(tryPath);
if (matched != null) {
matchedModuleName = tryPath;
break;
const moduleNames = [normalize(moduleName), join(moduleName, "/index")];
for (const currentModuleName of moduleNames) {
for (const extension of rest.supportedExtensions) {
const tryPath = setExtension(currentModuleName, extension);
matched = parsedExportedSymbolsMap.get(tryPath);
if (matched != null) {
matchedModuleName = tryPath;
break;
}
}
if (matched != null) break;
}

if (matched == null) {
Expand All @@ -100,18 +103,22 @@ export function updateExports({usedExports, ...rest}: IDeclarationBundlerOptions
}
return matched;
},

getExportsFromExternalModulesForModule(moduleName: string): Map<string, ExportDeclaration> {
let matched: Map<string, ExportDeclaration> | undefined;
let matchedModuleName: string = moduleName;

const extensions = extname(moduleName) !== "" ? [extname(moduleName)] : rest.supportedExtensions;
for (const extension of extensions) {
const tryPath = setExtension(moduleName, extension);
matched = exportsFromExternalModulesMap.get(tryPath);
if (matched != null) {
matchedModuleName = tryPath;
break;
const moduleNames = [normalize(moduleName), join(moduleName, "/index")];
for (const currentModuleName of moduleNames) {
for (const extension of rest.supportedExtensions) {
const tryPath = setExtension(currentModuleName, extension);
matched = exportsFromExternalModulesMap.get(tryPath);
if (matched != null) {
matchedModuleName = tryPath;
break;
}
}
if (matched != null) break;
}

if (matched == null) {
Expand All @@ -125,14 +132,17 @@ export function updateExports({usedExports, ...rest}: IDeclarationBundlerOptions
let matched: Set<string> | undefined;
let matchedModuleName: string = moduleName;

const extensions = extname(moduleName) !== "" ? [extname(moduleName)] : rest.supportedExtensions;
for (const extension of extensions) {
const tryPath = setExtension(moduleName, extension);
matched = exportedSpecifiersFromModuleMap.get(tryPath);
if (matched != null) {
matchedModuleName = tryPath;
break;
const moduleNames = [normalize(moduleName), join(moduleName, "/index")];
for (const currentModuleName of moduleNames) {
for (const extension of rest.supportedExtensions) {
const tryPath = setExtension(currentModuleName, extension);
matched = exportedSpecifiersFromModuleMap.get(tryPath);
if (matched != null) {
matchedModuleName = tryPath;
break;
}
}
if (matched != null) break;
}

if (matched == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ export function getChunkFilename(filename: string, supportedExtensions: string[]
const filenames = [normalize(filename), join(filename, "/index")];
for (const file of filenames) {
for (const originalSourceFilename of originalSourceFilenames) {
if (originalSourceFilename === file) return chunkFilename;
if (originalSourceFilename === file) {
return chunkFilename;
}

for (const extension of supportedExtensions) {
if (originalSourceFilename === setExtension(file, extension)) return chunkFilename;
if (originalSourceFilename === setExtension(file, extension)) {
return chunkFilename;
}
}
}
}
Expand Down
68 changes: 68 additions & 0 deletions test/declaration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,74 @@ test("Flattens declarations. #14", async t => {
);
});

test("Flattens declarations. #15", async t => {
const bundle = await generateRollupBundle([
{
entry: true,
fileName: "index.ts",
text: `\
export * from "./foo";
`
},
{
entry: false,
fileName: "foo/index.ts",
text: `\
export const Foo = "foo";
`
}
]);
const {
declarations: [file]
} = bundle;
t.deepEqual(
formatCode(file.code),
formatCode(`\
declare const Foo = "foo";
export {Foo};
`)
);
});

test.skip("Flattens declarations. #16", async t => {
const bundle = await generateRollupBundle([
{
entry: true,
fileName: "index.ts",
text: `\
export * from "./foo.baz";
export * from "./bar.baz";
`
},
{
entry: false,
fileName: "foo.baz.ts",
text: `\
export const Foo = "foo";
`
},
{
entry: false,
fileName: "bar.baz.ts",
text: `\
export const Bar = "bar";
`
}
]);
const {
declarations: [file]
} = bundle;
console.log(file.code);
t.deepEqual(
formatCode(file.code),
formatCode(`\
declare const Foo = "foo";
declare const Bar = "bar";
export {Foo, Bar};
`)
);
});

test("A file with no exports generates a .d.ts file with an 'export {}' declaration to mark it as a module. #1", async t => {
const bundle = await generateRollupBundle([
{
Expand Down
25 changes: 20 additions & 5 deletions test/setup/setup-rollup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {dirname, isAbsolute, join} from "path";
import {dirname, isAbsolute, join, normalize} from "path";
import {rollup, RollupOptions, RollupOutput} from "rollup";
import typescriptRollupPlugin from "../../src/plugin/typescript-plugin";
import {PathLike} from "fs";
Expand All @@ -22,6 +22,8 @@ interface FileResult {
code: string;
}

const EXTENSIONS = ["", ".ts", ".js", ".mjs"];

export interface GenerateRollupBundleResult {
bundle: RollupOutput;
declarations: FileResult[];
Expand Down Expand Up @@ -58,17 +60,24 @@ export async function generateRollupBundle(
)
.map(file => ({...file, fileName: join(cwd, file.fileName)}));

const directories = new Set(files.map(file => dirname(file.fileName)));

const entryFile = files.find(file => file.entry);
if (entryFile == null) {
throw new ReferenceError(`No entry could be found`);
}

const resolveId = (fileName: string, parent: string | undefined): string | undefined => {
const absolute = isAbsolute(fileName) ? fileName : join(parent == null ? "" : dirname(parent), fileName);
for (const ext of ["", ".ts", ".js", ".mjs"]) {
const withExtension = `${absolute}${ext}`;
const matchedFile = files.find(file => file.fileName === withExtension);
if (matchedFile != null) return withExtension;
const filenames = [normalize(absolute), join(absolute, "/index")];
for (const filename of filenames) {
for (const ext of EXTENSIONS) {
const withExtension = `${filename}${ext}`;
const matchedFile = files.find(file => file.fileName === withExtension);
if (matchedFile != null) {
return withExtension;
}
}
}
return undefined;
};
Expand All @@ -94,6 +103,8 @@ export async function generateRollupBundle(
tsconfig: {
target: "esnext",
declaration: true,
moduleResolution: "node",
baseUrl: ".",
...tsconfig
},
hook,
Expand All @@ -109,6 +120,10 @@ export async function generateRollupBundle(
if (files.some(file => file.fileName === fileName)) return true;
return sys.fileExists(fileName);
},
directoryExists: dirName => {
if (directories.has(dirName)) return true;
return sys.directoryExists(dirName);
},
writeFileSync<T>(path: PathLike, data: T): void {
if (typeof path !== "string" || typeof data !== "string") return;
if (path.endsWith(DECLARATION_MAP_EXTENSION)) {
Expand Down

0 comments on commit 5d329cd

Please sign in to comment.