Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f2efa4a
fix(types): add missing Primitive type
nberlette Dec 6, 2022
48082d0
chore: update repo config
nberlette Dec 8, 2022
a6475cc
chore: update hooks
nberlette Dec 8, 2022
40418be
feat: update + move tools to _util
nberlette Dec 8, 2022
0be44a0
chore: update dependencies
nberlette Dec 8, 2022
de7df5c
refactor!: full rewrite to class-syntax
nberlette Dec 8, 2022
72559ed
refactor: assert.ts
nberlette Dec 8, 2022
7ac3139
chore(types): update deno.jsonc
nberlette Dec 8, 2022
17f30d3
types: add missing imports
nberlette Dec 8, 2022
2e9365b
refactor: project style
nberlette Dec 8, 2022
dfdc038
feat: add is.not (negated checks)
nberlette Dec 13, 2022
7c8b305
refactor: move decorators to _util/decorators
nberlette Dec 13, 2022
19f9650
refactor(util): types and tools in _util
nberlette Dec 13, 2022
a958a4a
chore(config): update egg.json and deno.jsonc
nberlette Dec 13, 2022
fb14005
chore: update assert.ts
nberlette Dec 13, 2022
539c10b
feat(util): add constants.ts
nberlette Dec 13, 2022
f97dc01
chore: add triple-slash libs
nberlette Dec 22, 2022
d72b864
feat(types): add shape types
nberlette Dec 22, 2022
47b2840
feat(types): add IsNaN and IsInfinity
nberlette Dec 22, 2022
bafe215
test: add initial unit tests
nberlette Dec 22, 2022
40c8e6e
feat: is.not negated interface
nberlette Dec 22, 2022
403ac20
feat: is.{empty,nonEmpty,set,map} subcategories
nberlette Dec 22, 2022
6d609ea
feat(config): add .github folder
nberlette Dec 22, 2022
eb51b66
chore(config): update config files
nberlette Dec 22, 2022
114f4b9
chore: rename egg.json -> egg.yaml
nberlette Dec 24, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/hooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -168,23 +168,29 @@ function __pre_commit_hook() {

# run fmt
eval "$__fmt" || return $?

# run lint
eval "$__lint" || return $?

# run test
eval "$__test" || return $?
local tests=$(eval "$__test" 2>&1)

if [[ $tests =~ ^.*("No test modules"|"no test specified").*$ ]]; then
: # noop
else
echo "$tests"
return $?
fi
# still here? commit!
return 0
}

if [[ $1 =~ ^(-{0,2}(help|info|usage))|-(h|?)$ ]]; then
usage
[[ $DEBUG =~ ^(1|true)$ ]] && debug
return 1
fi

precommit "$@"
[[ $DEBUG =~ ^(1|true)$ ]] && debug
return $?
}

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ Thumbs.db
node
deno.lock
_mod.ts
_descriptors.json
227 changes: 8 additions & 219 deletions _util.ts
Original file line number Diff line number Diff line change
@@ -1,219 +1,8 @@
import {
objectTypeNames,
primitiveTypeNames,
typedArrayTypeNames,
} from "./types.ts";

import type {
ArrayMethod,
ObjectTypeName,
Predicate,
PrimitiveTypeName,
TypedArrayTypeName,
TypeName,
} from "./types.ts";

export { objectTypeNames, primitiveTypeNames, typedArrayTypeNames };

export type { ObjectTypeName, PrimitiveTypeName, TypedArrayTypeName, TypeName };

export const predicateOnArray = (
method: ArrayMethod,
predicate: Predicate,
values: unknown[],
) => {
if (typeof predicate !== "function") {
throw new TypeError(`Invalid predicate: ${JSON.stringify(predicate)}`);
}

if (values.length === 0) {
throw new TypeError("Invalid number of values");
}

return method.call(values, predicate);
};

export function isTypedArrayName(name: unknown): name is TypedArrayTypeName {
return typedArrayTypeNames.includes(name as TypedArrayTypeName);
}

export function isPrimitiveTypeName(name: unknown): name is PrimitiveTypeName {
return primitiveTypeNames.includes(name as PrimitiveTypeName);
}

export function isOfType<T extends Primitive | Function>(
type: PrimitiveTypeName | "function",
) {
// deno-lint-ignore valid-typeof
return (value: unknown): value is T => typeof value === type;
}

/**
* Objects
*/
export function isObjectTypeName(name: unknown): name is ObjectTypeName {
return objectTypeNames.includes(name as ObjectTypeName);
}

