From 510a9a6be54e9a4ae439235f91b8eaa07e0b80dc Mon Sep 17 00:00:00 2001 From: catloversg <152669316+catloversg@users.noreply.github.com> Date: Sun, 22 Jun 2025 03:31:52 +0700 Subject: [PATCH] BUGFIX: Documentation navigator does not handle external URL properly (#2202) --- .../doc/basic/codingcontracts.md | 2 +- src/Documentation/doc/basic/scripts.md | 4 +- .../doc/programming/game_frozen.md | 2 +- src/Documentation/doc/programming/learn.md | 4 +- src/Documentation/ui/DocumentationPopUp.tsx | 29 ++++--- src/Documentation/ui/DocumentationRoot.tsx | 53 ++++++++++--- src/GameOptions/ui/RemoteAPIPage.tsx | 2 +- src/Paths/FilePath.ts | 12 ++- src/Paths/ProgramFilePath.ts | 4 +- src/Terminal/Terminal.ts | 4 +- src/Themes/ui/ThemeCollaborate.tsx | 5 +- src/ui/MD/a.tsx | 9 ++- src/ui/React/Documentation.tsx | 12 ++- test/jest/Terminal/Path.test.ts | 75 +++++++++---------- 14 files changed, 139 insertions(+), 78 deletions(-) diff --git a/src/Documentation/doc/basic/codingcontracts.md b/src/Documentation/doc/basic/codingcontracts.md index 307f9f826..db45dfc06 100644 --- a/src/Documentation/doc/basic/codingcontracts.md +++ b/src/Documentation/doc/basic/codingcontracts.md @@ -34,7 +34,7 @@ Like most functions in other APIs, almost all of the functions in the [Coding Co Depending on which function you use, the initial [RAM](ram.md) on your home server might not be enough to allow you to use various [API](https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/bitburner.codingcontract.md) functions. Plan on upgrading the [RAM](ram.md) on your home server if you want to use the [Coding Contract API](https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/bitburner.codingcontract.md). -The [`getContractTypes`](https://github.com/bitburner-official/bitburner-src/blob/dev/markdown/bitburner.codingcontract.getcontracttypes.md) function is free, and returns a list of all of the contract types currently in the game. +The [`getContractTypes`](https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/bitburner.codingcontract.getcontracttypes.md) function is free, and returns a list of all of the contract types currently in the game. ## Submitting Solutions diff --git a/src/Documentation/doc/basic/scripts.md b/src/Documentation/doc/basic/scripts.md index 4b99ae387..2ec723ade 100644 --- a/src/Documentation/doc/basic/scripts.md +++ b/src/Documentation/doc/basic/scripts.md @@ -47,7 +47,7 @@ For details on references in terminal commands, see [Terminal](terminal.md). ## Script Arguments -When running a script, you can use [flags](https://github.com/bitburner-official/bitburner-src/blob/bec737a25307be29c7efef147fc31effca65eedc/markdown/bitburner.ns.flags.md) and [arguments](https://github.com/bitburner-official/bitburner-src/blob/bec737a25307be29c7efef147fc31effca65eedc/markdown/bitburner.ns.args.md), which the script's logic can access and act on, allowing flexibility in your script designs. For example allowing you to get different results or attack different targets without re-writing your code: +When running a script, you can use [flags](https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/bitburner.ns.flags.md) and [arguments](https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/bitburner.ns.args.md), which the script's logic can access and act on, allowing flexibility in your script designs. For example allowing you to get different results or attack different targets without re-writing your code: $ run hack.js "harakiri-sushi" $ run hack.js "silver-helix" @@ -60,7 +60,7 @@ For example, if a script run with 1 thread is able to hack \$10,000, then runnin [Note -- Scripts will not actually become multithreaded in the real-world sense - Javascript is a "single-threaded" coding language.] -When "multithreading" a script, the total [RAM](ram.md) cost can be calculated by simply multiplying the [RAM](ram.md) cost of a single instance of your script by the number of threads you will use. [See [`ns.getScriptRam()`](https://github.com/bitburner-official/bitburner-src/blob/bec737a25307be29c7efef147fc31effca65eedc/markdown/bitburner.ns.getscriptram.md) or the `mem` terminal command detailed below] +When "multithreading" a script, the total [RAM](ram.md) cost can be calculated by simply multiplying the [RAM](ram.md) cost of a single instance of your script by the number of threads you will use. [See [`ns.getScriptRam()`](https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/bitburner.ns.getscriptram.md) or the `mem` terminal command detailed below] ## Never-ending scripts diff --git a/src/Documentation/doc/programming/game_frozen.md b/src/Documentation/doc/programming/game_frozen.md index 5dee52cf8..0992c0a49 100644 --- a/src/Documentation/doc/programming/game_frozen.md +++ b/src/Documentation/doc/programming/game_frozen.md @@ -58,4 +58,4 @@ To prevent this from happening make sure to multithread the scripts as much as p ## Bug Otherwise, the game is probably frozen/stuck due to a bug. -To report a bug, follow the guidelines [here](https://github.com/bitburner-official/bitburner-src/blob/master/doc/CONTRIBUTING.md#reporting-bugs). +To report a bug, follow the guidelines [here](https://github.com/bitburner-official/bitburner-src/blob/stable/doc/CONTRIBUTING.md#reporting-bugs). diff --git a/src/Documentation/doc/programming/learn.md b/src/Documentation/doc/programming/learn.md index e77e3d126..09f2ee07d 100644 --- a/src/Documentation/doc/programming/learn.md +++ b/src/Documentation/doc/programming/learn.md @@ -8,7 +8,7 @@ In fact, this game could help you learn some basic programming concepts. Here are some good tutorials for learning programming/JavaScript as a beginner: -- [Learn-JS](http://www.learn-js.org/en/Welcome) +- [Learn-JS](https://www.learn-js.org/en/Welcome) - [programiz](https://www.programiz.com/javascript/get-started) - [Speaking JavaScript](https://exploringjs.com/es5/) This is a bit on the longer side. @@ -29,7 +29,7 @@ However, this means that ES5 engines and interpreters will fail if they encounte You'll see why this is important further down. - [MDN Introduction to JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript) -- [Eloquent JavaScript (ES6+)](http://eloquentjavascript.net/) +- [Eloquent JavaScript (ES6+)](https://eloquentjavascript.net/) Recommended Chapters: Introduction, 1-6 - [Modern JavaScript Tutorial (ES6+)](https://javascript.info/) Recommended Chapters: 2, 4-6 diff --git a/src/Documentation/ui/DocumentationPopUp.tsx b/src/Documentation/ui/DocumentationPopUp.tsx index cf006df60..e9112191f 100644 --- a/src/Documentation/ui/DocumentationPopUp.tsx +++ b/src/Documentation/ui/DocumentationPopUp.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { Modal } from "../../ui/React/Modal"; import { defaultNsApiPage, Navigator, openDocExternally } from "../../ui/React/Documentation"; import { MD } from "../../ui/MD/MD"; -import { asFilePath, type FilePath, resolveFilePath } from "../../Paths/FilePath"; +import { asFilePath, type FilePath, isFilePath, resolveFilePath } from "../../Paths/FilePath"; import { DocumentationPopUpEvents } from "../root"; export function DocumentationPopUp({ hidden }: { hidden: boolean }) { @@ -15,22 +15,33 @@ export function DocumentationPopUp({ hidden }: { hidden: boolean }) { [], ); const navigator = { - navigate(relativePath: string, external: boolean) { + navigate(href: string, openExternally: boolean) { /** * This function is used for navigating inside the documentation popup. * - * The "markdown" folder does not have any subfolders. All files are at the top-level. "relativePath" is always - * "./". E.g., "./bitburner.ns.md". + * Href can be: + * - Internal NS docs. The "markdown" folder does not have any subfolders. All files are at the top-level. "Href" + * is always "./". E.g., "./bitburner.ns.md". + * - HTTP URL (e.g., ns.printf has a link to https://github.com/alexei/sprintf.js). */ - const nsApiDocPath = resolveFilePath(relativePath, defaultNsApiPage); - if (!nsApiDocPath) { + let path; + if (href.startsWith("https://") || href.startsWith("http://")) { + openExternally = true; + path = href; + } else { + path = resolveFilePath(href, defaultNsApiPage); + } + if (!path) { + console.error(`Bad path ${href} while navigating docs.`); return; } - if (external) { - openDocExternally(nsApiDocPath); + if (openExternally) { + openDocExternally(path); return; } - setPath(nsApiDocPath); + if (isFilePath(path)) { + setPath(path); + } }, }; if (!path) { diff --git a/src/Documentation/ui/DocumentationRoot.tsx b/src/Documentation/ui/DocumentationRoot.tsx index d7a90cb36..7a32f1a6e 100644 --- a/src/Documentation/ui/DocumentationRoot.tsx +++ b/src/Documentation/ui/DocumentationRoot.tsx @@ -4,8 +4,14 @@ import Button from "@mui/material/Button"; import Box from "@mui/material/Box"; import { MD } from "../../ui/MD/MD"; -import { Navigator, windowTopPositionOfPages, useHistory, openDocExternally } from "../../ui/React/Documentation"; -import { asFilePath, resolveFilePath } from "../../Paths/FilePath"; +import { + Navigator, + windowTopPositionOfPages, + useHistory, + openDocExternally, + prefixOfHttpUrlOfNsDocs, +} from "../../ui/React/Documentation"; +import { asFilePath, isFilePath, resolveFilePath } from "../../Paths/FilePath"; import { Settings } from "../../Settings/Settings"; import { Router } from "../../ui/GameRoot"; import { Page } from "../../ui/Router"; @@ -15,19 +21,48 @@ export function DocumentationRoot({ docPage }: { docPage?: string }): React.Reac const history = useHistory(); const [deepLink, setDeepLink] = useState(docPage); const navigator = { - navigate(relativePath: string, external: boolean) { - const path = relativePath.startsWith("nsDoc/") - ? asFilePath(relativePath) - : resolveFilePath("./" + relativePath, history.page); + navigate(href: string, openExternally: boolean) { + let path; + /** + * Href can be: + * - Internal NS docs: nsDoc/bitburner.ns.md + * - Internal non-NS docs: help/getting_started.md + * - HTTP URL: + * - Point to NS docs. Some non-NS docs pages include links to NS docs. For example: basic/scripts.md has a + * link to https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/bitburner.ns.flags.md. In + * these cases, the link always points to a file at https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/ + * - Point to other places. + */ + if (href.startsWith("nsDoc/")) { + // Internal NS docs + path = asFilePath(href); + } else if (href.startsWith("https://") || href.startsWith("http://")) { + /** + * HTTP URL pointing to NS docs. + * Convert https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/page.md to nsDoc/page.md + */ + if (href.startsWith(prefixOfHttpUrlOfNsDocs)) { + path = asFilePath(`nsDoc/${href.replace(prefixOfHttpUrlOfNsDocs, "")}`); + } else { + // HTTP URL pointing to other places. + openExternally = true; + path = href; + } + } else { + // Internal non-NS docs + path = resolveFilePath("./" + href, history.page); + } if (!path) { - console.error(`Bad path ${relativePath} from ${history.page} while navigating docs.`); + console.error(`Bad path ${href} from ${history.page} while navigating docs.`); return; } - if (external) { + if (openExternally) { openDocExternally(path); return; } - history.push(path); + if (isFilePath(path)) { + history.push(path); + } }, }; diff --git a/src/GameOptions/ui/RemoteAPIPage.tsx b/src/GameOptions/ui/RemoteAPIPage.tsx index b90c006a4..bde025691 100644 --- a/src/GameOptions/ui/RemoteAPIPage.tsx +++ b/src/GameOptions/ui/RemoteAPIPage.tsx @@ -50,7 +50,7 @@ export const RemoteAPIPage = (): React.ReactElement => { Documentation diff --git a/src/Paths/FilePath.ts b/src/Paths/FilePath.ts index d4e92bc26..6a72a6e76 100644 --- a/src/Paths/FilePath.ts +++ b/src/Paths/FilePath.ts @@ -29,12 +29,18 @@ const basicFilePathRegex = new RegExp(directoryRegexString + filenameRegexString }; /** Simple validation function with no modification. Can be combined with isAbsolutePath to get a real FilePath */ -export function isFilePath(path: string): path is BasicFilePath { +export function isBasicFilePath(path: string): path is BasicFilePath { return basicFilePathRegex.test(path); } +export function isFilePath(path: string): path is FilePath { + return isBasicFilePath(path) && isAbsolutePath(path); +} + export function asFilePath(input: T): T & FilePath { - if (isFilePath(input) && isAbsolutePath(input)) return input; + if (isFilePath(input)) { + return input; + } throw new Error(`${input} failed to validate as a FilePath.`); } @@ -56,7 +62,7 @@ export function resolveFilePath(path: string, base = "" as FilePath | Directory) if (isAbsolutePath(path)) { if (path.startsWith("/")) path = path.substring(1); // Because we modified the string since checking absoluteness, we have to assert that it's still absolute here. - return isFilePath(path) ? (path as FilePath) : null; + return isBasicFilePath(path) ? (path as FilePath) : null; } // Turn base into a DirectoryName in case it was not base = getBaseDirectory(base); diff --git a/src/Paths/ProgramFilePath.ts b/src/Paths/ProgramFilePath.ts index 27d224ac1..d1a3c7e18 100644 --- a/src/Paths/ProgramFilePath.ts +++ b/src/Paths/ProgramFilePath.ts @@ -1,5 +1,5 @@ import { Directory, isAbsolutePath } from "./Directory"; -import { FilePath, isFilePath, resolveFilePath } from "./FilePath"; +import { FilePath, isBasicFilePath, resolveFilePath } from "./FilePath"; /** Filepath with the additional constraint of having a .exe extension */ type WithProgramExtension = string & { __fileType: "Program" }; @@ -19,6 +19,6 @@ export function resolveProgramFilePath(path: string, base = "" as FilePath | Dir } export function asProgramFilePath(path: T): T & ProgramFilePath { - if (isFilePath(path) && hasProgramExtension(path) && isAbsolutePath(path)) return path; + if (isBasicFilePath(path) && hasProgramExtension(path) && isAbsolutePath(path)) return path; throw new Error(`${path} failed to validate as a ProgramFilePath.`); } diff --git a/src/Terminal/Terminal.ts b/src/Terminal/Terminal.ts index 609c121b3..c985529fe 100644 --- a/src/Terminal/Terminal.ts +++ b/src/Terminal/Terminal.ts @@ -81,7 +81,7 @@ import { clear } from "./commands/clear"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; import { Engine } from "../engine"; import { Directory, resolveDirectory, root } from "../Paths/Directory"; -import { FilePath, isFilePath, resolveFilePath } from "../Paths/FilePath"; +import { FilePath, isBasicFilePath, resolveFilePath } from "../Paths/FilePath"; import { hasTextExtension } from "../Paths/TextFilePath"; import { ContractFilePath } from "../Paths/ContractFilePath"; import { ServerConstants } from "../Server/data/Constants"; @@ -799,7 +799,7 @@ export class Terminal { const commandName = commandArray[0]; if (typeof commandName !== "string") return this.error(`${commandName} is not a valid command.`); // run by path command - if (isFilePath(commandName)) return run(commandArray, currentServer); + if (isBasicFilePath(commandName)) return run(commandArray, currentServer); // Aside from the run-by-path command, we don't need the first entry once we've stored it in commandName. commandArray.shift(); diff --git a/src/Themes/ui/ThemeCollaborate.tsx b/src/Themes/ui/ThemeCollaborate.tsx index 9d34512a3..4293a06f6 100644 --- a/src/Themes/ui/ThemeCollaborate.tsx +++ b/src/Themes/ui/ThemeCollaborate.tsx @@ -7,7 +7,10 @@ export function ThemeCollaborate(): React.ReactElement { <> If you've created a theme that you believe should be added in game's theme browser, feel free to{" "} - + create a pull request . diff --git a/src/ui/MD/a.tsx b/src/ui/MD/a.tsx index e0a9108a5..651dea48a 100644 --- a/src/ui/MD/a.tsx +++ b/src/ui/MD/a.tsx @@ -9,12 +9,12 @@ export const isSpoiler = (title: string): boolean => title.includes("advanced/") export const A = (props: React.PropsWithChildren<{ href?: string }>): React.ReactElement => { const navigator = useNavigator(); - const ref = props.href ?? ""; + const href = props.href ?? ""; const onClick = (event: React.MouseEvent) => { - navigator.navigate(ref, event.ctrlKey); + navigator.navigate(href, event.ctrlKey); }; - if (ref === externalUrlOfNsApiPage) { + if (href === externalUrlOfNsApiPage) { return ( { @@ -31,7 +31,7 @@ export const A = (props: React.PropsWithChildren<{ href?: string }>): React.Reac ); } - if (isSpoiler(ref)) + if (isSpoiler(href)) { return ( ): React.Reac ); + } return ( {props.children} diff --git a/src/ui/React/Documentation.tsx b/src/ui/React/Documentation.tsx index ff14258b2..42ed2bf88 100644 --- a/src/ui/React/Documentation.tsx +++ b/src/ui/React/Documentation.tsx @@ -28,6 +28,7 @@ export const defaultNsApiPage = asFilePath("nsDoc/bitburner.ns.md"); */ export const externalUrlOfNsApiPage = "https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/bitburner.ns.md"; +export const prefixOfHttpUrlOfNsDocs = "https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/"; const HistoryContext = React.createContext({ page: defaultPage, @@ -92,8 +93,13 @@ export const HistoryProvider = (props: React.PropsWithChildren): React.R export function openDocExternally(path: string) { const ver = CONSTANTS.isDevBranch ? "dev" : "stable"; - const url = path.startsWith("nsDoc/") - ? `https://github.com/bitburner-official/bitburner-src/blob/${ver}/markdown/${path.replace("nsDoc/", "")}` - : `https://github.com/bitburner-official/bitburner-src/blob/${ver}/src/Documentation/doc/${path}`; + let url; + if (path.startsWith("http://") || path.startsWith("https://")) { + url = path; + } else if (path.startsWith("nsDoc/")) { + url = `https://github.com/bitburner-official/bitburner-src/blob/${ver}/markdown/${path.replace("nsDoc/", "")}`; + } else { + url = `https://github.com/bitburner-official/bitburner-src/blob/${ver}/src/Documentation/doc/${path}`; + } window.open(url, "_newtab"); } diff --git a/test/jest/Terminal/Path.test.ts b/test/jest/Terminal/Path.test.ts index d015c2f69..591da5e1a 100644 --- a/test/jest/Terminal/Path.test.ts +++ b/test/jest/Terminal/Path.test.ts @@ -1,6 +1,5 @@ -import { FilePath, isFilePath } from "../../../src/Paths/FilePath"; +import { isBasicFilePath } from "../../../src/Paths/FilePath"; import { - Directory, getFirstDirectoryInPath, isAbsolutePath, isDirectoryPath, @@ -17,7 +16,7 @@ function isValidDirectory(name: string) { return isAbsolutePath(name) && isDirectoryPath(name); } function isValidFilePath(name: string) { - return isAbsolutePath(name) && isFilePath(name); + return isAbsolutePath(name) && isBasicFilePath(name); } describe("Terminal Directory Tests", function () { @@ -42,37 +41,37 @@ describe("Terminal Directory Tests", function () { }); }); - describe("isFilePath()", function () { + describe("isBasicFilePath()", function () { // Actual validation occurs in two steps, validating the filepath structure and then validating that it's not a relative path it("should return true for valid filenames", function () { - expect(isFilePath("test.txt")).toBe(true); - expect(isFilePath("123.script")).toBe(true); - expect(isFilePath("foo123.b")).toBe(true); - expect(isFilePath("my_script.script")).toBe(true); - expect(isFilePath("my-script.script")).toBe(true); - expect(isFilePath("_foo.lit")).toBe(true); - expect(isFilePath("mult.periods.script")).toBe(true); - expect(isFilePath("mult.per-iods.again.script")).toBe(true); - expect(isFilePath("BruteSSH.exe-50%-INC")).toBe(true); - expect(isFilePath("DeepscanV1.exe-1.01%-INC")).toBe(true); - expect(isFilePath("DeepscanV2.exe-1.00%-INC")).toBe(true); - expect(isFilePath("AutoLink.exe-1.%-INC")).toBe(true); + expect(isBasicFilePath("test.txt")).toBe(true); + expect(isBasicFilePath("123.script")).toBe(true); + expect(isBasicFilePath("foo123.b")).toBe(true); + expect(isBasicFilePath("my_script.script")).toBe(true); + expect(isBasicFilePath("my-script.script")).toBe(true); + expect(isBasicFilePath("_foo.lit")).toBe(true); + expect(isBasicFilePath("mult.periods.script")).toBe(true); + expect(isBasicFilePath("mult.per-iods.again.script")).toBe(true); + expect(isBasicFilePath("BruteSSH.exe-50%-INC")).toBe(true); + expect(isBasicFilePath("DeepscanV1.exe-1.01%-INC")).toBe(true); + expect(isBasicFilePath("DeepscanV2.exe-1.00%-INC")).toBe(true); + expect(isBasicFilePath("AutoLink.exe-1.%-INC")).toBe(true); }); it("should return false for invalid filenames", function () { - expect(isFilePath("foo")).toBe(false); - expect(isFilePath("my script.script")).toBe(false); - //expect(isFilePath("a^.txt")).toBe(false); - //expect(isFilePath("b#.lit")).toBe(false); - //expect(isFilePath("lib().js")).toBe(false); - //expect(isFilePath("foo.script_")).toBe(false); - //expect(isFilePath("foo._script")).toBe(false); - //expect(isFilePath("foo.hyphened-ext")).toBe(false); - expect(isFilePath("")).toBe(false); - //expect(isFilePath("AutoLink-1.%-INC.exe")).toBe(false); - //expect(isFilePath("AutoLink.exe-1.%-INC.exe")).toBe(false); - //expect(isFilePath("foo%.exe")).toBe(false); - //expect(isFilePath("-1.00%-INC")).toBe(false); + expect(isBasicFilePath("foo")).toBe(false); + expect(isBasicFilePath("my script.script")).toBe(false); + //expect(isBasicFilePath("a^.txt")).toBe(false); + //expect(isBasicFilePath("b#.lit")).toBe(false); + //expect(isBasicFilePath("lib().js")).toBe(false); + //expect(isBasicFilePath("foo.script_")).toBe(false); + //expect(isBasicFilePath("foo._script")).toBe(false); + //expect(isBasicFilePath("foo.hyphened-ext")).toBe(false); + expect(isBasicFilePath("")).toBe(false); + //expect(isBasicFilePath("AutoLink-1.%-INC.exe")).toBe(false); + //expect(isBasicFilePath("AutoLink.exe-1.%-INC.exe")).toBe(false); + //expect(isBasicFilePath("foo%.exe")).toBe(false); + //expect(isBasicFilePath("-1.00%-INC")).toBe(false); }); }); @@ -185,14 +184,14 @@ describe("Terminal Directory Tests", function () { it("should return true for valid filepaths", function () { // Some of these include relative paths, will not check absoluteness - expect(isFilePath("foo/test.txt")).toBe(true); - expect(isFilePath("../123.script")).toBe(true); - expect(isFilePath("./foo123.b")).toBe(true); - expect(isFilePath("dir/my_script.script")).toBe(true); - expect(isFilePath("dir1/dir2/dir3/my-script.script")).toBe(true); - expect(isFilePath("dir1/dir2/././../_foo.lit")).toBe(true); - expect(isFilePath(".dir1/./../.dir2/mult.periods.script")).toBe(true); - expect(isFilePath("_dir/../dir2/mult.per-iods.again.script")).toBe(true); + expect(isBasicFilePath("foo/test.txt")).toBe(true); + expect(isBasicFilePath("../123.script")).toBe(true); + expect(isBasicFilePath("./foo123.b")).toBe(true); + expect(isBasicFilePath("dir/my_script.script")).toBe(true); + expect(isBasicFilePath("dir1/dir2/dir3/my-script.script")).toBe(true); + expect(isBasicFilePath("dir1/dir2/././../_foo.lit")).toBe(true); + expect(isBasicFilePath(".dir1/./../.dir2/mult.periods.script")).toBe(true); + expect(isBasicFilePath("_dir/../dir2/mult.per-iods.again.script")).toBe(true); }); it("should return false for strings that begin with a slash", function () { @@ -214,7 +213,7 @@ describe("Terminal Directory Tests", function () { // Strings cannot be passed in directly, so we'll wrap some typechecking function firstDirectory(path: string): string | null | undefined { if (!isAbsolutePath(path)) return undefined; - if (!isFilePath(path) && !isDirectoryPath(path)) return undefined; + if (!isBasicFilePath(path) && !isDirectoryPath(path)) return undefined; return getFirstDirectoryInPath(path); }