@isentinel/jest-roblox 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,17 +1,9 @@
1
- import { a as Argv, i as ResolvedConfig, n as Config, r as DEFAULT_CONFIG, t as CliOptions } from "./schema-hhbEFVXy.mjs";
2
- import { WebSocketServer } from "ws";
1
+ import { a as SnapshotFormatOptions, i as ResolvedConfig, n as Config, o as Argv, r as DEFAULT_CONFIG, t as CliOptions } from "./schema-ua9HqdbX.mjs";
2
+ import { WebSocket, WebSocketServer } from "ws";
3
3
  import buffer from "node:buffer";
4
4
 
5
5
  //#region src/types/jest-result.d.ts
6
- interface JestResult {
7
- numFailedTests: number;
8
- numPassedTests: number;
9
- numPendingTests: number;
10
- numTotalTests: number;
11
- startTime: number;
12
- success: boolean;
13
- testResults: Array<TestFileResult>;
14
- }
6
+ type TestStatus = "failed" | "passed" | "pending" | "skipped";
15
7
  interface TestCaseResult {
16
8
  ancestorTitles: Array<string>;
17
9
  duration?: number;
@@ -21,32 +13,51 @@ interface TestCaseResult {
21
13
  title: string;
22
14
  }
23
15
  interface TestFileResult {
16
+ failureMessage?: string;
24
17
  numFailingTests: number;
25
18
  numPassingTests: number;
26
19
  numPendingTests: number;
27
20
  testFilePath: string;
28
21
  testResults: Array<TestCaseResult>;
29
22
  }
30
- type TestStatus = "failed" | "passed" | "pending" | "skipped";
23
+ interface SnapshotSummary {
24
+ added: number;
25
+ matched: number;
26
+ total: number;
27
+ unmatched: number;
28
+ updated: number;
29
+ }
30
+ interface JestResult {
31
+ numFailedTests: number;
32
+ numPassedTests: number;
33
+ numPendingTests: number;
34
+ numTotalTests: number;
35
+ snapshot?: SnapshotSummary;
36
+ startTime: number;
37
+ success: boolean;
38
+ testResults: Array<TestFileResult>;
39
+ }
31
40
  //#endregion
32
41
  //#region src/reporter/parser.d.ts
42
+ type SnapshotWrites = Record<string, string>;
33
43
  interface ParseResult {
34
44
  luauTiming?: Record<string, number>;
35
45
  result: JestResult;
36
46
  snapshotWrites?: SnapshotWrites;
37
47
  }
38
- type SnapshotWrites = Record<string, string>;
39
48
  declare function extractJsonFromOutput(output: string): string | undefined;
40
49
  declare function parseJestOutput(output: string): ParseResult;
41
50
  //#endregion
42
51
  //#region src/backends/interface.d.ts
43
- interface Backend {
44
- runTests(options: BackendOptions): Promise<BackendResult>;
45
- }
46
52
  interface BackendOptions {
47
53
  config: ResolvedConfig;
48
54
  testFiles: Array<string>;
49
55
  }
56
+ interface BackendTiming {
57
+ executionMs: number;
58
+ uploadCached?: boolean;
59
+ uploadMs?: number;
60
+ }
50
61
  interface BackendResult {
51
62
  gameOutput?: string;
52
63
  luauTiming?: Record<string, number>;
@@ -54,16 +65,11 @@ interface BackendResult {
54
65
  snapshotWrites?: SnapshotWrites;
55
66
  timing: BackendTiming;
56
67
  }
57
- interface BackendTiming {
58
- executionMs: number;
59
- uploadCached?: boolean;
60
- uploadMs?: number;
68
+ interface Backend {
69
+ runTests(options: BackendOptions): Promise<BackendResult>;
61
70
  }
62
71
  //#endregion
63
72
  //#region src/backends/http-client.d.ts
64
- interface HttpClient {
65
- request(method: string, url: string, options?: RequestOptions): Promise<HttpResponse>;
66
- }
67
73
  interface HttpResponse {
68
74
  body: unknown;
69
75
  headers?: Record<string, string | undefined>;
@@ -74,6 +80,9 @@ interface RequestOptions {
74
80
  body?: unknown;
75
81
  headers?: Record<string, string>;
76
82
  }
83
+ interface HttpClient {
84
+ request(method: string, url: string, options?: RequestOptions): Promise<HttpResponse>;
85
+ }
77
86
  //#endregion
78
87
  //#region src/backends/open-cloud.d.ts
79
88
  interface OpenCloudCredentials {
@@ -101,15 +110,21 @@ declare class OpenCloudBackend implements Backend {
101
110
  declare function createOpenCloudBackend(): OpenCloudBackend;
102
111
  //#endregion
103
112
  //#region src/backends/studio.d.ts
113
+ interface PreConnected {
114
+ server: WebSocketServer;
115
+ socket: WebSocket;
116
+ }
104
117
  interface StudioOptions {
105
118
  createServer?: (port: number) => WebSocketServer;
106
119
  port: number;
120
+ preConnected?: PreConnected;
107
121
  timeout?: number;
108
122
  }
109
123
  declare class StudioBackend implements Backend {
110
124
  private readonly createServer;
111
125
  private readonly port;
112
126
  private readonly timeout;
127
+ private preConnected?;
113
128
  constructor(options: StudioOptions);
114
129
  runTests(options: BackendOptions): Promise<BackendResult>;
115
130
  private executeViaPlugin;
@@ -118,8 +133,8 @@ declare class StudioBackend implements Backend {
118
133
  declare function createStudioBackend(options: StudioOptions): StudioBackend;
119
134
  //#endregion
120
135
  //#region src/config/loader.d.ts
121
- declare function loadConfig(configPath?: string, cwd?: string): Promise<ResolvedConfig>;
122
136
  declare function resolveConfig(config: Config): ResolvedConfig;
137
+ declare function loadConfig(configPath?: string, cwd?: string): Promise<ResolvedConfig>;
123
138
  //#endregion
124
139
  //#region src/executor.d.ts
125
140
  interface ExecuteOptions {
@@ -137,20 +152,22 @@ interface ExecuteResult {
137
152
  declare function execute(options: ExecuteOptions): Promise<ExecuteResult>;
138
153
  //#endregion
139
154
  //#region src/source-mapper/index.d.ts
140
- interface MappedFailure {
141
- locations: Array<MappedLocation>;
142
- message: string;
143
- }
144
155
  interface MappedLocation {
145
156
  luauLine: number;
146
157
  luauPath: string;
158
+ sourceContent?: string;
147
159
  tsColumn?: number;
148
160
  tsLine: number;
149
161
  tsPath: string;
150
162
  }
163
+ interface MappedFailure {
164
+ locations: Array<MappedLocation>;
165
+ message: string;
166
+ }
151
167
  interface SourceMapper {
152
168
  mapFailureMessage(message: string): string;
153
169
  mapFailureWithLocations(message: string): MappedFailure;
170
+ resolveTestFilePath(testFilePath: string): string | undefined;
154
171
  }
155
172
  //#endregion
156
173
  //#region src/types/timing.d.ts
@@ -166,6 +183,8 @@ interface TimingResult {
166
183
  //#region src/formatters/formatter.d.ts
167
184
  interface FormatOptions {
168
185
  color: boolean;
186
+ gameOutput?: string;
187
+ outputFile?: string;
169
188
  rootDir: string;
170
189
  showLuau?: boolean;
171
190
  sourceMapper?: SourceMapper;
@@ -217,8 +236,8 @@ declare function formatFailure({
217
236
  totalFailures?: number;
218
237
  useColor?: boolean;
219
238
  }): string;
220
- declare function formatResult(result: JestResult, timing: TimingResult, options: FormatOptions): string;
221
239
  declare function formatTestSummary(result: JestResult, timing: TimingResult, styles?: Styles): string;
240
+ declare function formatResult(result: JestResult, timing: TimingResult, options: FormatOptions): string;
222
241
  //#endregion
223
242
  //#region src/formatters/json.d.ts
224
243
  declare function formatJson(result: JestResult): string;
@@ -226,11 +245,36 @@ declare function writeJsonFile(result: JestResult, filePath: string): Promise<vo
226
245
  //#endregion
227
246
  //#region src/test-script.d.ts
228
247
  type JestArgv = Argv & {
248
+ snapshotFormat?: SnapshotFormatOptions;
229
249
  testMatch: Array<string>;
230
250
  };
231
251
  declare function buildJestArgv(options: BackendOptions): JestArgv;
232
252
  declare function generateTestScript(options: BackendOptions): string;
233
253
  //#endregion
254
+ //#region src/typecheck/types.d.ts
255
+ interface TscErrorInfo {
256
+ column: number;
257
+ errCode: number;
258
+ errMsg: string;
259
+ filePath: string;
260
+ line: number;
261
+ }
262
+ interface TestDefinition {
263
+ name: string;
264
+ ancestorNames: Array<string>;
265
+ end: number;
266
+ start: number;
267
+ type: "suite" | "test";
268
+ }
269
+ //#endregion
270
+ //#region src/typecheck/runner.d.ts
271
+ interface TypecheckOptions {
272
+ files: Array<string>;
273
+ rootDir: string;
274
+ tsconfig?: string;
275
+ }
276
+ declare function runTypecheck(options: TypecheckOptions): JestResult;
277
+ //#endregion
234
278
  //#region src/types/game-output.d.ts
235
279
  interface GameOutputEntry {
236
280
  message: string;
@@ -243,4 +287,4 @@ declare function formatGameOutputNotice(filePath: string, entryCount: number): s
243
287
  declare function parseGameOutput(raw: string | undefined): Array<GameOutputEntry>;
244
288
  declare function writeGameOutput(filePath: string, entries: Array<GameOutputEntry>): void;
245
289
  //#endregion
246
- export { type Backend, type BackendOptions, type CliOptions, type Config, DEFAULT_CONFIG, type ExecuteOptions, type ExecuteResult, type GameOutputEntry, type JestArgv, type JestResult, OpenCloudBackend, type ResolvedConfig, StudioBackend, type TestCaseResult, type TestFileResult, type TestStatus, buildJestArgv, createOpenCloudBackend, createStudioBackend, execute, extractJsonFromOutput, formatFailure, formatGameOutputNotice, formatJson, formatResult, formatTestSummary, generateTestScript, loadConfig, parseGameOutput, parseJestOutput, resolveConfig, writeGameOutput, writeJsonFile };
290
+ export { type Backend, type BackendOptions, type CliOptions, type Config, DEFAULT_CONFIG, type ExecuteOptions, type ExecuteResult, type GameOutputEntry, type JestArgv, type JestResult, OpenCloudBackend, type ResolvedConfig, StudioBackend, type TestCaseResult, type TestDefinition, type TestFileResult, type TestStatus, type TscErrorInfo, type TypecheckOptions, buildJestArgv, createOpenCloudBackend, createStudioBackend, execute, extractJsonFromOutput, formatFailure, formatGameOutputNotice, formatJson, formatResult, formatTestSummary, generateTestScript, loadConfig, parseGameOutput, parseJestOutput, resolveConfig, runTypecheck, writeGameOutput, writeJsonFile };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { _ as buildJestArgv, a as formatJson, b as extractJsonFromOutput, c as formatResult, d as resolveConfig, f as DEFAULT_CONFIG, g as createOpenCloudBackend, h as OpenCloudBackend, i as execute, l as formatTestSummary, m as createStudioBackend, n as parseGameOutput, o as writeJsonFile, p as StudioBackend, r as writeGameOutput, s as formatFailure, t as formatGameOutputNotice, u as loadConfig, v as generateTestScript, x as parseJestOutput } from "./game-output--EpqHGEr.mjs";
1
+ import { C as extractJsonFromOutput, _ as createStudioBackend, a as execute, b as buildJestArgv, c as formatFailure, d as loadConfig, f as resolveConfig, g as StudioBackend, i as runTypecheck, l as formatResult, n as parseGameOutput, o as formatJson, p as DEFAULT_CONFIG, r as writeGameOutput, s as writeJsonFile, t as formatGameOutputNotice, u as formatTestSummary, v as OpenCloudBackend, w as parseJestOutput, x as generateTestScript, y as createOpenCloudBackend } from "./game-output-DO5fOpW3.mjs";
2
2
 
3
- export { DEFAULT_CONFIG, OpenCloudBackend, StudioBackend, buildJestArgv, createOpenCloudBackend, createStudioBackend, execute, extractJsonFromOutput, formatFailure, formatGameOutputNotice, formatJson, formatResult, formatTestSummary, generateTestScript, loadConfig, parseGameOutput, parseJestOutput, resolveConfig, writeGameOutput, writeJsonFile };
3
+ export { DEFAULT_CONFIG, OpenCloudBackend, StudioBackend, buildJestArgv, createOpenCloudBackend, createStudioBackend, execute, extractJsonFromOutput, formatFailure, formatGameOutputNotice, formatJson, formatResult, formatTestSummary, generateTestScript, loadConfig, parseGameOutput, parseJestOutput, resolveConfig, runTypecheck, writeGameOutput, writeJsonFile };
@@ -757,6 +757,17 @@ type Except<ObjectType, KeysType extends keyof ObjectType, Options extends Excep
757
757
  type _Except<ObjectType, KeysType extends keyof ObjectType, Options extends Required<ExceptOptions>> = { [KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType] } & (Options['requireExactProps'] extends true ? Partial<Record<KeysType, never>> : {});
758
758
  //#endregion
759
759
  //#region src/config/schema.d.ts
760
+ type Backend = "auto" | "open-cloud" | "studio";
761
+ interface SnapshotFormatOptions {
762
+ callToJSON?: boolean;
763
+ escapeRegex?: boolean;
764
+ escapeString?: boolean;
765
+ indent?: number;
766
+ maxDepth?: number;
767
+ min?: boolean;
768
+ printBasicPrototype?: boolean;
769
+ printFunctionName?: boolean;
770
+ }
760
771
  interface Config extends Except<Argv, "rootDir" | "setupFiles" | "setupFilesAfterEnv" | "testPathPattern"> {
761
772
  backend?: Backend;
762
773
  cache?: boolean;
@@ -772,13 +783,17 @@ interface Config extends Except<Argv, "rootDir" | "setupFiles" | "setupFilesAfte
772
783
  setupFiles?: Array<string>;
773
784
  setupFilesAfterEnv?: Array<string>;
774
785
  showLuau?: boolean;
786
+ snapshotFormat?: SnapshotFormatOptions;
775
787
  sourceMap?: boolean;
776
788
  testPathPattern?: string;
777
789
  timeout?: number;
790
+ typecheck?: boolean;
791
+ typecheckOnly?: boolean;
792
+ typecheckTsconfig?: string;
778
793
  updateSnapshot?: boolean;
779
794
  }
780
795
  interface ResolvedConfig extends Config {
781
- backend: "auto" | "open-cloud" | "studio";
796
+ backend: Backend;
782
797
  cache: boolean;
783
798
  color: boolean;
784
799
  compact: boolean;
@@ -795,9 +810,11 @@ interface ResolvedConfig extends Config {
795
810
  testMatch: Array<string>;
796
811
  testPathIgnorePatterns: Array<string>;
797
812
  timeout: number;
813
+ typecheck: boolean;
814
+ typecheckOnly: boolean;
815
+ typecheckTsconfig?: string;
798
816
  verbose: boolean;
799
817
  }
800
- type Backend = "auto" | "open-cloud" | "studio";
801
818
  declare const DEFAULT_CONFIG: ResolvedConfig;
802
819
  interface CliOptions {
803
820
  backend?: Backend;
@@ -823,9 +840,12 @@ interface CliOptions {
823
840
  testNamePattern?: string;
824
841
  testPathPattern?: string;
825
842
  timeout?: number;
843
+ typecheck?: boolean;
844
+ typecheckOnly?: boolean;
845
+ typecheckTsconfig?: string;
826
846
  updateSnapshot?: boolean;
827
847
  verbose?: boolean;
828
848
  version?: boolean;
829
849
  }
830
850
  //#endregion
831
- export { Argv as a, ResolvedConfig as i, Config as n, DEFAULT_CONFIG as r, CliOptions as t };
851
+ export { SnapshotFormatOptions as a, ResolvedConfig as i, Config as n, Argv as o, DEFAULT_CONFIG as r, CliOptions as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isentinel/jest-roblox",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Jest-compatible CLI for running roblox-ts tests via Roblox Open Cloud",
5
5
  "keywords": [
6
6
  "jest",
@@ -9,6 +9,9 @@
9
9
  "testing",
10
10
  "cli"
11
11
  ],
12
+ "bugs": {
13
+ "email": "christopher.buss@pm.me"
14
+ },
12
15
  "license": "MIT",
13
16
  "author": "Christopher Buss <christopher.buss@pm.me> (https://github.com/christopher-buss)",
14
17
  "sideEffects": false,
@@ -30,18 +33,25 @@
30
33
  "!dist/**/*.tsbuildinfo"
31
34
  ],
32
35
  "dependencies": {
36
+ "@jridgewell/trace-mapping": "^0.3.25",
33
37
  "arktype": "2.1.29",
38
+ "get-tsconfig": "4.13.6",
34
39
  "highlight.js": "11.11.1",
35
40
  "jiti": "2.6.1",
41
+ "oxc-parser": "0.116.0",
36
42
  "tinyrainbow": "3.0.3",
37
43
  "ws": "8.18.0"
38
44
  },
39
45
  "devDependencies": {
40
- "@isentinel/eslint-config": "4.7.6",
46
+ "@isentinel/eslint-config": "5.0.0-beta.8",
47
+ "@oxc-project/types": "0.116.0",
41
48
  "@rbxts/jest": "3.13.3-ts.1",
42
- "@rbxts/types": "1.0.896",
43
- "@types/node": "24.10.4",
49
+ "@rbxts/types": "1.0.911",
50
+ "@total-typescript/shoehorn": "^0.1.2",
51
+ "@types/node": "24.10.13",
44
52
  "@types/ws": "8.5.13",
53
+ "@typescript/native-preview": "7.0.0-dev.20260308.1",
54
+ "@vitest/eslint-plugin": "1.6.9",
45
55
  "better-typescript-lib": "2.12.0",
46
56
  "bumpp": "10.4.1",
47
57
  "publint": "0.3.15",
@@ -53,18 +63,37 @@
53
63
  "node": ">=20.0.0"
54
64
  },
55
65
  "nx": {
66
+ "name": "jest-roblox-cli",
56
67
  "tags": [
57
68
  "scope:tooling",
58
69
  "type:cli"
59
70
  ],
60
71
  "projectType": "application",
72
+ "includedScripts": [
73
+ "!build"
74
+ ],
61
75
  "targets": {
76
+ "build": {
77
+ "command": "pnpm build:bundle && tsdown && pnpm build:plugin",
78
+ "options": {
79
+ "cwd": "tools/jest-roblox-cli"
80
+ }
81
+ },
62
82
  "lint": {
63
83
  "command": "eslint",
64
84
  "hasTypeAwareRules": true
65
85
  },
86
+ "build:bundle": {
87
+ "command": "pnpm build:bundle",
88
+ "options": {
89
+ "cwd": "tools/jest-roblox-cli"
90
+ }
91
+ },
66
92
  "test": {
67
93
  "command": "vitest run",
94
+ "dependsOn": [
95
+ "build:bundle"
96
+ ],
68
97
  "options": {
69
98
  "cwd": "tools/jest-roblox-cli"
70
99
  }
@@ -72,8 +101,9 @@
72
101
  }
73
102
  },
74
103
  "scripts": {
75
- "build": "tsdown",
76
- "build:plugin": "rojo build plugin/plugin.project.json -o plugin/JestRobloxRunner.rbxm",
104
+ "build": "pnpm build:bundle && tsdown && pnpm build:plugin",
105
+ "build:bundle": "darklua process -c .darklua-bundle.json luau/entry.luau src/test-runner.bundled.luau",
106
+ "build:plugin": "rm -rf plugin/out && darklua process -c .darklua-plugin.json luau/ plugin/out/shared/ && rojo build plugin/plugin.project.json -o plugin/JestRobloxRunner.rbxm",
77
107
  "release": "bumpp",
78
108
  "watch": "tsdown --watch"
79
109
  }
Binary file
@@ -0,0 +1,8 @@
1
+ --!strict
2
+ local HttpService = game:GetService("HttpService")
3
+
4
+ local Runner = require(script.Parent:FindFirstChild('runner'))
5
+
6
+ local config = HttpService:JSONDecode([=[__CONFIG_JSON__]=])
7
+
8
+ return Runner.run(script, config)
@@ -0,0 +1,93 @@
1
+ --!strict
2
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
3
+
4
+ local module = {}
5
+
6
+ function module.findInstance(path: string): Instance
7
+ local parts = string.split(path, "/")
8
+
9
+ local success, current = pcall(function()
10
+ return game:FindService(parts[1])
11
+ end)
12
+ assert(success, `Failed to find service {parts[1]}: {current}`)
13
+
14
+ for i = 2, #parts do
15
+ assert(current, `Failed to find '{parts[i - 1]}' in path {path}`)
16
+ current = current:FindFirstChild(parts[i])
17
+ end
18
+
19
+ assert(current, `Failed to find instance at path {path}`)
20
+
21
+ return current
22
+ end
23
+
24
+ function module.getJest(config: { jestPath: string? }): ModuleScript
25
+ local jestPath = config.jestPath
26
+ if jestPath then
27
+ local instance = module.findInstance(jestPath)
28
+ assert(instance, `Failed to find Jest instance at path {jestPath}`)
29
+ assert(instance:IsA("ModuleScript"), `Instance at path {jestPath} is not a ModuleScript`)
30
+ return instance :: ModuleScript
31
+ end
32
+
33
+ local jestInstance = ReplicatedStorage:FindFirstChild("Jest", true)
34
+ assert(jestInstance, "Failed to find Jest instance in ReplicatedStorage")
35
+ assert(
36
+ jestInstance:IsA("ModuleScript"),
37
+ "Jest instance in ReplicatedStorage is not a ModuleScript"
38
+ )
39
+ return jestInstance
40
+ end
41
+
42
+ function module.findRobloxShared(jestModule: ModuleScript): Instance?
43
+ local parent = jestModule.Parent
44
+ if parent then
45
+ local found = parent:FindFirstChild("RobloxShared")
46
+ or parent:FindFirstChild("jest-roblox-shared")
47
+ if found then
48
+ return found
49
+ end
50
+
51
+ if parent.Parent then
52
+ found = parent.Parent:FindFirstChild("RobloxShared")
53
+ or parent.Parent:FindFirstChild("jest-roblox-shared")
54
+ if found then
55
+ return found
56
+ end
57
+ end
58
+ end
59
+
60
+ return game:FindFirstChild("RobloxShared", true)
61
+ end
62
+
63
+ function module.findSiblingPackage(jestModule: ModuleScript, ...: string): Instance?
64
+ local parent = jestModule.Parent
65
+ if parent then
66
+ for _, name in { ... } do
67
+ local found = parent:FindFirstChild(name)
68
+ if found then
69
+ return found
70
+ end
71
+ end
72
+
73
+ if parent.Parent then
74
+ for _, name in { ... } do
75
+ local found = parent.Parent:FindFirstChild(name)
76
+ if found then
77
+ return found
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ for _, name in { ... } do
84
+ local found = game:FindFirstChild(name, true)
85
+ if found then
86
+ return found
87
+ end
88
+ end
89
+
90
+ return nil
91
+ end
92
+
93
+ return module
@@ -0,0 +1,19 @@
1
+ --!strict
2
+ local function getInstancePath(instance: Instance): string
3
+ local parts = {} :: { string }
4
+ local current: Instance? = instance
5
+ while current and current ~= game do
6
+ table.insert(parts, 1, current.Name)
7
+ current = current.Parent
8
+ end
9
+
10
+ return table.concat(parts, "/")
11
+ end
12
+
13
+ local CoreScriptSyncService = {}
14
+
15
+ function CoreScriptSyncService:GetScriptFilePath(instance: Instance): string
16
+ return getInstancePath(instance)
17
+ end
18
+
19
+ return CoreScriptSyncService
@@ -0,0 +1,30 @@
1
+ --!strict
2
+ local function normalizeSnapPath(path: string): string
3
+ return (string.gsub(path, "%.snap%.lua$", ".snap.luau"))
4
+ end
5
+
6
+ local function create(snapshotWrites: { [string]: string })
7
+ local FileSystemService = {}
8
+
9
+ function FileSystemService:WriteFile(path: string, contents: string)
10
+ snapshotWrites[normalizeSnapPath(path)] = contents
11
+ end
12
+
13
+ function FileSystemService:CreateDirectories(_path: string) end
14
+
15
+ function FileSystemService:Exists(path: string): boolean
16
+ return snapshotWrites[normalizeSnapPath(path)] ~= nil
17
+ end
18
+
19
+ function FileSystemService:Remove(path: string)
20
+ snapshotWrites[normalizeSnapPath(path)] = nil
21
+ end
22
+
23
+ function FileSystemService:IsRegularFile(path: string): boolean
24
+ return snapshotWrites[normalizeSnapPath(path)] ~= nil
25
+ end
26
+
27
+ return FileSystemService
28
+ end
29
+
30
+ return create