export const { toString } = Object.prototype;

const NODE_TYPE_ELEMENT = 1;

const DOM_PROPERTIES_TO_CHECK: Array<(keyof HTMLElement)> = [
"innerHTML",
"ownerDocument",
"style",
"attributes",
"nodeValue",
];

export const isDomElement = (value: unknown): value is HTMLElement => {
return (typeof value === "object" && value !== null) &&
(value as HTMLElement).nodeType === NODE_TYPE_ELEMENT &&
typeof ((value as HTMLElement).nodeName) === "string" &&
!(toString.call(value).slice(8, -1) !== "Object") &&
DOM_PROPERTIES_TO_CHECK.every((property) => property in value);
};

export const getObjectType = (value: unknown): ObjectTypeName | undefined => {
if (typeof value !== "object") return undefined;

const objectTypeName = toString.call(value).slice(8, -1);

if (/(HTML|SVG)\w+Element/.test(objectTypeName) && isDomElement(value)) {
return "HTMLElement";
}

if (isObjectTypeName(objectTypeName)) {
return objectTypeName;
}

return "Object";
};

export const isObjectOfType = <T>(type: ObjectTypeName) =>
function (value: unknown): value is T {
return getObjectType(value) === type;
};

/**
* Some few keywords are reserved, but we'll populate them for Node.js users.
* @see https://github.com/Microsoft/TypeScript/issues/2536
*/
type union = Record<never, never>;
// for (const prop of ["class_", "function_", "null_"] as const) {

interface DeprecateOptions {
hide?: boolean;
seal?: boolean;
}

export function deprecate<T extends {}, K extends keyof T>(
target: T,
key: (keyof T) | Array<keyof T>,
options: DeprecateOptions,
): void;

export function deprecate<T extends {}>(
target: T,
...arg: Array<keyof T | (keyof T)[] | DeprecateOptions>
): void;

export function deprecate<T extends {}>(target: T, ...rest: unknown[]): void {
const options: DeprecateOptions = {};
const _keys: string[] = [];
if (Array.isArray(rest) && rest.length > 0) {
_keys.push(
...(rest.filter((a) => typeof a === "string" || Array.isArray(a))),
);
Object.assign(
options,
...(
rest.filter((a) => typeof a === "object" && !Array.isArray(a))
),
);
}

const keyExists = Object.hasOwn.bind(target, target);
// assemble the list of keys to deprecate, ensure they exist
const keys = [_keys].flat(2).filter(keyExists);
// get all property descriptors for the target object
const descriptorMap = Object.getOwnPropertyDescriptors(target);
// only include the descriptors that we are concerned with deprecating
const descriptors = Object.entries(descriptorMap).filter(
([key]) => keys.includes(key),
);

const { hide = true, seal = true } = (options || {});

for (const [key, desc] of descriptors) {
const descriptor: PropertyDescriptor = {
enumerable: !hide,
configurable: !seal,
};

if (
("value" in desc && typeof desc.value !== "undefined") ||
("writable" in desc && typeof desc.writable === "boolean")
) {
const { value, writable = !seal } = desc;
descriptor.writable = seal ? false : writable;
Reflect.defineProperty(target, key, { ...descriptor, value });
} else if (
("get" in desc && typeof desc.get === "function") ||
("set" in desc && typeof desc.set === "function")
) {
const { get, set = undefined } = desc;
Reflect.defineProperty(target, key, { ...descriptor, get, set });
}
}
}

export function freeze<T extends {}>(object: T): void;
export function freeze<T extends unknown[]>(...object: T): void;
export function freeze(...objects: unknown[]): void {
for (const o of objects) {
Object.freeze(o);
// freeze prototypes too
if (typeof o === "function" && o.prototype !== undefined) {
Object.freeze(o.prototype);
}
}
}

/** Type-safe version of Object.assign */
export function assign<T extends {}, U>(
target: T,
source: U,
): asserts target is T & U;
/** Type-safe version of Object.assign */
export function assign<T extends {}, U, V>(
to: T,
s_0: U,
s_1: V,
): asserts to is T & U & V;
export function assign<T extends {}, U, V, W>(
to: T,
s0: U,
s1: V,
s2: W,
): asserts to is T & U & V & W;
export function assign<T extends {}, U, V, W, X>(
to: T,
s0: U,
s1: V,
s2: W,
s3: X,
): asserts to is T & U & V & W & X;
export function assign<T extends {}, U, V, W, X, Y>(
to: T,
s0: U,
s1: V,
s2: W,
s3: X,
s4: Y,
): asserts to is T & U & V & W & X & Y;

