@tui-sandbox/library 11.7.1 → 11.8.0

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.
Files changed (78) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/browser/assets/{index-CQ6TDc4S.js → index-CSykLFiU.js} +1 -1
  3. package/dist/browser/index.html +1 -1
  4. package/dist/src/scripts/commands/commandTuiNeovimExec.js +1 -1
  5. package/dist/src/scripts/commands/commandTuiNeovimExec.js.map +1 -1
  6. package/dist/src/scripts/commands/commandTuiNeovimPrepare.js +1 -1
  7. package/dist/src/scripts/commands/commandTuiNeovimPrepare.js.map +1 -1
  8. package/dist/src/scripts/commands/commandTuiStart.js +1 -1
  9. package/dist/src/scripts/commands/commandTuiStart.js.map +1 -1
  10. package/dist/src/scripts/resolveConfig.test.js +1 -0
  11. package/dist/src/scripts/resolveConfig.test.js.map +1 -1
  12. package/dist/src/server/applications/neovim/api.d.ts +3 -3
  13. package/dist/src/server/applications/neovim/api.js +2 -2
  14. package/dist/src/server/applications/neovim/api.js.map +1 -1
  15. package/dist/src/server/applications/neovim/environment/createTempDir.d.ts +2 -2
  16. package/dist/src/server/applications/neovim/environment/createTempDir.js +6 -6
  17. package/dist/src/server/applications/neovim/environment/createTempDir.js.map +1 -1
  18. package/dist/src/server/applications/neovim/environment/createTempDir.test.js +6 -2
  19. package/dist/src/server/applications/neovim/environment/createTempDir.test.js.map +1 -1
  20. package/dist/src/server/applications/neovim/neovimRouter.d.ts +2 -2
  21. package/dist/src/server/applications/neovim/neovimRouter.js +7 -4
  22. package/dist/src/server/applications/neovim/neovimRouter.js.map +1 -1
  23. package/dist/src/server/applications/neovim/prepareNewTestDirectory.d.ts +2 -2
  24. package/dist/src/server/applications/neovim/prepareNewTestDirectory.js +4 -4
  25. package/dist/src/server/applications/neovim/prepareNewTestDirectory.js.map +1 -1
  26. package/dist/src/server/applications/neovim/prepareNewTestDirectory.test.js +6 -2
  27. package/dist/src/server/applications/neovim/prepareNewTestDirectory.test.js.map +1 -1
  28. package/dist/src/server/applications/terminal/api.d.ts +2 -2
  29. package/dist/src/server/applications/terminal/api.js.map +1 -1
  30. package/dist/src/server/applications/terminal/terminalRouter.d.ts +2 -2
  31. package/dist/src/server/applications/terminal/terminalRouter.js +1 -1
  32. package/dist/src/server/applications/terminal/terminalRouter.js.map +1 -1
  33. package/dist/src/server/config.js +14 -7
  34. package/dist/src/server/config.js.map +1 -1
  35. package/dist/src/server/config.test.d.ts +1 -0
  36. package/dist/src/server/config.test.js +7 -0
  37. package/dist/src/server/config.test.js.map +1 -0
  38. package/dist/src/server/cypress-support/contents.js +2 -1
  39. package/dist/src/server/cypress-support/contents.js.map +1 -1
  40. package/dist/src/server/dirtree/index.d.ts +3 -2
  41. package/dist/src/server/dirtree/index.js +11 -5
  42. package/dist/src/server/dirtree/index.js.map +1 -1
  43. package/dist/src/server/dirtree/index.test.js +110 -4
  44. package/dist/src/server/dirtree/index.test.js.map +1 -1
  45. package/dist/src/server/server.d.ts +2 -2
  46. package/dist/src/server/server.js +1 -1
  47. package/dist/src/server/server.js.map +1 -1
  48. package/dist/src/server/updateTestdirectorySchemaFile.d.ts +15 -1
  49. package/dist/src/server/updateTestdirectorySchemaFile.js +13 -4
  50. package/dist/src/server/updateTestdirectorySchemaFile.js.map +1 -1
  51. package/dist/src/server/updateTestdirectorySchemaFile.test.js +8 -4
  52. package/dist/src/server/updateTestdirectorySchemaFile.test.js.map +1 -1
  53. package/dist/src/server/utilities/Lazy.d.ts +1 -0
  54. package/dist/src/server/utilities/Lazy.js +3 -0
  55. package/dist/src/server/utilities/Lazy.js.map +1 -1
  56. package/dist/tsconfig.tsbuildinfo +1 -1
  57. package/package.json +4 -4
  58. package/src/scripts/commands/commandTuiNeovimExec.ts +1 -1
  59. package/src/scripts/commands/commandTuiNeovimPrepare.ts +1 -5
  60. package/src/scripts/commands/commandTuiStart.ts +1 -1
  61. package/src/scripts/resolveConfig.test.ts +1 -0
  62. package/src/server/applications/neovim/api.ts +4 -8
  63. package/src/server/applications/neovim/environment/createTempDir.test.ts +9 -3
  64. package/src/server/applications/neovim/environment/createTempDir.ts +8 -8
  65. package/src/server/applications/neovim/neovimRouter.ts +9 -6
  66. package/src/server/applications/neovim/prepareNewTestDirectory.test.ts +9 -3
  67. package/src/server/applications/neovim/prepareNewTestDirectory.ts +6 -6
  68. package/src/server/applications/terminal/api.ts +2 -2
  69. package/src/server/applications/terminal/terminalRouter.ts +3 -3
  70. package/src/server/config.test.ts +7 -0
  71. package/src/server/config.ts +9 -3
  72. package/src/server/cypress-support/contents.ts +2 -1
  73. package/src/server/dirtree/index.test.ts +116 -4
  74. package/src/server/dirtree/index.ts +19 -5
  75. package/src/server/server.ts +3 -4
  76. package/src/server/updateTestdirectorySchemaFile.test.ts +12 -7
  77. package/src/server/updateTestdirectorySchemaFile.ts +29 -7
  78. package/src/server/utilities/Lazy.ts +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tui-sandbox/library",
