Skip to content

Commit

Permalink
Merge branch 'unicode-and-case-wordSplit' of github.com:guy-borderles…
Browse files Browse the repository at this point in the history
…s/deno_std into unicode-and-case-wordSplit
  • Loading branch information
guy-borderless committed Jul 22, 2024
2 parents 165855d + 118c21f commit dfd42c7
Show file tree
Hide file tree
Showing 57 changed files with 703 additions and 679 deletions.
20 changes: 20 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,23 @@ delimiter. E.g.
* ```
*/
````

### Notices for unstable APIs

Each unstable API must have the
[`@experimental`](https://tsdoc.org/pages/tags/experimental/) TSDoc tag and a
[warning alert](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts)
after the starting description.

```ts
/**
* <description>
*
* > [!WARNING]
* > **UNSTABLE**: New API, yet to be vetted.
*
* @experimental
*
* ...
*/
```
99 changes: 99 additions & 0 deletions Releases.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,102 @@
### 2024.07.19

#### @std/async 1.0.0 (major)

- chore(async): release `[email protected]` (#5211)

#### @std/cli 1.0.0 (major)

- docs(cli): fix options arguments display (#5486)
- docs(cli): improve unstable API notices (#5482)
- docs(cli): documentation tweaks (#5458)
- chore(cli): release `[email protected]` (#5212)

#### @std/collections 1.0.5 (patch)

- refactor(collections): use `Set.prototype.intersection()` method in
`intersect()` (#5417)

#### @std/csv 1.0.0-rc.4 (prerelease)

- BREAKING(csv): remove `ParseError` (#5405)

#### @std/dotenv 0.225.0 (minor)

- BREAKING(dotenv): remove `defaultPath` option from `load[Sync]()` (#5451)
- BREAKING(dotenv): remove `examplePath` option from `load[Sync]()` (#5450)

#### @std/expect 1.0.0-rc.3 (prerelease)

- fix(expect): improve `expect` type to make it work better with `expect.extend`
(#5309)

#### @std/fs 1.0.0-rc.6 (prerelease)

- docs(fs): fix options argument display (#5491)
- docs(fs): fix options argument display (#5487)

#### @std/http 1.0.0-rc.5 (prerelease)

- docs(http): fix options argument display (#5488)
- docs(http): fix options argument display (#5489)
- docs(http): improve unstable API notices (#5483)

#### @std/json 1.0.0-rc.3 (prerelease)

- docs(json): fix options argument display (#5490)

#### @std/jsonc 1.0.0-rc.3 (prerelease)

- docs(jsonc): remove docs for removed `options` parameter (#5438)

#### @std/media-types 1.0.2 (patch)

- fix(media-types): return `video/mp4` for `.mp4` extension (#5475)

#### @std/net 1.0.0-rc.2 (prerelease)

- docs(net): tweak `getNetworkAddress()` return doc (#5473)

#### @std/path 1.0.1 (patch)

- fix(path): support use in dnt (#5478)

#### @std/semver 1.0.0-rc.3 (prerelease)

- BREAKING(semver): replace `prerelease` and `buildmetadata` arguments with
options object (#5471)

#### @std/streams 1.0.0-rc.4 (prerelease)

- docs(streams): address documentation issues (#5480)
- chore(streams): release `[email protected]` (#5213)

#### @std/text 1.0.0 (major)

- BREAKING(text): align to single-export file pattern (#5428)
- feat(text): add ability for user to control word comparison function (#5448)
- docs(text): state complexity of `levenshteinDistance()` (#5472)
- chore(text): release `[email protected]` (#5209)

#### @std/ulid 1.0.0 (major)

- fix(ulid): fix decode-time export path (#5432)
- chore(ulid): release `[email protected]` (#5206)

#### @std/yaml 1.0.0-rc.4 (prerelease)

- BREAKING(yaml): replace `YamlError` with `TypeError` in `stringify()` (#5452)
- BREAKING(yaml): replace `YamlError` with `SyntaxError` in `parse()` (#5446)
- docs(yaml): list `extended` schema (#5444)
- refactor(yaml): change `object` type from `any` to `unknown` in `writeNode`
(#5404)
- refactor(yaml): remove `instanceOf` field of `Type` (#5462)
- refactor(yaml): add `KindType` generic type argument to `Type` (#5461)
- refactor(yaml): improve `Type.predicate` behavior (#5460)
- refactor(yaml): remove `Type.loadKind` property (#5459)
- refactor(yaml): remove dead code in `Schema` constructor (#5445)
- refactor(yaml): make `Type.kind` required (#5442)

### 2024.07.12

#### @std/async 1.0.0-rc.4 (prerelease)
Expand Down
1 change: 0 additions & 1 deletion _tools/check_docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,6 @@ function assertConstructorDocs(
assertHasParamTag(constructor, param.left.name);
}
}
assertHasExampleTag(constructor);
}

/**
Expand Down
161 changes: 93 additions & 68 deletions assert/object_match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,82 +32,107 @@ export function assertObjectMatch(
expected: Record<PropertyKey, unknown>,
msg?: string,
): void {
type loose = Record<PropertyKey, unknown>;
return assertEquals(
// get the intersection of "actual" and "expected"
// side effect: all the instances' constructor field is "Object" now.
filter(actual, expected),
// set (nested) instances' constructor field to be "Object" without changing expected value.
// see https://github.com/denoland/deno_std/pull/1419
filter(expected, expected),
msg,
);
}

type loose = Record<PropertyKey, unknown>;

function isObject(val: unknown): boolean {
return typeof val === "object" && val !== null;
}

function filter(a: loose, b: loose) {
const seen = new WeakMap();
return filterObject(a, b);

function filterObject(a: loose, b: loose): loose {
// Prevent infinite loop with circular references with same filter
if ((seen.has(a)) && (seen.get(a) === b)) {
return a;
}

try {
seen.set(a, b);
} catch (err) {
if (err instanceof TypeError) {
throw new TypeError(
`Cannot assertObjectMatch ${a === null ? null : `type ${typeof a}`}`,
);
}
}

function filter(a: loose, b: loose) {
const seen = new WeakMap();
return fn(a, b);
// Filter keys and symbols which are present in both actual and expected
const filtered = {} as loose;
const keysA = Reflect.ownKeys(a);
const keysB = Reflect.ownKeys(b);
const entries = keysA.filter((key) => keysB.includes(key))
.map((key) => [key, a[key as string]]) as Array<[string, unknown]>;

function fn(a: loose, b: loose): loose {
// Prevent infinite loop with circular references with same filter
if ((seen.has(a)) && (seen.get(a) === b)) {
return a;
if (keysA.length && keysB.length && !entries.length) {
// If both objects are not empty but don't have the same keys or symbols,
// returns the entries in object a.
for (const key of keysA) {
filtered[key] = a[key];
}
try {
seen.set(a, b);
} catch (err) {
if (err instanceof TypeError) {
throw new TypeError(
`Cannot assertObjectMatch ${
a === null ? null : `type ${typeof a}`
}`,

return filtered;
}

for (const [key, value] of entries) {
// On regexp references, keep value as it to avoid loosing pattern and flags
if (value instanceof RegExp) {
filtered[key] = value;
continue;
}

const subset = (b as loose)[key];

// On array references, build a filtered array and filter nested objects inside
if (Array.isArray(value) && Array.isArray(subset)) {
filtered[key] = filterObject({ ...value }, { ...subset });
continue;
}

// On nested objects references, build a filtered object recursively
if (isObject(value) && isObject(subset)) {
// When both operands are maps, build a filtered map with common keys and filter nested objects inside
if ((value instanceof Map) && (subset instanceof Map)) {
filtered[key] = new Map(
[...value].filter(([k]) => subset.has(k)).map(
([k, v]) => {
const v2 = subset.get(k);
if (isObject(v) && isObject(v2)) {
return [k, filterObject(v as loose, v2 as loose)];
}

return [k, v];
},
),
);
continue;
}
}
// Filter keys and symbols which are present in both actual and expected
const filtered = {} as loose;
const entries = [
...Object.getOwnPropertyNames(a),
...Object.getOwnPropertySymbols(a),
]
.filter((key) => key in b)
.map((key) => [key, a[key as string]]) as Array<[string, unknown]>;
for (const [key, value] of entries) {
// On array references, build a filtered array and filter nested objects inside
if (Array.isArray(value)) {
const subset = (b as loose)[key];
if (Array.isArray(subset)) {
filtered[key] = fn({ ...value }, { ...subset });
continue;
}
} // On regexp references, keep value as it to avoid loosing pattern and flags
else if (value instanceof RegExp) {
filtered[key] = value;

// When both operands are set, build a filtered set with common values
if ((value instanceof Set) && (subset instanceof Set)) {
filtered[key] = value.intersection(subset);
continue;
} // On nested objects references, build a filtered object recursively
else if (typeof value === "object" && value !== null) {
const subset = (b as loose)[key];
if ((typeof subset === "object") && subset) {
// When both operands are maps, build a filtered map with common keys and filter nested objects inside
if ((value instanceof Map) && (subset instanceof Map)) {
filtered[key] = new Map(
[...value].filter(([k]) => subset.has(k)).map((
[k, v],
) => [k, typeof v === "object" ? fn(v, subset.get(k)) : v]),
);
continue;
}
// When both operands are set, build a filtered set with common values
if ((value instanceof Set) && (subset instanceof Set)) {
filtered[key] = new Set([...value].filter((v) => subset.has(v)));
continue;
}
filtered[key] = fn(value as loose, subset as loose);
continue;
}
}
filtered[key] = value;

filtered[key] = filterObject(value as loose, subset as loose);
continue;
}
return filtered;

filtered[key] = value;
}

return filtered;
}
return assertEquals(
// get the intersection of "actual" and "expected"
// side effect: all the instances' constructor field is "Object" now.
filter(actual, expected),
// set (nested) instances' constructor field to be "Object" without changing expected value.
// see https://github.com/denoland/deno_std/pull/1419
filter(expected, expected),
msg,
);
}
43 changes: 43 additions & 0 deletions assert/object_match_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ Deno.test("assertObjectMatch() throws assertion error when in the first argument
() => assertObjectMatch({ foo: undefined, bar: null }, { foo: null }),
AssertionError,
);
assertThrows(
() => assertObjectMatch(n, { baz: new Map([["b", null]]) }),
);
});

Deno.test("assertObjectMatch() throws readable type error for non mappable primitive types", () => {
Expand All @@ -319,3 +322,43 @@ Deno.test("assertObjectMatch() throws readable type error for non mappable primi
"assertObjectMatch",
);
});

Deno.test("assertObjectMatch() prints inputs correctly", () => {
const x = {
command: "error",
payload: {
message: "NodeNotFound",
},
protocol: "graph",
};

const y = {
protocol: "graph",
command: "addgroup",
payload: {
graph: "foo",
metadata: {
description: "foo",
},
name: "somegroup",
},
};

assertThrows(
() => assertObjectMatch(x, y),
AssertionError,
` {
+ command: "addgroup",
- command: "error",
payload: {
+ graph: "foo",
+ metadata: {
+ description: "foo",
+ },
+ name: "somegroup",
- message: "NodeNotFound",
},
protocol: "graph",
}`,
);
});
2 changes: 1 addition & 1 deletion async/deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@std/async",
"version": "1.0.0-rc.4",
"version": "1.0.0",
"exports": {
".": "./mod.ts",
"./abortable": "./abortable.ts",
Expand Down
2 changes: 1 addition & 1 deletion cli/deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@std/cli",
"version": "1.0.0-rc.5",
"version": "1.0.0",
"exports": {
".": "./mod.ts",
"./parse-args": "./parse_args.ts",
Expand Down
Loading

0 comments on commit dfd42c7

Please sign in to comment.