Skip to content

Commit

Permalink
feat: add registerRequireOnSelf on function
Browse files Browse the repository at this point in the history
  • Loading branch information
jsjoeio committed Jul 12, 2021
1 parent a96e16e commit 65c4f29
Show file tree
Hide file tree
Showing 2 changed files with 262 additions and 18 deletions.
103 changes: 85 additions & 18 deletions src/browser/pages/vscode.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getOptions } from "../../common/util"
import { getOptions, Options } from "../../common/util"
import "../register"

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

type RegisterRequireOnSelfType = {
// NOTE@jsjoeio
// We get the self type by looking at window.self.
self: Window & typeof globalThis
window: Window
nlsConfig: NlsConfiguration
options: Options
}

type RequireOnSelfType = {
baseUrl: string
recordStats: boolean
paths: {
[key: string]: string
}
"vs/nls": NlsConfiguration
}

/**
* A helper function to register the require on self.
*
* The require property is used by VSCode/code-server
* to load files.
*
* We extracted the logic into a function so that
* it's easier to test.
**/
export function registerRequireOnSelf({ self, window, nlsConfig, options }: RegisterRequireOnSelfType) {
const errorMsgPrefix = "[vscode]"

if (!self) {
throw new Error(`${errorMsgPrefix} Could not register require on self. self is undefined.`)
}

if (!window) {
throw new Error(`${errorMsgPrefix} Could not register require on self. window is undefined.`)
}

if (!options || !options.csStaticBase) {
throw new Error(
`${errorMsgPrefix} Could not register require on self. options or options.csStaticBase is undefined or missing.`,
)
}

if (!nlsConfig) {
throw new Error(`${errorMsgPrefix} Could not register require on self. nlsConfig is undefined.`)
}

const requireOnSelf: RequireOnSelfType = {
// Without the full URL VS Code will try to load file://.
baseUrl: `${window.location.origin}${options.csStaticBase}/lib/vscode/out`,
recordStats: true,
paths: {
"vscode-textmate": `../node_modules/vscode-textmate/release/main`,
"vscode-oniguruma": `../node_modules/vscode-oniguruma/release/main`,
xterm: `../node_modules/xterm/lib/xterm.js`,
"xterm-addon-search": `../node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
"xterm-addon-unicode11": `../node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
"xterm-addon-webgl": `../node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
"tas-client-umd": `../node_modules/tas-client-umd/lib/tas-client-umd.js`,
"iconv-lite-umd": `../node_modules/iconv-lite-umd/lib/iconv-lite-umd.js`,
jschardet: `../node_modules/jschardet/dist/jschardet.min.js`,
},
"vs/nls": nlsConfig,
}

// TODO@jsjoeio
// I'm not sure how to properly type cast this
// This might be our best bet
// Source: https://stackoverflow.com/a/30740935
type FixMeLater = any
;(self.require as FixMeLater) = requireOnSelf

// If everything worked, then return true
// so the caller knows it registered succesfully
return true
}

try {
const nlsConfig = getNlsConfiguration(document)
if (nlsConfig._resolvedLanguagePackCoreLocation) {
Expand All @@ -74,23 +152,12 @@ try {
.catch(cb)
}
}
;(self.require as any) = {
// Without the full URL VS Code will try to load file://.
baseUrl: `${window.location.origin}${options.csStaticBase}/lib/vscode/out`,
recordStats: true,
paths: {
"vscode-textmate": `../node_modules/vscode-textmate/release/main`,
"vscode-oniguruma": `../node_modules/vscode-oniguruma/release/main`,
xterm: `../node_modules/xterm/lib/xterm.js`,
"xterm-addon-search": `../node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
"xterm-addon-unicode11": `../node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
"xterm-addon-webgl": `../node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
"tas-client-umd": `../node_modules/tas-client-umd/lib/tas-client-umd.js`,
"iconv-lite-umd": `../node_modules/iconv-lite-umd/lib/iconv-lite-umd.js`,
jschardet: `../node_modules/jschardet/dist/jschardet.min.js`,
},
"vs/nls": nlsConfig,
}
registerRequireOnSelf({
self,
window,
nlsConfig,
options,
})
} catch (error) {
console.error(error)
}
Expand Down
177 changes: 177 additions & 0 deletions test/unit/browser/vscode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { JSDOM } from "jsdom"
import {
getNlsConfiguration,
nlsConfigElementId,
registerRequireOnSelf,
setBodyBackgroundToThemeBackgroundColor,
} from "../../../src/browser/pages/vscode"

