mirror of
https://github.com/tenrok/BBob.git
synced 2026-06-14 18:42:24 +03:00
feat: typescript support (#185)
* feat: initial typescript support * feat: typescript support * feat(plugin-helper): move files to typescript * chore: update lock files * feat: preset types * fix: build * fix: benchmark * fix: remove pnpm cache * fix: bench action * fix: pnpm recursive install * fix: nx cache * fix: lock file * fix: workflows * fix: lerna support in pnpm * fix: pnpm workspace * fix: remove unused files * fix: pnpm lock file * fix: update lerna for support pnpm * fix: lerna bootstrap * fix: rollup build * fix: update nx * fix: build * fix: add nx dep target * fix: remove nx cache * fix: workflow run on push only for master * fix: test workflow run on push only for master * fix: remove parallel for gen types * fix: benchmark * fix: benchmark imports * fix: pnpm * fix: types errors and pnpm * fix: types * fix: types * refactor: parser * fix(parser): tests * fix: preset tests * fix: react types * fix: react type declarations * fix: pnpm lock file * fix: react preset types * fix: lock file * fix: vue2 types * feat: dev container support * fix: types * fix: types * refactor: rewrite pkg-task, add nx gen-types deps, fix react/render.ts * refactor: types * fix: types * fix: rename gen-types to types * fix: nx build order * fix: nx reset * fix: define nx deps explicit * fix: build * fix: nx * fix: nx order build * fix: nx deps * fix: bbob cli tests * fix: tests * fix: cli tests and import * fix: test cover * fix: cli cover
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
|
||||
let C1 = 'C1'
|
||||
let C2 = 'C2'
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
C1 = '"parser" is not a function, please pass to "process(input, { parser })" right function'
|
||||
C2 = '"render" function not defined, please pass to "process(input, { render })"'
|
||||
}
|
||||
|
||||
export { C1, C2 }
|
||||
@@ -1,62 +0,0 @@
|
||||
import { parse } from '@bbob/parser';
|
||||
import { iterate, match } from './utils';
|
||||
|
||||
function walk(cb) {
|
||||
return iterate(this, cb);
|
||||
}
|
||||
|
||||
export default function bbob(plugs) {
|
||||
const plugins = typeof plugs === 'function' ? [plugs] : plugs || [];
|
||||
|
||||
let options = {
|
||||
skipParse: false,
|
||||
};
|
||||
|
||||
return {
|
||||
process(input, opts) {
|
||||
options = opts || {};
|
||||
|
||||
const parseFn = options.parser || parse;
|
||||
const renderFn = options.render;
|
||||
const data = options.data || null;
|
||||
|
||||
if (typeof parseFn !== 'function') {
|
||||
throw new Error('"parser" is not a function, please pass to "process(input, { parser })" right function');
|
||||
}
|
||||
|
||||
let tree = options.skipParse
|
||||
? input || []
|
||||
: parseFn(input, options);
|
||||
|
||||
// raw tree before modification with plugins
|
||||
const raw = tree;
|
||||
|
||||
tree.messages = [];
|
||||
tree.options = options;
|
||||
tree.walk = walk;
|
||||
tree.match = match;
|
||||
|
||||
plugins.forEach((plugin) => {
|
||||
tree = plugin(tree, {
|
||||
parse: parseFn,
|
||||
render: renderFn,
|
||||
iterate,
|
||||
match,
|
||||
data,
|
||||
}) || tree;
|
||||
});
|
||||
|
||||
return {
|
||||
get html() {
|
||||
if (typeof renderFn !== 'function') {
|
||||
throw new Error('"render" function not defined, please pass to "process(input, { render })"');
|
||||
}
|
||||
return renderFn(tree, tree.options);
|
||||
},
|
||||
tree,
|
||||
raw,
|
||||
messages: tree.messages,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { parse } from '@bbob/parser';
|
||||
import { iterate, match } from './utils';
|
||||
import { C1, C2 } from './errors'
|
||||
|
||||
import type { IterateCallback } from './utils';
|
||||
import type { NodeContent, PartialNodeContent } from "@bbob/plugin-helper";
|
||||
import type { BBobCore, BBobCoreOptions, BBobCoreTagNodeTree, BBobPlugins } from "./types";
|
||||
|
||||
export * from './types'
|
||||
|
||||
export function createTree<Options extends BBobCoreOptions = BBobCoreOptions>(tree: NodeContent[], options: Options) {
|
||||
const extendedTree = tree as BBobCoreTagNodeTree
|
||||
|
||||
extendedTree.messages = [...(extendedTree.messages || [])]
|
||||
extendedTree.options = {...options, ...extendedTree.options}
|
||||
extendedTree.walk = function walkNodes(cb: IterateCallback<NodeContent>) {
|
||||
return iterate(this, cb);
|
||||
}
|
||||
extendedTree.match = function matchNodes(expr: PartialNodeContent | PartialNodeContent[], cb: IterateCallback<NodeContent>) {
|
||||
return match(this, expr, cb)
|
||||
}
|
||||
|
||||
return extendedTree
|
||||
}
|
||||
|
||||
export default function bbob<InputValue = string | NodeContent[], Options extends BBobCoreOptions = BBobCoreOptions>(
|
||||
plugs?: BBobPlugins
|
||||
): BBobCore<InputValue, Options> {
|
||||
const plugins = typeof plugs === 'function' ? [plugs] : plugs || [];
|
||||
const mockRender = () => ""
|
||||
|
||||
return {
|
||||
process(input, opts) {
|
||||
const options = opts || { skipParse: false, parser: parse, render: mockRender, data: null } as BBobCoreOptions
|
||||
const parseFn = options.parser || parse;
|
||||
const renderFn = options.render;
|
||||
const data = options.data || null;
|
||||
|
||||
if (typeof parseFn !== 'function') {
|
||||
throw new Error(C1);
|
||||
}
|
||||
|
||||
// raw tree before modification with plugins
|
||||
const raw = options.skipParse && Array.isArray(input) ? input : parseFn(input as string, options);
|
||||
let tree = options.skipParse && Array.isArray(input) ? createTree((input || []) as NodeContent[], options) : createTree(raw, options)
|
||||
|
||||
for (let idx = 0; idx < plugins.length; idx++) {
|
||||
const plugin = plugins[idx]
|
||||
|
||||
if (typeof plugin === 'function' && renderFn) {
|
||||
const newTree = plugin(tree, {
|
||||
parse: parseFn,
|
||||
render: renderFn,
|
||||
iterate,
|
||||
data,
|
||||
})
|
||||
|
||||
tree = createTree(newTree || tree, options)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
get html() {
|
||||
if (typeof renderFn !== 'function') {
|
||||
throw new Error(C2);
|
||||
}
|
||||
|
||||
return renderFn(tree, tree.options);
|
||||
},
|
||||
tree,
|
||||
raw,
|
||||
messages: tree.messages,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { ParseOptions, TagNode } from "@bbob/parser";
|
||||
import type {
|
||||
NodeContent,
|
||||
PartialNodeContent,
|
||||
TagNodeTree,
|
||||
} from "@bbob/plugin-helper";
|
||||
import type { IterateCallback, iterate } from "./utils";
|
||||
|
||||
export interface BBobCoreOptions<
|
||||
Data = unknown | null,
|
||||
Options extends ParseOptions = ParseOptions
|
||||
> extends ParseOptions {
|
||||
skipParse?: boolean;
|
||||
parser?: (source: string, options?: Options) => TagNode[];
|
||||
render?: (ast: TagNodeTree, options?: Options) => string;
|
||||
data?: Data;
|
||||
}
|
||||
|
||||
export interface BbobPluginOptions<
|
||||
Options extends ParseOptions = ParseOptions
|
||||
> {
|
||||
parse: BBobCoreOptions["parser"];
|
||||
render: (ast: TagNodeTree, options?: Options) => string;
|
||||
iterate: typeof iterate;
|
||||
data: unknown | null;
|
||||
}
|
||||
|
||||
export interface BBobPluginFunction {
|
||||
(tree: BBobCoreTagNodeTree, options: BbobPluginOptions): BBobCoreTagNodeTree;
|
||||
}
|
||||
|
||||
export interface BBobCore<
|
||||
InputValue = string | TagNode[],
|
||||
Options extends BBobCoreOptions = BBobCoreOptions
|
||||
> {
|
||||
process(
|
||||
input: InputValue,
|
||||
opts?: Options
|
||||
): {
|
||||
readonly html: string;
|
||||
tree: BBobCoreTagNodeTree;
|
||||
raw: TagNode[] | string;
|
||||
messages: unknown[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface BBobCoreTagNodeTree extends Array<NodeContent> {
|
||||
messages: unknown[];
|
||||
options: BBobCoreOptions;
|
||||
walk: (cb: IterateCallback<NodeContent>) => BBobCoreTagNodeTree;
|
||||
match: (
|
||||
expression: PartialNodeContent | PartialNodeContent[],
|
||||
cb: IterateCallback<NodeContent>
|
||||
) => BBobCoreTagNodeTree;
|
||||
}
|
||||
|
||||
export type BBobPlugins = BBobPluginFunction | BBobPluginFunction[];
|
||||
@@ -1,60 +0,0 @@
|
||||
/* eslint-disable no-plusplus */
|
||||
const isObj = (value) => (typeof value === 'object');
|
||||
const isBool = (value) => (typeof value === 'boolean');
|
||||
|
||||
export function iterate(t, cb) {
|
||||
const tree = t;
|
||||
|
||||
if (Array.isArray(tree)) {
|
||||
for (let idx = 0; idx < tree.length; idx++) {
|
||||
tree[idx] = iterate(cb(tree[idx]), cb);
|
||||
}
|
||||
} else if (tree && isObj(tree) && tree.content) {
|
||||
iterate(tree.content, cb);
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
export function same(expected, actual) {
|
||||
if (typeof expected !== typeof actual) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isObj(expected) || expected === null) {
|
||||
return expected === actual;
|
||||
}
|
||||
|
||||
if (Array.isArray(expected)) {
|
||||
return expected.every((exp) => [].some.call(actual, (act) => same(exp, act)));
|
||||
}
|
||||
|
||||
return Object.keys(expected).every((key) => {
|
||||
const ao = actual[key];
|
||||
const eo = expected[key];
|
||||
|
||||
if (isObj(eo) && eo !== null && ao !== null) {
|
||||
return same(eo, ao);
|
||||
}
|
||||
|
||||
if (isBool(eo)) {
|
||||
return eo !== (ao === null);
|
||||
}
|
||||
|
||||
return ao === eo;
|
||||
});
|
||||
}
|
||||
|
||||
export function match(expression, cb) {
|
||||
return Array.isArray(expression)
|
||||
? iterate(this, (node) => {
|
||||
for (let idx = 0; idx < expression.length; idx++) {
|
||||
if (same(expression[idx], node)) {
|
||||
return cb(node);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
})
|
||||
: iterate(this, (node) => (same(expression, node) ? cb(node) : node));
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/* eslint-disable no-plusplus */
|
||||
const isObj = (value: unknown): value is Record<string, unknown> => (typeof value === 'object' && value !== null);
|
||||
const isBool = (value: unknown): value is boolean => (typeof value === 'boolean');
|
||||
|
||||
export type IterateCallback<Content> = (node: Content) => Content
|
||||
|
||||
export function iterate<Content, Iterable = ArrayLike<Content> | Content>(t: Iterable, cb: IterateCallback<Content>): Iterable {
|
||||
const tree = t;
|
||||
|
||||
if (Array.isArray(tree)) {
|
||||
for (let idx = 0; idx < tree.length; idx++) {
|
||||
tree[idx] = iterate(cb(tree[idx]), cb);
|
||||
}
|
||||
} else if (isObj(tree) && 'content' in tree) {
|
||||
iterate(tree.content, cb);
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
export function same(expected: unknown, actual: unknown): boolean {
|
||||
if (typeof expected !== typeof actual) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isObj(expected) || expected === null) {
|
||||
return expected === actual;
|
||||
}
|
||||
|
||||
if (Array.isArray(expected)) {
|
||||
return expected.every((exp) => [].some.call(actual, (act) => same(exp, act)));
|
||||
}
|
||||
|
||||
if (isObj(expected) && isObj(actual)) {
|
||||
return Object.keys(expected).every((key) => {
|
||||
const ao = actual[key];
|
||||
const eo = expected[key];
|
||||
|
||||
if (isObj(eo) && isObj(ao)) {
|
||||
return same(eo, ao);
|
||||
}
|
||||
|
||||
if (isBool(eo)) {
|
||||
return eo !== (ao === null);
|
||||
}
|
||||
|
||||
return ao === eo;
|
||||
});
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function match<Content, Iterable = ArrayLike<Content>>(
|
||||
t: Iterable,
|
||||
expression: Content | ArrayLike<Content>,
|
||||
cb: IterateCallback<Content>
|
||||
) {
|
||||
if (Array.isArray(expression)) {
|
||||
return iterate<Content, Iterable>(t, (node) => {
|
||||
for (let idx = 0; idx < expression.length; idx++) {
|
||||
if (same(expression[idx], node)) {
|
||||
return cb(node);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
})
|
||||
}
|
||||
|
||||
return iterate<Content, Iterable>(t, (node) => (same(expression, node) ? cb(node) : node));
|
||||
}
|
||||
Reference in New Issue
Block a user