3
- "version": "11.7.1",
3
+ "version": "11.8.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/mikavilpas/tui-sandbox"
@@ -19,10 +19,10 @@
19
19
  "@xterm/xterm": "5.5.0",
20
20
  "concurrently": "9.2.1",
21
21
  "cors": "2.8.5",
22
- "cypress": "15.0.0",
22
+ "cypress": "15.1.0",
23
23
  "dree": "5.1.5",
24
24
  "express": "5.1.0",
25
- "neovim": "5.3.0",
25
+ "neovim": "5.4.0",
26
26
  "node-pty": "1.0.0",
27
27
  "prettier": "3.6.2",
28
28
  "tsx": "4.20.5",
@@ -36,7 +36,7 @@
36
36
  "@types/express": "5.0.3",
37
37
  "@types/node": "24.3.0",
38
38
  "nodemon": "3.1.10",
39
- "vite": "7.1.3",
39
+ "vite": "7.1.4",
40
40
  "vitest": "3.2.4"
41
41
  },
42
42
  "peerDependencies": {
@@ -9,7 +9,7 @@ export async function commandTuiNeovimExec(command: NeovimExec, config: TestServ
9
9
  app.events.on("stdout" satisfies StdoutOrStderrMessage, data => {
10
10
  console.log(` neovim output: ${data}`)
11
11
  })
12
- const testDirectory = await prepareNewTestDirectory(config.directories)
12
+ const testDirectory = await prepareNewTestDirectory(config)
13
13
  await app.startNextAndKillCurrent(
14
14
  testDirectory,
15
15
  {
@@ -4,11 +4,7 @@ import type { TestServerConfig } from "../../server/updateTestdirectorySchemaFil
4
4
  export async function commandTuiNeovimPrepare(config: TestServerConfig): Promise<void> {
5
5
  const NVIM_APPNAME = process.env["NVIM_APPNAME"]
6
6
  console.log(`🚀 Installing neovim dependencies${NVIM_APPNAME ? ` for NVIM_APPNAME=${NVIM_APPNAME}` : ""}...`)
7
- await installDependencies(
8
- config.directories.testEnvironmentPath,
9
- process.env["NVIM_APPNAME"],
10
- config.directories
11
- ).catch((err: unknown) => {
7
+ await installDependencies(process.env["NVIM_APPNAME"], config).catch((err: unknown) => {
12
8
  console.error("Error installing neovim dependencies", err)
13
9
  process.exit(1)
14
10
  })
@@ -18,7 +18,7 @@ export async function commandTuiStart(config: TestServerConfig): Promise<void> {
18
18
 
19
19
  async function updateSchemaFile(config: TestServerConfig): Promise<void> {
20
20
  try {
21
- await updateTestdirectorySchemaFile(config.directories)
21
+ await updateTestdirectorySchemaFile(config)
22
22
  } catch (e) {
23
23
  console.error("Failed to updateTestdirectorySchemaFile", e)
24
24
  }
@@ -22,6 +22,7 @@ it("loads a custom configuration file if it exists", async () => {
22
22
  testEnvironmentPath: "./test-environment2/",
23
23
  outputFilePath: "./output.ts",
24
24
  },
25
+ integrations: { neovim: { NVIM_APPNAMEs: ["nvim", "nvim_2"] } },
25
26
  port: 12345,
26
27
  }
27
28
  {
@@ -10,7 +10,7 @@ import type {
10
10
  StartNeovimGenericArguments,
11
11
  TestDirectory,
12
12
  } from "../../types.js"
13
- import type { DirectoriesConfig } from "../../updateTestdirectorySchemaFile.js"
13
+ import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
14
14
  import { convertEventEmitterToAsyncGenerator } from "../../utilities/generator.js"
15
15
  import { Lazy } from "../../utilities/Lazy.js"
16
16
  import type { TabId } from "../../utilities/tabId.js"
@@ -28,12 +28,8 @@ const resources: Lazy<AsyncDisposableStack> = new Lazy(() => {
28
28
 
29
29
  const log = debuglog("tui-sandbox.neovim.api")
30
30
 
31
- export async function installDependencies(
32
- testEnvironmentPath: string,
33
- NVIM_APPNAME: string | undefined,
34
- config: DirectoriesConfig
35
- ): Promise<void> {
36
- await using app = new NeovimApplication(testEnvironmentPath)
31
+ export async function installDependencies(NVIM_APPNAME: string | undefined, config: TestServerConfig): Promise<void> {
32
+ await using app = new NeovimApplication(config.directories.testEnvironmentPath)
37
33
  const testDirectory = await prepareNewTestDirectory(config)
38
34
  const prepareFilePath = path.join(testDirectory.rootPathAbsolute, ".config", NVIM_APPNAME ?? "nvim", "prepare.lua")
39
35
  try {
@@ -102,7 +98,7 @@ export async function start(
102
98
  options: StartNeovimGenericArguments,
103
99
  terminalDimensions: TerminalDimensions,
104
100
  tabId: TabId,
105
- config: DirectoriesConfig
101
+ config: TestServerConfig
106
102
  ): Promise<TestDirectory> {
107
103
  const neovim = neovims.get(tabId.tabId)
108
104
  assert(neovim, `Neovim instance not found for client id ${tabId.tabId}`)
@@ -1,5 +1,8 @@
1
1
  import nodePath from "path"
2
+ import type { PartialDeep } from "type-fest"
2
3
  import { expect, it, vi } from "vitest"
4
+ import { createDefaultConfig } from "../../../config.js"
5
+ import type { TestServerConfig } from "../../../updateTestdirectorySchemaFile.js"
3
6
  import type { TestDirsPath } from "./createTempDir.js"
4
7
  import { createTempDir } from "./createTempDir.js"
5
8
  import type { TestTempDirPrefix } from "./TempDirectory.js"
@@ -11,9 +14,12 @@ it("should create a temp dir with no contents", async () => {
11
14
  // typically the user will want to have contents, but this should not be an error
12
15
  using dir = TempDirectory.create()
13
16
  const result = await createTempDir({
14
- testEnvironmentPath: dir.path,
15
- outputFilePath: nodePath.join(dir.path, "MyTestDirectory.ts"),
16
- })
17
+ ...createDefaultConfig("cwd", {}),
18
+ directories: {
19
+ testEnvironmentPath: dir.path,
20
+ outputFilePath: nodePath.join(dir.path, "MyTestDirectory.ts"),
21
+ },
22
+ } satisfies PartialDeep<TestServerConfig>)
17
23
 
18
24
  expect(result.contents).toEqual({})
19
25
  expect(result.testEnvironmentPath).toEqual(dir.path)
@@ -7,22 +7,22 @@ import path from "path"
7
7
  import { debuglog } from "util"
8
8
  import { convertDree, getDirectoryTree } from "../../../dirtree/index.js"
9
9
  import type { TestDirectory } from "../../../types.js"
10
- import type { DirectoriesConfig } from "../../../updateTestdirectorySchemaFile.js"
10
+ import type { TestServerConfig } from "../../../updateTestdirectorySchemaFile.js"
11
11
  import { updateTestdirectorySchemaFile } from "../../../updateTestdirectorySchemaFile.js"
12
12
 
13
13
  const log = debuglog("tui-sandbox.createTempDir")
14
14
 
15
- export async function createTempDir(config: DirectoriesConfig): Promise<TestDirectory> {
15
+ export async function createTempDir(config: TestServerConfig): Promise<TestDirectory> {
16
16
  try {
17
17
  // before calling this function, the testEnvironmentPath should already exist
18
- statSync(config.testEnvironmentPath)
19
- const dir = await createUniqueDirectory(config.testEnvironmentPath)
18
+ statSync(config.directories.testEnvironmentPath)
19
+ const dir = await createUniqueDirectory(config.directories.testEnvironmentPath)
20
20
 
21
- readdirSync(config.testEnvironmentPath).forEach(entry => {
21
+ readdirSync(config.directories.testEnvironmentPath).forEach(entry => {
22
22
  if (entry === ("testdirs" satisfies TestDirsPath)) return
23
23
  if (entry === ".repro") return
24
24
 
25
- execSync(`cp -R '${path.join(config.testEnvironmentPath, entry)}' ${dir}/`)
25
+ execSync(`cp -R '${path.join(config.directories.testEnvironmentPath, entry)}' ${dir}/`)
26
26
  })
27
27
  log(`Created test directory at ${dir}`)
28
28
 
@@ -33,8 +33,8 @@ export async function createTempDir(config: DirectoriesConfig): Promise<TestDire
33
33
  return {
34
34
  rootPathAbsolute: dir,
35
35
  contents: tree.contents,
36
- testEnvironmentPath: config.testEnvironmentPath,
37
- testEnvironmentPathRelative: path.relative(config.testEnvironmentPath, dir),
36
+ testEnvironmentPath: config.directories.testEnvironmentPath,
37
+ testEnvironmentPathRelative: path.relative(config.directories.testEnvironmentPath, dir),
38
38
  }
39
39
  } catch (err) {
40
40
  console.error(err)
@@ -3,7 +3,7 @@ import * as z from "zod"
3
3
  import { blockingCommandInputSchema } from "../../blockingCommandInputSchema.js"
4
4
  import { trpc } from "../../connection/trpc.js"
5
5
  import { serverTestDirectorySchema } from "../../types.js"
6
- import type { DirectoriesConfig } from "../../updateTestdirectorySchemaFile.js"
6
+ import type { NeovimIntegrationDefaultAppName, TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
7
7
  import { tabIdSchema } from "../../utilities/tabId.js"
8
8
  import { timeoutable } from "../../utilities/timeoutable.js"
9
9
  import * as neovim from "./api.js"
@@ -37,7 +37,7 @@ export type RunLuaFileClientInput = Except<RunLuaFileInput, "tabId">
37
37
 
38
38
  // let trpc infer the type as that is what it is designed to do
39
39
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
40
- export function createNeovimRouter(config: DirectoriesConfig) {
40
+ export function createNeovimRouter(config: TestServerConfig) {
41
41
  return trpc.router({
42
42
  start: trpc.procedure
43
43
  .input(
@@ -52,7 +52,10 @@ export function createNeovimRouter(config: DirectoriesConfig) {
52
52
  ]),
53
53
  startupScriptModifications: z.array(z.string()).optional(),
54
54
  additionalEnvironmentVariables: z.record(z.string(), z.string()).optional(),
55
- NVIM_APPNAME: z.string().optional().default("nvim"),
55
+ NVIM_APPNAME: z
56
+ .string()
57
+ .optional()
58
+ .default("nvim" satisfies NeovimIntegrationDefaultAppName),
56
59
  }),
57
60
  terminalDimensions: z.object({
58
61
  cols: z.number(),
@@ -61,7 +64,7 @@ export function createNeovimRouter(config: DirectoriesConfig) {
61
64
  })
62
65
  )
63
66
  .output(serverTestDirectorySchema)
64
- .mutation(options => {
67
+ .mutation(async options => {
65
68
  return neovim.start(
66
69
  options.input.startNeovimArguments,
67
70
  options.input.terminalDimensions,
@@ -70,11 +73,11 @@ export function createNeovimRouter(config: DirectoriesConfig) {
70
73
  )
71
74
  }),
72
75
  onStdout: trpc.procedure.input(z.object({ client: tabIdSchema })).subscription(options => {
73
- return neovim.initializeStdout(options.input, options.signal, config.testEnvironmentPath)
76
+ return neovim.initializeStdout(options.input, options.signal, config.directories.testEnvironmentPath)
74
77
  }),
75
78
 
76
79
  initializeStdout: trpc.procedure.input(z.object({ client: tabIdSchema })).subscription(options => {
77
- return neovim.initializeStdout(options.input, options.signal, config.testEnvironmentPath)
80
+ return neovim.initializeStdout(options.input, options.signal, config.directories.testEnvironmentPath)
78
81
  }),
79
82
  sendStdin: trpc.procedure.input(z.object({ tabId: tabIdSchema, data: z.string() })).mutation(options => {
80
83
  return neovim.sendStdin(options.input)
@@ -1,6 +1,9 @@
1
1
  import { rm } from "fs/promises"
2
2
  import path from "path"
3
+ import type { PartialDeep } from "type-fest"
3
4
  import { assert, describe, expect, it } from "vitest"
5
+ import { createDefaultConfig } from "../../config.js"
6
+ import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
4
7
  import { prepareNewTestDirectory } from "./prepareNewTestDirectory.js"
5
8
 
6
9
  describe("prepareNewTestDirectory when the testEnvironmentPath does not exist", () => {
@@ -9,9 +12,12 @@ describe("prepareNewTestDirectory when the testEnvironmentPath does not exist",
9
12
  // has not been created yet
10
13
  const testEnvironmentPath = "/tmp/test"
11
14
  const testDirectory = await prepareNewTestDirectory({
12
- outputFilePath: path.join(testEnvironmentPath, "foo.ts"),
13
- testEnvironmentPath,
14
- })
15
+ ...createDefaultConfig("cwd", {}),
16
+ directories: {
17
+ outputFilePath: path.join(testEnvironmentPath, "foo.ts"),
18
+ testEnvironmentPath,
19
+ },
20
+ } satisfies PartialDeep<TestServerConfig>)
15
21
 
16
22
  try {
17
23
  expect(testDirectory.contents).toEqual({})
@@ -1,21 +1,21 @@
1
1
  import { access, mkdir } from "fs/promises"
2
2
  import { debuglog } from "util"
3
3
  import type { TestDirectory } from "../../types.js"
4
- import type { DirectoriesConfig } from "../../updateTestdirectorySchemaFile.js"
4
+ import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
5
5
  import { createTempDir, removeTestDirectories } from "./environment/createTempDir.js"
6
6
 
7
7
  const log = debuglog("tui-sandbox.neovim.prepareNewTestDirectory")
8
8
 
9
- export async function prepareNewTestDirectory(config: DirectoriesConfig): Promise<TestDirectory> {
9
+ export async function prepareNewTestDirectory(config: TestServerConfig): Promise<TestDirectory> {
10
10
  try {
11
11
  // if the directory does not exist, create it
12
- await access(config.testEnvironmentPath)
12
+ await access(config.directories.testEnvironmentPath)
13
13
  } catch {
14
- log(`Creating testEnvironmentPath directory at ${config.testEnvironmentPath}`)
15
- await mkdir(config.testEnvironmentPath, { recursive: true })
14
+ log(`Creating testEnvironmentPath directory at ${config.directories.testEnvironmentPath}`)
15
+ await mkdir(config.directories.testEnvironmentPath, { recursive: true })
16
16
  }
17
17
 
18
- await removeTestDirectories(config.testEnvironmentPath)
18
+ await removeTestDirectories(config.directories.testEnvironmentPath)
19
19
  const testDirectory = await createTempDir(config)
20
20
  return testDirectory
21
21
  }
@@ -1,7 +1,7 @@
1
1
  import assert from "assert"
2
2
  import type { BlockingCommandInput } from "../../blockingCommandInputSchema.js"
3
3
  import type { BlockingShellCommandOutput, TestDirectory } from "../../types.js"
4
- import type { DirectoriesConfig } from "../../updateTestdirectorySchemaFile.js"
4
+ import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
5
5
  import { convertEventEmitterToAsyncGenerator } from "../../utilities/generator.js"
6
6
  import { Lazy } from "../../utilities/Lazy.js"
7
7
  import type { TabId } from "../../utilities/tabId.js"
@@ -17,7 +17,7 @@ const resources: Lazy<AsyncDisposableStack> = new Lazy(() => {
17
17
 
18
18
  export async function start(
19
19
  { tabId, startTerminalArguments }: StartTerminalInput,
20
- config: DirectoriesConfig
20
+ config: TestServerConfig
21
21
  ): Promise<TestDirectory> {
22
22
  const app = terminals.get(tabId.tabId)
23
23
  assert(app, `Terminal with tabId ${tabId.tabId} not found.`)
@@ -2,7 +2,7 @@ import * as z from "zod"
2
2
  import { blockingCommandInputSchema } from "../../blockingCommandInputSchema.js"
3
3
  import { trpc } from "../../connection/trpc.js"
4
4
  import { serverTestDirectorySchema } from "../../types.js"
5
- import type { DirectoriesConfig } from "../../updateTestdirectorySchemaFile.js"
5
+ import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
6
6
  import { tabIdSchema } from "../../utilities/tabId.js"
7
7
  import * as terminal from "./api.js"
8
8
 
@@ -21,7 +21,7 @@ export type StartTerminalInput = z.infer<typeof startTerminalInputSchema>
21
21
 
22
22
  // let trpc infer the type as that is what it is designed to do
23
23
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
24
- export function createTerminalRouter(config: DirectoriesConfig) {
24
+ export function createTerminalRouter(config: TestServerConfig) {
25
25
  const terminalRouter = trpc.router({
26
26
  onStdout: trpc.procedure.input(z.object({ client: tabIdSchema })).subscription(options => {
27
27
  return terminal.initializeStdout(options.input, options.signal)
@@ -30,7 +30,7 @@ export function createTerminalRouter(config: DirectoriesConfig) {
30
30
  start: trpc.procedure
31
31
  .input(startTerminalInputSchema)
32
32
  .output(serverTestDirectorySchema)
33
- .mutation(options => {
33
+ .mutation(async options => {
34
34
  return terminal.start(options.input, config)
35
35
  }),
36
36
 
@@ -0,0 +1,7 @@
1
+ import { createDefaultConfig } from "./config.js"
2
+ import { testServerConfigSchema } from "./updateTestdirectorySchemaFile.js"
3
+
4
+ it("is possible not to customize anything", () => {
5
+ const defaultConfig = createDefaultConfig(process.cwd(), process.env)
6
+ testServerConfigSchema.parse(defaultConfig)
7
+ })
@@ -1,11 +1,17 @@
1
1
  import path from "path"
2
2
  import type { TestServerConfig } from "./updateTestdirectorySchemaFile.js"
3
3
 
4
- export const createDefaultConfig = (cwd: string, environment: NodeJS.ProcessEnv): TestServerConfig =>
5
- ({
4
+ export const createDefaultConfig = (cwd: string, environment: NodeJS.ProcessEnv): TestServerConfig => {
5
+ return {
6
6
  directories: {
7
7
  testEnvironmentPath: path.join(cwd, "test-environment/"),
8
8
  outputFilePath: path.join(cwd, "MyTestDirectory.ts"),
9
9
  },
10
10
  port: environment["PORT"] ? parseInt(environment["PORT"]) : 3000,
11
- }) satisfies TestServerConfig
11
+ integrations: {
12
+ neovim: {
13
+ NVIM_APPNAMEs: ["nvim"],
14
+ },
15
+ },
16
+ }
17
+ }
@@ -33,7 +33,7 @@ import type {
33
33
  import type { StartTerminalGenericArguments } from "@tui-sandbox/library/src/server/applications/terminal/TerminalTestApplication"
34
34
  import type { BlockingCommandClientInput } from "@tui-sandbox/library/src/server/blockingCommandInputSchema"
35
35
  import type { OverrideProperties } from "type-fest"
36
- import type { MyTestDirectory, MyTestDirectoryFile } from "../../MyTestDirectory"
36
+ import type { MyNeovimAppName, MyTestDirectory, MyTestDirectoryFile } from "../../MyTestDirectory"
37
37
 
38
38
  export type TerminalTestApplicationContext = {
39
39
  /** Types text into the terminal, making the terminal application receive the
@@ -93,6 +93,7 @@ export type NeovimContext = {
93
93
  export type MyStartNeovimServerArguments = OverrideProperties<
94
94
  StartNeovimGenericArguments,
95
95
  {
96
+ NVIM_APPNAME?: MyNeovimAppName
96
97
  filename?: MyTestDirectoryFile | { openInVerticalSplits: MyTestDirectoryFile[] }
97
98
  startupScriptModifications?: Array<MyNeovimConfigModification<MyTestDirectoryFile>>
98
99
  }
@@ -1,6 +1,8 @@
1
1
  import assert from "assert"
2
2
  import path from "path"
3
3
  import { describe, expect, it } from "vitest"
4
+ import { createDefaultConfig } from "../config.js"
5
+ import type { NeovimIntegrationDefaultAppName, TestServerConfig } from "../updateTestdirectorySchemaFile.js"
4
6
  import { Lazy } from "../utilities/Lazy.js"
5
7
  import { buildSchemaForDirectoryTree, getDirectoryTree } from "./index.js"
6
8
 
@@ -19,7 +21,15 @@ describe("dirtree", () => {
19
21
  })
20
22
 
21
23
  it("should be able to build a typescript type for the tree", async () => {
22
- const result = await buildSchemaForDirectoryTree(output.get(), "MyDirectoryTree")
24
+ const config: TestServerConfig = {
25
+ ...createDefaultConfig("/my/root/path", {}),
26
+ integrations: {
27
+ neovim: {
28
+ NVIM_APPNAMEs: ["nvim" satisfies NeovimIntegrationDefaultAppName, "nvim_alt"],
29
+ },
30
+ },
31
+ }
32
+ const result = await buildSchemaForDirectoryTree(output.get(), "MyDirectoryTree", config)
23
33
 
24
34
  expect(result).toMatchInlineSnapshot(`
25
35
  "
@@ -201,13 +211,15 @@ describe("dirtree", () => {
201
211
  "symlink-test",
202
212
  "."
203
213
  ])
204
- export type MyTestDirectoryFile = z.infer<typeof testDirectoryFiles>"
214
+ export type MyTestDirectoryFile = z.infer<typeof testDirectoryFiles>
215
+ export type MyNeovimAppName = "nvim" | "nvim_alt""
205
216
  `)
206
217
  })
207
218
 
208
219
  it("creates an empty schema when the directory cannot be read", async () => {
220
+ const config: TestServerConfig = createDefaultConfig("/my/root/path", {})
209
221
  const tree = getDirectoryTree("nonexistent")
210
- const result = await buildSchemaForDirectoryTree(tree, "MyDirectoryTree")
222
+ const result = await buildSchemaForDirectoryTree(tree, "MyDirectoryTree", config)
211
223
  expect(result).toMatchInlineSnapshot(`
212
224
  "
213
225
  // Note: This file is autogenerated. Do not edit it directly.
@@ -234,7 +246,107 @@ describe("dirtree", () => {
234
246
  export type MyDirectoryTree = MyDirectoryTreeContentsSchemaType["contents"]
235
247
 
236
248
  export const testDirectoryFiles = z.enum([])
237
- export type MyTestDirectoryFile = z.infer<typeof testDirectoryFiles>"
249
+ export type MyTestDirectoryFile = z.infer<typeof testDirectoryFiles>
250
+ export type MyNeovimAppName = "nvim""
238
251
  `)
239
252
  })
253
+
254
+ describe("provides type-safe access to NVIM_APPNAMEs", () => {
255
+ // the user can configure the NVIM_APPNAMEs they are using in their
256
+ // configuration. These should be available for them in a type-safe manner so
257
+ // that they have the least possible friction when coding.
258
+ //
259
+ const smallDirectory = path.resolve(__dirname, "../../../../integration-tests/test-environment/other-subdirectory/")
260
+
261
+ it("when there is a single name", async () => {
262
+ const config: TestServerConfig = createDefaultConfig("/my/root/path", {})
263
+ assert(config.integrations.neovim.NVIM_APPNAMEs.length === 1)
264
+ const tinytree = getDirectoryTree(smallDirectory)
265
+ const result = await buildSchemaForDirectoryTree(tinytree, "MyDirectoryTree", config)
266
+
267
+ expect(result).toMatchInlineSnapshot(`
268
+ "
269
+ // Note: This file is autogenerated. Do not edit it directly.
270
+ //
271
+ // Describes the contents of the test directory, which is a blueprint for
272
+ // files and directories. Tests can create a unique, safe environment for
273
+ // interacting with the contents of such a directory.
274
+ //
275
+ // Having strong typing for the test directory contents ensures that tests can
276
+ // be written with confidence that the files and directories they expect are
277
+ // actually found. Otherwise the tests are brittle and can break easily.
278
+
279
+ import * as z from "zod"
280
+
281
+ export const MyDirectoryTreeSchema = z.object({
282
+ name: z.literal("other-subdirectory/"),
283
+ type: z.literal("directory"),
284
+ contents: z.object({
285
+ "other-sub-file.txt": z.object({ name: z.literal("other-sub-file.txt"), type: z.literal("file") }),
286
+ }),
287
+ })
288
+
289
+ export const MyDirectoryTreeContentsSchema = MyDirectoryTreeSchema.shape.contents
290
+ export type MyDirectoryTreeContentsSchemaType = z.infer<typeof MyDirectoryTreeSchema>
291
+
292
+ export type MyDirectoryTree = MyDirectoryTreeContentsSchemaType["contents"]
293
+
294
+ export const testDirectoryFiles = z.enum([
295
+ "other-sub-file.txt",
296
+ "."
297
+ ])
298
+ export type MyTestDirectoryFile = z.infer<typeof testDirectoryFiles>
299
+ export type MyNeovimAppName = "nvim""
300
+ `)
301
+ // 👆🏻 from the default config
302
+ })
303
+
304
+ it("when there are multiple names", async () => {
305
+ const config: TestServerConfig = {
306
+ ...createDefaultConfig("/my/root/path", {}),
307
+ integrations: {
308
+ neovim: {
309
+ NVIM_APPNAMEs: ["nvim" satisfies NeovimIntegrationDefaultAppName, "nvim_alt"],
310
+ },
311
+ },
312
+ }
313
+ const tinytree = getDirectoryTree(smallDirectory)
314
+ const result = await buildSchemaForDirectoryTree(tinytree, "MyDirectoryTree", config)
315
+
316
+ expect(result).toMatchInlineSnapshot(`
317
+ "
318
+ // Note: This file is autogenerated. Do not edit it directly.
319
+ //
320
+ // Describes the contents of the test directory, which is a blueprint for
321
+ // files and directories. Tests can create a unique, safe environment for
322
+ // interacting with the contents of such a directory.
323
+ //
324
+ // Having strong typing for the test directory contents ensures that tests can
325
+ // be written with confidence that the files and directories they expect are
326
+ // actually found. Otherwise the tests are brittle and can break easily.
327
+
328
+ import * as z from "zod"
329
+
330
+ export const MyDirectoryTreeSchema = z.object({
331
+ name: z.literal("other-subdirectory/"),
332
+ type: z.literal("directory"),
333
+ contents: z.object({
334
+ "other-sub-file.txt": z.object({ name: z.literal("other-sub-file.txt"), type: z.literal("file") }),
335
+ }),
336
+ })
337
+
338
+ export const MyDirectoryTreeContentsSchema = MyDirectoryTreeSchema.shape.contents
339
+ export type MyDirectoryTreeContentsSchemaType = z.infer<typeof MyDirectoryTreeSchema>
340
+
341
+ export type MyDirectoryTree = MyDirectoryTreeContentsSchemaType["contents"]
342
+
343
+ export const testDirectoryFiles = z.enum([
344
+ "other-sub-file.txt",
345
+ "."
346
+ ])
347
+ export type MyTestDirectoryFile = z.infer<typeof testDirectoryFiles>
348
+ export type MyNeovimAppName = "nvim" | "nvim_alt""
349
+ `)
350
+ })
351
+ })
240
352
  })
@@ -1,9 +1,11 @@
1
+ import assert from "assert"
1
2
  import type { Dree } from "dree"
2
3
  import { scan, Type } from "dree"
3
4
  import { readlinkSync } from "fs"
4
5
  import { format, resolveConfig } from "prettier"
5
6
  import { fileURLToPath } from "url"
6
7
  import { debuglog } from "util"
8
+ import type { TestServerConfig } from "../updateTestdirectorySchemaFile.js"
7
9
  import { jsonToZod } from "./json-to-zod.js"
8
10
 
9
11
  const log = debuglog("tui-sandbox.dirtree")
@@ -83,7 +85,11 @@ export function convertDree(root: Dree | undefined): TreeNode {
83
85
  return node
84
86
  }
85
87
 
86
- export async function buildSchemaForDirectoryTree(result: TreeResult, name: string): Promise<string> {
88
+ export async function buildSchemaForDirectoryTree(
89
+ result: TreeResult,
90
+ name: string,
91
+ config: TestServerConfig
92
+ ): Promise<string> {
87
93
  const root = convertDree(result.dree)
88
94
 
89
95
  const schema = (await jsonToZod(root, `${name}Schema`)).split("\n")
@@ -103,6 +109,13 @@ export async function buildSchemaForDirectoryTree(result: TreeResult, name: stri
103
109
  const allFilePaths = result.allFiles.map(f => f.relativePath)
104
110
  const ContentsSchema = `${name}ContentsSchema`
105
111
  const ContentsSchemaType = `${name}ContentsSchemaType`
112
+
113
+ assert(config.integrations.neovim.NVIM_APPNAMEs.length > 0, "At least one NVIM_APPNAME must be configured")
114
+ const NVIM_APPNAMEsUnion =
115
+ config.integrations.neovim.NVIM_APPNAMEs.length > 1
116
+ ? config.integrations.neovim.NVIM_APPNAMEs.map(a => `"${a}"`).join(" | ")
117
+ : `"${config.integrations.neovim.NVIM_APPNAMEs[0]}"`
118
+
106
119
  return [
107
120
  ...lines,
108
121
  ...schema,
@@ -113,15 +126,16 @@ export async function buildSchemaForDirectoryTree(result: TreeResult, name: stri
113
126
  "",
114
127
  `export const testDirectoryFiles = z.enum(${JSON.stringify(allFilePaths, null, 2)})`,
115
128
  `export type MyTestDirectoryFile = z.infer<typeof testDirectoryFiles>`,
129
+ `export type MyNeovimAppName = ${NVIM_APPNAMEsUnion}`,
116
130
  ].join("\n")
117
131
  }
118
132
 
119
133
  const __filename = fileURLToPath(import.meta.url)
120
134
 
121
- export async function buildTestDirectorySchema(testDirectoryPath: string): Promise<string> {
122
- log("Building schema for test directory", testDirectoryPath)
123
- const dree = getDirectoryTree(testDirectoryPath)
124
- let text = await buildSchemaForDirectoryTree(dree, "MyTestDirectory")
135
+ export async function buildTestDirectorySchema(config: TestServerConfig): Promise<string> {
136
+ log("Building schema for test directory", config.directories.testEnvironmentPath)
137
+ const dree = getDirectoryTree(config.directories.testEnvironmentPath)
138
+ let text = await buildSchemaForDirectoryTree(dree, "MyTestDirectory", config)
125
139
 
126
140
  const options = await resolveConfig(__filename)
127
141
  text = await format(text, { ...options, parser: "typescript" })
@@ -3,14 +3,13 @@ import { createNeovimRouter } from "./applications/neovim/neovimRouter.js"
3
3
  import { createTerminalRouter } from "./applications/terminal/terminalRouter.js"
4
4
  import { trpc } from "./connection/trpc.js"
5
5
  import { TestServer } from "./TestServer.js"
6
- import type { DirectoriesConfig, TestServerConfig } from "./updateTestdirectorySchemaFile.js"
6
+ import type { TestServerConfig } from "./updateTestdirectorySchemaFile.js"
7
7
 
8
8
  /** @private */
9
9
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
10
- export async function createAppRouter(config: DirectoriesConfig) {
10
+ export async function createAppRouter(config: TestServerConfig) {
11
11
  const appRouter = trpc.router({
12
12
  terminal: createTerminalRouter(config),
13
-
14
13
  neovim: createNeovimRouter(config),
15
14
  })
16
15
 
@@ -25,7 +24,7 @@ export async function startTestServer(config: TestServerConfig): Promise<void> {
25
24
  const testServer = new TestServer({
26
25
  port: config.port,
27
26
  })
28
- const appRouter = await createAppRouter(config.directories)
27
+ const appRouter = await createAppRouter(config)
29
28
 
30
29
  await testServer.startAndRun(appRouter)
31
30
  } catch (err: unknown) {
@@ -1,7 +1,8 @@
1
1
  import { readFileSync, writeFileSync } from "fs"
2
+ import type { PartialDeep } from "type-fest"
2
3
  import { describe, expect, it, vi } from "vitest"
3
4
  import { buildTestDirectorySchema } from "./dirtree/index.js"
4
- import type { UpdateTestdirectorySchemaFileResult } from "./updateTestdirectorySchemaFile.js"
5
+ import type { TestServerConfig, UpdateTestdirectorySchemaFileResult } from "./updateTestdirectorySchemaFile.js"
5
6
  import { updateTestdirectorySchemaFile } from "./updateTestdirectorySchemaFile.js"
6
7
 
7
8
  vi.mock("fs")
@@ -20,9 +21,11 @@ describe("when the schema has not changed", () => {
20
21
  mock.readFileSync.mockImplementation(() => "schema")
21
22
 
22
23
  const result = await updateTestdirectorySchemaFile({
23
- testEnvironmentPath: "path",
24
- outputFilePath: "path",
25
- })
24
+ directories: {
25
+ testEnvironmentPath: "path",
26
+ outputFilePath: "path",
27
+ },
28
+ } satisfies PartialDeep<TestServerConfig> as TestServerConfig)
26
29
 
27
30
  expect(result).toBe("did-nothing" satisfies UpdateTestdirectorySchemaFileResult)
28
31
  })
@@ -34,9 +37,11 @@ describe("when the schema has changed", () => {
34
37
  mock.readFileSync.mockImplementation(() => "old schema")
35
38
 
36
39
  const result = await updateTestdirectorySchemaFile({
37
- testEnvironmentPath: "path",
38
- outputFilePath: "path",
39
- })
40
+ directories: {
41
+ testEnvironmentPath: "path",
42
+ outputFilePath: "path",
43
+ },
44
+ } satisfies PartialDeep<TestServerConfig> as TestServerConfig)
40
45
 
41
46
  expect(result).toBe("updated" satisfies UpdateTestdirectorySchemaFileResult)
42
47
  expect(mock.writeFileSync).toHaveBeenCalledWith("path", "new schema")