diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval.ts | 22 | ||||
| -rw-r--r-- | src/index.ts | 43 | ||||
| -rw-r--r-- | src/parser.ts | 11 | ||||
| -rw-r--r-- | src/utils/adt.ts | 17 | ||||
| -rw-r--r-- | src/utils/parser-comb.ts | 171 | ||||
| -rw-r--r-- | src/utils/result.ts | 18 |
6 files changed, 170 insertions, 112 deletions
diff --git a/src/eval.ts b/src/eval.ts index 191a76b..5e99282 100644 --- a/src/eval.ts +++ b/src/eval.ts @@ -12,7 +12,11 @@ export type EvalActions = { setAttribute(name: string, value: string): Promise<void> withEvent(fn: (e: any) => void): Promise<void> getFormData(): Promise<FormData | undefined> - sendRequest(_: { method: string, url: string, data: FormData | undefined }): Promise<void> + sendRequest(_: { + method: string + url: string + data: FormData | undefined + }): Promise<void> // calculate ?? } @@ -20,19 +24,19 @@ type EvalValue = string | undefined | void export const evalExpr = async ( expr: Expr, - actions: EvalActions + actions: EvalActions, ): Promise<EvalValue> => match<Promise<EvalValue>, Expr>(expr, { Call: async ({ name, args }) => getFunctions(name, args, actions), - LiteralString: async (s) => s, + LiteralString: async s => s, LiteralNumber: async ({ value, unit }) => matchString<number, CSSUnit>(unit, { s: () => value * 1000, _: () => value, }).toString(), - Identifier: async (s) => s, - VarIdentifier: async (s) => s, - _: async (_) => undefined, + Identifier: async s => s, + VarIdentifier: async s => s, + _: async _ => undefined, }) const getFunctions = (name: string, args: Expr[], actions: EvalActions) => @@ -93,9 +97,11 @@ const getFunctions = (name: string, args: Expr[], actions: EvalActions) => }, 'prevent-default': async () => actions.withEvent(e => e.preventDefault()), - 'request': async () => { + request: async () => { const url = await evalExpr(args[0], actions) - const method = args[1] ? (await evalExpr(args[1], actions) ?? 'post') : 'post' + const method = args[1] + ? (await evalExpr(args[1], actions)) ?? 'post' + : 'post' if (url) { const data = await actions.getFormData() diff --git a/src/index.ts b/src/index.ts index b48b1d8..2d70c3f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,7 @@ const EVENT_HANDLERS = { const PROPERTIES = [ '--cssx-children', '--cssx-text', - '--cssx-disgustingly-set-innerhtml' + '--cssx-disgustingly-set-innerhtml', ] const injectStyles = () => { @@ -25,7 +25,7 @@ const injectStyles = () => { const properties = [...PROPERTIES, ...Object.values(EVENT_HANDLERS)] $style.textContent = `.cssx-layer { - ${properties.map((p) => `${p}: ${UNSET_PROPERTY_VALUE};`).join(' ')} + ${properties.map(p => `${p}: ${UNSET_PROPERTY_VALUE};`).join(' ')} }` document.body.appendChild($style) @@ -45,8 +45,8 @@ const getEvalActions = ($element: Element, event: any): EvalActions => ({ addClass: async (id, cls) => document.getElementById(id)?.classList.add(cls), removeClass: async (id, cls) => document.getElementById(id)?.classList.remove(cls), - delay: (delay) => new Promise((res) => setTimeout(res, delay)), - jsEval: async (js) => (0, eval)(js), + delay: delay => new Promise(res => setTimeout(res, delay)), + jsEval: async js => (0, eval)(js), loadCssx: async (id, url) => new Promise((resolve, reject) => { const $link = Object.assign(document.createElement('link'), { @@ -66,7 +66,7 @@ const getEvalActions = ($element: Element, event: any): EvalActions => ({ } document.body.appendChild($link) }), - getVariable: async (varName) => getPropertyValue($element, varName), + getVariable: async varName => getPropertyValue($element, varName), updateVariable: async (targetId, varName, value) => { const $el = document.getElementById(targetId) if ($el) { @@ -76,15 +76,21 @@ const getEvalActions = ($element: Element, event: any): EvalActions => ({ setAttribute: async (name, value) => { $element.setAttribute(name, value) }, - withEvent: async (fn) => fn(event), - getFormData: async () => $element.nodeName === 'FORM' ? new FormData($element as HTMLFormElement) : undefined, + withEvent: async fn => fn(event), + getFormData: async () => + $element.nodeName === 'FORM' + ? new FormData($element as HTMLFormElement) + : undefined, sendRequest: async ({ url, method, data }) => { await fetch(url, { method, body: data }) // TODO: Handle response? }, }) -const handleEvents = async ($element: Element, isNewElement: boolean = false) => { +const handleEvents = async ( + $element: Element, + isNewElement: boolean = false, +) => { for (const [eventType, property] of Object.entries(EVENT_HANDLERS)) { const handlerExpr = getPropertyValue($element, property) @@ -106,17 +112,17 @@ const handleEvents = async ($element: Element, isNewElement: boolean = false) => } } -let nodeCount = 0 -const manageElement = async ($element: Element, isNewElement: boolean = false) => { - if (nodeCount++ > 100) return // NOTE: Temporary. To prevent infinite rec - +const manageElement = async ( + $element: Element, + isNewElement: boolean = false, +) => { await handleEvents($element, isNewElement) const text = getPropertyValue($element, '--cssx-text') if (text) $element.textContent = text const html = getPropertyValue($element, '--cssx-disgustingly-set-innerhtml') - if (html) $element.innerHTML = html + if (html) $element.innerHTML = html.replace(/(^'|")|('|"$)/g, '') const childrenIds = getChildrenIds($element) if (childrenIds.length > 0) { @@ -127,12 +133,13 @@ const manageElement = async ($element: Element, isNewElement: boolean = false) = $element.appendChild($childrenRoot) for (const childId of childrenIds) { - let isNewElement = false; const selector = childId.split('#') const [tag, id] = selector.length >= 2 ? selector : ['div', ...selector] - const $child = - $childrenRoot.querySelector(`:scope > #${id}`) ?? - (isNewElement = true, Object.assign(document.createElement(tag || 'div'), { id })) + let $child = $childrenRoot.querySelector(`:scope > #${id}`) + const isNewElement = !$child + if (!$child) { + $child = Object.assign(document.createElement(tag || 'div'), { id }) + } $childrenRoot.appendChild($child) await manageElement($child, isNewElement) } @@ -147,4 +154,4 @@ const render = async ({ root = document.body }: Options = {}) => { await manageElement(root) } -render() +render()
\ No newline at end of file diff --git a/src/parser.ts b/src/parser.ts index 9cb0405..3279f57 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -8,7 +8,7 @@ export type Expr = Enum<{ Identifier: string VarIdentifier: string LiteralString: string - LiteralNumber: { value: number, unit: CSSUnit } + LiteralNumber: { value: number; unit: CSSUnit } }> export const Expr = constructors<Expr>() @@ -31,9 +31,9 @@ const callExprParser = (input: string) => P.map( P.zip2( consumeWhitespace(identifierParser), - parens(consumeWhitespace(P.sepBy(exprParser, comma))) + parens(consumeWhitespace(P.sepBy(exprParser, comma))), ), - ([name, args]) => Expr.Call({ name, args }) + ([name, args]) => Expr.Call({ name, args }), )(input) const stringLiteralParser: P.Parser<Expr> = P.map( @@ -41,14 +41,15 @@ const stringLiteralParser: P.Parser<Expr> = P.map( P.between(singleQuote, P.regex(/^[^']*/), singleQuote), P.between(doubleQuote, P.regex(/^[^"]*/), doubleQuote), ]), - Expr.LiteralString + Expr.LiteralString, ) const numberParser = P.regex(/^[-+]?((\d*\.\d+)|\d+)/) const numberExprParser: P.Parser<Expr> = P.map( P.zip2(numberParser, P.optional(P.regex(/^(s|ms)/i))), - ([value, unit]) => Expr.LiteralNumber({ value: Number(value), unit: (unit ?? '') as CSSUnit }), + ([value, unit]) => + Expr.LiteralNumber({ value: Number(value), unit: (unit ?? '') as CSSUnit }), ) const exprParser: P.Parser<Expr> = P.or([ diff --git a/src/utils/adt.ts b/src/utils/adt.ts index 78ab667..45d9e50 100644 --- a/src/utils/adt.ts +++ b/src/utils/adt.ts @@ -1,14 +1,18 @@ type TagValue<T, N> = T extends Tag<N, infer V> ? V : never -export const match = - <R, T extends Tag<string, any>>(tag: T, pattern: { +export const match = <R, T extends Tag<string, any>>( + tag: T, + pattern: { [key in T['tag'] | '_']?: (v: TagValue<T, key>) => R - }): R => ((pattern as any)[tag.tag] || (pattern._ as any))(tag.value) + }, +): R => ((pattern as any)[tag.tag] || (pattern._ as any))(tag.value) -export const matchString = - <R, T extends string = string>(key: T, pattern: { +export const matchString = <R, T extends string = string>( + key: T, + pattern: { [key in T | '_']?: (key: key) => R - }): R => ((pattern as any)[key] || (pattern._ as any))(key) + }, +): R => ((pattern as any)[key] || (pattern._ as any))(key) type Tag<N, V> = { tag: N; value: V } export type Enum<T> = { [N in keyof T]: Tag<N, T[N]> }[keyof T] @@ -26,4 +30,3 @@ export const constructors = <T extends Tag<string, any>>(): { }, }, ) as any - diff --git a/src/utils/parser-comb.ts b/src/utils/parser-comb.ts index f682068..3f171b8 100644 --- a/src/utils/parser-comb.ts +++ b/src/utils/parser-comb.ts @@ -1,83 +1,120 @@ -import { match } from './adt'; -import { Result, mapResult, chainResult } from './result'; +import { match } from './adt' +import { Result, mapResult, chainResult } from './result' -export type ParseResult<T> = Result<{ value: T, input: string }, { error: string, input: string }>; +export type ParseResult<T> = Result< + { value: T; input: string }, + { error: string; input: string } +> -export type Parser<T> = (input: string) => ParseResult<T>; +export type Parser<T> = (input: string) => ParseResult<T> -export const regex = (re: RegExp): Parser<string> => input => { - if (input.length === 0) return Result.Err({ error: 'fuckedinput', input }) - const res = input.match(re) - if (!res) return Result.Err({ error: 'fucked', input }); - return Result.Ok({ value: res[0], input: input.replace(re, '') }); -} +export const regex = + (re: RegExp): Parser<string> => + input => { + if (input.length === 0) return Result.Err({ error: 'fuckedinput', input }) + const res = input.match(re) + if (!res) return Result.Err({ error: 'fucked', input }) + return Result.Ok({ value: res[0], input: input.replace(re, '') }) + } -export const string = (str: string): Parser<string> => input => { - if (input.length === 0) return Result.Err({ error: 'fuckedinput', input }) - if (!input.startsWith(str)) return Result.Err({ error: 'fuckedstring', input }) - return Result.Ok({ value: str, input: input.slice(str.length) }); -} +export const string = + (str: string): Parser<string> => + input => { + if (input.length === 0) return Result.Err({ error: 'fuckedinput', input }) + if (!input.startsWith(str)) + return Result.Err({ error: 'fuckedstring', input }) + return Result.Ok({ value: str, input: input.slice(str.length) }) + } -export const or = <T>([parser, ...rest]: Array<Parser<T>>): Parser<T> => input => { - if (rest.length === 0) return parser(input); - const result = parser(input) - return match(result, { - Ok: () => result, - Err: (_) => or(rest)(input), - }); -} +export const or = + <T>([parser, ...rest]: Array<Parser<T>>): Parser<T> => + input => { + if (rest.length === 0) return parser(input) + const result = parser(input) + return match(result, { + Ok: () => result, + Err: _ => or(rest)(input), + }) + } -export const mapParseResult = <T, R>(parser: Parser<T>, fn: (_: { value: T, input: string }) => { value: R, input: string }): Parser<R> => input => - mapResult(parser(input), fn) +export const mapParseResult = + <T, R>( + parser: Parser<T>, + fn: (_: { value: T; input: string }) => { value: R; input: string }, + ): Parser<R> => + input => + mapResult(parser(input), fn) export const map = <T, R>(parser: Parser<T>, fn: (_: T) => R): Parser<R> => - mapParseResult(parser, ({ value, ...rest }) => ({ ...rest, value: fn(value) })); + mapParseResult(parser, ({ value, ...rest }) => ({ + ...rest, + value: fn(value), + })) -export const zip2 = <A, B>(parserA: Parser<A>, parserB: Parser<B>): Parser<readonly [A, B]> => input => { - // TODO: refactor please. shit code - const resa: Result<{ value: A, input: string }, { error: string, input: string }> = parserA(input); - return chainResult(resa, ({ value: a, input: inputB }) => { - const res: Result<{ value: readonly [A, B], input: string }, { error: string, input: string }> = - map(parserB, (b) => [a, b] as const)(inputB) - return res - }) -} +export const zip2 = + <A, B>(parserA: Parser<A>, parserB: Parser<B>): Parser<readonly [A, B]> => + input => { + // TODO: refactor please. shit code + const resa: Result< + { value: A; input: string }, + { error: string; input: string } + > = parserA(input) + return chainResult(resa, ({ value: a, input: inputB }) => { + const res: Result< + { value: readonly [A, B]; input: string }, + { error: string; input: string } + > = map(parserB, b => [a, b] as const)(inputB) + return res + }) + } -export const prefixed = <A>(parserPrefix: Parser<any>, parser: Parser<A>): Parser<A> => - map(zip2(parserPrefix, parser), ([_, a]) => a); +export const prefixed = <A>( + parserPrefix: Parser<any>, + parser: Parser<A>, +): Parser<A> => map(zip2(parserPrefix, parser), ([_, a]) => a) -export const suffixed = <A>(parser: Parser<A>, parserSuffix: Parser<any>): Parser<A> => - map(zip2(parser, parserSuffix), ([a, _]) => a); +export const suffixed = <A>( + parser: Parser<A>, + parserSuffix: Parser<any>, +): Parser<A> => map(zip2(parser, parserSuffix), ([a, _]) => a) -export const between = <A>(prefix: Parser<any>, parser: Parser<A>, suffix: Parser<any>): Parser<A> => - suffixed(prefixed(prefix, parser), suffix) +export const between = <A>( + prefix: Parser<any>, + parser: Parser<A>, + suffix: Parser<any>, +): Parser<A> => suffixed(prefixed(prefix, parser), suffix) -export const many0 = <A>(parser: Parser<A>): Parser<Array<A>> => originalInput => - match(parser(originalInput), { - Ok: ({ value, input }) => map(many0(parser), ls => [value, ...ls])(input), - Err: ({ input }) => Result.Ok({ value: [], input }), - }) +export const many0 = + <A>(parser: Parser<A>): Parser<Array<A>> => + originalInput => + match(parser(originalInput), { + Ok: ({ value, input }) => map(many0(parser), ls => [value, ...ls])(input), + Err: ({ input }) => Result.Ok({ value: [], input }), + }) -export const many1 = <A>(parser: Parser<A>): Parser<Array<A>> => originalInput => - match(parser(originalInput), { - Ok: ({ value, input }) => map(many0(parser), ls => [value, ...ls])(input), - Err: err => Result.Err(err), - }) +export const many1 = + <A>(parser: Parser<A>): Parser<Array<A>> => + originalInput => + match(parser(originalInput), { + Ok: ({ value, input }) => map(many0(parser), ls => [value, ...ls])(input), + Err: err => Result.Err(err), + }) -export const sepBy = <A>(parser: Parser<A>, sepP: Parser<any>): Parser<Array<A>> => originalInput => - match(parser(originalInput), { - Ok: ({ value, input }) => map( - many0(prefixed(sepP, parser)), - ls => [value, ...ls] - )(input), - Err: _ => Result.Ok({ value: [], input: originalInput }), - }) - -export const optional = <A>(parser: Parser<A>): Parser<undefined | A> => input => { - const result = parser(input) - return match(result, { - Ok: _ => result, - Err: _ => Result.Ok({ value: undefined, input }) - }) -} +export const sepBy = + <A>(parser: Parser<A>, sepP: Parser<any>): Parser<Array<A>> => + originalInput => + match(parser(originalInput), { + Ok: ({ value, input }) => + map(many0(prefixed(sepP, parser)), ls => [value, ...ls])(input), + Err: _ => Result.Ok({ value: [], input: originalInput }), + }) +export const optional = + <A>(parser: Parser<A>): Parser<undefined | A> => + input => { + const result = parser(input) + return match(result, { + Ok: _ => result, + Err: _ => Result.Ok({ value: undefined, input }), + }) + } diff --git a/src/utils/result.ts b/src/utils/result.ts index c0120b8..39a7fe6 100644 --- a/src/utils/result.ts +++ b/src/utils/result.ts @@ -1,14 +1,18 @@ -import { Enum, constructors, match } from "./adt"; +import { Enum, constructors, match } from './adt' -export type Result<V, E> = Enum<{ Ok: V, Err: E }> +export type Result<V, E> = Enum<{ Ok: V; Err: E }> export const Result = constructors<Result<any, any>>() -export const mapResult = <A, B, E>(res: Result<A, E>, fn: (_: A) => B): Result<B, E> => - chainResult(res, a => Result.Ok(fn(a))) +export const mapResult = <A, B, E>( + res: Result<A, E>, + fn: (_: A) => B, +): Result<B, E> => chainResult(res, a => Result.Ok(fn(a))) -export const chainResult = <A, B, E>(res: Result<A, E>, fn: (_: A) => Result<B, E>): Result<B, E> => +export const chainResult = <A, B, E>( + res: Result<A, E>, + fn: (_: A) => Result<B, E>, +): Result<B, E> => match(res, { Ok: a => fn(a), Err: e => Result.Err(e), - }); - + }) |
