Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cbc753cbb |
@@ -23,9 +23,6 @@ rules:
|
|||||||
no-dupe-class-members: off
|
no-dupe-class-members: off
|
||||||
"@typescript-eslint/no-use-before-define": off
|
"@typescript-eslint/no-use-before-define": off
|
||||||
"@typescript-eslint/no-non-null-assertion": off
|
"@typescript-eslint/no-non-null-assertion": off
|
||||||
eqeqeq: error
|
|
||||||
import/order:
|
|
||||||
[error, { alphabetize: { order: "asc" }, groups: [["builtin", "external", "internal"], "parent", "sibling"] }]
|
|
||||||
|
|
||||||
settings:
|
settings:
|
||||||
# Does not work with CommonJS unfortunately.
|
# Does not work with CommonJS unfortunately.
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ MINIFY=${MINIFY-true}
|
|||||||
main() {
|
main() {
|
||||||
cd "$(dirname "${0}")/../.."
|
cd "$(dirname "${0}")/../.."
|
||||||
|
|
||||||
tsc
|
tsc --outDir out --tsBuildInfoFile .cache/out.tsbuildinfo
|
||||||
|
|
||||||
# If out/node/entry.js does not already have the shebang,
|
# If out/node/entry.js does not already have the shebang,
|
||||||
# we make sure to add it and make it executable.
|
# we make sure to add it and make it executable.
|
||||||
if ! grep -q -m1 "^#!/usr/bin/env node" out/node/entry.js; then
|
if ! grep -q -m1 "^#!/usr/bin/env node" out/node/entry.js; then
|
||||||
@@ -23,9 +22,7 @@ main() {
|
|||||||
--out-dir dist \
|
--out-dir dist \
|
||||||
$([[ $MINIFY ]] || echo --no-minify) \
|
$([[ $MINIFY ]] || echo --no-minify) \
|
||||||
src/browser/register.ts \
|
src/browser/register.ts \
|
||||||
src/browser/serviceWorker.ts \
|
src/browser/serviceWorker.ts
|
||||||
src/browser/pages/login.ts \
|
|
||||||
src/browser/pages/vscode.ts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
@@ -164,12 +164,7 @@ class Watcher {
|
|||||||
|
|
||||||
private createBundler(out = "dist"): Bundler {
|
private createBundler(out = "dist"): Bundler {
|
||||||
return new Bundler(
|
return new Bundler(
|
||||||
[
|
[path.join(this.rootPath, "src/browser/register.ts"), path.join(this.rootPath, "src/browser/serviceWorker.ts")],
|
||||||
path.join(this.rootPath, "src/browser/register.ts"),
|
|
||||||
path.join(this.rootPath, "src/browser/serviceWorker.ts"),
|
|
||||||
path.join(this.rootPath, "src/browser/pages/login.ts"),
|
|
||||||
path.join(this.rootPath, "src/browser/pages/vscode.ts"),
|
|
||||||
],
|
|
||||||
{
|
{
|
||||||
outDir: path.join(this.rootPath, out),
|
outDir: path.join(this.rootPath, out),
|
||||||
cacheDir: path.join(this.rootPath, ".cache"),
|
cacheDir: path.join(this.rootPath, ".cache"),
|
||||||
|
|||||||
@@ -47,5 +47,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/dist/register.js"></script>
|
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/dist/register.js"></script>
|
||||||
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/dist/login.js"></script>
|
<script>
|
||||||
|
const parts = window.location.pathname.replace(/^\//g, "").split("/")
|
||||||
|
parts[parts.length - 1] = "{{BASE}}"
|
||||||
|
const url = new URL(window.location.origin + "/" + parts.join("/"))
|
||||||
|
document.getElementById("base").value = url.pathname
|
||||||
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { getOptions } from "../../common/util"
|
|
||||||
|
|
||||||
const options = getOptions()
|
|
||||||
const el = document.getElementById("base") as HTMLInputElement
|
|
||||||
if (el) {
|
|
||||||
el.value = options.base
|
|
||||||
}
|
|
||||||
@@ -43,7 +43,47 @@
|
|||||||
<body aria-label=""></body>
|
<body aria-label=""></body>
|
||||||
|
|
||||||
<!-- Startup (do not modify order of script tags!) -->
|
<!-- Startup (do not modify order of script tags!) -->
|
||||||
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/dist/pages/vscode.js"></script>
|
<script>
|
||||||
|
let nlsConfig
|
||||||
|
try {
|
||||||
|
nlsConfig = JSON.parse(document.getElementById("vscode-remote-nls-configuration").getAttribute("data-settings"))
|
||||||
|
if (nlsConfig._resolvedLanguagePackCoreLocation) {
|
||||||
|
const bundles = Object.create(null)
|
||||||
|
nlsConfig.loadBundle = (bundle, language, cb) => {
|
||||||
|
let result = bundles[bundle]
|
||||||
|
if (result) {
|
||||||
|
return cb(undefined, result)
|
||||||
|
}
|
||||||
|
// FIXME: Only works if path separators are /.
|
||||||
|
const path = nlsConfig._resolvedLanguagePackCoreLocation + "/" + bundle.replace(/\//g, "!") + ".nls.json"
|
||||||
|
fetch(`{{BASE}}/resource/?path=${encodeURIComponent(path)}`)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((json) => {
|
||||||
|
bundles[bundle] = json
|
||||||
|
cb(undefined, json)
|
||||||
|
})
|
||||||
|
.catch(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
/* Probably fine. */
|
||||||
|
}
|
||||||
|
self.require = {
|
||||||
|
baseUrl: "{{CS_STATIC_BASE}}/lib/vscode/out",
|
||||||
|
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`,
|
||||||
|
"semver-umd": `../node_modules/semver-umd/lib/semver-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,
|
||||||
|
}
|
||||||
|
</script>
|
||||||
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/dist/register.js"></script>
|
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/dist/register.js"></script>
|
||||||
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/lib/vscode/out/vs/loader.js"></script>
|
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/lib/vscode/out/vs/loader.js"></script>
|
||||||
<!-- PROD_ONLY
|
<!-- PROD_ONLY
|
||||||
@@ -53,4 +93,11 @@
|
|||||||
<script>
|
<script>
|
||||||
require(["vs/code/browser/workbench/workbench"], function () {})
|
require(["vs/code/browser/workbench/workbench"], function () {})
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
try {
|
||||||
|
document.body.style.background = JSON.parse(localStorage.getItem("colorThemeData")).colorMap["editor.background"]
|
||||||
|
} catch (error) {
|
||||||
|
// Oh well.
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
import { getOptions } from "../../common/util"
|
|
||||||
|
|
||||||
const options = getOptions()
|
|
||||||
|
|
||||||
// TODO: Add proper types.
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
|
|
||||||
let nlsConfig: any
|
|
||||||
try {
|
|
||||||
nlsConfig = JSON.parse(document.getElementById("vscode-remote-nls-configuration")!.getAttribute("data-settings")!)
|
|
||||||
if (nlsConfig._resolvedLanguagePackCoreLocation) {
|
|
||||||
const bundles = Object.create(null)
|
|
||||||
nlsConfig.loadBundle = (bundle: any, _language: any, cb: any): void => {
|
|
||||||
const result = bundles[bundle]
|
|
||||||
if (result) {
|
|
||||||
return cb(undefined, result)
|
|
||||||
}
|
|
||||||
// FIXME: Only works if path separators are /.
|
|
||||||
const path = nlsConfig._resolvedLanguagePackCoreLocation + "/" + bundle.replace(/\//g, "!") + ".nls.json"
|
|
||||||
fetch(`{{BASE}}/resource/?path=${encodeURIComponent(path)}`)
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((json) => {
|
|
||||||
bundles[bundle] = json
|
|
||||||
cb(undefined, json)
|
|
||||||
})
|
|
||||||
.catch(cb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
/* Probably fine. */
|
|
||||||
}
|
|
||||||
|
|
||||||
;(self.require as any) = {
|
|
||||||
baseUrl: `${options.csStaticBase}/lib/vscode/out`,
|
|
||||||
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`,
|
|
||||||
"semver-umd": `../node_modules/semver-umd/lib/semver-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,
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
document.body.style.background = JSON.parse(localStorage.getItem("colorThemeData")!).colorMap["editor.background"]
|
|
||||||
} catch (error) {
|
|
||||||
// Oh well.
|
|
||||||
}
|
|
||||||
@@ -172,7 +172,7 @@ export const parse = (
|
|||||||
const arg = argv[i]
|
const arg = argv[i]
|
||||||
|
|
||||||
// -- signals the end of option parsing.
|
// -- signals the end of option parsing.
|
||||||
if (!ended && arg === "--") {
|
if (!ended && arg == "--") {
|
||||||
ended = true
|
ended = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -220,7 +220,7 @@ export const parse = (
|
|||||||
throw error(`--${key} requires a value`)
|
throw error(`--${key} requires a value`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option.type === OptionalString && value === "false") {
|
if (option.type == OptionalString && value == "false") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -868,7 +868,6 @@ export class HttpServer {
|
|||||||
// isn't setting the host header to match the access domain.
|
// isn't setting the host header to match the access domain.
|
||||||
host === "localhost"
|
host === "localhost"
|
||||||
) {
|
) {
|
||||||
logger.debug("no valid cookie doman", field("host", host))
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -878,7 +877,6 @@ export class HttpServer {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.debug("got cookie doman", field("host", host))
|
|
||||||
return host ? `Domain=${host}` : undefined
|
return host ? `Domain=${host}` : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { logger } from "@coder/logger"
|
|
||||||
import * as fs from "fs-extra"
|
import * as fs from "fs-extra"
|
||||||
import * as path from "path"
|
import * as path from "path"
|
||||||
|
import { extend, paths } from "./util"
|
||||||
|
import { logger } from "@coder/logger"
|
||||||
import { Route } from "./http"
|
import { Route } from "./http"
|
||||||
import { paths } from "./util"
|
|
||||||
|
|
||||||
export type Settings = { [key: string]: Settings | string | boolean | number }
|
export type Settings = { [key: string]: Settings | string | boolean | number }
|
||||||
|
|
||||||
@@ -30,12 +30,12 @@ export class SettingsProvider<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Write settings combined with current settings. On failure log a warning.
|
* Write settings combined with current settings. On failure log a warning.
|
||||||
* Settings will be merged shallowly.
|
* Settings can be shallow or deep merged.
|
||||||
*/
|
*/
|
||||||
public async write(settings: Partial<T>): Promise<void> {
|
public async write(settings: Partial<T>, shallow = true): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const oldSettings = await this.read()
|
const oldSettings = await this.read()
|
||||||
const nextSettings = { ...oldSettings, ...settings }
|
const nextSettings = shallow ? Object.assign({}, oldSettings, settings) : extend(oldSettings, settings)
|
||||||
await fs.writeFile(this.settingsPath, JSON.stringify(nextSettings, null, 2))
|
await fs.writeFile(this.settingsPath, JSON.stringify(nextSettings, null, 2))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(error.message)
|
logger.warn(error.message)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as cp from "child_process"
|
import * as cp from "child_process"
|
||||||
import * as crypto from "crypto"
|
import * as crypto from "crypto"
|
||||||
import envPaths from "env-paths"
|
|
||||||
import * as fs from "fs-extra"
|
import * as fs from "fs-extra"
|
||||||
import * as os from "os"
|
import * as os from "os"
|
||||||
import * as path from "path"
|
import * as path from "path"
|
||||||
import * as util from "util"
|
import * as util from "util"
|
||||||
|
import envPaths from "env-paths"
|
||||||
import xdgBasedir from "xdg-basedir"
|
import xdgBasedir from "xdg-basedir"
|
||||||
|
|
||||||
export const tmpdir = path.join(os.tmpdir(), "code-server")
|
export const tmpdir = path.join(os.tmpdir(), "code-server")
|
||||||
@@ -199,6 +199,25 @@ export const isObject = <T extends object>(obj: T): obj is T => {
|
|||||||
return !Array.isArray(obj) && typeof obj === "object" && obj !== null
|
return !Array.isArray(obj) && typeof obj === "object" && obj !== null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend a with b and return a new object. Properties with objects will be
|
||||||
|
* recursively merged while all other properties are just overwritten.
|
||||||
|
*/
|
||||||
|
export function extend<A, B>(a: A, b: B): A & B
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export function extend(...args: any[]): any {
|
||||||
|
const c = {} as any // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
|
for (const obj of args) {
|
||||||
|
if (!isObject(obj)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (const key in obj) {
|
||||||
|
c[key] = isObject(obj[key]) ? extend(c[key], obj[key]) : obj[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Taken from vs/base/common/charCode.ts. Copied for now instead of importing so
|
* Taken from vs/base/common/charCode.ts. Copied for now instead of importing so
|
||||||
* we don't have to set up a `vs` alias to be able to import with types (since
|
* we don't have to set up a `vs` alias to be able to import with types (since
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class IpcMain {
|
|||||||
public readonly onMessage = this._onMessage.event
|
public readonly onMessage = this._onMessage.event
|
||||||
private readonly _onDispose = new Emitter<NodeJS.Signals | undefined>()
|
private readonly _onDispose = new Emitter<NodeJS.Signals | undefined>()
|
||||||
public readonly onDispose = this._onDispose.event
|
public readonly onDispose = this._onDispose.event
|
||||||
public readonly exit: (code?: number) => never
|
public readonly processExit: (code?: number) => never
|
||||||
|
|
||||||
public constructor(public readonly parentPid?: number) {
|
public constructor(public readonly parentPid?: number) {
|
||||||
process.on("SIGINT", () => this._onDispose.emit("SIGINT"))
|
process.on("SIGINT", () => this._onDispose.emit("SIGINT"))
|
||||||
@@ -40,7 +40,7 @@ export class IpcMain {
|
|||||||
process.on("exit", () => this._onDispose.emit(undefined))
|
process.on("exit", () => this._onDispose.emit(undefined))
|
||||||
|
|
||||||
// Ensure we control when the process exits.
|
// Ensure we control when the process exits.
|
||||||
this.exit = process.exit
|
this.processExit = process.exit
|
||||||
process.exit = function (code?: number) {
|
process.exit = function (code?: number) {
|
||||||
logger.warn(`process.exit() was prevented: ${code || "unknown code"}.`)
|
logger.warn(`process.exit() was prevented: ${code || "unknown code"}.`)
|
||||||
} as (code?: number) => never
|
} as (code?: number) => never
|
||||||
@@ -71,6 +71,14 @@ export class IpcMain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public exit(error?: number | ProcessError): never {
|
||||||
|
if (error && typeof error !== "number") {
|
||||||
|
this.processExit(typeof error.code === "number" ? error.code : 1)
|
||||||
|
} else {
|
||||||
|
this.processExit(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public handshake(child?: cp.ChildProcess): Promise<void> {
|
public handshake(child?: cp.ChildProcess): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const target = child || process
|
const target = child || process
|
||||||
@@ -161,11 +169,26 @@ export class WrapperProcess {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain().onMessage(async (message) => {
|
ipcMain().onMessage((message) => {
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case "relaunch":
|
case "relaunch":
|
||||||
logger.info(`Relaunching: ${this.currentVersion} -> ${message.version}`)
|
logger.info(`Relaunching: ${this.currentVersion} -> ${message.version}`)
|
||||||
this.currentVersion = message.version
|
this.currentVersion = message.version
|
||||||
|
this.relaunch()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
logger.error(`Unrecognized message ${message}`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
process.on("SIGUSR1", async () => {
|
||||||
|
logger.info("Received SIGUSR1; hotswapping")
|
||||||
|
this.relaunch()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private async relaunch(): Promise<void> {
|
||||||
this.started = undefined
|
this.started = undefined
|
||||||
if (this.process) {
|
if (this.process) {
|
||||||
this.process.removeAllListeners()
|
this.process.removeAllListeners()
|
||||||
@@ -177,12 +200,6 @@ export class WrapperProcess {
|
|||||||
logger.error(error.message)
|
logger.error(error.message)
|
||||||
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
|
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
|
||||||
}
|
}
|
||||||
break
|
|
||||||
default:
|
|
||||||
logger.error(`Unrecognized message ${message}`)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public start(): Promise<void> {
|
public start(): Promise<void> {
|
||||||
@@ -244,13 +261,13 @@ export const wrap = (fn: () => Promise<void>): void => {
|
|||||||
.then(() => fn())
|
.then(() => fn())
|
||||||
.catch((error: ProcessError): void => {
|
.catch((error: ProcessError): void => {
|
||||||
logger.error(error.message)
|
logger.error(error.message)
|
||||||
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
|
ipcMain().exit(error)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
const wrapper = new WrapperProcess(require("../../package.json").version)
|
const wrapper = new WrapperProcess(require("../../package.json").version)
|
||||||
wrapper.start().catch((error) => {
|
wrapper.start().catch((error) => {
|
||||||
logger.error(error.message)
|
logger.error(error.message)
|
||||||
ipcMain().exit(typeof error.code === "number" ? error.code : 1)
|
ipcMain().exit(error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import * as net from "net"
|
|||||||
import * as path from "path"
|
import * as path from "path"
|
||||||
import * as tls from "tls"
|
import * as tls from "tls"
|
||||||
import { Emitter } from "../src/common/emitter"
|
import { Emitter } from "../src/common/emitter"
|
||||||
import { SocketProxyProvider } from "../src/node/socket"
|
|
||||||
import { generateCertificate, tmpdir } from "../src/node/util"
|
import { generateCertificate, tmpdir } from "../src/node/util"
|
||||||
|
import { SocketProxyProvider } from "../src/node/socket"
|
||||||
|
|
||||||
describe("SocketProxyProvider", () => {
|
describe("SocketProxyProvider", () => {
|
||||||
const provider = new SocketProxyProvider()
|
const provider = new SocketProxyProvider()
|
||||||
|
|||||||
@@ -1,7 +1,43 @@
|
|||||||
import * as assert from "assert"
|
import * as assert from "assert"
|
||||||
import { normalize } from "../src/common/util"
|
import { normalize } from "../src/common/util"
|
||||||
|
import { extend } from "../src/node/util"
|
||||||
|
|
||||||
describe("util", () => {
|
describe("util", () => {
|
||||||
|
describe("extend", () => {
|
||||||
|
it("should extend", () => {
|
||||||
|
const a = { foo: { bar: 0, baz: 2 }, garply: 4, waldo: 6 }
|
||||||
|
const b = { foo: { bar: 1, qux: 3 }, garply: "5", fred: 7 }
|
||||||
|
const extended = extend(a, b)
|
||||||
|
assert.deepEqual(extended, {
|
||||||
|
foo: { bar: 1, baz: 2, qux: 3 },
|
||||||
|
garply: "5",
|
||||||
|
waldo: 6,
|
||||||
|
fred: 7,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should make deep copies of the original objects", () => {
|
||||||
|
const a = { foo: 0, bar: { frobnozzle: 2 }, mumble: { qux: { thud: 4 } } }
|
||||||
|
const b = { foo: 1, bar: { chad: 3 } }
|
||||||
|
const extended = extend(a, b)
|
||||||
|
assert.notEqual(a.bar, extended.bar)
|
||||||
|
assert.notEqual(b.bar, extended.bar)
|
||||||
|
assert.notEqual(a.mumble, extended.mumble)
|
||||||
|
assert.notEqual(a.mumble.qux, extended.mumble.qux)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle mismatch in type", () => {
|
||||||
|
const a = { foo: { bar: 0, baz: 2, qux: { mumble: 11 } }, garply: 4, waldo: { thud: 10 } }
|
||||||
|
const b = { foo: { bar: [1], baz: { plugh: 8 }, qux: 12 }, garply: { nox: 9 }, waldo: 7 }
|
||||||
|
const extended = extend(a, b)
|
||||||
|
assert.deepEqual(extended, {
|
||||||
|
foo: { bar: [1], baz: { plugh: 8 }, qux: 12 },
|
||||||
|
garply: { nox: 9 },
|
||||||
|
waldo: 7,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("normalize", () => {
|
describe("normalize", () => {
|
||||||
it("should remove multiple slashes", () => {
|
it("should remove multiple slashes", () => {
|
||||||
assert.equal(normalize("//foo//bar//baz///mumble"), "/foo/bar/baz/mumble")
|
assert.equal(normalize("//foo//bar//baz///mumble"), "/foo/bar/baz/mumble")
|
||||||
|
|||||||
@@ -8,15 +8,17 @@
|
|||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"outDir": "./out",
|
"outDir": "./out",
|
||||||
|
"allowJs": false,
|
||||||
|
"jsx": "react",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"tsBuildInfoFile": "./.cache/tsbuildinfo",
|
"tsBuildInfoFile": "./.tsbuildinfo",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"typeRoots": ["./node_modules/@types", "./typings"]
|
"typeRoots": ["./node_modules/@types", "./typings"]
|
||||||
},
|
},
|
||||||
"include": ["./src/**/*.ts"]
|
"include": ["./src/**/*.ts", "./src/**/*.tsx"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user