Skip to content

Commit 9e583fa

Browse files
authored
Add separate function for VS Code arguments (#4599)
The problem before was that the pop() caused the open in existing instance functionality to break because the arguments no longer contained the file. We could simply remove the pop() but since `workspace` and `folder` are not CLI arguments I think it makes sense to handle them in a separate function which can be called at the point where they are needed. This also lets us de-duplicate some logic since we create these arguments in two spots and lets us skip this logic when we do not need it. The pop() is still avoided because manipulating a passed-in object in-place seems like a risky move. If we really need to do this we should copy the positional argument array instead.
1 parent 3b91cff commit 9e583fa

File tree

5 files changed

+112
-58
lines changed

5 files changed

+112
-58
lines changed

src/node/cli.ts

+35-20
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,10 @@ export const parse = (
405405
throw new Error("--cert-key is missing")
406406
}
407407

408-
logger.debug(() => ["parsed command line", field("args", { ...args, password: undefined })])
408+
logger.debug(() => [
409+
`parsed ${opts?.configFile ? "config" : "command line"}`,
410+
field("args", { ...args, password: undefined }),
411+
])
409412

410413
return args
411414
}
@@ -430,8 +433,6 @@ export interface DefaultedArgs extends ConfigArgs {
430433
"user-data-dir": string
431434
/* Positional arguments. */
432435
_: []
433-
folder: string
434-
workspace: string
435436
}
436437

437438
/**
@@ -539,25 +540,8 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
539540
args._ = []
540541
}
541542

542-
let workspace = ""
543-
let folder = ""
544-
if (args._.length) {
545-
const lastEntry = path.resolve(process.cwd(), args._[args._.length - 1])
546-
const entryIsFile = await isFile(lastEntry)
547-
548-
if (entryIsFile && path.extname(lastEntry) === ".code-workspace") {
549-
workspace = lastEntry
550-
args._.pop()
551-
} else if (!entryIsFile) {
552-
folder = lastEntry
553-
args._.pop()
554-
}
555-
}
556-
557543
return {
558544
...args,
559-
workspace,
560-
folder,
561545
usingEnvPassword,
562546
usingEnvHashedPassword,
563547
} as DefaultedArgs // TODO: Technically no guarantee this is fulfilled.
@@ -760,3 +744,34 @@ export const shouldOpenInExistingInstance = async (args: UserProvidedArgs): Prom
760744

761745
return undefined
762746
}
747+
748+
/**
749+
* Convert our arguments to VS Code server arguments.
750+
*/
751+
export const toVsCodeArgs = async (args: DefaultedArgs): Promise<CodeServerLib.ServerParsedArgs> => {
752+
let workspace = ""
753+
let folder = ""
754+
if (args._.length) {
755+
const lastEntry = path.resolve(args._[args._.length - 1])
756+
const entryIsFile = await isFile(lastEntry)
757+
if (entryIsFile && path.extname(lastEntry) === ".code-workspace") {
758+
workspace = lastEntry
759+
} else if (!entryIsFile) {
760+
folder = lastEntry
761+
}
762+
// Otherwise it is a regular file. Spawning VS Code with a file is not yet
763+
// supported but it can be done separately after code-server spawns.
764+
}
765+
766+
return {
767+
"connection-token": "0000",
768+
...args,
769+
workspace,
770+
folder,
771+
"accept-server-license-terms": true,
772+
/** Type casting. */
773+
help: !!args.help,
774+
version: !!args.version,
775+
port: args.port?.toString(),
776+
}
777+
}

