Skip to content

Commit 65c4f29

Browse files
committed
feat: add registerRequireOnSelf on function
1 parent a96e16e commit 65c4f29

File tree

2 files changed

+262
-18
lines changed

2 files changed

+262
-18
lines changed

src/browser/pages/vscode.ts

+85-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getOptions } from "../../common/util"
1+
import { getOptions, Options } from "../../common/util"
22
import "../register"
33

44
const options = getOptions()
@@ -54,6 +54,84 @@ export function getNlsConfiguration(document: Document) {
5454
return JSON.parse(nlsConfig) as NlsConfiguration
5555
}
5656

57+
type RegisterRequireOnSelfType = {
58+
// NOTE@jsjoeio
59+
// We get the self type by looking at window.self.
60+
self: Window & typeof globalThis
61+
window: Window
62+
nlsConfig: NlsConfiguration
63+
options: Options
64+
}
65+
66+
type RequireOnSelfType = {
67+
baseUrl: string
68+
recordStats: boolean
69+
paths: {
70+
[key: string]: string
71+
}
72+
"vs/nls": NlsConfiguration
73+
}
74+
75+
/**
76+
* A helper function to register the require on self.
77+
*
78+
* The require property is used by VSCode/code-server
79+
* to load files.
80+
*
81+
* We extracted the logic into a function so that
82+
* it's easier to test.
83+
**/
84+
export function registerRequireOnSelf({ self, window, nlsConfig, options }: RegisterRequireOnSelfType) {
85+
const errorMsgPrefix = "[vscode]"
86+
87+
if (!self) {
88+
throw new Error(`${errorMsgPrefix} Could not register require on self. self is undefined.`)
89+
}
90+
91+
if (!window) {
92+
throw new Error(`${errorMsgPrefix} Could not register require on self. window is undefined.`)
93+
}
94+
95+
if (!options || !options.csStaticBase) {
96+
throw new Error(
97+
`${errorMsgPrefix} Could not register require on self. options or options.csStaticBase is undefined or missing.`,
98+
)
99+
}
100+
101+
if (!nlsConfig) {
102+
throw new Error(`${errorMsgPrefix} Could not register require on self. nlsConfig is undefined.`)
103+
}
104+
105+
const requireOnSelf: RequireOnSelfType = {
106+
// Without the full URL VS Code will try to load file://.
107+
baseUrl: `${window.location.origin}${options.csStaticBase}/lib/vscode/out`,
108+
recordStats: true,
109+
paths: {
110+
"vscode-textmate": `../node_modules/vscode-textmate/release/main`,
111+
"vscode-oniguruma": `../node_modules/vscode-oniguruma/release/main`,
112+
xterm: `../node_modules/xterm/lib/xterm.js`,
113+
"xterm-addon-search": `../node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
114+
"xterm-addon-unicode11": `../node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
115+
"xterm-addon-webgl": `../node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
116+
"tas-client-umd": `../node_modules/tas-client-umd/lib/tas-client-umd.js`,
117+
"iconv-lite-umd": `../node_modules/iconv-lite-umd/lib/iconv-lite-umd.js`,
118+
jschardet: `../node_modules/jschardet/dist/jschardet.min.js`,
119+
},
120+
"vs/nls": nlsConfig,
121+
}
122+
123+
// TODO@jsjoeio
124+
// I'm not sure how to properly type cast this
125+
// This might be our best bet
126+
// Source: https://stackoverflow.com/a/30740935
127+
type FixMeLater = any
128+
;(self.require as FixMeLater) = requireOnSelf
129+
130+
// If everything worked, then return true
131+
// so the caller knows it registered succesfully
132+
return true
133+
}
134+
57135
try {
58136
const nlsConfig = getNlsConfiguration(document)
59137
if (nlsConfig._resolvedLanguagePackCoreLocation) {
@@ -74,23 +152,12 @@ try {
74152
.catch(cb)
75153
}
76154
}
77-
;(self.require as any) = {
78-
// Without the full URL VS Code will try to load file://.
79-
baseUrl: `${window.location.origin}${options.csStaticBase}/lib/vscode/out`,
80-
recordStats: true,
81-
paths: {
82-
"vscode-textmate": `../node_modules/vscode-textmate/release/main`,
83-
"vscode-oniguruma": `../node_modules/vscode-oniguruma/release/main`,
84-
xterm: `../node_modules/xterm/lib/xterm.js`,
85-
"xterm-addon-search": `../node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
86-
"xterm-addon-unicode11": `../node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
87-
"xterm-addon-webgl": `../node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
88-
"tas-client-umd": `../node_modules/tas-client-umd/lib/tas-client-umd.js`,
89-
"iconv-lite-umd": `../node_modules/iconv-lite-umd/lib/iconv-lite-umd.js`,
90-
jschardet: `../node_modules/jschardet/dist/jschardet.min.js`,
91-
},
92-
"vs/nls": nlsConfig,
93-
}
155+
registerRequireOnSelf({
156+
self,
157+
window,
158+
nlsConfig,
159+
options,
160+
})
94161
} catch (error) {
95162
console.error(error)
96163
}

test/unit/browser/vscode.test.ts

+177
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { JSDOM } from "jsdom"
55
import {
66
getNlsConfiguration,
77
nlsConfigElementId,
8+
registerRequireOnSelf,
89
setBodyBackgroundToThemeBackgroundColor,
910
} from "../../../src/browser/pages/vscode"
1011