export function assign(target: object, ...source: object[]) {
Object.assign(target, ...source);
}
export * from "./_util/ansi.ts";
export * from "./_util/assign.ts";
export * from "./_util/constants.ts";
export * from "./_util/decorators.ts";
export * from "./_util/predicates.ts";
export * from "./_util/prototypes.ts";
export * from "./_util/typenames.ts";
export * from "./_util/types.d.ts";
11 changes: 11 additions & 0 deletions _util/ansi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const ansi = (s: string, pre = 1, post = 22) =>
`\x1b[${pre}m${s}\x1b[${post}m`;

ansi.bold = (s: string) => ansi(s, 1, 22);
ansi.dim = (s: string) => ansi(s, 2, 22);
ansi.italic = (s: string) => ansi(s, 3, 23);
ansi.underline = (s: string) => ansi(s, 4, 24);
ansi.inverse = (s: string) => ansi(s, 7, 27);
ansi.hidden = (s: string) => ansi(s, 8, 28);
ansi.strikethrough = (s: string) => ansi(s, 9, 29);
ansi.reset = (s: string) => ansi(s, 0, 0);
17 changes: 17 additions & 0 deletions _util/assign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { UnionToIntersection } from "./types.d.ts";

/** Type-safe version of Object.assign */
export function assign<T extends {}, U>(
target: T,
source: U,
): asserts target is T & U;

/** Type-safe version of Object.assign */
export function assign<T extends {}, U extends {}[]>(
target: T,
...source: U
): asserts target is T & UnionToIntersection<U[number]>;

export function assign(target: object, ...source: object[]) {
Object.assign(target, ...source);
}
9 changes: 9 additions & 0 deletions _util/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export enum MetadataKey {
Alias = "metadata:is.alias",
Negated = "metadata:is.negated",
Deprecated = "metadata:is.deprecated",
}

export const AliasSymbol = Symbol.for(MetadataKey.Alias);
export const NegatedSymbol = Symbol.for(MetadataKey.Negated);
export const DeprecatedSymbol = Symbol.for(MetadataKey.Deprecated);
5 changes: 5 additions & 0 deletions _util/decorators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./decorators/alias.decorator.ts";
export * from "./decorators/deprecated.decorator.ts";
export * from "./decorators/enumerable.decorator.ts";
export * from "./decorators/freeze.decorator.ts";
export * from "./decorators/inspect.decorator.ts";
79 changes: 79 additions & 0 deletions _util/decorators/alias.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import "https://deno.land/x/reflection@0.0.2/mod.ts";

export const MetadataKeyAlias = "metadata:alias";

export function alias<A, T extends Object, D>(
alias: A,
): (target: T, propertyKey: string | symbol) => void;

export function alias<A, T extends Object, D>(alias: A): (
target: T,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<D>,
) => void | TypedPropertyDescriptor<D>;

export function alias<A, T extends Object, D>(alias: A):
| ((
target: T,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<D>,
) => void | TypedPropertyDescriptor<D>)
| ((target: T, propertyKey: string | symbol) => void) {
return (target: T, propertyKey: string | symbol, descriptor?: any) => {
if (typeof alias === "string") {
alias = (target as any)[alias] ?? null;
} else if (typeof alias === "symbol") {
alias = (target as any)[alias] ?? null;
}

// sanity check
if (typeof alias !== "function" || alias === null) {
throw new TypeError(
`The @alias decorator must be provided with a valid identifier for a class method/property, or a string containing a valid identifier. Received ${typeof alias}: ${alias}`,
);
}

Reflect.defineMetadata(
MetadataKeyAlias,
alias,
target,
propertyKey,
);

const AliasSymbol = Symbol.for(MetadataKeyAlias);
const existingMetadata =
Reflect.getOwnPropertyDescriptor(target, AliasSymbol)?.value ?? {};

Reflect.defineProperty(target, AliasSymbol, {
value: {
...existingMetadata,
[propertyKey]: alias,
},
configurable: true,
writable: true,
enumerable: false,
});

if (typeof propertyKey === "symbol") {
return;
}

if (typeof descriptor.value === "function") {
return {
...descriptor,
value: function aliasedMethod() {
return descriptor.value?.apply(this, arguments);
},
};
} else {
return {
...descriptor,
get: function aliasedGetter() {
return descriptor?.get?.() ?? descriptor?.value ?? (
(target as any)[propertyKey]
);
},
};
}
};
}
Loading