Expand Down Expand Up @@ -175,4 +176,180 @@ describe("vscode", () => {
localStorage.removeItem("colorThemeData")
})
})
describe("registerRequireOnSelf", () => {
beforeAll(() => {
const { window } = new JSDOM()
// @ts-expect-error We know these are the exact same type
// but we need to do this for the test to work
global.self = window.self
global.window = window as unknown as Window & typeof globalThis
global.document = window.document
global.navigator = window.navigator
global.location = location as Location
})

beforeEach(() => {
jest.clearAllMocks()
})

afterEach(() => {
jest.resetModules()
})

afterAll(() => {
jest.restoreAllMocks()

global.window = undefined as unknown as Window & typeof globalThis
global.document = undefined as unknown as Document & typeof globalThis
global.navigator = undefined as unknown as Navigator & typeof globalThis
global.location = undefined as unknown as Location & typeof globalThis
})
it("should throw an error if self is undefined", () => {
const options = {
base: "/",
csStaticBase: "/hello",
logLevel: 1,
}
const nlsConfig = {
first: "Jane",
last: "Doe",
locale: "en",
availableLanguages: {},
}
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not register require on self. self is undefined.`
expect(() => {
registerRequireOnSelf({
// @ts-expect-error We are checking what happens if self is undefined.
self: undefined,
window: global.window,
nlsConfig: nlsConfig,
options,
})
}).toThrowError(errorMessage)
})

it("should throw an error if window is undefined", () => {
const options = {
base: "/",
csStaticBase: "/hello",
logLevel: 1,
}
const nlsConfig = {
first: "Jane",
last: "Doe",
locale: "en",
availableLanguages: {},
}
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not register require on self. window is undefined.`
const mockSelf = {} as Window & typeof globalThis
expect(() => {
registerRequireOnSelf({
self: mockSelf,
// @ts-expect-error We need to test if window is undefined
window: undefined,
nlsConfig: nlsConfig,
options,
})
}).toThrowError(errorMessage)
})
it("should throw an error if options.csStaticBase is undefined or an empty string", () => {
const options = {
base: "/",
csStaticBase: "",
logLevel: 1,
}
const nlsConfig = {
first: "Jane",
last: "Doe",
locale: "en",
availableLanguages: {},
}
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not register require on self. options or options.csStaticBase is undefined or missing.`
const mockSelf = {} as Window & typeof globalThis
expect(() => {
registerRequireOnSelf({
self: mockSelf,
window: window,
nlsConfig: nlsConfig,
options,
})
}).toThrowError(errorMessage)
expect(() => {
registerRequireOnSelf({
self: mockSelf,
window: window,
nlsConfig: nlsConfig,
// @ts-expect-error We need to check what happens when options is undefined
options: undefined,
})
}).toThrowError(errorMessage)
})
it("should throw an error if nlsConfig is undefined", () => {
const options = {
base: "/",
csStaticBase: "/",
logLevel: 1,
}
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not register require on self. nlsConfig is undefined.`
const mockSelf = {} as Window & typeof globalThis
expect(() => {
registerRequireOnSelf({
self: mockSelf,
window: window,
// @ts-expect-error We need to check that it works when this is undefined
nlsConfig: undefined,
options,
})
}).toThrowError(errorMessage)
})
it("should declare require on self", () => {
const options = {
base: "/",
csStaticBase: "/",
logLevel: 1,
}
const nlsConfig = {
first: "Jane",
last: "Doe",
locale: "en",
availableLanguages: {},
}
const mockSelf = {} as Window & typeof globalThis
registerRequireOnSelf({
self: mockSelf,
window: window,
nlsConfig: nlsConfig,
options,
})

const hasRequireProperty = Object.prototype.hasOwnProperty.call(mockSelf, "require")
expect(hasRequireProperty).toBeTruthy()
})
it("should return true if it registered succesfully", () => {
const options = {
base: "/",
csStaticBase: "/",
logLevel: 1,
}
const nlsConfig = {
first: "Jane",
last: "Doe",
locale: "en",
availableLanguages: {},
}
const mockSelf = {} as Window & typeof globalThis
const didRegister = registerRequireOnSelf({
self: mockSelf,
window: window,
nlsConfig: nlsConfig,
options,
})

expect(didRegister).toBe(true)
})
})
})

0 comments on commit 65c4f29

Please sign in to comment.