diff --git a/.gitignore b/.gitignore index 76add87..14ebf5c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -dist \ No newline at end of file +dist +api/.env diff --git a/.npmignore b/.npmignore index 1cb0c1b..6dc8668 100644 --- a/.npmignore +++ b/.npmignore @@ -8,3 +8,4 @@ tsconfig.esm.json tsconfig.json api src +mod.ts diff --git a/README.md b/README.md index 959dcc6..028204f 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ import markupToJSON from 'markuptojson' // COMMON JS const markupToJSON require('markuptojson') +// DENO +import markupToJSON from 'https://deno.land/x/markuptojson_deno/mod.ts' + // -- HTML TO JSON -- // const HTML = "

Hello World Master

" const JSON = markupToJSON.htmlToJSON(HTML) @@ -54,7 +57,12 @@ You can also import the functions separately. // ESMODULES import { htmlToJSON, jsonToHTML } from 'markuptojson' + // COMMONJS const { htmlToJSON, jsonToHTML } = require('markuptojson') + +// DENO +import { htmlToJSON, jsonToHTML } from 'https://deno.land/x/markuptojson_deno/mod.ts' + ``` Thanks to **typescript** you will be able to have autocompletion in visual studio code and other popular IDEs, so you can easily extract the api responses and some performance information. Just like **typescript** helps with module methods for easy use. \ No newline at end of file diff --git a/api/.env b/api/.env index df5132c..6abf669 100644 --- a/api/.env +++ b/api/.env @@ -1 +1 @@ -set PORT = "400" \ No newline at end of file +PORT = 4000 \ No newline at end of file diff --git a/api/deno.json b/api/deno.json index a5d1834..07f7e0a 100644 --- a/api/deno.json +++ b/api/deno.json @@ -8,7 +8,9 @@ "no-local", "no-inferrable-types", "no-prototype-builtins", - "no-cond-assign" + "no-cond-assign", + "strict-boolean-expressions", + "@typescript-eslint/strict-boolean-expressions" ] } }, diff --git a/api/imports.json b/api/imports.json index c28d1c3..701114a 100644 --- a/api/imports.json +++ b/api/imports.json @@ -1,6 +1,10 @@ { "imports": { "https://esm.sh/": "https://esm.sh/", - "https://cdn.skypack.dev/": "https://cdn.skypack.dev/" + "https://cdn.skypack.dev/": "https://cdn.skypack.dev/", + "opine/": "https://deno.land/x/opine@2.3.3/", + "colors/": "https://deno.land/x/colorify@1.0.5/", + "cors/": "https://deno.land/x/cors@v1.2.2/", + "markuptojson/": "https://esm.sh/markuptojson@1.0.3/" } } \ No newline at end of file diff --git a/api/src/app.ts b/api/src/app.ts index c1ebca8..0abd7d5 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -1,12 +1,167 @@ -import { Application, Router } from 'https://deno.land/x/oak@v12.1.0/mod.ts' +import { opine, json, urlencoded } from 'opine/mod.ts' +import { fg } from 'colors/mod.ts' +import { opineCors } from 'cors/mod.ts' import config from './config.ts' +import { htmlToJSON, jsonToHTML, type JsonDBO } from 'markuptojson/' -const router = new Router() -const app = new Application() +const app = opine() -router.post('/', (ctx) => { - // const s = ctx.request.body()?.value('asdsad') +app.use(json({ limit: '100mb' })) +app.use(urlencoded({ extends: false, limit: '100mb' })) +app.use(opineCors({ + allowedHeaders: '*', + origin: '*', + methods: 'PUT, POST, GET, DELETE, OPTIONS, PATCH', + credentials: true +})) + +// routes + +app.get('/', (_req, res) => { + res.setStatus(200).json([ + { + route: '/api/json', + description: 'Create valid Json DBO for interactivity with the [markupjson(NodeJS, Deno, CDNs, Browser)] package and this API', + format: 'HTML', + type: 'string', + method: 'POST', + exampleInput: "

Hello World By markuptojson