@@ -175,4 +176,180 @@ describe("vscode", () => {
175176
localStorage.removeItem("colorThemeData")
176177
})
177178
})
179+
describe("registerRequireOnSelf", () => {
180+
beforeAll(() => {
181+
const { window } = new JSDOM()
182+
// @ts-expect-error We know these are the exact same type
183+
// but we need to do this for the test to work
184+
global.self = window.self
185+
global.window = window as unknown as Window & typeof globalThis
186+
global.document = window.document
187+
global.navigator = window.navigator
188+
global.location = location as Location
189+
})
190+
191+
beforeEach(() => {
192+
jest.clearAllMocks()
193+
})
194+
195+
afterEach(() => {
196+
jest.resetModules()
197+
})
198+
199+
afterAll(() => {
200+
jest.restoreAllMocks()
201+
202+
global.window = undefined as unknown as Window & typeof globalThis
203+
global.document = undefined as unknown as Document & typeof globalThis
204+
global.navigator = undefined as unknown as Navigator & typeof globalThis
205+
global.location = undefined as unknown as Location & typeof globalThis
206+
})
207+
it("should throw an error if self is undefined", () => {
208+
const options = {
209+
base: "/",
210+
csStaticBase: "/hello",
211+
logLevel: 1,
212+
}
213+
const nlsConfig = {
214+
first: "Jane",
215+
last: "Doe",
216+
locale: "en",
217+
availableLanguages: {},
218+
}
219+
const errorMsgPrefix = "[vscode]"
220+
const errorMessage = `${errorMsgPrefix} Could not register require on self. self is undefined.`
221+
expect(() => {
222+
registerRequireOnSelf({
223+
// @ts-expect-error We are checking what happens if self is undefined.
224+
self: undefined,
225+
window: global.window,
226+
nlsConfig: nlsConfig,
227+
options,
228+
})
229+
}).toThrowError(errorMessage)
230+
})
231+
232+
it("should throw an error if window is undefined", () => {
233+
const options = {
234+
base: "/",
235+
csStaticBase: "/hello",
236+
logLevel: 1,
237+
}
238+
const nlsConfig = {
239+
first: "Jane",
240+
last: "Doe",
241+
locale: "en",
242+
availableLanguages: {},
243+
}
244+
const errorMsgPrefix = "[vscode]"
245+
const errorMessage = `${errorMsgPrefix} Could not register require on self. window is undefined.`
246+
const mockSelf = {} as Window & typeof globalThis
247+
expect(() => {
248+
registerRequireOnSelf({
249+
self: mockSelf,
250+
// @ts-expect-error We need to test if window is undefined
251+
window: undefined,
252+
nlsConfig: nlsConfig,
253+
options,
254+
})
255+
}).toThrowError(errorMessage)
256+
})
257+
it("should throw an error if options.csStaticBase is undefined or an empty string", () => {
258+
const options = {
259+
base: "/",
260+
csStaticBase: "",
261+
logLevel: 1,
262+
}
263+
const nlsConfig = {
264+
first: "Jane",
265+
last: "Doe",
266+
locale: "en",
267+
availableLanguages: {},
268+
}
269+
const errorMsgPrefix = "[vscode]"
270+
const errorMessage = `${errorMsgPrefix} Could not register require on self. options or options.csStaticBase is undefined or missing.`
271+
const mockSelf = {} as Window & typeof globalThis
272+
expect(() => {
273+
registerRequireOnSelf({
274+
self: mockSelf,
275+
window: window,
276+
nlsConfig: nlsConfig,
277+
options,
278+
})
279+
}).toThrowError(errorMessage)
280+
expect(() => {
281+
registerRequireOnSelf({
282+
self: mockSelf,
283+
window: window,
284+
nlsConfig: nlsConfig,
285+
// @ts-expect-error We need to check what happens when options is undefined
286+
options: undefined,
287+
})
288+
}).toThrowError(errorMessage)
289+
})
290+
it("should throw an error if nlsConfig is undefined", () => {
291+
const options = {
292+
base: "/",
293+
csStaticBase: "/",
294+
logLevel: 1,
295+
}
296+
const errorMsgPrefix = "[vscode]"
297+
const errorMessage = `${errorMsgPrefix} Could not register require on self. nlsConfig is undefined.`
298+
const mockSelf = {} as Window & typeof globalThis
299+
expect(() => {
300+
registerRequireOnSelf({
301+
self: mockSelf,
302+
window: window,
303+
// @ts-expect-error We need to check that it works when this is undefined
304+
nlsConfig: undefined,
305+
options,
306+
})
307+
}).toThrowError(errorMessage)
308+
})
309+
it("should declare require on self", () => {
310+
const options = {
311+
base: "/",
312+
csStaticBase: "/",
313+
logLevel: 1,
314+
}
315+
const nlsConfig = {
316+
first: "Jane",
317+
last: "Doe",
318+
locale: "en",
319+
availableLanguages: {},
320+
}
321+
const mockSelf = {} as Window & typeof globalThis
322+
registerRequireOnSelf({
323+
self: mockSelf,
324+
window: window,
325+
nlsConfig: nlsConfig,
326+
options,
327+
})
328+
329+
const hasRequireProperty = Object.prototype.hasOwnProperty.call(mockSelf, "require")
330+
expect(hasRequireProperty).toBeTruthy()
331+
})
332+
it("should return true if it registered succesfully", () => {
333+
const options = {
334+
base: "/",
335+
csStaticBase: "/",
336+
logLevel: 1,
337+
}
338+
const nlsConfig = {
339+
first: "Jane",
340+
last: "Doe",
341+
locale: "en",
342+
availableLanguages: {},
343+
}
344+
const mockSelf = {} as Window & typeof globalThis
345+
const didRegister = registerRequireOnSelf({
346+
self: mockSelf,
347+
window: window,
348+
nlsConfig: nlsConfig,
349+
options,
350+
})
351+
352+
expect(didRegister).toBe(true)
353+
})
354+
})
178355
})

0 commit comments

Comments
 (0)