BUGFIX: fix relative imports (#1305)

Relative paths work in imports, at last.
This commit is contained in:
Caldwell 2024-06-03 02:38:01 +02:00 committed by GitHub
parent 76ce2f9955
commit f40d4f8e92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 219 additions and 51 deletions

@ -8,7 +8,6 @@ import { parse } from "acorn";
import { LoadedModule, ScriptURL, ScriptModule } from "./Script/LoadedModule"; import { LoadedModule, ScriptURL, ScriptModule } from "./Script/LoadedModule";
import { Script } from "./Script/Script"; import { Script } from "./Script/Script";
import { ScriptFilePath, resolveScriptFilePath } from "./Paths/ScriptFilePath"; import { ScriptFilePath, resolveScriptFilePath } from "./Paths/ScriptFilePath";
import { root } from "./Paths/Directory";
// Acorn type def is straight up incomplete so we have to fill with our own. // Acorn type def is straight up incomplete so we have to fill with our own.
export type Node = any; export type Node = any;
@ -125,7 +124,7 @@ function generateLoadedModule(script: Script, scripts: Map<ScriptFilePath, Scrip
let newCode = script.code; let newCode = script.code;
// Loop through each node and replace the script name with a blob url. // Loop through each node and replace the script name with a blob url.
for (const node of importNodes) { for (const node of importNodes) {
const filename = resolveScriptFilePath(node.filename, root, ".js"); const filename = resolveScriptFilePath(node.filename, script.filename, ".js");
if (!filename) throw new Error(`Failed to parse import: ${node.filename}`); if (!filename) throw new Error(`Failed to parse import: ${node.filename}`);
// Find the corresponding script. // Find the corresponding script.
@ -149,6 +148,7 @@ function generateLoadedModule(script: Script, scripts: Map<ScriptFilePath, Scrip
// script dedupe properly. // script dedupe properly.
const adjustedCode = newCode + `\n//# sourceURL=${script.server}/${script.filename}`; const adjustedCode = newCode + `\n//# sourceURL=${script.server}/${script.filename}`;
// At this point we have the full code and can construct a new blob / assign the URL. // At this point we have the full code and can construct a new blob / assign the URL.
const url = URL.createObjectURL(makeScriptBlob(adjustedCode)) as ScriptURL; const url = URL.createObjectURL(makeScriptBlob(adjustedCode)) as ScriptURL;
const module = config.doImport(url).catch((e) => { const module = config.doImport(url).catch((e) => {
script.invalidateModule(); script.invalidateModule();

@ -14,7 +14,7 @@ import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator";
import { Script } from "./Script"; import { Script } from "./Script";
import { Node } from "../NetscriptJSEvaluator"; import { Node } from "../NetscriptJSEvaluator";
import { ScriptFilePath, resolveScriptFilePath } from "../Paths/ScriptFilePath"; import { ScriptFilePath, resolveScriptFilePath } from "../Paths/ScriptFilePath";
import { root } from "../Paths/Directory"; import { ServerName } from "../Types/strings";
export interface RamUsageEntry { export interface RamUsageEntry {
type: "ns" | "dom" | "fn" | "misc"; type: "ns" | "dom" | "fn" | "misc";
@ -56,12 +56,21 @@ function getNumericCost(cost: number | (() => number)): number {
* Parses code into an AST and walks through it recursively to calculate * Parses code into an AST and walks through it recursively to calculate
* RAM usage. Also accounts for imported modules. * RAM usage. Also accounts for imported modules.
* @param otherScripts - All other scripts on the server. Used to account for imported scripts * @param otherScripts - All other scripts on the server. Used to account for imported scripts
* @param code - The code being parsed */ * @param code - The code being parsed
function parseOnlyRamCalculate(otherScripts: Map<ScriptFilePath, Script>, code: string, ns1?: boolean): RamCalculation { * @param scriptname - The name of the script that ram needs to be added to
* @param server - Servername of the scripts for Error Message
* */
function parseOnlyRamCalculate(
otherScripts: Map<ScriptFilePath, Script>,
code: string,
scriptname: ScriptFilePath,
server: ServerName,
ns1?: boolean,
): RamCalculation {
/** /**
* Maps dependent identifiers to their dependencies. * Maps dependent identifiers to their dependencies.
* *
* The initial identifier is __SPECIAL_INITIAL_MODULE__.__GLOBAL__. * The initial identifier is <name of the main script>.__GLOBAL__.
* It depends on all the functions declared in the module, all the global scopes * It depends on all the functions declared in the module, all the global scopes
* of its imports, and any identifiers referenced in this global scope. Each * of its imports, and any identifiers referenced in this global scope. Each
* function depends on all the identifiers referenced internally. * function depends on all the identifiers referenced internally.
@ -74,10 +83,10 @@ function parseOnlyRamCalculate(otherScripts: Map<ScriptFilePath, Script>, code:
const completedParses = new Set(); const completedParses = new Set();
// Scripts we've discovered that need to be parsed. // Scripts we've discovered that need to be parsed.
const parseQueue: string[] = []; const parseQueue: ScriptFilePath[] = [];
// Parses a chunk of code with a given module name, and updates parseQueue and dependencyMap. // Parses a chunk of code with a given module name, and updates parseQueue and dependencyMap.
function parseCode(code: string, moduleName: string): void { function parseCode(code: string, moduleName: ScriptFilePath): void {
const result = parseOnlyCalculateDeps(code, moduleName); const result = parseOnlyCalculateDeps(code, moduleName, ns1);
completedParses.add(moduleName); completedParses.add(moduleName);
// Add any additional modules to the parse queue; // Add any additional modules to the parse queue;
@ -92,7 +101,7 @@ function parseOnlyRamCalculate(otherScripts: Map<ScriptFilePath, Script>, code:
} }
// Parse the initial module, which is the "main" script that is being run // Parse the initial module, which is the "main" script that is being run
const initialModule = "__SPECIAL_INITIAL_MODULE__"; const initialModule = scriptname;
parseCode(code, initialModule); parseCode(code, initialModule);
// Process additional modules, which occurs if the "main" script has any imports // Process additional modules, which occurs if the "main" script has any imports
@ -101,21 +110,19 @@ function parseOnlyRamCalculate(otherScripts: Map<ScriptFilePath, Script>, code:
if (nextModule === undefined) throw new Error("nextModule should not be undefined"); if (nextModule === undefined) throw new Error("nextModule should not be undefined");
if (nextModule.startsWith("https://") || nextModule.startsWith("http://")) continue; if (nextModule.startsWith("https://") || nextModule.startsWith("http://")) continue;
// Using root as the path base right now. Difficult to implement const script = otherScripts.get(nextModule);
const filename = resolveScriptFilePath(nextModule, root, ns1 ? ".script" : ".js");
if (!filename) {
return { errorCode: RamCalculationErrorCode.ImportError, errorMessage: `Invalid import path: "${nextModule}"` };
}
const script = otherScripts.get(filename);
if (!script) { if (!script) {
return { errorCode: RamCalculationErrorCode.ImportError, errorMessage: `No such file on server: "${filename}"` }; return {
errorCode: RamCalculationErrorCode.ImportError,
errorMessage: `File: "${nextModule}" not found on server: ${server}`,
};
} }
parseCode(script.code, nextModule); parseCode(script.code, nextModule);
} }
// Finally, walk the reference map and generate a ram cost. The initial set of keys to scan // Finally, walk the reference map and generate a ram cost. The initial set of keys to scan
// are those that start with __SPECIAL_INITIAL_MODULE__. // are those that start with the name of the main script.
let ram = RamCostConstants.Base; let ram = RamCostConstants.Base;
const detailedCosts: RamUsageEntry[] = [{ type: "misc", name: "baseCost", cost: RamCostConstants.Base }]; const detailedCosts: RamUsageEntry[] = [{ type: "misc", name: "baseCost", cost: RamCostConstants.Base }];
const unresolvedRefs = Object.keys(dependencyMap).filter((s) => s.startsWith(initialModule)); const unresolvedRefs = Object.keys(dependencyMap).filter((s) => s.startsWith(initialModule));
@ -250,7 +257,7 @@ export function checkInfiniteLoop(code: string): number[] {
interface ParseDepsResult { interface ParseDepsResult {
dependencyMap: Record<string, Set<string> | undefined>; dependencyMap: Record<string, Set<string> | undefined>;
additionalModules: string[]; additionalModules: ScriptFilePath[];
} }
/** /**
@ -259,7 +266,7 @@ interface ParseDepsResult {
* for RAM usage calculations. It also returns an array of additional modules * for RAM usage calculations. It also returns an array of additional modules
* that need to be parsed (i.e. are 'import'ed scripts). * that need to be parsed (i.e. are 'import'ed scripts).
*/ */
function parseOnlyCalculateDeps(code: string, currentModule: string): ParseDepsResult { function parseOnlyCalculateDeps(code: string, currentModule: ScriptFilePath, ns1?: boolean): ParseDepsResult {
const ast = parse(code, { sourceType: "module", ecmaVersion: "latest" }); const ast = parse(code, { sourceType: "module", ecmaVersion: "latest" });
// Everything from the global scope goes in ".". Everything else goes in ".function", where only // Everything from the global scope goes in ".". Everything else goes in ".function", where only
// the outermost layer of functions counts. // the outermost layer of functions counts.
@ -271,7 +278,7 @@ function parseOnlyCalculateDeps(code: string, currentModule: string): ParseDepsR
// Filled when we import names from other modules. // Filled when we import names from other modules.
const internalToExternal: Record<string, string | undefined> = {}; const internalToExternal: Record<string, string | undefined> = {};
const additionalModules: string[] = []; const additionalModules: ScriptFilePath[] = [];
// References get added pessimistically. They are added for thisModule.name, name, and for // References get added pessimistically. They are added for thisModule.name, name, and for
// any aliases. // any aliases.
@ -338,7 +345,12 @@ function parseOnlyCalculateDeps(code: string, currentModule: string): ParseDepsR
Object.assign( Object.assign(
{ {
ImportDeclaration: (node: Node, st: State) => { ImportDeclaration: (node: Node, st: State) => {
const importModuleName = node.source.value; const importModuleName = resolveScriptFilePath(node.source.value, currentModule, ns1 ? ".script" : ".js");
if (!importModuleName)
throw new Error(
`ScriptFilePath couldnt be resolved in ImportDeclaration. Value: ${node.source.value} ScriptFilePath: ${currentModule}`,
);
additionalModules.push(importModuleName); additionalModules.push(importModuleName);
// This module's global scope refers to that module's global scope, no matter how we // This module's global scope refers to that module's global scope, no matter how we
@ -397,16 +409,21 @@ function parseOnlyCalculateDeps(code: string, currentModule: string): ParseDepsR
/** /**
* Calculate's a scripts RAM Usage * Calculate's a scripts RAM Usage
* @param {string} code - The script's code * @param {string} code - The script's code
* @param {ScriptFilePath} scriptname - The script's name. Used to resolve relative paths
* @param {Script[]} otherScripts - All other scripts on the server. * @param {Script[]} otherScripts - All other scripts on the server.
* Used to account for imported scripts * Used to account for imported scripts
* @param {ServerName} server - Servername of the scripts for Error Message
* @param {boolean} ns1 - Deprecated: is the fileExtension .script or .js
*/ */
export function calculateRamUsage( export function calculateRamUsage(
code: string, code: string,
scriptname: ScriptFilePath,
otherScripts: Map<ScriptFilePath, Script>, otherScripts: Map<ScriptFilePath, Script>,
server: ServerName,
ns1?: boolean, ns1?: boolean,
): RamCalculation { ): RamCalculation {
try { try {
return parseOnlyRamCalculate(otherScripts, code, ns1); return parseOnlyRamCalculate(otherScripts, code, scriptname, server, ns1);
} catch (e) { } catch (e) {
return { return {
errorCode: RamCalculationErrorCode.SyntaxError, errorCode: RamCalculationErrorCode.SyntaxError,

@ -73,7 +73,13 @@ export class Script implements ContentFile {
* @param {Script[]} otherScripts - Other scripts on the server. Used to process imports * @param {Script[]} otherScripts - Other scripts on the server. Used to process imports
*/ */
updateRamUsage(otherScripts: Map<ScriptFilePath, Script>): void { updateRamUsage(otherScripts: Map<ScriptFilePath, Script>): void {
const ramCalc = calculateRamUsage(this.code, otherScripts, this.filename.endsWith(".script")); const ramCalc = calculateRamUsage(
this.code,
this.filename,
otherScripts,
this.server,
this.filename.endsWith(".script"),
);
if (ramCalc.cost && ramCalc.cost >= RamCostConstants.Base) { if (ramCalc.cost && ramCalc.cost >= RamCostConstants.Base) {
this.ramUsage = roundToTwo(ramCalc.cost); this.ramUsage = roundToTwo(ramCalc.cost);
this.ramUsageEntries = ramCalc.entries as RamUsageEntry[]; this.ramUsageEntries = ramCalc.entries as RamUsageEntry[];

@ -8,11 +8,13 @@ import { useBoolean } from "../../ui/React/hooks";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { Options } from "./Options"; import { Options } from "./Options";
import { FilePath } from "../../Paths/FilePath";
import { hasScriptExtension } from "../../Paths/ScriptFilePath";
export interface ScriptEditorContextShape { export interface ScriptEditorContextShape {
ram: string; ram: string;
ramEntries: string[][]; ramEntries: string[][];
updateRAM: (newCode: string | null, server: BaseServer | null) => void; updateRAM: (newCode: string | null, filename: FilePath | null, server: BaseServer | null) => void;
isUpdatingRAM: boolean; isUpdatingRAM: boolean;
startUpdatingRAM: () => void; startUpdatingRAM: () => void;
@ -28,14 +30,13 @@ export function ScriptEditorContextProvider({ children, vim }: { children: React
const [ram, setRAM] = useState("RAM: ???"); const [ram, setRAM] = useState("RAM: ???");
const [ramEntries, setRamEntries] = useState<string[][]>([["???", ""]]); const [ramEntries, setRamEntries] = useState<string[][]>([["???", ""]]);
const updateRAM: ScriptEditorContextShape["updateRAM"] = (newCode, server) => { const updateRAM: ScriptEditorContextShape["updateRAM"] = (newCode, filename, server) => {
if (newCode === null || server === null) { if (newCode == null || filename == null || server == null || !hasScriptExtension(filename)) {
setRAM("N/A"); setRAM("N/A");
setRamEntries([["N/A", ""]]); setRamEntries([["N/A", ""]]);
return; return;
} }
const ramUsage = calculateRamUsage(newCode, filename, server.scripts, server.hostname);
const ramUsage = calculateRamUsage(newCode, server.scripts);
if (ramUsage.cost && ramUsage.cost > 0) { if (ramUsage.cost && ramUsage.cost > 0) {
const entries = ramUsage.entries?.sort((a, b) => b.cost - a.cost) ?? []; const entries = ramUsage.entries?.sort((a, b) => b.cost - a.cost) ?? [];
const entriesDisp = []; const entriesDisp = [];

@ -143,6 +143,7 @@ function Root(props: IProps): React.ReactElement {
infLoop(newCode); infLoop(newCode);
updateRAM( updateRAM(
!currentScript || currentScript.isTxt ? null : newCode, !currentScript || currentScript.isTxt ? null : newCode,
currentScript && currentScript.path,
currentScript && GetServer(currentScript.hostname), currentScript && GetServer(currentScript.hostname),
); );
finishUpdatingRAM(); finishUpdatingRAM();

@ -9,6 +9,7 @@ import { calculateRamUsage } from "../../../src/Script/RamCalculations";
import { ns } from "../../../src/NetscriptFunctions"; import { ns } from "../../../src/NetscriptFunctions";
import { InternalAPI } from "src/Netscript/APIWrapper"; import { InternalAPI } from "src/Netscript/APIWrapper";
import { Singularity } from "@nsdefs"; import { Singularity } from "@nsdefs";
import { ScriptFilePath } from "src/Paths/ScriptFilePath";
type PotentiallyAsyncFunction = (arg?: unknown) => { catch?: PotentiallyAsyncFunction }; type PotentiallyAsyncFunction = (arg?: unknown) => { catch?: PotentiallyAsyncFunction };
@ -71,13 +72,15 @@ describe("Netscript RAM Calculation/Generation Tests", function () {
extraLayerCost = 0, extraLayerCost = 0,
) { ) {
const code = `${fnPath.join(".")}();\n`.repeat(3); const code = `${fnPath.join(".")}();\n`.repeat(3);
const filename = "testfile.js" as ScriptFilePath;
const fnName = fnPath[fnPath.length - 1]; const fnName = fnPath[fnPath.length - 1];
const server = "testserver";
//check imported getRamCost fn vs. expected ram from test //check imported getRamCost fn vs. expected ram from test
expect(getRamCost(fnPath, true)).toEqual(expectedRamCost); expect(getRamCost(fnPath, true)).toEqual(expectedRamCost);
// Static ram check // Static ram check
const staticCost = calculateRamUsage(code, new Map()).cost; const staticCost = calculateRamUsage(code, filename, new Map(), server).cost;
expect(staticCost).toBeCloseTo(Math.min(baseCost + expectedRamCost + extraLayerCost, maxCost)); expect(staticCost).toBeCloseTo(Math.min(baseCost + expectedRamCost + extraLayerCost, maxCost));
// reset workerScript for dynamic check // reset workerScript for dynamic check

@ -10,6 +10,10 @@ const GrowCost = 0.15;
const SleeveGetTaskCost = 4; const SleeveGetTaskCost = 4;
const HacknetCost = 4; const HacknetCost = 4;
const MaxCost = 1024; const MaxCost = 1024;
const filename = "testfile.js" as ScriptFilePath;
const folderFilename = "test/testfile.js" as ScriptFilePath;
const server = "testserver";
describe("Parsing NetScript code to work out static RAM costs", function () { describe("Parsing NetScript code to work out static RAM costs", function () {
jest.spyOn(console, "error").mockImplementation(() => {}); jest.spyOn(console, "error").mockImplementation(() => {});
/** Tests numeric equality, allowing for floating point imprecision - and includes script base cost */ /** Tests numeric equality, allowing for floating point imprecision - and includes script base cost */
@ -24,7 +28,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
const code = ` const code = `
export async function main(ns) { } export async function main(ns) { }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, 0); expectCost(calculated, 0);
}); });
@ -34,7 +38,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
ns.print("Slum snakes r00l!"); ns.print("Slum snakes r00l!");
} }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, 0); expectCost(calculated, 0);
}); });
@ -44,7 +48,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
await ns.hack("joesguns"); await ns.hack("joesguns");
} }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, HackCost); expectCost(calculated, HackCost);
}); });
@ -54,7 +58,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
await X.hack("joesguns"); await X.hack("joesguns");
} }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, HackCost); expectCost(calculated, HackCost);
}); });
@ -65,7 +69,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
await ns.hack("joesguns"); await ns.hack("joesguns");
} }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, HackCost); expectCost(calculated, HackCost);
}); });
@ -76,7 +80,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
await ns.grow("joesguns"); await ns.grow("joesguns");
} }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, HackCost + GrowCost); expectCost(calculated, HackCost + GrowCost);
}); });
@ -89,7 +93,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
await ns.hack("joesguns"); await ns.hack("joesguns");
} }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, HackCost); expectCost(calculated, HackCost);
}); });
@ -104,7 +108,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
async doHacking() { await this.ns.hack("joesguns"); } async doHacking() { await this.ns.hack("joesguns"); }
} }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, HackCost); expectCost(calculated, HackCost);
}); });
@ -119,7 +123,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
async doHacking() { await this.#ns.hack("joesguns"); } async doHacking() { await this.#ns.hack("joesguns"); }
} }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, HackCost); expectCost(calculated, HackCost);
}); });
}); });
@ -132,7 +136,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
} }
function get() { return 0; } function get() { return 0; }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, 0); expectCost(calculated, 0);
}); });
@ -143,7 +147,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
} }
function purchaseNode() { return 0; } function purchaseNode() { return 0; }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
// Works at present, because the parser checks the namespace only, not the function name // Works at present, because the parser checks the namespace only, not the function name
expectCost(calculated, 0); expectCost(calculated, 0);
}); });
@ -156,7 +160,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
} }
function getTask() { return 0; } function getTask() { return 0; }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, 0); expectCost(calculated, 0);
}); });
}); });
@ -168,7 +172,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
ns.hacknet.purchaseNode(0); ns.hacknet.purchaseNode(0);
} }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, HacknetCost); expectCost(calculated, HacknetCost);
}); });
@ -178,7 +182,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
ns.sleeve.getTask(3); ns.sleeve.getTask(3);
} }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, SleeveGetTaskCost); expectCost(calculated, SleeveGetTaskCost);
}); });
}); });
@ -196,7 +200,12 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
dummy(); dummy();
} }
`; `;
const calculated = calculateRamUsage(code, new Map([["libTest.js" as ScriptFilePath, lib]])).cost; const calculated = calculateRamUsage(
code,
filename,
new Map([["libTest.js" as ScriptFilePath, lib]]),
server,
).cost;
expectCost(calculated, 0); expectCost(calculated, 0);
}); });
@ -212,7 +221,12 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
await doHack(ns); await doHack(ns);
} }
`; `;
const calculated = calculateRamUsage(code, new Map([["libTest.js" as ScriptFilePath, lib]])).cost; const calculated = calculateRamUsage(
code,
filename,
new Map([["libTest.js" as ScriptFilePath, lib]]),
server,
).cost;
expectCost(calculated, HackCost); expectCost(calculated, HackCost);
}); });
@ -229,7 +243,12 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
await doHack(ns); await doHack(ns);
} }
`; `;
const calculated = calculateRamUsage(code, new Map([["libTest.js" as ScriptFilePath, lib]])).cost; const calculated = calculateRamUsage(
code,
filename,
new Map([["libTest.js" as ScriptFilePath, lib]]),
server,
).cost;
expectCost(calculated, HackCost); expectCost(calculated, HackCost);
}); });
@ -246,7 +265,12 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
await test.doHack(ns); await test.doHack(ns);
} }
`; `;
const calculated = calculateRamUsage(code, new Map([["libTest.js" as ScriptFilePath, lib]])).cost; const calculated = calculateRamUsage(
code,
filename,
new Map([["libTest.js" as ScriptFilePath, lib]]),
server,
).cost;
expectCost(calculated, HackCost + GrowCost); expectCost(calculated, HackCost + GrowCost);
}); });
@ -267,7 +291,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
${lines.join("\n")}; ${lines.join("\n")};
} }
`; `;
const calculated = calculateRamUsage(code, new Map()).cost; const calculated = calculateRamUsage(code, filename, new Map(), server).cost;
expectCost(calculated, MaxCost); expectCost(calculated, MaxCost);
}); });
@ -289,7 +313,12 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
await test.doHack(ns); await test.doHack(ns);
} }
`; `;
const calculated = calculateRamUsage(code, new Map([["libTest.js" as ScriptFilePath, lib]])).cost; const calculated = calculateRamUsage(
code,
filename,
new Map([["libTest.js" as ScriptFilePath, lib]]),
server,
).cost;
expectCost(calculated, HackCost); expectCost(calculated, HackCost);
}); });
@ -315,8 +344,119 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
await growerInstance.doGrow(); await growerInstance.doGrow();
} }
`; `;
const calculated = calculateRamUsage(code, new Map([["libTest.js" as ScriptFilePath, lib]])).cost; const calculated = calculateRamUsage(
code,
filename,
new Map([["libTest.js" as ScriptFilePath, lib]]),
server,
).cost;
expectCost(calculated, GrowCost); expectCost(calculated, GrowCost);
}); });
it("Importing with a relative path - One Layer Deep", async function () {
const libCode = `
export async function testRelative(ns) {
await ns.hack("n00dles")
}
`;
const lib = new Script("test/libTest.js" as ScriptFilePath, libCode);
const code = `
import { testRelative } from "./libTest";
export async function main(ns) {
await testRelative(ns)
}
`;
const calculated = calculateRamUsage(
code,
folderFilename,
new Map([["test/libTest.js" as ScriptFilePath, lib]]),
server,
).cost;
expectCost(calculated, HackCost);
});
it("Importing with a relative path - Two Layer Deep", async function () {
const libNameOne = "test/libTestOne.js" as ScriptFilePath;
const libNameTwo = "test/libTestTwo.js" as ScriptFilePath;
const libCodeOne = `
import { testRelativeAgain } from "./libTestTwo";
export function testRelative(ns) {
return testRelativeAgain(ns)
}
`;
const libScriptOne = new Script(libNameOne, libCodeOne);
const libCodeTwo = `
export function testRelativeAgain(ns) {
return ns.hack("n00dles")
}
`;
const libScriptTwo = new Script(libNameTwo, libCodeTwo);
const code = `
import { testRelative } from "./libTestOne";
export async function main(ns) {
await testRelative(ns)
}
`;
const calculated = calculateRamUsage(
code,
folderFilename,
new Map([
[libNameOne, libScriptOne],
[libNameTwo, libScriptTwo],
]),
server,
).cost;
expectCost(calculated, HackCost);
});
it("Importing with a relative path - possible path conflict", async function () {
const libNameOne = "foo/libTestOne.js" as ScriptFilePath;
const libNameTwo = "foo/libTestTwo.js" as ScriptFilePath;
const incorrect_libNameTwo = "test/libTestTwo.js" as ScriptFilePath;
const libCodeOne = `
import { testRelativeAgain } from "./libTestTwo";
export function testRelative(ns) {
return testRelativeAgain(ns)
}
`;
const libScriptOne = new Script(libNameOne, libCodeOne);
const libCodeTwo = `
export function testRelativeAgain(ns) {
return ns.hack("n00dles")
}
`;
const libScriptTwo = new Script(libNameTwo, libCodeTwo);
const incorrect_libCodeTwo = `
export function testRelativeAgain(ns) {
return ns.grow("n00dles")
}
`;
const incorrect_libScriptTwo = new Script(incorrect_libNameTwo, incorrect_libCodeTwo);
const code = `
import { testRelative } from "foo/libTestOne";
export async function main(ns) {
await testRelative(ns)
}
`;
const calculated = calculateRamUsage(
code,
folderFilename,
new Map([
[libNameOne, libScriptOne],
[libNameTwo, libScriptTwo],
[incorrect_libNameTwo, incorrect_libScriptTwo],
]),
server,
).cost;
expectCost(calculated, HackCost);
});
}); });
}); });