src/node/main.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import path from "path"
55
import { Disposable } from "../common/emitter"
66
import { plural } from "../common/util"
77
import { createApp, ensureAddress } from "./app"
8-
import { AuthType, DefaultedArgs, Feature, UserProvidedArgs } from "./cli"
8+
import { AuthType, DefaultedArgs, Feature, toVsCodeArgs, UserProvidedArgs } from "./cli"
99
import { coderCloudBind } from "./coder_cloud"
1010
import { commit, version } from "./constants"
1111
import { register } from "./routes"
@@ -35,14 +35,7 @@ export const runVsCodeCli = async (args: DefaultedArgs): Promise<void> => {
3535
const spawnCli = await loadAMDModule<CodeServerLib.SpawnCli>("vs/server/remoteExtensionHostAgent", "spawnCli")
3636

3737
try {
38-
await spawnCli({
39-
...args,
40-
/** Type casting. */
41-
"accept-server-license-terms": true,
42-
help: !!args.help,
43-
version: !!args.version,
44-
port: args.port?.toString(),
45-
})
38+
await spawnCli(await toVsCodeArgs(args))
4639
} catch (error: any) {
4740
logger.error("Got error from VS Code", error)
4841
}

src/node/routes/vscode.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as express from "express"
33
import { WebsocketRequest } from "../../../typings/pluginapi"
44
import { logError } from "../../common/util"
55
import { isDevMode } from "../constants"
6+
import { toVsCodeArgs } from "../cli"
67
import { ensureAuthenticated, authenticated, redirect } from "../http"
78
import { loadAMDModule, readCompilationStats } from "../util"
89
import { Router as WsRouter } from "../wsRouter"
@@ -87,15 +88,7 @@ export class CodeServerRouteWrapper {
8788
)
8889

8990
try {
90-
this._codeServerMain = await createVSServer(null, {
91-
"connection-token": "0000",
92-
"accept-server-license-terms": true,
93-
...args,
94-
/** Type casting. */
95-
help: !!args.help,
96-
version: !!args.version,
97-
port: args.port?.toString(),
98-
})
91+
this._codeServerMain = await createVSServer(null, await toVsCodeArgs(args))
9992
} catch (createServerError) {
10093
logError(logger, "CodeServerRouteWrapper", createServerError)
10194
return next(createServerError)

test/unit/node/cli.test.ts

+73-18
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,26 @@ import {
1212
setDefaults,
1313
shouldOpenInExistingInstance,
1414
splitOnFirstEquals,
15+
toVsCodeArgs,
1516
} from "../../../src/node/cli"
1617
import { shouldSpawnCliProcess } from "../../../src/node/main"
1718
import { generatePassword, paths } from "../../../src/node/util"
18-
import { useEnv, tmpdir } from "../../utils/helpers"
19+
import { clean, useEnv, tmpdir } from "../../utils/helpers"
20+
21+
// The parser should not set any defaults so the caller can determine what
22+
// values the user actually set. These are only set after explicitly calling
23+
// `setDefaults`.
24+
const defaults = {
25+
auth: "password",
26+
host: "localhost",
27+
port: 8080,
28+
"proxy-domain": [],
29+
usingEnvPassword: false,
30+
usingEnvHashedPassword: false,
31+
"extensions-dir": path.join(paths.data, "extensions"),
32+
"user-data-dir": paths.data,
33+
_: [],
34+
}
1935

2036
describe("parser", () => {
2137
beforeEach(() => {
@@ -24,23 +40,6 @@ describe("parser", () => {
2440
console.log = jest.fn()
2541
})
2642

27-
// The parser should not set any defaults so the caller can determine what
28-
// values the user actually set. These are only set after explicitly calling
29-
// `setDefaults`.
30-
const defaults = {
31-
auth: "password",
32-
host: "localhost",
33-
port: 8080,
34-
"proxy-domain": [],
35-
usingEnvPassword: false,
36-
usingEnvHashedPassword: false,
37-
"extensions-dir": path.join(paths.data, "extensions"),
38-
"user-data-dir": paths.data,
39-
_: [],
40-
workspace: "",
41-
folder: "",
42-
}
43-
4443
it("should parse nothing", async () => {
4544
expect(parse([])).toStrictEqual({})
4645
})
@@ -667,3 +666,59 @@ describe("readSocketPath", () => {
667666
expect(contents2).toBe(contents1)
668667
})
669668
})
669+
670+
describe("toVsCodeArgs", () => {
671+
const vscodeDefaults = {
672+
...defaults,
673+
"connection-token": "0000",
674+
"accept-server-license-terms": true,
675+
help: false,
676+
port: "8080",
677+
version: false,
678+
}
679+
680+
beforeAll(async () => {
681+
// Clean up temporary directories from the previous run.
682+
await clean("vscode-args")
683+
})
684+
685+
it("should convert empty args", async () => {
686+
expect(await toVsCodeArgs(await setDefaults(parse([])))).toStrictEqual({
687+
...vscodeDefaults,
688+
folder: "",
689+
workspace: "",
690+
})
691+
})
692+
693+
it("should convert with workspace", async () => {
694+
const workspace = path.join(await tmpdir("vscode-args"), "test.code-workspace")
695+
await fs.writeFile(workspace, "foobar")
696+
expect(await toVsCodeArgs(await setDefaults(parse([workspace])))).toStrictEqual({
697+
...vscodeDefaults,
698+
workspace,
699+
folder: "",
700+
_: [workspace],
701+
})
702+
})
703+
704+
it("should convert with folder", async () => {
705+
const folder = await tmpdir("vscode-args")
706+
expect(await toVsCodeArgs(await setDefaults(parse([folder])))).toStrictEqual({
707+
...vscodeDefaults,
708+
folder,
709+
workspace: "",
710+
_: [folder],
711+
})
712+
})
713+
714+
it("should ignore regular file", async () => {
715+
const file = path.join(await tmpdir("vscode-args"), "file")
716+
await fs.writeFile(file, "foobar")
717+
expect(await toVsCodeArgs(await setDefaults(parse([file])))).toStrictEqual({
718+
...vscodeDefaults,
719+
folder: "",
720+
workspace: "",
721+
_: [file],
722+
})
723+
})
724+
})

test/unit/node/plugin.test.ts

-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ describe("plugin", () => {
4242
usingEnvHashedPassword: false,
4343
"extensions-dir": "",
4444
"user-data-dir": "",
45-
workspace: "",
46-
folder: "",
4745
}
4846
next()
4947
}

0 commit comments

Comments
 (0)