Skip to content

Commit 7a26391

Browse files
committed
feat: add registerRequireOnSelf on function
1 parent a96e16e commit 7a26391

File tree

2 files changed

+261
-18
lines changed

2 files changed

+261
-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

+176
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,179 @@ 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+
it("should throw an error if window is undefined", () => {
232+
const options = {
233+
base: "/",
234+
csStaticBase: "/hello",
235+
logLevel: 1,
236+
}
237+
const nlsConfig = {
238+
first: "Jane",
239+
last: "Doe",
240+
locale: "en",
241+
availableLanguages: {},
242+
}
243+
const errorMsgPrefix = "[vscode]"
244+
const errorMessage = `${errorMsgPrefix} Could not register require on self. window is undefined.`
245+
const mockSelf = {} as Window & typeof globalThis
246+
expect(() => {
247+
registerRequireOnSelf({
248+
self: mockSelf,
249+
// @ts-expect-error We need to test if window is undefined
250+
window: undefined,
251+
nlsConfig: nlsConfig,
252+
options,
253+
})
254+
}).toThrowError(errorMessage)
255+
})
256+
it("should throw an error if options.csStaticBase is undefined or an empty string", () => {
257+
const options = {
258+
base: "/",
259+
csStaticBase: "",
260+
logLevel: 1,
261+
}
262+
const nlsConfig = {
263+
first: "Jane",
264+
last: "Doe",
265+
locale: "en",
266+
availableLanguages: {},
267+
}
268+
const errorMsgPrefix = "[vscode]"
269+
const errorMessage = `${errorMsgPrefix} Could not register require on self. options or options.csStaticBase is undefined or missing.`
270+
const mockSelf = {} as Window & typeof globalThis
271+
expect(() => {
272+
registerRequireOnSelf({
273+
self: mockSelf,
274+
window: window,
275+
nlsConfig: nlsConfig,
276+
options,
277+
})
278+
}).toThrowError(errorMessage)
279+
expect(() => {
280+
registerRequireOnSelf({
281+
self: mockSelf,
282+
window: window,
283+
nlsConfig: nlsConfig,
284+
// @ts-expect-error We need to check what happens when options is undefined
285+
options: undefined,
286+
})
287+
}).toThrowError(errorMessage)
288+
})
289+
it("should throw an error if nlsConfig is undefined", () => {
290+
const options = {
291+
base: "/",
292+
csStaticBase: "/",
293+
logLevel: 1,
294+
}
295+
const errorMsgPrefix = "[vscode]"
296+
const errorMessage = `${errorMsgPrefix} Could not register require on self. nlsConfig is undefined.`
297+
const mockSelf = {} as Window & typeof globalThis
298+
expect(() => {
299+
registerRequireOnSelf({
300+
self: mockSelf,
301+
window: window,
302+
// @ts-expect-error We need to check that it works when this is undefined
303+
nlsConfig: undefined,
304+
options,
305+
})
306+
}).toThrowError(errorMessage)
307+
})
308+
it("should declare require on self", () => {
309+
const options = {
310+
base: "/",
311+
csStaticBase: "/",
312+
logLevel: 1,
313+
}
314+
const nlsConfig = {
315+
first: "Jane",
316+
last: "Doe",
317+
locale: "en",
318+
availableLanguages: {},
319+
}
320+
const mockSelf = {} as Window & typeof globalThis
321+
registerRequireOnSelf({
322+
self: mockSelf,
323+
window: window,
324+
nlsConfig: nlsConfig,
325+
options,
326+
})
327+
328+
const hasRequireProperty = Object.prototype.hasOwnProperty.call(mockSelf, "require")
329+
expect(hasRequireProperty).toBeTruthy()
330+
})
331+
it("should return true if it registered succesfully", () => {
332+
const options = {
333+
base: "/",
334+
csStaticBase: "/",
335+
logLevel: 1,
336+
}
337+
const nlsConfig = {
338+
first: "Jane",
339+
last: "Doe",
340+
locale: "en",
341+
availableLanguages: {},
342+
}
343+
const mockSelf = {} as Window & typeof globalThis
344+
const didRegister = registerRequireOnSelf({
345+
self: mockSelf,
346+
window: window,
347+
nlsConfig: nlsConfig,
348+
options,
349+
})
350+
351+
expect(didRegister).toBe(true)
352+
})
353+
})
178354
})

0 commit comments

Comments
 (0)