Skip to content

Commit

Permalink
fix: Cover edge case issues with case-insensitive filesystems
Browse files Browse the repository at this point in the history
  • Loading branch information
nonara committed Jan 6, 2023
1 parent bf61058 commit 968ee7b
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 19 deletions.
15 changes: 0 additions & 15 deletions src/utils/general-utils.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
import url from "url";
import path from "path";
import { realpathSync } from "fs";

/* ****************************************************************************************************************** *
* General Utilities & Helpers
* ****************************************************************************************************************** */

export const isURL = (s: string): boolean => !!s && (!!url.parse(s).host || !!url.parse(s).hostname);

export const cast = <T>(v: any): T => v;

export const isBaseDir = (baseDir: string, testDir: string): boolean => {
const relative = path.relative(baseDir, testDir);
return relative ? !relative.startsWith("..") && !path.isAbsolute(relative) : true;
};

export const maybeAddRelativeLocalPrefix = (p: string) => (p[0] === "." ? p : `./${p}`);

export function tryRealpathNative(value: string) {
try {
return realpathSync.native(value);
} catch {
return value;
}
}

export function nativeRelativePath(from: string, to: string) {
return path.relative(tryRealpathNative(from), tryRealpathNative(to));
}
85 changes: 85 additions & 0 deletions src/utils/get-relative-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import fs from "fs";
import * as os from "os";
import path from "path";

/* ****************************************************************************************************************** */
// region: Locals
/* ****************************************************************************************************************** */

let isCaseSensitiveFilesystem: boolean | undefined;

// endregion

/* ****************************************************************************************************************** */
// region: Helpers
/* ****************************************************************************************************************** */

function tryRmFile(fileName: string) {
try {
if (fs.existsSync(fileName)) fs.rmSync(fileName, { force: true });
} catch {}
}

function getIsFsCaseSensitive() {
if (isCaseSensitiveFilesystem != null) return isCaseSensitiveFilesystem;

for (let i = 0; i < 1000; i++) {
const tmpFileName = path.join(os.tmpdir(), `tstp~${i}.tmp`);

tryRmFile(tmpFileName);

try {
fs.writeFileSync(tmpFileName, "");
isCaseSensitiveFilesystem = !fs.existsSync(tmpFileName.replace("tstp", "TSTP"));
return isCaseSensitiveFilesystem;
} catch {
} finally {
tryRmFile(tmpFileName);
}
}

console.warn(
`Could not determine filesystem's case sensitivity. Please file a bug report with your system's details`
);
isCaseSensitiveFilesystem = false;

return isCaseSensitiveFilesystem;
}

function getMatchPortion(from: string, to: string) {
const lowerFrom = from.toLocaleLowerCase();
const lowerTo = to.toLocaleLowerCase();

const maxLen = Math.max(lowerFrom.length, lowerTo.length);

let i = 0;
while (i < maxLen) {
if (lowerFrom[i] !== lowerTo[i]) break;
i++;
}

return from.slice(0, i);
}

// endregion

/* ****************************************************************************************************************** */
// region: Utils
/* ****************************************************************************************************************** */

export function getRelativePath(from: string, to: string) {
try {
from = fs.realpathSync.native(from);
to = fs.realpathSync.native(to);
} catch {
if (!getIsFsCaseSensitive()) {
const matchPortion = getMatchPortion(from, to);
from = matchPortion + from.slice(matchPortion.length);
to = matchPortion + to.slice(matchPortion.length);
}
}

return path.relative(from, to);
}

// endregion
9 changes: 5 additions & 4 deletions src/utils/resolve-module-name.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { VisitorContext } from "../types";
import { isBaseDir, isURL, maybeAddRelativeLocalPrefix, nativeRelativePath } from "./general-utils";
import { isBaseDir, isURL, maybeAddRelativeLocalPrefix } from "./general-utils";
import * as path from "path";
import { removeFileExtension, removeSuffix, ResolvedModuleFull, SourceFile } from "typescript";
import { getOutputDirForSourceFile } from "./ts-helpers";
import { getRelativePath } from "./get-relative-path";

/* ****************************************************************************************************************** */
// region: Types
Expand Down Expand Up @@ -167,12 +168,12 @@ export function resolveModuleName(context: VisitorContext, moduleName: string):

/* Remove base dirs to make relative to root */
if (fileRootDir && moduleRootDir) {
srcFileOutputDir = nativeRelativePath(fileRootDir, srcFileOutputDir);
moduleFileOutputDir = nativeRelativePath(moduleRootDir, moduleFileOutputDir);
srcFileOutputDir = getRelativePath(fileRootDir, srcFileOutputDir);
moduleFileOutputDir = getRelativePath(moduleRootDir, moduleFileOutputDir);
}
}

const outputDir = nativeRelativePath(srcFileOutputDir, moduleFileOutputDir);
const outputDir = getRelativePath(srcFileOutputDir, moduleFileOutputDir);

/* Compose final output path */
const outputPath = maybeAddRelativeLocalPrefix(tsInstance.normalizePath(path.join(outputDir, outputBaseName)));
Expand Down

0 comments on commit 968ee7b

Please sign in to comment.