", + output: [ + { + tagName: 'h1', + tagAttrs: { + class: 'main-title', + id: 'title' + }, + children: [ + { + tagName: 'span', + tagAttrs: {}, + children: [], + content: 'By markuptojson' + } + ], + content: 'Hello World' + } + ], + limit: '100mb', + bodyRequestNameParam: 'htmlContent', + responseParamName: 'data', + statusCodes: [ + { + 500: 'Internal error server', + 400: 'Bad request, not valid html content', + 200: 'Success' + } + ] + }, + { + route: '/api/html', + description: 'Convert JsonDBO create by API or markuptojson module to html', + format: 'ARRAY', + type: { + tagAttrs: 'string', + content: 'string', + children: 'array of this type', + tagName: 'string' + }, + method: 'POST', + exampleInput: [ + { + tagName: 'h1', + tagAttrs: { + class: 'main-title', + id: 'title' + }, + children: [ + { + tagName: 'span', + tagAttrs: {}, + children: [], + content: 'By markuptojson' + } + ], + content: 'Hello World' + } + ], + output: "

Hello World By markuptojson

", + limit: '100mb', + bodyRequestNameParam: 'jsonContent', + responseParamName: 'data', + statusCodes: [ + { + 500: 'Internal error server', + 400: 'Bad request, not valid JsonDBO array content', + 200: 'Success' + } + ] + } + ]) +}) + +app.post('/api/json', (req, res) => { + const html: string = String(req.body.htmlContent) + const regex = /<[a-z][\s\S]*>/i + try { + if (!regex.test(html)) { + return res.setStatus(400).json({ + message: 'Require a valid body param [htmlContent] => html content' + }) + } + + const jsonResult = htmlToJSON(html) + + res.setStatus(200).json({ + data: jsonResult + }) + } catch (_error) { + res.setStatus(500).json({ + message: 'Internal error server...' + }) + } +}) + +app.post('/api/html', (req, res) => { + const json = req.body.jsonContent as JsonDBO[] + + try { + if (!Array.isArray(json)) { + return res.setStatus(400).json({ + message: 'Require Array of param [jsonContent]' + }) + } + + const validData: JsonDBO[] = json.filter((item: any) => { + return ( + typeof item === 'object' && + (Boolean(item.tagAttrs)) && + typeof item.tagAttrs === 'object' && + (Boolean(item.content)) && + typeof item.content === 'string' && + Array.isArray(item.children) && + (Boolean(item.tagName)) && + typeof item.tagName === 'string' + ) + }) + + if (validData.length !== json.length) { + return res.setStatus(400).json({ + message: 'Incorrect format, generate valid [jsonContent] in [POST] /api/json' + }) + } + + const html = jsonToHTML(json) + + res.setStatus(200).json({ + data: html + }) + } catch (_error) { + res.setStatus(500).json({ + message: 'Internal error server...' + }) + } }) -app.use(router.routes()) -await app.listen({ port: config.PORT }) +app.listen(config.PORT, () => { + fg.green(`[APP]: listening in http://localhost:${String(config.PORT)}`) +}) diff --git a/api/src/config.ts b/api/src/config.ts index dd682fc..15cefc0 100644 --- a/api/src/config.ts +++ b/api/src/config.ts @@ -1,8 +1,12 @@ +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ +import { config as access } from 'https://deno.land/std@0.173.0/dotenv/mod.ts' + +const env = await access() const config: { PORT: number } = { - PORT: 4000 + PORT: Number(env.PORT || Deno.env.get('PORT') || 4000) } export default config diff --git a/mod.ts b/mod.ts new file mode 100644 index 0000000..ba88be8 --- /dev/null +++ b/mod.ts @@ -0,0 +1,21 @@ +// @ts-expect-error deno +import markuptojson, { htmlToJSON, jsonToHTML } from './src/index.ts' + +export interface JsonDBO { + tagAttrs: Record + content: string + children: JsonDBO[] | never[] + tagName: string +} + +export { + htmlToJSON, + jsonToHTML +} + +export interface MarkupToJSON { + jsonToHTML: (arrayJson: JsonDBO[]) => string + htmlToJSON: (htmlString: string) => JsonDBO[] +} + +export default markuptojson