In the dom directory of a .bkext, I have a parser.ts file with the contents below (still in progress) which is imported in an inspector.tsx file with a line like:
import { parsePandocString } from './parser';
Expand disclosure triangle to view TS source
const VALID_ROW_TYPES = [
'body', 'heading', 'quote', 'code', 'note',
'unordered', 'ordered', 'task', 'hr'
];
// Generic Either types
type Either<L, R> = { type: "Either"; Left: L } | { type: "Either"; Right: R };
const Left = <L>(x: L): Either<L, never> => ({ type: "Either", Left: x });
const Right = <R>(x: R): Either<never, R> => ({ type: "Either", Right: x });
export const bindLR = <L, R>(lr: Either<L, R>) => <R2>(mf: (r: R) => Either<L, R2>): Either<L, R2> =>
"Left" in lr ? Left(lr.Left) : mf(lr.Right);
// String helpers
const words = (s: string) => s.split(/\s+/u).filter(Boolean);
// Validators
const validCustomDataKeyLR = (k: string): Either<string, string> => {
const m = k.match(/[^a-z\-]+/u);
return m ? Left(`Unexpected '${m.join("")}' in '${k}' (use lower a-z and hyphens)`) : Right(k);
};
const validNameLR = (isClass: boolean) => (s: string): Either<string, string> => {
const [prefix, type] = isClass ? [".", "class"] : ["#", "identifier"];
if ((/\s/u).test(s)) return Left(`Invalid ${type} name (contains whitespace): '${s}'`);
const m = s.match(/[^a-zA-Z0-9\-_]+/u);
if (m) return Left(`Unexpected '${m.join("")}' in ${type} name '${s}'`);
if (!(/^[A-Za-z]/u).exec(s)) return Left(`Expected letter after '${prefix}' in ${type}: '${s}'`);
return Right(s);
};
const validPrefixCharLR = (token: string): Either<string, string> => {
const prefix = token[0];
return ".#".includes(prefix) ? Right(prefix) : Left(`Expected '.' or '#'. Saw '${prefix}'`);
};
// Token Parsers
const readNameLR = (token: string): Either<string, [string, string]> =>
1 < token.length
? bindLR(validPrefixCharLR(token))((prefix: string) =>
bindLR(validNameLR("." === prefix)(token.slice(1)))((name: any) => Right([prefix, name]))
)
: Left(`No name seen after prefix '${token}'`);
const readKVLR = (token: string): Either<string, [string, string]> => {
const parts = token.split("=");
if (parts.length !== 2) {
return Left(`Expected single "=", saw ${parts.length - 1} in ${token}`);
}
const k = parts[0];
const v = parts[1].replace(/[\'\"]+/ug, "");
// Intercept 'type' and validate against Bike's RowType API
if (k === "type" && !VALID_ROW_TYPES.includes(v)) {
return Left(`Invalid type '${v}'. Allowed: ${VALID_ROW_TYPES.join(", ")}`);
}
return bindLR(validCustomDataKeyLR(k))(validK => Right([validK, v]));
};
// Main Parser Export
export const parsePandocString = (s: string): Either<string, Record<string, any>> => {
const tokens = words(s.replace(/^[\{\s]+/, "").replace(/[\}\s]+$/u, ""));
const kvs: [string, string][] = [];
for (const token of tokens) {
const result = token.includes("=") ? readKVLR(token) : readNameLR(token);
if ("Left" in result) return result; // Fail fast on first error
kvs.push(result.Right);
}
const dict = kvs.reduce((acc: any, [k, v]) => ({
...acc,
[k]: "." === k ? (acc[k] || []).concat(v) : v
}), {});
return Right(dict);
};
at build time, unless I remove all the /../u flags, I see:
Expand disclosure triangle to view messages
src/data-inspector.bkext/dom/parser.ts:14:43 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.
14 const words = (s: string) => s.split(/\s+/u).filter(Boolean);
~
src/data-inspector.bkext/dom/parser.ts:18:34 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.
18 const m = k.match(/[^a-z\-]+/u);
~
src/data-inspector.bkext/dom/parser.ts:24:14 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.
24 if ((/\s/u).test(s)) return Left(`Invalid ${type} name (contains whitespace): '${s}'`);
~
src/data-inspector.bkext/dom/parser.ts:25:41 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.
25 const m = s.match(/[^a-zA-Z0-9\-_]+/u);
~
src/data-inspector.bkext/dom/parser.ts:27:22 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.
27 if (!(/^[A-Za-z]/u).exec(s)) return Left(`Expected letter after '${prefix}' in ${type}: '${s}'`);
~
src/data-inspector.bkext/dom/parser.ts:51:41 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.
51 const v = parts[1].replace(/[\'\"]+/ug, "");
~
src/data-inspector.bkext/dom/parser.ts:63:70 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.
63 const tokens = words(s.replace(/^[\{\s]+/, "").replace(/[\}\s]+$/u, "")); ~
Perhaps parser.ts should import a missing context ?