mirror of
https://github.com/denoland/deno.git
synced 2024-12-27 01:29:14 -05:00
Add TOML module (#300)
This commit is contained in:
parent
cac060f388
commit
fa1664e6cc
21 changed files with 1072 additions and 0 deletions
1
test.ts
1
test.ts
|
@ -15,6 +15,7 @@ import "./prettier/test.ts";
|
||||||
import "./strings/test.ts";
|
import "./strings/test.ts";
|
||||||
import "./testing/test.ts";
|
import "./testing/test.ts";
|
||||||
import "./textproto/test.ts";
|
import "./textproto/test.ts";
|
||||||
|
import "./toml/test.ts";
|
||||||
import "./ws/test.ts";
|
import "./ws/test.ts";
|
||||||
|
|
||||||
import "./testing/main.ts";
|
import "./testing/main.ts";
|
||||||
|
|
95
toml/README.md
Normal file
95
toml/README.md
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
# TOML
|
||||||
|
|
||||||
|
This module parse TOML files. It follows as much as possible the
|
||||||
|
[TOML specs](https://github.com/toml-lang/toml). Be sure to read the supported
|
||||||
|
types as not every specs is supported at the moment and the handling in
|
||||||
|
TypeScript side is a bit different.
|
||||||
|
|
||||||
|
## Supported types and handling
|
||||||
|
|
||||||
|
- :heavy_check_mark: [Keys](https://github.com/toml-lang/toml#string)
|
||||||
|
- :exclamation: [String](https://github.com/toml-lang/toml#string)
|
||||||
|
- :heavy_check_mark:
|
||||||
|
[Multiline String](https://github.com/toml-lang/toml#string)
|
||||||
|
- :heavy_check_mark: [Literal String](https://github.com/toml-lang/toml#string)
|
||||||
|
- :exclamation: [Integer](https://github.com/toml-lang/toml#integer)
|
||||||
|
- :heavy_check_mark: [Float](https://github.com/toml-lang/toml#float)
|
||||||
|
- :heavy_check_mark: [Boolean](https://github.com/toml-lang/toml#boolean)
|
||||||
|
- :heavy_check_mark:
|
||||||
|
[Offset Date-time](https://github.com/toml-lang/toml#offset-date-time)
|
||||||
|
- :heavy_check_mark:
|
||||||
|
[Local Date-time](https://github.com/toml-lang/toml#local-date-time)
|
||||||
|
- :heavy_check_mark: [Local Date](https://github.com/toml-lang/toml#local-date)
|
||||||
|
- :exclamation: [Local Time](https://github.com/toml-lang/toml#local-time)
|
||||||
|
- :heavy_check_mark: [Table](https://github.com/toml-lang/toml#table)
|
||||||
|
- :exclamation: [Inline Table](https://github.com/toml-lang/toml#inline-table)
|
||||||
|
- :exclamation: [Array of Tables](https://github.com/toml-lang/toml#array-of-tables)
|
||||||
|
|
||||||
|
:exclamation: _Supported with warnings see [Warning](#Warning)._
|
||||||
|
|
||||||
|
### :warning: Warning
|
||||||
|
|
||||||
|
#### String
|
||||||
|
|
||||||
|
- Regex : Due to the spec, there is no flag to detect regex properly
|
||||||
|
in a TOML declaration. So the regex is stored as string.
|
||||||
|
|
||||||
|
#### Integer
|
||||||
|
|
||||||
|
For **Binary** / **Octal** / **Hexadecimal** numbers,
|
||||||
|
they are stored as string to be not interpreted as Decimal.
|
||||||
|
|
||||||
|
#### Local Time
|
||||||
|
|
||||||
|
Because local time does not exist in JavaScript, the local time is stored as a string.
|
||||||
|
|
||||||
|
#### Inline Table
|
||||||
|
|
||||||
|
Inline tables are supported but nested inline property name are **not**. See below:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
animal = { type = { name = "pug" } } # Supported
|
||||||
|
animal = { type.name = "pug" }
|
||||||
|
# not supported. Will output { "animal" : {"type.name":"pug"} }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Array of Tables
|
||||||
|
|
||||||
|
At the moment only simple declarations like below are supported:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[bin]]
|
||||||
|
name = "deno"
|
||||||
|
path = "cli/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "deno_core"
|
||||||
|
path = "src/foo.rs"
|
||||||
|
|
||||||
|
[[nib]]
|
||||||
|
name = "node"
|
||||||
|
path = "not_found"
|
||||||
|
```
|
||||||
|
|
||||||
|
will output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"bin": [
|
||||||
|
{ "name": "deno", "path": "cli/main.rs" },
|
||||||
|
{ "name": "deno_core", "path": "src/foo.rs" }
|
||||||
|
],
|
||||||
|
"nib": [{ "name": "node", "path": "not_found" }]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { parseFile, parse } from "./parser.ts";
|
||||||
|
|
||||||
|
const tomlObject = parseFile("file.toml");
|
||||||
|
|
||||||
|
const tomlString = 'foo.bar = "Deno"';
|
||||||
|
const tomlObject22 = parse(tomlString);
|
||||||
|
```
|
327
toml/parser.ts
Normal file
327
toml/parser.ts
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import { existsSync } from "../fs/exists.ts";
|
||||||
|
import { deepAssign } from "../util/deep_assign.ts";
|
||||||
|
|
||||||
|
class KeyValuePair {
|
||||||
|
key: string;
|
||||||
|
value: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParserGroup {
|
||||||
|
type: string;
|
||||||
|
name: string;
|
||||||
|
arrValues: unknown[] = [];
|
||||||
|
objValues: object = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParserContext {
|
||||||
|
currentGroup?: ParserGroup;
|
||||||
|
output: object = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parser {
|
||||||
|
tomlLines: string[];
|
||||||
|
context: ParserContext;
|
||||||
|
constructor(tomlString: string) {
|
||||||
|
this.tomlLines = this._split(tomlString);
|
||||||
|
this.context = new ParserContext();
|
||||||
|
}
|
||||||
|
_sanitize(): void {
|
||||||
|
const out = [];
|
||||||
|
for (let i = 0; i < this.tomlLines.length; i++) {
|
||||||
|
const s = this.tomlLines[i].split("#")[0];
|
||||||
|
if (s !== "") {
|
||||||
|
out.push(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tomlLines = out;
|
||||||
|
this._mergeMultilines();
|
||||||
|
}
|
||||||
|
|
||||||
|
_mergeMultilines(): void {
|
||||||
|
function arrayStart(line: string): boolean {
|
||||||
|
const reg = /.*=\s*\[/g;
|
||||||
|
return reg.test(line) && !(line[line.length - 1] === "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrayEnd(line: string): boolean {
|
||||||
|
return line[line.length - 1] === "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringStart(line: string): boolean {
|
||||||
|
const m = line.match(/.*=\s*(?:\"\"\"|''')/);
|
||||||
|
if (!m) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !line.endsWith(`"""`) || !line.endsWith(`'''`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringEnd(line: string): boolean {
|
||||||
|
return line.endsWith(`'''`) || line.endsWith(`"""`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLiteralString(line: string): boolean {
|
||||||
|
return line.match(/'''/) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let merged = [],
|
||||||
|
acc = [],
|
||||||
|
isLiteral = false,
|
||||||
|
capture = false,
|
||||||
|
captureType = "",
|
||||||
|
merge = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.tomlLines.length; i++) {
|
||||||
|
const line = this.tomlLines[i];
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (!capture && arrayStart(trimmed)) {
|
||||||
|
capture = true;
|
||||||
|
captureType = "array";
|
||||||
|
} else if (!capture && stringStart(trimmed)) {
|
||||||
|
isLiteral = isLiteralString(trimmed);
|
||||||
|
capture = true;
|
||||||
|
captureType = "string";
|
||||||
|
} else if (capture && arrayEnd(trimmed)) {
|
||||||
|
merge = true;
|
||||||
|
} else if (capture && stringEnd(trimmed)) {
|
||||||
|
merge = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capture) {
|
||||||
|
if (isLiteral) {
|
||||||
|
acc.push(line);
|
||||||
|
} else {
|
||||||
|
acc.push(trimmed);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isLiteral) {
|
||||||
|
merged.push(line);
|
||||||
|
} else {
|
||||||
|
merged.push(trimmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (merge) {
|
||||||
|
capture = false;
|
||||||
|
merge = false;
|
||||||
|
if (captureType === "string") {
|
||||||
|
merged.push(
|
||||||
|
acc
|
||||||
|
.join("\n")
|
||||||
|
.replace(/"""/g, '"')
|
||||||
|
.replace(/'''/g, `'`)
|
||||||
|
.replace(/\n/g, "\\n")
|
||||||
|
);
|
||||||
|
isLiteral = false;
|
||||||
|
} else {
|
||||||
|
merged.push(acc.join(""));
|
||||||
|
}
|
||||||
|
captureType = "";
|
||||||
|
acc = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tomlLines = merged;
|
||||||
|
}
|
||||||
|
_unflat(keys: string[], values: object = {}, cObj: object = {}): object {
|
||||||
|
let out = {};
|
||||||
|
if (keys.length === 0) {
|
||||||
|
return cObj;
|
||||||
|
} else {
|
||||||
|
if (Object.keys(cObj).length === 0) {
|
||||||
|
cObj = values;
|
||||||
|
}
|
||||||
|
let key = keys.pop();
|
||||||
|
out[key] = cObj;
|
||||||
|
return this._unflat(keys, values, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_groupToOutput(): void {
|
||||||
|
const arrProperty = this.context.currentGroup.name
|
||||||
|
.replace(/"/g, "")
|
||||||
|
.replace(/'/g, "")
|
||||||
|
.split(".");
|
||||||
|
let u = {};
|
||||||
|
if (this.context.currentGroup.type === "array") {
|
||||||
|
u = this._unflat(arrProperty, this.context.currentGroup.arrValues);
|
||||||
|
} else {
|
||||||
|
u = this._unflat(arrProperty, this.context.currentGroup.objValues);
|
||||||
|
}
|
||||||
|
deepAssign(this.context.output, u);
|
||||||
|
delete this.context.currentGroup;
|
||||||
|
}
|
||||||
|
_split(str: string): string[] {
|
||||||
|
let out = [];
|
||||||
|
out.push(...str.split("\n"));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
_isGroup(line: string): boolean {
|
||||||
|
const t = line.trim();
|
||||||
|
return t[0] === "[" && t[t.length - 1] === "]";
|
||||||
|
}
|
||||||
|
_isDeclaration(line: string): boolean {
|
||||||
|
return line.split("=").length > 1;
|
||||||
|
}
|
||||||
|
_createGroup(line: string): void {
|
||||||
|
const captureReg = /\[(.*)\]/;
|
||||||
|
if (this.context.currentGroup) {
|
||||||
|
this._groupToOutput();
|
||||||
|
}
|
||||||
|
let g = new ParserGroup();
|
||||||
|
g.name = line.match(captureReg)[1];
|
||||||
|
if (g.name.match(/\[.*\]/)) {
|
||||||
|
g.type = "array";
|
||||||
|
g.name = g.name.match(captureReg)[1];
|
||||||
|
} else {
|
||||||
|
g.type = "object";
|
||||||
|
}
|
||||||
|
this.context.currentGroup = g;
|
||||||
|
}
|
||||||
|
_processDeclaration(line: string): KeyValuePair {
|
||||||
|
let kv = new KeyValuePair();
|
||||||
|
const idx = line.indexOf("=");
|
||||||
|
kv.key = line.substring(0, idx).trim();
|
||||||
|
kv.value = this._parseData(line.slice(idx + 1));
|
||||||
|
return kv;
|
||||||
|
}
|
||||||
|
_parseData(dataString: string): unknown {
|
||||||
|
dataString = dataString.trim();
|
||||||
|
if (this._isDate(dataString)) {
|
||||||
|
return new Date(dataString);
|
||||||
|
}
|
||||||
|
if (this._isLocalTime(dataString)) {
|
||||||
|
return eval(`"${dataString}"`);
|
||||||
|
}
|
||||||
|
if (dataString === "inf" || dataString === "+inf") {
|
||||||
|
return Infinity;
|
||||||
|
}
|
||||||
|
if (dataString === "-inf") {
|
||||||
|
return -Infinity;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
dataString === "nan" ||
|
||||||
|
dataString === "+nan" ||
|
||||||
|
dataString === "-nan"
|
||||||
|
) {
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
// inline table
|
||||||
|
if (dataString[0] === "{" && dataString[dataString.length - 1] === "}") {
|
||||||
|
const reg = /([a-zA-Z0-9-_\.]*) (=)/gi;
|
||||||
|
let result;
|
||||||
|
while ((result = reg.exec(dataString))) {
|
||||||
|
let ogVal = result[0];
|
||||||
|
let newVal = ogVal
|
||||||
|
.replace(result[1], `"${result[1]}"`)
|
||||||
|
.replace(result[2], ":");
|
||||||
|
dataString = dataString.replace(ogVal, newVal);
|
||||||
|
}
|
||||||
|
// TODO : unflat if necessary
|
||||||
|
return JSON.parse(dataString);
|
||||||
|
}
|
||||||
|
// If binary / octal / hex
|
||||||
|
if (
|
||||||
|
dataString[0] === "0" &&
|
||||||
|
(dataString[1] === "b" || dataString[1] === "o" || dataString[1] === "x")
|
||||||
|
) {
|
||||||
|
return dataString;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._isParsableNumber(dataString)) {
|
||||||
|
return eval(dataString.replace(/_/g, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle First and last EOL for multiline strings
|
||||||
|
if (dataString.startsWith(`"\\n`)) {
|
||||||
|
dataString = dataString.replace(`"\\n`, `"`);
|
||||||
|
} else if (dataString.startsWith(`'\\n`)) {
|
||||||
|
dataString = dataString.replace(`'\\n`, `'`);
|
||||||
|
}
|
||||||
|
if (dataString.endsWith(`\\n"`)) {
|
||||||
|
dataString = dataString.replace(`\\n"`, `"`);
|
||||||
|
} else if (dataString.endsWith(`\\n'`)) {
|
||||||
|
dataString = dataString.replace(`\\n'`, `'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// dataString = dataString.replace(/\\/, "\\\\");
|
||||||
|
|
||||||
|
return eval(dataString);
|
||||||
|
}
|
||||||
|
_isLocalTime(str: string): boolean {
|
||||||
|
const reg = /(\d{2}):(\d{2}):(\d{2})/;
|
||||||
|
return reg.test(str);
|
||||||
|
}
|
||||||
|
_isParsableNumber(dataString: string): boolean {
|
||||||
|
let d = dataString.replace(/_/g, "");
|
||||||
|
return !isNaN(parseFloat(d));
|
||||||
|
}
|
||||||
|
_isDate(dateStr: string): boolean {
|
||||||
|
const reg = /\d{4}-\d{2}-\d{2}/;
|
||||||
|
return reg.test(dateStr);
|
||||||
|
}
|
||||||
|
_parseLines(): void {
|
||||||
|
for (let i = 0; i < this.tomlLines.length; i++) {
|
||||||
|
const line = this.tomlLines[i];
|
||||||
|
|
||||||
|
// TODO (zekth) Handle unflat of array of tables
|
||||||
|
if (this._isGroup(line)) {
|
||||||
|
// if the current group is an array we push the
|
||||||
|
// parsed objects in it.
|
||||||
|
if (
|
||||||
|
this.context.currentGroup &&
|
||||||
|
this.context.currentGroup.type === "array"
|
||||||
|
) {
|
||||||
|
this.context.currentGroup.arrValues.push(
|
||||||
|
this.context.currentGroup.objValues
|
||||||
|
);
|
||||||
|
this.context.currentGroup.objValues = {};
|
||||||
|
}
|
||||||
|
// If we need to create a group or to change group
|
||||||
|
if (
|
||||||
|
!this.context.currentGroup ||
|
||||||
|
(this.context.currentGroup &&
|
||||||
|
this.context.currentGroup.name !==
|
||||||
|
line.replace(/\[/g, "").replace(/\]/g, ""))
|
||||||
|
) {
|
||||||
|
this._createGroup(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this._isDeclaration(line)) {
|
||||||
|
let kv = this._processDeclaration(line);
|
||||||
|
if (!this.context.currentGroup) {
|
||||||
|
this.context.output[kv.key] = kv.value;
|
||||||
|
} else {
|
||||||
|
this.context.currentGroup.objValues[kv.key] = kv.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.context.currentGroup) {
|
||||||
|
if (this.context.currentGroup.type === "array") {
|
||||||
|
this.context.currentGroup.arrValues.push(
|
||||||
|
this.context.currentGroup.objValues
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this._groupToOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parse(): object {
|
||||||
|
this._sanitize();
|
||||||
|
this._parseLines();
|
||||||
|
return this.context.output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parse(tomlString: string): object {
|
||||||
|
// File is potentially using EOL CRLF
|
||||||
|
tomlString = tomlString.replace(/\r\n/g, "\n").replace(/\\\n/g, "\n");
|
||||||
|
return new Parser(tomlString).parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseFile(filePath: string): object {
|
||||||
|
if (!existsSync(filePath)) {
|
||||||
|
throw new Error("File not found");
|
||||||
|
}
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const strFile = decoder.decode(Deno.readFileSync(filePath));
|
||||||
|
return parse(strFile);
|
||||||
|
}
|
263
toml/parser_test.ts
Normal file
263
toml/parser_test.ts
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import { test } from "../testing/mod.ts";
|
||||||
|
import { assertEquals } from "../testing/asserts.ts";
|
||||||
|
import { parseFile } from "./parser.ts";
|
||||||
|
import * as path from "../fs/path/mod.ts";
|
||||||
|
const testFilesDir = path.resolve("toml", "testdata");
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Strings",
|
||||||
|
fn() {
|
||||||
|
const expected = {
|
||||||
|
strings: {
|
||||||
|
str0: "deno",
|
||||||
|
str1: "Roses are not Deno\nViolets are not Deno either",
|
||||||
|
str2: "Roses are not Deno\nViolets are not Deno either",
|
||||||
|
str3: "Roses are not Deno\r\nViolets are not Deno either",
|
||||||
|
str4: 'this is a "quote"',
|
||||||
|
str5: "The quick brown\nfox jumps over\nthe lazy dog.",
|
||||||
|
str6: "The quick brown\nfox jumps over\nthe lazy dog.",
|
||||||
|
lines:
|
||||||
|
"The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved."
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "string.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] CRLF",
|
||||||
|
fn() {
|
||||||
|
const expected = { boolean: { bool1: true, bool2: false } };
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "CRLF.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Boolean",
|
||||||
|
fn() {
|
||||||
|
const expected = { boolean: { bool1: true, bool2: false } };
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "boolean.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Integer",
|
||||||
|
fn() {
|
||||||
|
const expected = {
|
||||||
|
integer: {
|
||||||
|
int1: 99,
|
||||||
|
int2: 42,
|
||||||
|
int3: 0,
|
||||||
|
int4: -17,
|
||||||
|
int5: 1000,
|
||||||
|
int6: 5349221,
|
||||||
|
int7: 12345,
|
||||||
|
hex1: "0xDEADBEEF",
|
||||||
|
hex2: "0xdeadbeef",
|
||||||
|
hex3: "0xdead_beef",
|
||||||
|
oct1: "0o01234567",
|
||||||
|
oct2: "0o755",
|
||||||
|
bin1: "0b11010110"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "integer.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Float",
|
||||||
|
fn() {
|
||||||
|
const expected = {
|
||||||
|
float: {
|
||||||
|
flt1: 1.0,
|
||||||
|
flt2: 3.1415,
|
||||||
|
flt3: -0.01,
|
||||||
|
flt4: 5e22,
|
||||||
|
flt5: 1e6,
|
||||||
|
flt6: -2e-2,
|
||||||
|
flt7: 6.626e-34,
|
||||||
|
flt8: 224_617.445_991_228,
|
||||||
|
sf1: Infinity,
|
||||||
|
sf2: Infinity,
|
||||||
|
sf3: -Infinity,
|
||||||
|
sf4: NaN,
|
||||||
|
sf5: NaN,
|
||||||
|
sf6: NaN
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "float.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Arrays",
|
||||||
|
fn() {
|
||||||
|
const expected = {
|
||||||
|
arrays: {
|
||||||
|
data: [["gamma", "delta"], [1, 2]],
|
||||||
|
hosts: ["alpha", "omega"]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "arrays.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Table",
|
||||||
|
fn() {
|
||||||
|
const expected = {
|
||||||
|
deeply: {
|
||||||
|
nested: {
|
||||||
|
object: {
|
||||||
|
in: {
|
||||||
|
the: {
|
||||||
|
toml: {
|
||||||
|
name: "Tom Preston-Werner"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
servers: {
|
||||||
|
alpha: {
|
||||||
|
ip: "10.0.0.1",
|
||||||
|
dc: "eqdc10"
|
||||||
|
},
|
||||||
|
beta: {
|
||||||
|
ip: "10.0.0.2",
|
||||||
|
dc: "eqdc20"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "table.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Simple",
|
||||||
|
fn() {
|
||||||
|
const expected = {
|
||||||
|
deno: "is",
|
||||||
|
not: "[node]",
|
||||||
|
regex: "<ic*s*>",
|
||||||
|
NANI: "何?!"
|
||||||
|
};
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "simple.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Datetime",
|
||||||
|
fn() {
|
||||||
|
const expected = {
|
||||||
|
datetime: {
|
||||||
|
odt1: new Date("1979-05-27T07:32:00Z"),
|
||||||
|
odt2: new Date("1979-05-27T00:32:00-07:00"),
|
||||||
|
odt3: new Date("1979-05-27T00:32:00.999999-07:00"),
|
||||||
|
odt4: new Date("1979-05-27 07:32:00Z"),
|
||||||
|
ld1: new Date("1979-05-27"),
|
||||||
|
lt1: "07:32:00",
|
||||||
|
lt2: "00:32:00.999999"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "datetime.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Inline Table",
|
||||||
|
fn() {
|
||||||
|
const expected = {
|
||||||
|
inlinetable: {
|
||||||
|
name: {
|
||||||
|
first: "Tom",
|
||||||
|
last: "Preston-Werner"
|
||||||
|
},
|
||||||
|
point: {
|
||||||
|
x: 1,
|
||||||
|
y: 2
|
||||||
|
},
|
||||||
|
animal: {
|
||||||
|
type: {
|
||||||
|
name: "pug"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "inlineTable.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Array of Tables",
|
||||||
|
fn() {
|
||||||
|
const expected = {
|
||||||
|
bin: [
|
||||||
|
{ name: "deno", path: "cli/main.rs" },
|
||||||
|
{ name: "deno_core", path: "src/foo.rs" }
|
||||||
|
],
|
||||||
|
nib: [{ name: "node", path: "not_found" }]
|
||||||
|
};
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "arrayTable.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test({
|
||||||
|
name: "[TOML] Cargo",
|
||||||
|
fn() {
|
||||||
|
/* eslint-disable @typescript-eslint/camelcase */
|
||||||
|
const expected = {
|
||||||
|
workspace: { members: ["./", "core"] },
|
||||||
|
bin: [{ name: "deno", path: "cli/main.rs" }],
|
||||||
|
package: { name: "deno", version: "0.3.4", edition: "2018" },
|
||||||
|
dependencies: {
|
||||||
|
deno_core: { path: "./core" },
|
||||||
|
ansi_term: "0.11.0",
|
||||||
|
atty: "0.2.11",
|
||||||
|
dirs: "1.0.5",
|
||||||
|
flatbuffers: "0.5.0",
|
||||||
|
futures: "0.1.25",
|
||||||
|
getopts: "0.2.18",
|
||||||
|
http: "0.1.16",
|
||||||
|
hyper: "0.12.24",
|
||||||
|
"hyper-rustls": "0.16.0",
|
||||||
|
"integer-atomics": "1.0.2",
|
||||||
|
lazy_static: "1.3.0",
|
||||||
|
libc: "0.2.49",
|
||||||
|
log: "0.4.6",
|
||||||
|
rand: "0.6.5",
|
||||||
|
regex: "1.1.0",
|
||||||
|
remove_dir_all: "0.5.1",
|
||||||
|
ring: "0.14.6",
|
||||||
|
rustyline: "3.0.0",
|
||||||
|
serde_json: "1.0.38",
|
||||||
|
"source-map-mappings": "0.5.0",
|
||||||
|
tempfile: "3.0.7",
|
||||||
|
tokio: "0.1.15",
|
||||||
|
"tokio-executor": "0.1.6",
|
||||||
|
"tokio-fs": "0.1.5",
|
||||||
|
"tokio-io": "0.1.11",
|
||||||
|
"tokio-process": "0.2.3",
|
||||||
|
"tokio-threadpool": "0.1.11",
|
||||||
|
url: "1.7.2"
|
||||||
|
},
|
||||||
|
target: { "cfg(windows)": { dependencies: { winapi: "0.3.6" } } }
|
||||||
|
};
|
||||||
|
/* eslint-enable @typescript-eslint/camelcase */
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "cargo.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
}
|
||||||
|
});
|
2
toml/test.ts
Normal file
2
toml/test.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import "./parser_test.ts";
|
3
toml/testdata/CRLF.toml
vendored
Normal file
3
toml/testdata/CRLF.toml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[boolean]
|
||||||
|
bool1 = true
|
||||||
|
bool2 = false
|
12
toml/testdata/arrayTable.toml
vendored
Normal file
12
toml/testdata/arrayTable.toml
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "deno"
|
||||||
|
path = "cli/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "deno_core"
|
||||||
|
path = "src/foo.rs"
|
||||||
|
|
||||||
|
[[nib]]
|
||||||
|
name = "node"
|
||||||
|
path = "not_found"
|
8
toml/testdata/arrays.toml
vendored
Normal file
8
toml/testdata/arrays.toml
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[arrays]
|
||||||
|
data = [ ["gamma", "delta"], [1, 2] ]
|
||||||
|
|
||||||
|
# Line breaks are OK when inside arrays
|
||||||
|
hosts = [
|
||||||
|
"alpha",
|
||||||
|
"omega"
|
||||||
|
]
|
3
toml/testdata/boolean.toml
vendored
Normal file
3
toml/testdata/boolean.toml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[boolean]
|
||||||
|
bool1 = true
|
||||||
|
bool2 = false
|
56
toml/testdata/cargo.toml
vendored
Normal file
56
toml/testdata/cargo.toml
vendored
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
|
# Dummy package info required by `cargo fetch`.
|
||||||
|
# Use tools/sync_third_party.py to install deps after editing this file.
|
||||||
|
# Deno does not build with cargo. Deno uses a build system called gn.
|
||||||
|
# See build_extra/rust/BUILD.gn for the manually built configuration of rust
|
||||||
|
# crates.
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"./",
|
||||||
|
"core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "deno"
|
||||||
|
path = "cli/main.rs"
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "deno"
|
||||||
|
version = "0.3.4"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
deno_core = { path = "./core" }
|
||||||
|
|
||||||
|
ansi_term = "0.11.0"
|
||||||
|
atty = "0.2.11"
|
||||||
|
dirs = "1.0.5"
|
||||||
|
flatbuffers = "0.5.0"
|
||||||
|
futures = "0.1.25"
|
||||||
|
getopts = "0.2.18"
|
||||||
|
http = "0.1.16"
|
||||||
|
hyper = "0.12.24"
|
||||||
|
hyper-rustls = "0.16.0"
|
||||||
|
integer-atomics = "1.0.2"
|
||||||
|
lazy_static = "1.3.0"
|
||||||
|
libc = "0.2.49"
|
||||||
|
log = "0.4.6"
|
||||||
|
rand = "0.6.5"
|
||||||
|
regex = "1.1.0"
|
||||||
|
remove_dir_all = "0.5.1"
|
||||||
|
ring = "0.14.6"
|
||||||
|
rustyline = "3.0.0"
|
||||||
|
serde_json = "1.0.38"
|
||||||
|
source-map-mappings = "0.5.0"
|
||||||
|
tempfile = "3.0.7"
|
||||||
|
tokio = "0.1.15"
|
||||||
|
tokio-executor = "0.1.6"
|
||||||
|
tokio-fs = "0.1.5"
|
||||||
|
tokio-io = "0.1.11"
|
||||||
|
tokio-process = "0.2.3"
|
||||||
|
tokio-threadpool = "0.1.11"
|
||||||
|
url = "1.7.2"
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
winapi = "0.3.6"
|
147
toml/testdata/cargoTest.toml
vendored
Normal file
147
toml/testdata/cargoTest.toml
vendored
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
# This is a TOML document.
|
||||||
|
|
||||||
|
title = "TOML Example"
|
||||||
|
|
||||||
|
[deeply.nested.object.in.the.toml]
|
||||||
|
name = "Tom Preston-Werner"
|
||||||
|
dob = 2009-05-27T07:32:00
|
||||||
|
|
||||||
|
[database]
|
||||||
|
server = "192.168.1.1"
|
||||||
|
ports = [ 8001, 8001, 8002 ]
|
||||||
|
connection_max = 5000
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[servers]
|
||||||
|
|
||||||
|
# Indentation (tabs and/or spaces) is allowed but not required
|
||||||
|
[servers.alpha]
|
||||||
|
ip = "10.0.0.1"
|
||||||
|
dc = "eqdc10"
|
||||||
|
|
||||||
|
[servers.beta]
|
||||||
|
ip = "10.0.0.2"
|
||||||
|
dc = "eqdc10"
|
||||||
|
|
||||||
|
[clients]
|
||||||
|
data = [ ["gamma", "delta"], [1, 2] ]
|
||||||
|
|
||||||
|
# Line breaks are OK when inside arrays
|
||||||
|
hosts = [
|
||||||
|
"alpha",
|
||||||
|
"omega"
|
||||||
|
]
|
||||||
|
|
||||||
|
[strings]
|
||||||
|
str0 = "deno"
|
||||||
|
str1 = """
|
||||||
|
Roses are red
|
||||||
|
Violets are blue"""
|
||||||
|
# On a Unix system, the above multi-line string will most likely be the same as:
|
||||||
|
str2 = "Roses are red\nViolets are blue"
|
||||||
|
|
||||||
|
# On a Windows system, it will most likely be equivalent to:
|
||||||
|
str3 = "Roses are red\r\nViolets are blue"
|
||||||
|
str4 = "The quick brown fox jumps over the lazy dog."
|
||||||
|
str5 = "this is a \"quote\""
|
||||||
|
|
||||||
|
str5 = """
|
||||||
|
The quick brown \
|
||||||
|
|
||||||
|
|
||||||
|
fox jumps over \
|
||||||
|
the lazy dog."""
|
||||||
|
|
||||||
|
str6 = """\
|
||||||
|
The quick brown \
|
||||||
|
fox jumps over \
|
||||||
|
the lazy dog.\
|
||||||
|
"""
|
||||||
|
lines = '''
|
||||||
|
The first newline is
|
||||||
|
trimmed in raw strings.
|
||||||
|
All other whitespace
|
||||||
|
is preserved.
|
||||||
|
'''
|
||||||
|
|
||||||
|
[Integer]
|
||||||
|
int1 = +99
|
||||||
|
int2 = 42
|
||||||
|
int3 = 0
|
||||||
|
int4 = -17
|
||||||
|
int5 = 1_000
|
||||||
|
int6 = 5_349_221
|
||||||
|
int7 = 1_2_3_4_5 # VALID but discouraged
|
||||||
|
|
||||||
|
# hexadecimal with prefix `0x`
|
||||||
|
hex1 = 0xDEADBEEF
|
||||||
|
hex2 = 0xdeadbeef
|
||||||
|
hex3 = 0xdead_beef
|
||||||
|
|
||||||
|
# octal with prefix `0o`
|
||||||
|
oct1 = 0o01234567
|
||||||
|
oct2 = 0o755 # useful for Unix file permissions
|
||||||
|
|
||||||
|
# binary with prefix `0b`
|
||||||
|
bin1 = 0b11010110
|
||||||
|
|
||||||
|
[Date-Time]
|
||||||
|
odt1 = 1979-05-27T07:32:00Z
|
||||||
|
odt2 = 1979-05-27T00:32:00-07:00
|
||||||
|
odt3 = 1979-05-27T00:32:00.999999-07:00
|
||||||
|
odt4 = 1979-05-27 07:32:00Z
|
||||||
|
ld1 = 1979-05-27
|
||||||
|
lt1 = 07:32:00 #buggy
|
||||||
|
lt2 = 00:32:00.999999 #buggy
|
||||||
|
|
||||||
|
[boolean]
|
||||||
|
bool1 = true
|
||||||
|
bool2 = false
|
||||||
|
|
||||||
|
[float]
|
||||||
|
# fractional
|
||||||
|
flt1 = +1.0
|
||||||
|
flt2 = 3.1415
|
||||||
|
flt3 = -0.01
|
||||||
|
|
||||||
|
# exponent
|
||||||
|
flt4 = 5e+22
|
||||||
|
flt5 = 1e6
|
||||||
|
flt6 = -2E-2
|
||||||
|
|
||||||
|
# both
|
||||||
|
flt7 = 6.626e-34
|
||||||
|
flt8 = 224_617.445_991_228
|
||||||
|
# infinity
|
||||||
|
sf1 = inf # positive infinity
|
||||||
|
sf2 = +inf # positive infinity
|
||||||
|
sf3 = -inf # negative infinity
|
||||||
|
|
||||||
|
# not a number
|
||||||
|
sf4 = nan # actual sNaN/qNaN encoding is implementation specific
|
||||||
|
sf5 = +nan # same as `nan`
|
||||||
|
sf6 = -nan # valid, actual encoding is implementation specific
|
||||||
|
|
||||||
|
[Table]
|
||||||
|
name = { first = "Tom", last = "Preston-Werner" }
|
||||||
|
point = { x = 1, y = 2 }
|
||||||
|
animal = { type.name = "pug" }
|
||||||
|
|
||||||
|
[[fruit]]
|
||||||
|
name = "apple"
|
||||||
|
|
||||||
|
[fruit.physical]
|
||||||
|
color = "red"
|
||||||
|
shape = "round"
|
||||||
|
|
||||||
|
[[fruit.variety]]
|
||||||
|
name = "red delicious"
|
||||||
|
|
||||||
|
[[fruit.variety]]
|
||||||
|
name = "granny smith"
|
||||||
|
|
||||||
|
[[fruit]]
|
||||||
|
name = "banana"
|
||||||
|
|
||||||
|
[[fruit.variety]]
|
||||||
|
name = "plantain"
|
8
toml/testdata/datetime.toml
vendored
Normal file
8
toml/testdata/datetime.toml
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[datetime]
|
||||||
|
odt1 = 1979-05-27T07:32:00Z
|
||||||
|
odt2 = 1979-05-27T00:32:00-07:00
|
||||||
|
odt3 = 1979-05-27T00:32:00.999999-07:00
|
||||||
|
odt4 = 1979-05-27 07:32:00Z
|
||||||
|
ld1 = 1979-05-27
|
||||||
|
lt1 = 07:32:00
|
||||||
|
lt2 = 00:32:00.999999
|
23
toml/testdata/float.toml
vendored
Normal file
23
toml/testdata/float.toml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[float]
|
||||||
|
# fractional
|
||||||
|
flt1 = +1.0
|
||||||
|
flt2 = 3.1415
|
||||||
|
flt3 = -0.01
|
||||||
|
|
||||||
|
# exponent
|
||||||
|
flt4 = 5e+22
|
||||||
|
flt5 = 1e6
|
||||||
|
flt6 = -2E-2
|
||||||
|
|
||||||
|
# both
|
||||||
|
flt7 = 6.626e-34
|
||||||
|
flt8 = 224_617.445_991_228
|
||||||
|
# infinity
|
||||||
|
sf1 = inf # positive infinity
|
||||||
|
sf2 = +inf # positive infinity
|
||||||
|
sf3 = -inf # negative infinity
|
||||||
|
|
||||||
|
# not a number
|
||||||
|
sf4 = nan # actual sNaN/qNaN encoding is implementation specific
|
||||||
|
sf5 = +nan # same as `nan`
|
||||||
|
sf6 = -nan # valid, actual encoding is implementation specific
|
4
toml/testdata/inlineTable.toml
vendored
Normal file
4
toml/testdata/inlineTable.toml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[inlinetable]
|
||||||
|
name = { first = "Tom", last = "Preston-Werner" }
|
||||||
|
point = { x = 1, y = 2 }
|
||||||
|
animal = { type = { name = "pug" } }
|
20
toml/testdata/integer.toml
vendored
Normal file
20
toml/testdata/integer.toml
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[integer]
|
||||||
|
int1 = +99
|
||||||
|
int2 = 42
|
||||||
|
int3 = 0
|
||||||
|
int4 = -17
|
||||||
|
int5 = 1_000
|
||||||
|
int6 = 5_349_221
|
||||||
|
int7 = 1_2_3_4_5 # VALID but discouraged
|
||||||
|
|
||||||
|
# hexadecimal with prefix `0x`
|
||||||
|
hex1 = 0xDEADBEEF
|
||||||
|
hex2 = 0xdeadbeef
|
||||||
|
hex3 = 0xdead_beef
|
||||||
|
|
||||||
|
# octal with prefix `0o`
|
||||||
|
oct1 = 0o01234567
|
||||||
|
oct2 = 0o755 # useful for Unix file permissions
|
||||||
|
|
||||||
|
# binary with prefix `0b`
|
||||||
|
bin1 = 0b11010110
|
4
toml/testdata/simple.toml
vendored
Normal file
4
toml/testdata/simple.toml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
deno = "is"
|
||||||
|
not = "[node]"
|
||||||
|
regex = '<\i\c*\s*>'
|
||||||
|
NANI = '何?!'
|
30
toml/testdata/string.toml
vendored
Normal file
30
toml/testdata/string.toml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
[strings]
|
||||||
|
str0 = "deno"
|
||||||
|
str1 = """
|
||||||
|
Roses are not Deno
|
||||||
|
Violets are not Deno either"""
|
||||||
|
# On a Unix system, the above multi-line string will most likely be the same as:
|
||||||
|
str2 = "Roses are not Deno\nViolets are not Deno either"
|
||||||
|
|
||||||
|
# On a Windows system, it will most likely be equivalent to:
|
||||||
|
str3 = "Roses are not Deno\r\nViolets are not Deno either"
|
||||||
|
str4 = "this is a \"quote\""
|
||||||
|
|
||||||
|
str5 = """
|
||||||
|
The quick brown \
|
||||||
|
|
||||||
|
|
||||||
|
fox jumps over \
|
||||||
|
the lazy dog."""
|
||||||
|
|
||||||
|
str6 = """\
|
||||||
|
The quick brown \
|
||||||
|
fox jumps over \
|
||||||
|
the lazy dog.\
|
||||||
|
"""
|
||||||
|
lines = '''
|
||||||
|
The first newline is
|
||||||
|
trimmed in raw strings.
|
||||||
|
All other whitespace
|
||||||
|
is preserved.
|
||||||
|
'''
|
13
toml/testdata/table.toml
vendored
Normal file
13
toml/testdata/table.toml
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[deeply.nested.object.in.the.toml]
|
||||||
|
name = "Tom Preston-Werner"
|
||||||
|
|
||||||
|
[servers]
|
||||||
|
|
||||||
|
# Indentation (tabs and/or spaces) is allowed but not required
|
||||||
|
[servers.alpha]
|
||||||
|
ip = "10.0.0.1"
|
||||||
|
dc = "eqdc10"
|
||||||
|
|
||||||
|
[servers.beta]
|
||||||
|
ip = "10.0.0.2"
|
||||||
|
dc = "eqdc20"
|
28
util/deep_assign.ts
Normal file
28
util/deep_assign.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
|
export function deepAssign(target: object, ...sources: object[]): object {
|
||||||
|
for (let i = 0; i < sources.length; i++) {
|
||||||
|
const source = sources[i];
|
||||||
|
if (!source || typeof source !== `object`) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object.entries(source).forEach(([key, value]) => {
|
||||||
|
if (value instanceof Date) {
|
||||||
|
target[key] = new Date(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!value || typeof value !== `object`) {
|
||||||
|
target[key] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
target[key] = [];
|
||||||
|
}
|
||||||
|
// value is an Object
|
||||||
|
if (typeof target[key] !== `object` || !target[key]) {
|
||||||
|
target[key] = {};
|
||||||
|
}
|
||||||
|
deepAssign(target[key], value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
24
util/deep_assign_test.ts
Normal file
24
util/deep_assign_test.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import { test } from "../testing/mod.ts";
|
||||||
|
import { assertEquals, assert } from "../testing/asserts.ts";
|
||||||
|
import { deepAssign } from "./deep_assign.ts";
|
||||||
|
|
||||||
|
test(function deepAssignTest() {
|
||||||
|
const date = new Date("1979-05-27T07:32:00Z");
|
||||||
|
const reg = RegExp(/DENOWOWO/);
|
||||||
|
const obj1 = { deno: { bar: { deno: ["is", "not", "node"] } } };
|
||||||
|
const obj2 = { foo: { deno: date } };
|
||||||
|
const obj3 = { foo: { bar: "deno" }, reg: reg };
|
||||||
|
const actual = deepAssign(obj1, obj2, obj3);
|
||||||
|
const expected = {
|
||||||
|
foo: {
|
||||||
|
deno: new Date("1979-05-27T07:32:00Z"),
|
||||||
|
bar: "deno"
|
||||||
|
},
|
||||||
|
deno: { bar: { deno: ["is", "not", "node"] } },
|
||||||
|
reg: RegExp(/DENOWOWO/)
|
||||||
|
};
|
||||||
|
assert(date !== expected.foo.deno);
|
||||||
|
assert(reg !== expected.reg);
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
});
|
1
util/test.ts
Normal file
1
util/test.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import "./deep_assign_test.ts";
|
Loading…
Reference in a new issue