feat(sdk, plugins): jsx support
This commit is contained in:
parent
bdf625bb1f
commit
3cee45b591
13
packages/plugins/src/engines/habr.tsx
Normal file
13
packages/plugins/src/engines/habr.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Engine } from '@txtdot/sdk';
|
||||||
|
|
||||||
|
import { JSX } from '@txtdot/sdk';
|
||||||
|
|
||||||
|
const Habr = new Engine('Habr', 'Habr parser', ['*']);
|
||||||
|
|
||||||
|
Habr.route('*path', async (input, ro) => {
|
||||||
|
return {
|
||||||
|
content: <div>Test</div>,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Habr;
|
@ -1,5 +1,6 @@
|
|||||||
import StackOverflow from './stackoverflow';
|
import StackOverflow from './stackoverflow';
|
||||||
import Readability from './readability';
|
import Readability from './readability';
|
||||||
import SearX from './searx';
|
import SearX from './searx';
|
||||||
|
import Habr from './habr';
|
||||||
|
|
||||||
export { StackOverflow, Readability, SearX };
|
export { StackOverflow, Readability, SearX, Habr };
|
||||||
|
@ -18,7 +18,7 @@ Readability.route('*path', async (input, ro) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
document: parseHTML(parsed.content).document,
|
content: parsed.content,
|
||||||
textContent: parsed.textContent,
|
textContent: parsed.textContent,
|
||||||
title: parsed.title,
|
title: parsed.title,
|
||||||
lang: parsed.lang,
|
lang: parsed.lang,
|
||||||
|
@ -46,7 +46,7 @@ async function search(
|
|||||||
const textContent = articles_parsed.map((a) => a.text).join('');
|
const textContent = articles_parsed.map((a) => a.text).join('');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
document: parseHTML(content).document,
|
content: content,
|
||||||
textContent,
|
textContent,
|
||||||
title: `${search} - Searx - Page ${page}`,
|
title: `${search} - Searx - Page ${page}`,
|
||||||
lang: document.documentElement.lang,
|
lang: document.documentElement.lang,
|
||||||
|
@ -16,9 +16,7 @@ async function questions(
|
|||||||
const answers = allAnswers.map((a) => postParser(a));
|
const answers = allAnswers.map((a) => postParser(a));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
document: parseHTML(
|
content: `${question}<hr>${answers.length} answers <hr>${answers.join('<hr>')}`,
|
||||||
`${question}<hr>${answers.length} answers <hr>${answers.join('<hr>')}`
|
|
||||||
).document,
|
|
||||||
textContent: `${ro.q.id}/${ro.q.slug}\nText output not supported`, // TODO
|
textContent: `${ro.q.id}/${ro.q.slug}\nText output not supported`, // TODO
|
||||||
title,
|
title,
|
||||||
lang: document.documentElement.lang,
|
lang: document.documentElement.lang,
|
||||||
|
@ -27,8 +27,7 @@ async function users(
|
|||||||
.join('<br/>');
|
.join('<br/>');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
document: parseHTML(`${userInfo}<hr><h3>Top Posts</h3>${topPosts}`)
|
content: `${userInfo}<hr><h3>Top Posts</h3>${topPosts}`,
|
||||||
.document,
|
|
||||||
textContent: `${ro.q.id}/${ro.q.slug}\n`, // TODO
|
textContent: `${ro.q.id}/${ro.q.slug}\n`, // TODO
|
||||||
title: document.querySelector('title')?.textContent || '',
|
title: document.querySelector('title')?.textContent || '',
|
||||||
lang: document.documentElement.lang,
|
lang: document.documentElement.lang,
|
||||||
|
@ -13,13 +13,13 @@
|
|||||||
/* Language and Environment */
|
/* Language and Environment */
|
||||||
"target": "ES2020" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
"target": "ES2020" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
"jsx": "react" /* Specify what JSX code is generated. */,
|
||||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
"reactNamespace": "JSX" /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */,
|
||||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||||
|
24
packages/sdk/src/jsx.ts
Normal file
24
packages/sdk/src/jsx.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
export namespace JSX {
|
||||||
|
export type Element = string;
|
||||||
|
export interface IntrinsicElements {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
[elemName: string]: any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createElement(
|
||||||
|
name: string,
|
||||||
|
props: { [id: string]: string },
|
||||||
|
...content: string[]
|
||||||
|
) {
|
||||||
|
props = props || {};
|
||||||
|
const propsstr = Object.keys(props)
|
||||||
|
.map((key) => {
|
||||||
|
const value = props[key];
|
||||||
|
if (key === 'className') return `class=${value}`;
|
||||||
|
else return `${key}=${value}`;
|
||||||
|
})
|
||||||
|
.join(' ');
|
||||||
|
return `<${name} ${propsstr}>${content.join('')}</${name}>`;
|
||||||
|
}
|
@ -18,6 +18,8 @@ import {
|
|||||||
handlerSchema,
|
handlerSchema,
|
||||||
} from './types/handler';
|
} from './types/handler';
|
||||||
|
|
||||||
|
import * as JSX from './jsx';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Engine,
|
Engine,
|
||||||
EngineParseError,
|
EngineParseError,
|
||||||
@ -32,4 +34,5 @@ export {
|
|||||||
HandlerOutput,
|
HandlerOutput,
|
||||||
Route,
|
Route,
|
||||||
handlerSchema,
|
handlerSchema,
|
||||||
|
JSX,
|
||||||
};
|
};
|
||||||
|
@ -33,7 +33,7 @@ export interface HandlerOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface EngineOutput {
|
export interface EngineOutput {
|
||||||
document: Document;
|
content: string;
|
||||||
textContent?: string;
|
textContent?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
lang?: string;
|
lang?: string;
|
||||||
|
@ -9,6 +9,7 @@ import replaceHref from './utils/replace-href';
|
|||||||
import { Engine } from '@txtdot/sdk';
|
import { Engine } from '@txtdot/sdk';
|
||||||
import { HandlerInput, HandlerOutput } from '@txtdot/sdk';
|
import { HandlerInput, HandlerOutput } from '@txtdot/sdk';
|
||||||
import config from './config';
|
import config from './config';
|
||||||
|
import { parseHTML } from 'linkedom';
|
||||||
|
|
||||||
interface IEngineId {
|
interface IEngineId {
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
@ -51,6 +52,7 @@ export class Distributor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const engine = this.getFallbackEngine(urlObj.hostname, engineName);
|
const engine = this.getFallbackEngine(urlObj.hostname, engineName);
|
||||||
|
|
||||||
const output = await engine.handle(
|
const output = await engine.handle(
|
||||||
new HandlerInput(
|
new HandlerInput(
|
||||||
await decodeStream(data, parseEncodingName(mime)),
|
await decodeStream(data, parseEncodingName(mime)),
|
||||||
@ -58,22 +60,25 @@ export class Distributor {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const dom = parseHTML(output.content);
|
||||||
|
|
||||||
// post-process
|
// post-process
|
||||||
// TODO: generate dom in handler and not parse here twice
|
// TODO: generate dom in handler and not parse here twice
|
||||||
replaceHref(
|
replaceHref(
|
||||||
output.document,
|
dom.document,
|
||||||
requestUrl,
|
requestUrl,
|
||||||
new URL(remoteUrl),
|
new URL(remoteUrl),
|
||||||
engineName,
|
engineName,
|
||||||
redirectPath
|
redirectPath
|
||||||
);
|
);
|
||||||
|
|
||||||
const purify = DOMPurify();
|
const purify = DOMPurify(dom);
|
||||||
const content = purify.sanitize(output.document.toString());
|
const content = purify.sanitize(output.content);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content,
|
content,
|
||||||
textContent: output.textContent || output.document.textContent || '',
|
textContent:
|
||||||
|
output.textContent || dom.document.documentElement.textContent || '',
|
||||||
title: output.title,
|
title: output.title,
|
||||||
lang: output.lang,
|
lang: output.lang,
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user