Compare commits

37 Commits

Author SHA1 Message Date
e54429833b Fix path issues
All checks were successful
CI / build (push) Successful in 40s
2023-11-28 16:50:22 +01:00
ae69bcd48e The official image does not support arm and building it yourself is garbage, so some random image from docker hub is used. Great Work
All checks were successful
CI / build (push) Successful in 52s
2023-11-28 16:47:09 +01:00
675a73e551 Add CI
All checks were successful
CI / build (push) Successful in 51s
2023-11-28 16:14:45 +01:00
310c8e12fa Merge branch 'master' of https://git.stamm.me/Deno/DenReg 2023-11-28 16:12:49 +01:00
a14a5b9462 Make it modern 2023-11-28 16:10:33 +01:00
a0fef1ef76 Improve upgrade script
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-28 12:20:30 +02:00
3cda4ee8c8 Improve upgrade script
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-28 12:16:50 +02:00
e5829d9a4f Add script running abilities
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-28 11:47:52 +02:00
3efe4fc34e Add dpm alias 2021-04-28 11:19:29 +02:00
22a447604b Update to work with newer deno versions
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-15 17:20:09 +02:00
9dfb8d65d3 Add support for intellisense imports for the vscode extension
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-15 02:32:51 +01:00
6bc090e51b Fix dockerfile
Some checks failed
continuous-integration/drone/push Build is failing
2021-01-11 15:38:44 +01:00
965ca33d33 Improve startup speed
Some checks failed
continuous-integration/drone/push Build is failing
2021-01-11 15:37:06 +01:00
0202946813 Implement fragment support
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-11 15:32:47 +01:00
53a11eccf6 Fix tsconfig wrong path
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-11 15:24:00 +01:00
325c1a4d7d Add tsconfig to docker
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-11 15:20:37 +01:00
c718e8898d Change import
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-11 15:10:43 +01:00
6fe3ddbd37 Many improvements
Some checks failed
continuous-integration/drone/push Build is failing
2021-01-11 15:06:02 +01:00
79bcef0698 Remove unused console output 2021-01-11 15:05:03 +01:00
2af5d4f823 Move to own JSX implementation
Some checks failed
continuous-integration/drone/push Build is failing
2021-01-11 14:55:54 +01:00
7c1166bf87 Update dependencies
Some checks failed
continuous-integration/drone/push Build is failing
2021-01-04 22:57:14 +01:00
639287663d Remove useless log output
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-27 03:23:49 +01:00
fbbb66d5af Add option to add tracking to denreg
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-27 03:16:57 +01:00
df1f4965ad Switch src image to new registry
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-19 19:06:29 +01:00
10930db13d Change registry
Some checks failed
continuous-integration/drone/push Build is failing
2020-12-19 19:02:24 +01:00
6b88be1b18 Add content type to registry using filenames
All checks were successful
continuous-integration/drone/push Build is passing
2020-10-21 02:40:55 +02:00
c9bd5c7d18 Adding support for readme field in meta.json to registry
All checks were successful
continuous-integration/drone/push Build is passing
2020-10-19 21:10:53 +02:00
d1244ac0a7 A few SEO
All checks were successful
continuous-integration/drone/push Build is passing
2020-10-14 22:34:57 +02:00
e56d8b4548 Add prism.css to public folder instead of unpkg
All checks were successful
continuous-integration/drone/push Build is passing
2020-10-14 22:20:50 +02:00
ed1ce0cc0f Improving update support
All checks were successful
continuous-integration/drone/push Build is passing
2020-10-14 22:04:18 +02:00
252bf4aac3 Remove logging output
All checks were successful
continuous-integration/drone/push Build is passing
2020-10-14 21:47:09 +02:00
a3a7370be0 Add log
All checks were successful
continuous-integration/drone/push Build is passing
2020-10-14 17:51:49 +02:00
10a7c26642 Configurable region
All checks were successful
continuous-integration/drone/push Build is passing
2020-10-14 17:44:36 +02:00
7e821f9771 Updating dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2020-10-14 17:07:51 +02:00
1b7c4847dc Add s3 error output
All checks were successful
continuous-integration/drone/push Build is passing
2020-10-14 16:48:27 +02:00
2194bf199e Add debug output
All checks were successful
continuous-integration/drone/push Build is passing
2020-10-14 16:34:26 +02:00
f3f6f0d7bc Add ability to add root folder to meta.json (only affects upload) 2020-10-14 16:34:18 +02:00
130 changed files with 1259 additions and 10758 deletions

View File

@ -12,7 +12,7 @@ steps:
from_secret: docker_password
auto_tag: true
context: registry
repo: hibas123.azurecr.io/denreg
registry: hibas123.azurecr.io
repo: docker.hibas123.de/denreg
registry: docker.hibas123.de
dockerfile: registry/Dockerfile
debug: false

36
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,36 @@
# .github/workflows/ci.yml
name: CI
on:
push:
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
env:
MY_DOCKER_USERNAME: ${{ secrets.MY_DOCKER_USERNAME }}
MY_DOCKER_PASSWORD: ${{ secrets.MY_DOCKER_PASSWORD }}
FORCE_COLOR: 1
steps:
- uses: https://github.com/earthly/actions-setup@v1
with:
version: v0.7.0
- uses: actions/checkout@v2
- name: Put back the git branch into git (Earthly uses it for tagging)
run: |
branch=""
if [ -n "$GITHUB_HEAD_REF" ]; then
branch="$GITHUB_HEAD_REF"
else
branch="${GITHUB_REF##*/}"
fi
git checkout -b "$branch" || true
- name: Docker Login
run: docker login git.hibas.dev --username "$MY_DOCKER_USERNAME" --password "$MY_DOCKER_PASSWORD"
- name: Earthly version
run: earthly --version
- name: Run build
run: cd registry && earthly --push +docker-multi

View File

@ -4,5 +4,9 @@
"debug.javascript.usePreview": false,
"deno.import_intellisense_origins": {
"https://deno.land": true
},
"deno.suggest.imports.hosts": {
"https://deno.land": true,
"https://deno.hibas123.de": false
}
}

View File

@ -1,11 +1,11 @@
import { Colors, Cliffy } from "../deps.ts";
import { Colors, CliffyPrompt } from "../deps.ts";
import { getMeta, setMeta, log } from "../global.ts";
export default async function deprecate(options: any) {
const meta = await getMeta();
const res = await Cliffy.Confirm.prompt(
const res = await CliffyPrompt.Confirm.prompt(
"Are you sure you want to deprecat this package?"
);

View File

@ -1,4 +1,4 @@
import { Cliffy, Path, FS } from "../deps.ts";
import { CliffyPrompt, Path, FS } from "../deps.ts";
import {
getMeta,
setMeta,
@ -11,7 +11,7 @@ export default async function init() {
let existing = {};
try {
existing = await getMeta();
} catch (err) {}
} catch (err) { }
let meta: IMeta = {
name: Path.basename(Deno.cwd()).toLowerCase().replace(/\s+/g, "-"),
version: "0.0.1",
@ -23,21 +23,21 @@ export default async function init() {
};
if (isInteractive()) {
meta.name = await Cliffy.Input.prompt({
meta.name = await CliffyPrompt.Input.prompt({
message: "What's the name of your package?",
default: meta.name,
});
meta.description = await Cliffy.Input.prompt({
meta.description = await CliffyPrompt.Input.prompt({
message: "What's the description of your package?",
default: meta.description,
});
meta.author = await Cliffy.Input.prompt({
meta.author = await CliffyPrompt.Input.prompt({
message: "Who's the author of your package?",
default: meta.author,
});
if (!(await FS.exists("README.md"))) {
const res = await Cliffy.Confirm.prompt({
const res = await CliffyPrompt.Confirm.prompt({
message: "Autogenerate README?",
default: true,
});

View File

@ -1,64 +1,31 @@
import { Colors, Path, FS, Compress, Base64 } from "../deps.ts";
import { getMeta, IMeta, log, getConfig } from "../global.ts";
import { runHooks } from "../helper/run_script.ts";
import { ServerError } from "../helper/server_error.ts";
async function runScript(script: string) {
console.log(Colors.bold(Colors.blue("Running script:")), script);
const runPerm = await Deno.permissions.query({
name: "run",
});
if (runPerm.state !== "granted") {
console.log(
Colors.red("Missing --allow-run permission. Cannot run hooks!")
);
throw new Error("Missing --allow-run permission. Cannot run hooks!");
}
const process = Deno.run({
cmd: ["deno", "run", "-A", "--unstable", script],
});
const status = await process.status();
console.log(Colors.bold(Colors.blue("Finished script:")), script);
if (!status.success) {
throw new Error(
"A hook did not complete sucessfully. This is not a issue of denreg!"
);
}
}
async function runHooks(hooks: undefined | string | string[]) {
if (!hooks) return;
if (typeof hooks === "string") {
hooks = [hooks];
}
for (const hook of hooks) {
await runScript(hook);
}
}
import { checkPermOrExit } from "../helper/permission.ts";
export default async function publish(options: { dry: boolean }) {
const meta: IMeta = await getMeta();
const originalMeta = await getMeta();
if (originalMeta.hooks) {
log("Running prepublish hooks");
await runHooks(originalMeta.hooks.prepublish);
}
const meta = await getMeta();
//TODO: Output Diff between original and result meta
if (!meta.name) throw new Error("name is not set in meta.json");
if (!meta.version) throw new Error("version is not set in meta.json");
if (!meta.files || !Array.isArray(meta.files) || meta.files.length <= 0)
throw new Error("files is not set or empty in meta.json");
if (meta.hooks) {
log("Running prepublish hooks");
await runHooks(meta.hooks.prepublish);
}
const tmpDir = await Deno.makeTempDir();
const packedFile = (await Deno.makeTempFile()) + ".tar";
try {
const walker = FS.walk(".", {
const walker = FS.walk(meta.root || ".", {
includeDirs: false,
includeFiles: true,
match: meta.files.map((file) => Path.globToRegExp(file)),
@ -66,17 +33,18 @@ export default async function publish(options: { dry: boolean }) {
log("Copying files to package to", tmpDir);
const copy = async (path: string) => {
const dest = Path.join(tmpDir, path);
const copy = async (path: string, abs?: boolean) => {
const relative = abs ? path : Path.relative(meta.root || ".", path);
log("Adding file:", path, "as", relative);
const dest = Path.join(tmpDir, relative);
await FS.ensureDir(Path.dirname(dest));
await FS.copy(path, dest);
};
await copy("meta.json");
await copy("meta.json", true);
for await (const file of walker) {
await copy(file.path);
log("Adding file:", file.path);
}
log("Compressing files into", packedFile);
@ -88,9 +56,16 @@ export default async function publish(options: { dry: boolean }) {
const url = new URL(getConfig("registry"));
url.pathname = "/api/package/" + meta.name;
if (!options.dry) {
log("Uploading new package version");
console.log(
"Pushing version",
Colors.blue(meta.version),
"to repository"
);
if (!options.dry) {
await checkPermOrExit("net", "Net permission required for publishing");
log("Uploading new package version");
await fetch(url, {
method: "POST",
body: await Deno.readFile(packedFile),

18
cli/commands/run.ts Normal file
View File

@ -0,0 +1,18 @@
import { Colors, Path, FS, Compress, Base64 } from "../deps.ts";
import { getMeta, IMeta, log, getConfig } from "../global.ts";
import { ServerError } from "../helper/server_error.ts";
import { runScript } from "../helper/run_script.ts";
export default async function run(options: {}, name: string) {
const { scripts } = await getMeta();
if (!scripts || !scripts[name]) {
console.log(Colors.bold(Colors.red("Script not found:")), name);
} else {
let script = scripts[name];
if (!Array.isArray(script)) script = [script];
for (const s of script) {
await runScript(s);
}
}
}

View File

@ -1,23 +1,23 @@
import { Cliffy } from "../deps.ts";
import { CliffyPrompt } from "../deps.ts";
import { getConfig, setConfig } from "../global.ts";
export default async function setup() {
const registry = await Cliffy.Input.prompt({
const registry = await CliffyPrompt.Input.prompt({
message: "What's your registry?",
default: getConfig("registry"),
});
const username = await Cliffy.Input.prompt({
const username = await CliffyPrompt.Input.prompt({
message: "What's your username?",
default: getConfig("username"),
});
const password = await Cliffy.Secret.prompt({
const password = await CliffyPrompt.Secret.prompt({
message: "What's your password?",
hidden: true,
default: getConfig("password"),
});
const author = await Cliffy.Input.prompt({
const author = await CliffyPrompt.Input.prompt({
message: "Who are you? (optional) Name <email@example.com>",
default: getConfig("author"),
});

View File

@ -1,25 +1,63 @@
import { Cliffy, Colors } from "../deps.ts";
import { CliffyPrompt, Colors } from "../deps.ts";
import { version } from "../version.ts";
import { requestPermOrExit } from "../helper/permission.ts";
export default async function upgrade() {
const res = await Cliffy.Confirm.prompt({
message: "Are you sure you want to upgrade the denreg cli?",
await requestPermOrExit(
"net",
"Net permission required to fetch new version"
);
const meta = await fetch(
"https://deno.hibas123.de/raw/@denreg-cli/meta.json"
).then((e) => e.json());
if (meta.version === version) {
const res = await CliffyPrompt.Confirm.prompt({
message: Colors.yellow("No update available!") + " Continue?",
default: false,
});
if (!res) return;
}
const res = await CliffyPrompt.Confirm.prompt({
message: "Are you sure you want to upgrade?",
default: true,
});
if (res) {
const process = Deno.run({
cmd: [
"deno",
"install",
"-A",
"--unstable",
"-f",
"https://deno.hibas123.de/raw/@denreg-cli/denreg.ts",
],
const cmd_base = ["deno", "install", "-A", "--unstable", "-f"];
const cmd1 = [
...cmd_base,
`https://deno.hibas123.de/raw/@denreg-cli@${meta.version}/denreg.ts`,
];
const cmd2 = [
...cmd_base,
`https://deno.hibas123.de/raw/@denreg-cli@${meta.version}/dpm.ts`,
];
await requestPermOrExit(
"run",
"Run permission required to install new version. Or run it manually: " +
cmd1.join(" ")
);
const process1 = Deno.run({
cmd: cmd1,
});
const s = await process.status();
if (!s) {
const s1 = await process1.status();
if (!s1) {
console.log(Colors.red("Upgrade failed!"));
}
const process2 = Deno.run({
cmd: cmd2,
});
const s2 = await process2.status();
if (!s2) {
console.log(Colors.red("Upgrade failed!"));
}
}

View File

@ -1,14 +1,20 @@
import { Cliffy, Path, Colors } from "./deps.ts";
import { CliffyCommand, Path, Colors } from "./deps.ts";
import { checkPermOrExit } from "./helper/permission.ts";
await checkPermOrExit("env", "Requires --allow-env");
await checkPermOrExit("read", "Requires --allow-read");
await checkPermOrExit("write", "Requires --allow-write");
import { init } from "./global.ts";
import { version } from "./version.ts";
import setupCMD from "./commands/setup.ts";
import initCMD from "./commands/init.ts";
import bumpCMD from "./commands/bump.ts";
import publishCMD from "./commands/publish.ts";
import deprecateCMD from "./commands/deprecate.ts";
import upgradeCMD from "./commands/upgrade.ts";
import runCMD from "./commands/run.ts";
const HOME_FOLDER = Deno.env.get("HOME") || Deno.env.get("USERPROFILE") || "";
@ -26,7 +32,7 @@ const commandWrapper = (cmd: CommandHandler) => {
opts = params;
};
};
const flags = await new Cliffy.Command()
const flags = await new CliffyCommand.Command()
.name("denreg")
.version(version)
.description("CLI for the Open Source DenReg package registry")
@ -44,13 +50,13 @@ const flags = await new Cliffy.Command()
})
.command(
"setup",
new Cliffy.Command()
new CliffyCommand.Command()
.description("Configure cli")
.action(commandWrapper(setupCMD))
)
.command(
"publish",
new Cliffy.Command()
new CliffyCommand.Command()
.description("Upload package")
.action(commandWrapper(publishCMD))
.option("-d, --dry [dry:boolean]", "Dry run", {
@ -59,13 +65,13 @@ const flags = await new Cliffy.Command()
)
.command(
"init",
new Cliffy.Command()
new CliffyCommand.Command()
.description("Create meta.json")
.action(commandWrapper(initCMD))
)
.command(
"bump",
new Cliffy.Command()
new CliffyCommand.Command()
.complete("major|minor|patch", () => ["major", "minor", "patch"])
.arguments("<major|minor|patch>")
.description("Change package version")
@ -73,17 +79,26 @@ const flags = await new Cliffy.Command()
)
.command(
"deprecate",
new Cliffy.Command()
new CliffyCommand.Command()
.description("Deprecate package")
.action(commandWrapper(deprecateCMD))
)
.command(
"upgrade",
new Cliffy.Command()
new CliffyCommand.Command()
.description("Upgrade to latest version of denreg cli")
.action(commandWrapper(upgradeCMD))
)
.command("completions", new Cliffy.CompletionsCommand())
.command(
"run",
new CliffyCommand.Command()
.arguments("<name>")
// .complete("name", ()=>) //TODO: add autocomplete?
.description("Run script from meta.json")
.action(commandWrapper(runCMD))
)
.command("completions", new CliffyCommand.CompletionsCommand())
.command("help", new CliffyCommand.HelpCommand().global())
.parse(Deno.args);
await init(flags.options);
@ -95,5 +110,5 @@ if (command) {
console.log(Colors.bold(Colors.red("An error occured:")), err.message);
}
} else {
flags.cmd.help();
flags.cmd.showHelp();
}

View File

@ -1,7 +1,11 @@
export * as Compress from "https://deno.hibas123.de/raw/@denreg-tar/mod.ts";
export * as Ini from "https://deno.land/x/ini@v2.1.0/mod.ts";
export * as Cliffy from "https://deno.land/x/cliffy@v0.14.1/mod.ts";
export * as Base64 from "https://deno.land/std@0.65.0/encoding/base64.ts";
export * as FS from "https://deno.land/std@0.65.0/fs/mod.ts";
export * as Colors from "https://deno.land/std@0.65.0/fmt/colors.ts";
export * as Path from "https://deno.land/std@0.65.0/path/mod.ts";
export * as CliffyPrompt from "https://deno.land/x/cliffy@v1.0.0-rc.3/prompt/mod.ts";
export * as CliffyCommand from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";
export * as Cliffy from "https://deno.land/x/cliffy@v0.18.2/mod.ts";
export * as Base64 from "https://deno.land/std@0.95.0/encoding/base64.ts";
export * as FS from "https://deno.land/std@0.95.0/fs/mod.ts";
export * as Colors from "https://deno.land/std@0.95.0/fmt/colors.ts";
export * as Path from "https://deno.land/std@0.95.0/path/mod.ts";

1
cli/dpm.ts Normal file
View File

@ -0,0 +1 @@
import "./denreg.ts";

View File

@ -9,10 +9,14 @@ export interface IMeta {
contributors?: string[];
deprecated?: boolean;
files: string[];
root?: string;
hooks?: {
prepublish?: string | string[];
postpublish?: string | string[];
};
scripts?: {
[key: string]: string | string[];
};
}
let verbose = false;
@ -41,16 +45,18 @@ export async function setConfig(name: string, value: string) {
});
}
const readJson = (name: string) => Deno.readTextFile(name).then(JSON.parse);
const writeJson = (name: string, value: any, spaces?: string) =>
Deno.writeTextFile(name, JSON.stringify(value, null, spaces));
export async function getMeta() {
log("Reading meta.json");
return (await FS.readJson("meta.json")) as IMeta;
return (await readJson("meta.json")) as IMeta;
}
export async function setMeta(meta: IMeta): Promise<void> {
log("Saving meta.json");
return FS.writeJson("meta.json", meta, {
spaces: " ",
});
return writeJson("meta.json", meta, " ");
}
let interactive = true;

26
cli/helper/permission.ts Normal file
View File

@ -0,0 +1,26 @@
import { Colors } from "../deps.ts";
export const checkPermOrExit = (name: string, err: string) =>
Deno.permissions.query({ name: name as any }).then((res) => {
if (res.state !== "granted") {
console.log(Colors.bold(Colors.red(err)));
Deno.exit(1);
}
});
export const requestPermOrExit = (name: string, err: string) => {
Deno.permissions
.query({ name: name as any })
.then((res) => {
if (res.state === "prompt") {
return Deno.permissions.request({ name: name as any });
}
return res;
})
.then((res) => {
if (res.state !== "granted") {
console.log(Colors.bold(Colors.red(err)));
Deno.exit(1);
}
});
};

51
cli/helper/run_script.ts Normal file
View File

@ -0,0 +1,51 @@
import { Colors } from "../deps.ts";
import { checkPermOrExit } from "../helper/permission.ts";
export async function runScript(script: string) {
await checkPermOrExit(
"run",
"Requires --allow-run to run scripts and hooks"
);
console.log(Colors.bold(Colors.blue("Running script:")), script);
const runPerm = await Deno.permissions.query({
name: "run",
});
if (runPerm.state !== "granted") {
console.log(
Colors.red("Missing --allow-run permission. Cannot run hooks!")
);
throw new Error("Missing --allow-run permission. Cannot run hooks!");
}
const process = Deno.run({
cmd: ["deno", "run", "-A", "--unstable", script],
});
const status = await process.status();
console.log(Colors.bold(Colors.blue("Finished script:")), script);
if (!status.success) {
throw new Error(
"A script did not complete sucessfully. This is not a issue of denreg!"
);
}
}
export async function runHooks(hooks: undefined | string | string[]) {
if (!hooks) return;
if (typeof hooks === "string") {
hooks = [hooks];
}
for (const hook of hooks) {
try {
await runScript(hook);
} catch (err) {
throw new Error(
"A hook did not complete sucessfully. This is not a issue of denreg!"
);
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@denreg-cli",
"version": "0.2.6",
"version": "1.0.0",
"description": "CLI for the DenReg package registry",
"author": "Fabian Stamm <dev@fabianstamm.de>",
"contributors": [],
@ -9,7 +9,10 @@
"**/*.js",
"README.md"
],
"scripts": {
"test": "version.ts"
},
"hooks": {
"prepublish": "pre.ts"
}
}
}

View File

@ -1,6 +1,6 @@
import { FS } from "./deps.ts";
const meta = (await FS.readJson("./meta.json")) as any;
const meta = (await Deno.readTextFile("./meta.json").then(JSON.parse)) as any;
await Deno.writeTextFile(
"version.ts",

0
cli/test.ts Normal file
View File

View File

@ -1 +1 @@
export const version = "0.2.6"
export const version = "1.0.0"

View File

@ -1,16 +0,0 @@
name: ci
on:
push:
branches:
- master
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: denolib/setup-deno@master
- run: deno --version
- run: deno test examples/01.tsx
- run: deno test examples/03-async.tsx

3
jsx-html/.gitignore vendored
View File

@ -1,3 +0,0 @@
/dist
node_modules
tmp

View File

@ -1,5 +0,0 @@
{
"trailingComma": "all",
"tabWidth": 4,
"singleQuote": true
}

View File

@ -1,4 +0,0 @@
{
"deno.enable": true,
"deno.unstable": true
}

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2020 Alexandre Piel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,103 +0,0 @@
# jsx-html
`jsx-html` render JSX template to HTML asynchronously. Compatible with Deno, NodeJs and can also run in browser.
Try with runkit: https://runkit.com/apiel/jsx-html-example
## NodeJs
```sh
yarn add async-jsx-html
# or
npm install async-jsx-html
```
```tsx
import { React } from 'async-jsx-html';
const View = () => <div>Hello</div>;
// render return a Promise
(<View />).render().then((html: string) => console.log(html));
```
## Deno
```tsx
/// <reference path="https://raw.githubusercontent.com/apiel/jsx-html/master/jsx.d.ts" />
import { React } from 'https://raw.githubusercontent.com/apiel/jsx-html/master/mod.ts';
const View = () => <div>Hello</div>;
// render return a Promise
(<View />).render().then((html: string) => console.log(html));
```
```sh
deno run https://raw.githubusercontent.com/apiel/jsx-html/master/examples/00.tsx
```
## TsConfig
As you would do with React, you need to import `React` from `jsx-html` for the transpiler. If you are not feeling confortable with using `React` as import since it is not React, you can import `jsx` from `jsx-html` but you would have to update your tsconfig file: https://github.com/denoland/deno/issues/3572
```json
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "jsx"
}
}
```
```tsx
/// <reference path="https://raw.githubusercontent.com/apiel/jsx-html/master/jsx.d.ts" />
import { jsx } from 'https://raw.githubusercontent.com/apiel/jsx-html/master/mod.ts';
const View = () => <div>Hello</div>;
(<View />).render().then(console.log);
```
> **Note:** prefer using sermver tags version instead of master to avoid conflict with caching, e.g:
> `import { jsx } from 'https://raw.githubusercontent.com/apiel/jsx-html/1.0.0/mod.ts';`.
## Async component
Unlike React, components can be asynchrone, so you can fetch for data without to handle states.
```tsx
import { React } from 'https://raw.githubusercontent.com/apiel/jsx-html/master/mod.ts';
const Data = async () => {
const res = await fetch('http://example.com/some/api');
const content = new Uint8Array(await res.arrayBuffer());
return <div>{content}</div>;
};
const View = () => (
<div>
<Data />
</div>
);
(<View />).render().then(console.log);
```
# InnerHTML
The Element property innerHTML sets the HTML or XML markup contained within the property.
In general, setting HTML from code is risky because its easy to inadvertently expose your users to a cross-site scripting (XSS) attack.
```tsx
/// <reference path="https://raw.githubusercontent.com/apiel/jsx-html/master/jsx.d.ts" />
import { jsx } from 'https://raw.githubusercontent.com/apiel/jsx-html/master/mod.ts';
const View = () => <div innerHTML="<b>hello</b> world" />;
(<View />).render().then(console.log); // will output <div><b>hello</b> world</div>
```
## Browser
`jsx-html` can also be used directly in browser. Find an example with webpack [here](https://github.com/apiel/jsx-html/tree/master/examples/browser).

View File

@ -1,26 +0,0 @@
'use strict';
var regExp = /\.(ts|tsx|js|jsx)$/i;
module.exports = function () {
return {
visitor: {
ImportDeclaration: function ImportDeclaration(path) {
var source = path.node.source;
if (!source.value.match(regExp)) {
return;
}
source.value = source.value.replace(regExp, '');
},
ExportDeclaration: function ExportDeclaration(path) {
var source = path.node.source;
if (source) {
if (!source.value.match(regExp)) {
return;
}
source.value = source.value.replace(regExp, '');
}
},
},
};
};

View File

@ -1,6 +0,0 @@
export enum NODE_TYPE {
ELEMENT = 'element',
TEXT = 'text',
COMPONENT = 'component',
FRAGMENT = 'fragment',
};

3496
jsx-html/deno.d.ts vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
{
"cmds": {
"start": ["den 00", "den 01", "den 02", "den 03", "den 04", "den 05", "den 06"],
"test": ["den 01:test", "den 03:test", "den 05:test", "den 06:test"],
"00": "deno run examples/00.tsx",
"01": "deno run examples/01.tsx",
"01:test": "deno test examples/01.tsx",
"02": "deno run -c examples/02/tsconfig.json examples/02/02.tsx",
"03": "deno run examples/03-async.tsx",
"03:test": "deno test examples/03-async.tsx",
"04": "deno run examples/04.tsx",
"05": "deno run examples/05.tsx",
"05:test": "deno test examples/05.tsx",
"06": "deno run examples/06.tsx",
"06:test": "deno test examples/06.tsx"
}
}

View File

@ -1,101 +0,0 @@
const fs = require('fs');
const { parse } = require('@babel/parser');
const { default: generator } = require('@babel/generator');
const { default: traverse } = require('@babel/traverse');
const { resolve, extname, join, dirname } = require('path');
const { tmpdir } = require('os');
const { readdir } = require('fs').promises;
const { cwd } = require('process');
const exts = ['.ts'];
const excludes = [
'/node_modules/',
'/examples/',
'/dist/',
'/jsx.d.ts',
'/mod.d.ts',
'/deno.d.ts',
];
const regExpRemoveExts = /\.(ts|tsx|js|jsx)$/i;
const tsconfig = {
compilerOptions: {
types: ['node'],
module: 'commonjs',
declaration: true,
removeComments: true,
emitDecoratorMetadata: true,
experimentalDecorators: true,
allowSyntheticDefaultImports: true,
target: 'es6',
sourceMap: true,
outDir: join(cwd(), 'nodejs'),
baseUrl: './',
},
};
// const distFolder = join(tmpdir(), `deno2nodejs-${+new Date()}`);
const distFolder = join(cwd(), `tmp`);
console.log('distFolder:', distFolder);
fs.mkdirSync(distFolder);
fs.writeFileSync(join(distFolder, 'tsconfig.json'), JSON.stringify(tsconfig));
async function getFiles(dir) {
const dirents = await readdir(dir, { withFileTypes: true });
const files = await Promise.all(
dirents.map((dirent) => {
const res = resolve(dir, dirent.name);
return dirent.isDirectory() ? getFiles(res) : res;
}),
);
return Array.prototype.concat(...files);
}
getFiles('./').then((files) => {
const tsFiles = files.filter(
(f) =>
exts.includes(extname(f)) &&
!excludes.some((val) => f.includes(val)),
);
tsFiles.forEach((file) => {
const code = deno2nodejs(file);
const dist = join(distFolder, file.substr(cwd().length));
const ensureDir = dirname(dist);
fs.mkdirSync(ensureDir, { recursive: true });
fs.writeFileSync(dist, code);
// console.log({ file, dist, ensureDir });
});
});
// we might want to execute `tsc -p ./tmp/tsconfig.json` in here
function deno2nodejs(file) {
const source = fs.readFileSync(file).toString();
const ast = parse(source, {
sourceType: 'module',
plugins: ['typescript', 'classProperties'],
});
traverse(ast, {
ImportDeclaration: function ImportDeclaration(path) {
var source = path.node.source;
if (!source.value.match(regExpRemoveExts)) {
return;
}
source.value = source.value.replace(regExpRemoveExts, '');
},
ExportDeclaration: function ExportDeclaration(path) {
var source = path.node.source;
if (source) {
if (!source.value.match(regExpRemoveExts)) {
return;
}
source.value = source.value.replace(regExpRemoveExts, '');
}
},
});
const { code } = generator(ast);
return code;
}

View File

@ -1,6 +0,0 @@
/// <reference path="https://raw.githubusercontent.com/apiel/jsx-html/latest/jsx.d.ts" />
import { React } from 'https://raw.githubusercontent.com/apiel/jsx-html/latest/mod.ts';
const View = () => <div>Hello</div>;
console.log((<View />).render());

View File

@ -1,62 +0,0 @@
/// <reference path="../jsx.d.ts" />
import { assertEquals } from 'https://deno.land/std/testing/asserts.ts';
import { React, Fragment } from '../mod.ts';
const Title = () => <h1>title</h1>;
const Value = ({ val }: { val: string }) => <p>value: {val}</p>;
const Numeric = ({ num }: { num: number }) => <p>num: {num}</p>;
const View = () => (
<div class="deno">
<Title />
<p onclick={() => 'lol'} valid checked={true} select="">
land
</p>
<br />
<hr />
<Fragment>
<Value val="hello" />
<Numeric num={23} />
</Fragment>
</div>
);
if (import.meta.main) {
(<View />).render().then(console.log);
} else {
// Run test
Deno.test('render title', async() => {
assertEquals(await (<Title />).render(), '<h1>title</h1>');
});
Deno.test('render value', async() => {
const val = 'hello';
assertEquals(await (<Value val={val} />).render(), `<p>value: ${val}</p>`);
});
Deno.test('render numeric', async() => {
const num = 123;
assertEquals(await (<Numeric num={num} />).render(), `<p>num: ${num}</p>`);
});
Deno.test('render view', async() => {
assertEquals(
await (<View />).render(),
'<div class="deno"><h1>title</h1><p valid checked select>land</p><br /><hr /><p>value: hello</p><p>num: 23</p></div>',
);
});
Deno.test('render empty', async ()=>{
assertEquals(
await (<div/>).render(),
`<div></div>`
)
assertEquals(
await (<img/>).render(),
`<img />`
)
})
}

View File

@ -1,6 +0,0 @@
/// <reference path="../../jsx.d.ts" />
import { jsx } from '../../mod.ts';
const View = () => <div>Hello</div>;
(<View />).render().then(console.log);

View File

@ -1,6 +0,0 @@
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "jsx"
}
}

View File

@ -1,30 +0,0 @@
/// <reference path="../jsx.d.ts" />
import { assertEquals } from 'https://deno.land/std/testing/asserts.ts';
import { delay } from 'https://deno.land/std/async/delay.ts';
import { React } from '../mod.ts';
const Title = async () => {
await delay(100);
return <h1>title{ await delay(100) }</h1>;
};
const View = () => (
<div>
<Title />
</div>
);
if (import.meta.main) {
(<View />).render().then(console.log);
} else {
// Run test
Deno.test('render title', async () => {
assertEquals(await (<Title />).render(), '<h1>title</h1>');
});
Deno.test('render view', async () => {
assertEquals(await (<View />).render(), '<div><h1>title</h1></div>');
});
}

View File

@ -1,9 +0,0 @@
/// <reference path="../jsx.d.ts" />
import { React } from '../mod.ts';
const View = () => <div>Hello</div>;
if (import.meta.main) {
(<View />).render().then(console.log);
}

View File

@ -1,27 +0,0 @@
/// <reference path="../jsx.d.ts" />
import { assertEquals } from 'https://deno.land/std/testing/asserts.ts';
import { React } from '../mod.ts';
const View = () => {
const techs = ['NodeJS', 'React Native', 'Next'];
return (
<ul>
{techs.map((tech: any) => (
<li>{tech}</li>
))}
</ul>
);
};
if (import.meta.main) {
(<View />).render().then(console.log);
} else {
Deno.test('render with array', async () => {
assertEquals(
await (<View />).render(),
'<ul><li>NodeJS</li><li>React Native</li><li>Next</li></ul>',
);
});
}

View File

@ -1,19 +0,0 @@
/// <reference path="../jsx.d.ts" />
import { assertEquals } from 'https://deno.land/std/testing/asserts.ts';
import { React } from '../mod.ts';
const View = () => {
return <div innerHTML="<b>hello</b> world" />;
};
if (import.meta.main) {
(<View />).render().then(console.log);
} else {
Deno.test('render with array', async () => {
assertEquals(
await (<View />).render(),
'<div><b>hello</b> world</div>',
);
});
}

View File

@ -1,19 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<script src="index.js"></script>
<div id="div-container" />
<script>
demo.Test().render('#div-container').then((html) => {
document.getElementById('div-container').innerHTML = html;
});
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -1,25 +0,0 @@
{
"name": "jsx-to-html-testing",
"version": "1.0.0",
"description": "jsx-to-html-testing",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --watch",
"start": "webpack-dev-server --open",
"build": "webpack"
},
"keywords": [],
"author": "Alex",
"license": "ISC",
"devDependencies": {
"ts-loader": "^8.0.1",
"typescript": "^3.9.7",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"async-jsx-html": "^1.2.1"
}
}

View File

@ -1,5 +0,0 @@
import { jsx, ElementNode } from 'async-jsx-html';
export function Test(): ElementNode {
return <div>Hello World</div>;
}

View File

@ -1,15 +0,0 @@
{
"compilerOptions": {
"outDir": "./dist/",
"module": "commonjs",
"jsx": "react",
"jsxFactory": "jsx",
"esModuleInterop": true,
"sourceMap": true,
"allowJs": true,
"lib": [
"es6",
"dom"
]
}
}

View File

@ -1,33 +0,0 @@
const path = require('path');
const MODULE_NAME = 'demo';
module.exports = {
mode: 'development',
entry: {
index: './src/index.tsx'
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ],
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'umd',
globalObject: 'this',
// libraryExport: 'default',
library: MODULE_NAME
},
};

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +0,0 @@
let {
React,
Fragment
} = require('../nodejs/mod');
const Title = () => /*#__PURE__*/React.createElement("h1", null, "title");
const Value = ({
val
}) => /*#__PURE__*/React.createElement("p", null, "value: ", val);
const Numeric = ({
num
}) => /*#__PURE__*/React.createElement("p", null, "num: ", num);
const View = () => /*#__PURE__*/React.createElement("div", {
class: "deno"
}, /*#__PURE__*/React.createElement(Title, null), /*#__PURE__*/React.createElement("p", {
onclick: () => 'lol',
valid: true,
checked: true,
select: ""
}, "land"), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement("hr", null), /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(Value, {
val: "hello"
}), /*#__PURE__*/React.createElement(Numeric, {
num: 23
})));
/*#__PURE__*/
React.createElement(View, null).render().then(console.log);

View File

@ -1,22 +0,0 @@
let { React, Fragment } = require('../nodejs/mod');
const Title = () => <h1>title</h1>;
const Value = ({ val }) => <p>value: {val}</p>;
const Numeric = ({ num }) => <p>num: {num}</p>;
const View = () => (
<div class="deno">
<Title />
<p onclick={() => 'lol'} valid checked={true} select="">
land
</p>
<br />
<hr />
<Fragment>
<Value val="hello" />
<Numeric num={23} />
</Fragment>
</div>
);
(<View />).render().then(console.log);

5
jsx-html/jsx.d.ts vendored
View File

@ -1,5 +0,0 @@
declare namespace JSX {
interface IntrinsicElements {
[elemName: string]: any;
}
}

View File

@ -1,33 +0,0 @@
import type {
NodePropsType,
ComponentFunctionType,
NullableChildType,
ChildType,
} from './types.ts';
import { ElementNode } from './node/ElementNode.ts';
import { ComponentNode } from './node/ComponentNode.ts';
export const jsx = <P extends NodePropsType = NodePropsType>(
element: string | ComponentFunctionType,
props: P | null,
...children: NullableChildType[]
) => {
const nodeProps = props || {};
if (typeof element === 'string') {
return new ElementNode(element, nodeProps, children);
}
if (typeof element === 'function') {
return new ComponentNode(element, nodeProps, children);
}
throw new TypeError(`Expected jsx element to be a string or a function`);
};
export const Fragment = (
props: NodePropsType,
children: ChildType,
): NullableChildType => {
return children;
};

View File

@ -1,12 +0,0 @@
{
"name": "jsx-html",
"version": "0.0.1",
"description": "",
"author": "Fabian Stamm <dev@fabianstamm.de>",
"contributors": [],
"files": [
"**/*.ts",
"**/*.js",
"README.md"
]
}

View File

@ -1,27 +0,0 @@
import { jsx, Fragment } from './jsx.ts';
import type {
ComponentFunctionType,
NodePropsType,
NullableChildType,
} from './types.ts';
export { ElementNode } from './node/ElementNode.ts';
export { ComponentNode } from './node/ComponentNode.ts';
export type {
jsx,
Fragment,
ComponentFunctionType,
NullableChildType,
NodePropsType,
};
export const React = {
Fragment,
createElement<P extends NodePropsType = NodePropsType>(
element: string | ComponentFunctionType,
props: P | null,
...children: NullableChildType[]
) {
return jsx(element, props, ...children);
},
};

View File

@ -1,37 +0,0 @@
import type {
NodePropsType,
ComponentFunctionType,
NullableChildType,
} from '../types.ts';
import { NODE_TYPE } from '../constants.ts';
import { FragmentNode } from './FragmentNode.ts';
import { Node } from './Node.ts';
import { normalizeChildren } from './utils/normalizeChildren.ts';
export class ComponentNode extends Node {
type = NODE_TYPE.COMPONENT;
constructor(
public component: ComponentFunctionType,
public props: NodePropsType,
children: NullableChildType[],
) {
super(children);
}
async render(): Promise<string | any[]> {
return [].concat((await this.renderComponent()) as any).join('');
}
async renderComponent() {
const child = await this.component(this.props, this.children);
const children = normalizeChildren(
Array.isArray(child) ? child : [child],
);
if (children.length === 1) {
return children[0].render();
} else if (children.length > 1) {
return new FragmentNode(children).render();
}
}
}

View File

@ -1,91 +0,0 @@
import { NODE_TYPE } from '../constants.ts';
import type { NodePropsType, NullableChildType } from '../types.ts';
import { Node } from './Node.ts';
import { doubleQuoteEncode } from './utils/htmlEncode.ts';
const ELEMENT_PROP = {
INNER_HTML: 'innerHTML',
};
// List taken from http://w3c.github.io/html-reference/syntax.html
const VOID_ELEMENTS = new Set<string>([
'area',
'base',
'br',
'col',
'command',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'meta',
'param',
'source',
'track',
'wbr',
]);
export class ElementNode extends Node {
type = NODE_TYPE.ELEMENT;
constructor(
public name: string,
public props: NodePropsType,
children: NullableChildType[],
) {
super(children);
}
async render(): Promise<string | any[]> {
const renderedProps = this.propsToHTML();
const renderedChildren =
typeof this.props[ELEMENT_PROP.INNER_HTML] === 'string'
? this.props[ELEMENT_PROP.INNER_HTML]
: (await this.renderChildren()).join('');
return renderedChildren || !VOID_ELEMENTS.has(this.name)
? `<${this.name}${renderedProps}>${renderedChildren || ''}</${
this.name
}>`
: `<${this.name}${renderedProps} />`;
}
private getValidProps() {
const props = this.props;
return Object.keys(this.props).filter((key) => {
if (key === ELEMENT_PROP.INNER_HTML) {
return false;
}
const val = props[key];
return (
typeof val === 'string' ||
typeof val === 'number' ||
val === true
);
});
}
private propsToHTML(): string {
const keys = this.getValidProps();
if (!keys.length) {
return '';
}
const props = this.props;
const pairs = keys.map((key) => {
if (!/^[a-zA-Z0-9-:\._]+$/.test(key)) {
throw new Error(`Invalid attribute name format ${key}`);
}
const val = props[key];
// https://html.spec.whatwg.org/multipage/dom.html#attributes
return val === true || val === ''
? key
: `${key}="${doubleQuoteEncode(val.toString())}"`;
});
return ` ${pairs.join(' ')}`;
}
}

View File

@ -1,15 +0,0 @@
import { NODE_TYPE } from '../constants.ts';
import type { ChildNodeType } from '../types.ts';
import { Node } from './Node.ts';
export class FragmentNode extends Node {
type = NODE_TYPE.FRAGMENT;
constructor(children: ChildNodeType[]) {
super(children);
}
render() {
return this.renderChildren();
}
}

View File

@ -1,29 +0,0 @@
import type { NODE_TYPE } from '../constants.ts';
import type { NullableChildType } from '../types.ts';
import { normalizeChildren } from './utils/normalizeChildren.ts';
export abstract class Node {
abstract type: NODE_TYPE;
constructor(public children: NullableChildType[]) {}
abstract async render(): Promise<string | any[]>;
async renderChildren() {
const result: string[] = [];
const children = normalizeChildren(this.children);
for (const child of children) {
const renderedChild = await child.render();
if (renderedChild) {
if (Array.isArray(renderedChild)) {
renderedChild.forEach(
(subchild) => subchild && result.push(subchild),
);
} else {
result.push(renderedChild);
}
}
}
return result;
}
}

View File

@ -1,12 +0,0 @@
import { NODE_TYPE } from '../constants.ts';
import { htmlEncode } from './utils/htmlEncode.ts';
export class TextNode {
type = NODE_TYPE.TEXT;
constructor(public text: string) {}
async render(): Promise<string | any[]> {
return htmlEncode(this.text);
}
}

View File

@ -1,13 +0,0 @@
export function doubleQuoteEncode(text: string): string {
return text
.replace(/"/g, '&quot;')
}
export function htmlEncode(text: string): string {
return doubleQuoteEncode(text
.replace(/&/g, '&amp;')
.replace(/\//g, '&#x2F;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/'/g, '&#39;'));
}

View File

@ -1,31 +0,0 @@
import type { NullableChildType, ChildNodeType } from '../../types.ts';
import { TextNode } from '../TextNode.ts';
import { NODE_TYPE } from '../../constants.ts';
export function normalizeChildren(
children: NullableChildType[],
): ChildNodeType[] {
const result: any[] = [];
for (const child of children) {
if (child && typeof child !== 'boolean') {
if (typeof child === 'string' || typeof child === 'number') {
result.push(new TextNode(`${child}`));
} else if (Array.isArray(child)) {
normalizeChildren(child).forEach((normalized) =>
result.push(normalized),
);
} else if (
child.type === NODE_TYPE.ELEMENT ||
child.type === NODE_TYPE.TEXT ||
child.type === NODE_TYPE.COMPONENT
) {
result.push(child);
} else {
throw new TypeError(`Unrecognized node type: ${typeof child}`);
}
}
}
return result;
}

View File

@ -1,6 +0,0 @@
export declare enum NODE_TYPE {
ELEMENT = "element",
TEXT = "text",
COMPONENT = "component",
FRAGMENT = "fragment"
}

View File

@ -1,12 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NODE_TYPE = void 0;
var NODE_TYPE;
(function (NODE_TYPE) {
NODE_TYPE["ELEMENT"] = "element";
NODE_TYPE["TEXT"] = "text";
NODE_TYPE["COMPONENT"] = "component";
NODE_TYPE["FRAGMENT"] = "fragment";
})(NODE_TYPE = exports.NODE_TYPE || (exports.NODE_TYPE = {}));
;
//# sourceMappingURL=constants.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../tmp/constants.ts"],"names":[],"mappings":";;;AAAA,IAAY,SAKX;AALD,WAAY,SAAS;IACnB,gCAAmB,CAAA;IACnB,0BAAa,CAAA;IACb,oCAAuB,CAAA;IACvB,kCAAqB,CAAA;AACvB,CAAC,EALW,SAAS,GAAT,iBAAS,KAAT,iBAAS,QAKpB;AACD,CAAC"}

View File

@ -1,5 +0,0 @@
import { NodePropsType, ComponentFunctionType, NullableChildType, ChildType } from "./types";
import { ElementNode } from "./node/ElementNode";
import { ComponentNode } from "./node/ComponentNode";
export declare const jsx: <P extends NodePropsType = NodePropsType>(element: string | ComponentFunctionType, props: P, ...children: NullableChildType[]) => ElementNode | ComponentNode;
export declare const Fragment: (props: NodePropsType, children: ChildType) => NullableChildType;

View File

@ -1,19 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Fragment = exports.jsx = void 0;
const ElementNode_1 = require("./node/ElementNode");
const ComponentNode_1 = require("./node/ComponentNode");
exports.jsx = (element, props, ...children) => {
const nodeProps = props || {};
if (typeof element === 'string') {
return new ElementNode_1.ElementNode(element, nodeProps, children);
}
if (typeof element === 'function') {
return new ComponentNode_1.ComponentNode(element, nodeProps, children);
}
throw new TypeError(`Expected jsx element to be a string or a function`);
};
exports.Fragment = (props, children) => {
return children;
};
//# sourceMappingURL=jsx.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"jsx.js","sourceRoot":"","sources":["../tmp/jsx.ts"],"names":[],"mappings":";;;AACA,oDAAiD;AACjD,wDAAqD;AACxC,QAAA,GAAG,GAAG,CAA0C,OAAuC,EAAE,KAAe,EAAE,GAAG,QAA6B,EAAE,EAAE;IACzJ,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE,CAAC;IAE9B,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;QAC/B,OAAO,IAAI,yBAAW,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;KACtD;IAED,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE;QACjC,OAAO,IAAI,6BAAa,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;KACxD;IAED,MAAM,IAAI,SAAS,CAAC,mDAAmD,CAAC,CAAC;AAC3E,CAAC,CAAC;AACW,QAAA,QAAQ,GAAG,CAAC,KAAoB,EAAE,QAAmB,EAAqB,EAAE;IACvF,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC"}

View File

@ -1,9 +0,0 @@
import { jsx, Fragment } from "./jsx";
import { ComponentFunctionType, NodePropsType, NullableChildType } from "./types";
export { ElementNode } from "./node/ElementNode";
export { ComponentNode } from "./node/ComponentNode";
export { jsx, Fragment, ComponentFunctionType, NullableChildType, NodePropsType };
export declare const React: {
Fragment: (props: NodePropsType, children: import("./types").ChildType) => NullableChildType;
createElement<P extends NodePropsType = NodePropsType>(element: string | ComponentFunctionType, props: P, ...children: NullableChildType[]): import("./node/ElementNode").ElementNode | import("./node/ComponentNode").ComponentNode;
};

View File

@ -1,17 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.React = exports.Fragment = exports.jsx = void 0;
const jsx_1 = require("./jsx");
Object.defineProperty(exports, "jsx", { enumerable: true, get: function () { return jsx_1.jsx; } });
Object.defineProperty(exports, "Fragment", { enumerable: true, get: function () { return jsx_1.Fragment; } });
var ElementNode_1 = require("./node/ElementNode");
Object.defineProperty(exports, "ElementNode", { enumerable: true, get: function () { return ElementNode_1.ElementNode; } });
var ComponentNode_1 = require("./node/ComponentNode");
Object.defineProperty(exports, "ComponentNode", { enumerable: true, get: function () { return ComponentNode_1.ComponentNode; } });
exports.React = {
Fragment: jsx_1.Fragment,
createElement(element, props, ...children) {
return jsx_1.jsx(element, props, ...children);
}
};
//# sourceMappingURL=mod.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../tmp/mod.ts"],"names":[],"mappings":";;;AAAA,+BAAsC;AAI7B,oFAJA,SAAG,OAIA;AAAE,yFAJA,cAAQ,OAIA;AAFtB,kDAAiD;AAAxC,0GAAA,WAAW,OAAA;AACpB,sDAAqD;AAA5C,8GAAA,aAAa,OAAA;AAET,QAAA,KAAK,GAAG;IACnB,QAAQ,EAAR,cAAQ;IAER,aAAa,CAA0C,OAAuC,EAAE,KAAe,EAAE,GAAG,QAA6B;QAC/I,OAAO,SAAG,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,CAAC;IAC1C,CAAC;CAEF,CAAC"}

View File

@ -1,11 +0,0 @@
import { NodePropsType, ComponentFunctionType, NullableChildType } from "../types";
import { NODE_TYPE } from "../constants";
import { Node } from "./Node";
export declare class ComponentNode extends Node {
component: ComponentFunctionType;
props: NodePropsType;
type: NODE_TYPE;
constructor(component: ComponentFunctionType, props: NodePropsType, children: NullableChildType[]);
render(): Promise<string | any[]>;
renderComponent(): Promise<string | any[]>;
}

View File

@ -1,43 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ComponentNode = void 0;
const constants_1 = require("../constants");
const FragmentNode_1 = require("./FragmentNode");
const Node_1 = require("./Node");
const normalizeChildren_1 = require("./utils/normalizeChildren");
class ComponentNode extends Node_1.Node {
constructor(component, props, children) {
super(children);
this.component = component;
this.props = props;
this.type = constants_1.NODE_TYPE.COMPONENT;
}
render() {
return __awaiter(this, void 0, void 0, function* () {
return [].concat(yield this.renderComponent()).join('');
});
}
renderComponent() {
return __awaiter(this, void 0, void 0, function* () {
const child = yield this.component(this.props, this.children);
const children = normalizeChildren_1.normalizeChildren(Array.isArray(child) ? child : [child]);
if (children.length === 1) {
return children[0].render();
}
else if (children.length > 1) {
return new FragmentNode_1.FragmentNode(children).render();
}
});
}
}
exports.ComponentNode = ComponentNode;
//# sourceMappingURL=ComponentNode.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"ComponentNode.js","sourceRoot":"","sources":["../../tmp/node/ComponentNode.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,4CAAyC;AACzC,iDAA8C;AAC9C,iCAA8B;AAC9B,iEAA8D;AAC9D,MAAa,aAAc,SAAQ,WAAI;IAGrC,YAAmB,SAAgC,EAAS,KAAoB,EAAE,QAA6B;QAC7G,KAAK,CAAC,QAAQ,CAAC,CAAC;QADC,cAAS,GAAT,SAAS,CAAuB;QAAS,UAAK,GAAL,KAAK,CAAe;QAFhF,SAAI,GAAG,qBAAS,CAAC,SAAS,CAAC;IAI3B,CAAC;IAEK,MAAM;;YACV,OAAO,EAAE,CAAC,MAAM,CAAE,MAAM,IAAI,CAAC,eAAe,EAAU,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;KAAA;IAEK,eAAe;;YACnB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC9D,MAAM,QAAQ,GAAG,qCAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YAE3E,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;gBACzB,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aAC7B;iBAAM,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC9B,OAAO,IAAI,2BAAY,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;aAC5C;QACH,CAAC;KAAA;CAEF;AAtBD,sCAsBC"}

View File

@ -1,12 +0,0 @@
import { NODE_TYPE } from "../constants";
import { NodePropsType, NullableChildType } from "../types";
import { Node } from "./Node";
export declare class ElementNode extends Node {
name: string;
props: NodePropsType;
type: NODE_TYPE;
constructor(name: string, props: NodePropsType, children: NullableChildType[]);
render(): Promise<string | any[]>;
private getValidProps;
private propsToHTML;
}

View File

@ -1,60 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ElementNode = void 0;
const constants_1 = require("../constants");
const Node_1 = require("./Node");
const htmlEncode_1 = require("./utils/htmlEncode");
const ELEMENT_PROP = {
INNER_HTML: 'innerHTML'
};
class ElementNode extends Node_1.Node {
constructor(name, props, children) {
super(children);
this.name = name;
this.props = props;
this.type = constants_1.NODE_TYPE.ELEMENT;
}
render() {
return __awaiter(this, void 0, void 0, function* () {
const renderedProps = this.propsToHTML();
const renderedChildren = typeof this.props[ELEMENT_PROP.INNER_HTML] === 'string' ? this.props[ELEMENT_PROP.INNER_HTML] : (yield this.renderChildren()).join('');
return renderedChildren ? `<${this.name}${renderedProps}>${renderedChildren}</${this.name}>` : `<${this.name}${renderedProps} />`;
});
}
getValidProps() {
const props = this.props;
return Object.keys(this.props).filter(key => {
if (key === ELEMENT_PROP.INNER_HTML) {
return false;
}
const val = props[key];
return typeof val === 'string' || typeof val === 'number' || val === true;
});
}
propsToHTML() {
const keys = this.getValidProps();
if (!keys.length) {
return '';
}
const props = this.props;
const pairs = keys.map(key => {
if (!/^[a-zA-Z0-9-:\._]+$/.test(key)) {
throw new Error(`Invalid attribute name format ${key}`);
}
const val = props[key];
return val === true || val === '' ? key : `${key}="${htmlEncode_1.doubleQuoteEncode(val.toString())}"`;
});
return ` ${pairs.join(' ')}`;
}
}
exports.ElementNode = ElementNode;
//# sourceMappingURL=ElementNode.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"ElementNode.js","sourceRoot":"","sources":["../../tmp/node/ElementNode.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,4CAAyC;AAEzC,iCAA8B;AAC9B,mDAAuD;AACvD,MAAM,YAAY,GAAG;IACnB,UAAU,EAAE,WAAW;CACxB,CAAC;AACF,MAAa,WAAY,SAAQ,WAAI;IAGnC,YAAmB,IAAY,EAAS,KAAoB,EAAE,QAA6B;QACzF,KAAK,CAAC,QAAQ,CAAC,CAAC;QADC,SAAI,GAAJ,IAAI,CAAQ;QAAS,UAAK,GAAL,KAAK,CAAe;QAF5D,SAAI,GAAG,qBAAS,CAAC,OAAO,CAAC;IAIzB,CAAC;IAEK,MAAM;;YACV,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YACzC,MAAM,gBAAgB,GAAG,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAChK,OAAO,gBAAgB,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,aAAa,IAAI,gBAAgB,KAAK,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,aAAa,KAAK,CAAC;QACpI,CAAC;KAAA;IAEO,aAAa;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;YAC1C,IAAI,GAAG,KAAK,YAAY,CAAC,UAAU,EAAE;gBACnC,OAAO,KAAK,CAAC;aACd;YAED,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,WAAW;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAElC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;YAChB,OAAO,EAAE,CAAC;SACX;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;YAC3B,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;gBACpC,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,EAAE,CAAC,CAAC;aACzD;YAED,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;YAEvB,OAAO,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,8BAAiB,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC;QAC5F,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IAC/B,CAAC;CAEF;AA7CD,kCA6CC"}

View File

@ -1,8 +0,0 @@
import { NODE_TYPE } from "../constants";
import { ChildNodeType } from "../types";
import { Node } from "./Node";
export declare class FragmentNode extends Node {
type: NODE_TYPE;
constructor(children: ChildNodeType[]);
render(): Promise<string[]>;
}

View File

@ -1,16 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FragmentNode = void 0;
const constants_1 = require("../constants");
const Node_1 = require("./Node");
class FragmentNode extends Node_1.Node {
constructor(children) {
super(children);
this.type = constants_1.NODE_TYPE.FRAGMENT;
}
render() {
return this.renderChildren();
}
}
exports.FragmentNode = FragmentNode;
//# sourceMappingURL=FragmentNode.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"FragmentNode.js","sourceRoot":"","sources":["../../tmp/node/FragmentNode.ts"],"names":[],"mappings":";;;AAAA,4CAAyC;AAEzC,iCAA8B;AAC9B,MAAa,YAAa,SAAQ,WAAI;IAGpC,YAAY,QAAyB;QACnC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAHlB,SAAI,GAAG,qBAAS,CAAC,QAAQ,CAAC;IAI1B,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;IAC/B,CAAC;CAEF;AAXD,oCAWC"}

View File

@ -1,9 +0,0 @@
import { NODE_TYPE } from "../constants";
import { NullableChildType } from "../types";
export declare abstract class Node {
children: NullableChildType[];
abstract type: NODE_TYPE;
constructor(children: NullableChildType[]);
abstract render(): Promise<string | any[]>;
renderChildren(): Promise<string[]>;
}

View File

@ -1,38 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Node = void 0;
const normalizeChildren_1 = require("./utils/normalizeChildren");
class Node {
constructor(children) {
this.children = children;
}
renderChildren() {
return __awaiter(this, void 0, void 0, function* () {
const result = [];
const children = normalizeChildren_1.normalizeChildren(this.children);
for (const child of children) {
const renderedChild = yield child.render();
if (renderedChild) {
if (Array.isArray(renderedChild)) {
renderedChild.forEach(subchild => subchild && result.push(subchild));
}
else {
result.push(renderedChild);
}
}
}
return result;
});
}
}
exports.Node = Node;
//# sourceMappingURL=Node.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"Node.js","sourceRoot":"","sources":["../../tmp/node/Node.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,iEAA8D;AAC9D,MAAsB,IAAI;IAGxB,YAAmB,QAA6B;QAA7B,aAAQ,GAAR,QAAQ,CAAqB;IAAG,CAAC;IAI9C,cAAc;;YAClB,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,qCAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAElD,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE;gBAC5B,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC;gBAE3C,IAAI,aAAa,EAAE;oBACjB,IAAI,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE;wBAChC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;qBACtE;yBAAM;wBACL,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;qBAC5B;iBACF;aACF;YAED,OAAO,MAAM,CAAC;QAChB,CAAC;KAAA;CAEF;AA1BD,oBA0BC"}

View File

@ -1,7 +0,0 @@
import { NODE_TYPE } from "../constants";
export declare class TextNode {
text: string;
type: NODE_TYPE;
constructor(text: string);
render(): Promise<string | any[]>;
}

View File

@ -1,27 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TextNode = void 0;
const constants_1 = require("../constants");
const htmlEncode_1 = require("./utils/htmlEncode");
class TextNode {
constructor(text) {
this.text = text;
this.type = constants_1.NODE_TYPE.TEXT;
}
render() {
return __awaiter(this, void 0, void 0, function* () {
return htmlEncode_1.htmlEncode(this.text);
});
}
}
exports.TextNode = TextNode;
//# sourceMappingURL=TextNode.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"TextNode.js","sourceRoot":"","sources":["../../tmp/node/TextNode.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,4CAAyC;AACzC,mDAAgD;AAChD,MAAa,QAAQ;IAGnB,YAAmB,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;QAF/B,SAAI,GAAG,qBAAS,CAAC,IAAI,CAAC;IAEY,CAAC;IAE7B,MAAM;;YACV,OAAO,uBAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;KAAA;CAEF;AATD,4BASC"}

View File

@ -1,2 +0,0 @@
export declare function doubleQuoteEncode(text: string): string;
export declare function htmlEncode(text: string): string;

View File

@ -1,12 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.htmlEncode = exports.doubleQuoteEncode = void 0;
function doubleQuoteEncode(text) {
return text.replace(/"/g, '&quot;');
}
exports.doubleQuoteEncode = doubleQuoteEncode;
function htmlEncode(text) {
return doubleQuoteEncode(text.replace(/&/g, '&amp;').replace(/\//g, '&#x2F;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/'/g, '&#39;'));
}
exports.htmlEncode = htmlEncode;
//# sourceMappingURL=htmlEncode.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"htmlEncode.js","sourceRoot":"","sources":["../../../tmp/node/utils/htmlEncode.ts"],"names":[],"mappings":";;;AAAA,SAAgB,iBAAiB,CAAC,IAAY;IAC5C,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACtC,CAAC;AAFD,8CAEC;AACD,SAAgB,UAAU,CAAC,IAAY;IACrC,OAAO,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AACpJ,CAAC;AAFD,gCAEC"}

View File

@ -1,2 +0,0 @@
import { NullableChildType, ChildNodeType } from "../../types";
export declare function normalizeChildren(children: NullableChildType[]): ChildNodeType[];

View File

@ -1,27 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeChildren = void 0;
const TextNode_1 = require("../TextNode");
const constants_1 = require("../../constants");
function normalizeChildren(children) {
const result = [];
for (const child of children) {
if (child && typeof child !== 'boolean') {
if (typeof child === 'string' || typeof child === 'number') {
result.push(new TextNode_1.TextNode(`${child}`));
}
else if (Array.isArray(child)) {
normalizeChildren(child).forEach(result.push);
}
else if (child.type === constants_1.NODE_TYPE.ELEMENT || child.type === constants_1.NODE_TYPE.TEXT || child.type === constants_1.NODE_TYPE.COMPONENT) {
result.push(child);
}
else {
throw new TypeError(`Unrecognized node type: ${typeof child}`);
}
}
}
return result;
}
exports.normalizeChildren = normalizeChildren;
//# sourceMappingURL=normalizeChildren.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"normalizeChildren.js","sourceRoot":"","sources":["../../../tmp/node/utils/normalizeChildren.ts"],"names":[],"mappings":";;;AACA,0CAAuC;AACvC,+CAA4C;AAC5C,SAAgB,iBAAiB,CAAC,QAA6B;IAC7D,MAAM,MAAM,GAAU,EAAE,CAAC;IAEzB,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE;QAC5B,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE;YACvC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;gBAC1D,MAAM,CAAC,IAAI,CAAC,IAAI,mBAAQ,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;aACvC;iBAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;gBAC/B,iBAAiB,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;aAC/C;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,qBAAS,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,qBAAS,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,qBAAS,CAAC,SAAS,EAAE;gBAClH,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;aACpB;iBAAM;gBACL,MAAM,IAAI,SAAS,CAAC,2BAA2B,OAAO,KAAK,EAAE,CAAC,CAAC;aAChE;SACF;KACF;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAlBD,8CAkBC"}

View File

@ -1,15 +0,0 @@
import { ElementNode } from "./node/ElementNode";
import { TextNode } from "./node/TextNode";
import { ComponentNode } from "./node/ComponentNode";
import { FragmentNode } from "./node/FragmentNode";
export declare type NodePropsType = {
[key: string]: any;
};
declare type Primitive = string | boolean | number;
declare type NullablePrimitive = Primitive | null | void;
export declare type ChildNodeType = ElementNode | TextNode | ComponentNode;
export declare type NodeType = ChildNodeType | FragmentNode;
export declare type ChildType = ChildNodeType | Primitive;
export declare type NullableChildType = ChildType | ChildNodeType | NullablePrimitive;
export declare type ComponentFunctionType = (props: NodePropsType, child?: NullableChildType[]) => NullableChildType | Promise<NullableChildType>;
export {};

View File

@ -1,3 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../tmp/types.ts"],"names":[],"mappings":""}

View File

@ -1,16 +0,0 @@
deno test examples/01.tsx
deno run examples/00.tsx
deno run examples/01.tsx
deno run -c examples/02/tsconfig.json examples/02/02.tsx
https://deno.land/manual/contributing/style_guide
- would be great to make it as well node compatible:
tsc examples/01.tsx --jsx react --outDir dist && node dist/examples/01.js
git tag --delete latest
git push --delete origin latest
git tag latest
git push --tags

View File

@ -1,25 +0,0 @@
{
"name": "async-jsx-html",
"version": "1.2.1",
"main": "nodejs/mod.js",
"types": "nodejs/mod.d.ts",
"repository": "git@github.com:apiel/jsx-html.git",
"author": "Alexandre Piel <alexandre.piel@gmail.com>",
"license": "MIT",
"scripts": {
"clean": "rm -rf nodejs/ && rm -rf tmp/",
"build": "yarn clean && node deno2nodejs.js && tsc -p ./tmp/tsconfig.json",
"transpile": "babel --no-babelrc --plugins @babel/plugin-transform-react-jsx ./examples/node_01.jsx -o ./examples/node_01.js",
"start": "yarn transpile && node ./examples/node_01.js"
},
"devDependencies": {
"@babel/cli": "^7.10.3",
"@babel/core": "^7.10.3",
"@babel/generator": "^7.10.3",
"@babel/parser": "^7.10.3",
"@babel/plugin-transform-react-jsx": "^7.10.3",
"@babel/traverse": "^7.10.3",
"@types/node": "^14.0.13",
"typescript": "^3.9.5"
}
}

View File

@ -1,23 +0,0 @@
import type { ElementNode } from './node/ElementNode.ts';
import type { TextNode } from './node/TextNode.ts';
import type { ComponentNode } from './node/ComponentNode.ts';
import type { FragmentNode } from './node/FragmentNode.ts';
export type NodePropsType = {
[key: string]: any;
};
type Primitive = string | boolean | number;
type NullablePrimitive = Primitive | null | void;
export type ChildNodeType = ElementNode | TextNode | ComponentNode;
export type NodeType = ChildNodeType | FragmentNode;
export type ChildType = ChildNodeType | Primitive;
export type NullableChildType = ChildType | ChildNodeType | NullablePrimitive;
export type ComponentFunctionType = (
props: NodePropsType,
child?: NullableChildType[],
) => NullableChildType | Promise<NullableChildType>;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
Denreg JSX renderer
**deprecated**
**DO NOT USE**

View File

@ -1,13 +1,14 @@
{
"name": "@denreg-jsx",
"version": "0.0.3",
"version": "0.1.4",
"description": "Denreg JSX renderer",
"author": "Fabian Stamm <dev@fabianstamm.de>",
"contributors": [],
"deprecated": true,
"deprecated": false,
"files": [
"**/*.ts",
"**/*.js",
"tsconfig.json",
"README.md"
]
}

145
jsx/mod.ts Normal file
View File

@ -0,0 +1,145 @@
import "./types.ts";
const Fragment = Symbol("fragment");
declare namespace JSX {
interface Element {}
interface IntrinsicElements {
div: any;
}
}
export type Element = {
component: Component | string | typeof Fragment;
props: any;
children: any[];
};
export type ComponentRetElm = Element | Element[];
export type Component = (
props: any,
children: any
) => ComponentRetElm | Promise<ComponentRetElm>;
export function h(
component: string | Component,
props: any,
...children: Element[]
): Element {
return {
component,
props,
children,
};
}
const createElement = h;
export { Fragment, createElement };
export async function renderSSR(element: Element | string): Promise<string> {
if (typeof element === "string") return element;
else if (typeof element.component === "string")
return await renderHTML(element as Element);
else if (
typeof element.component === "function" ||
element.component === Fragment
)
return await renderCustom(element as Element);
console.warn("renderSSR: invalid element", element);
return "";
}
const selfClosing = new Set([
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"link",
"meta",
"param",
"source",
"track",
"wbr",
]);
function flatDeep(arr: any, d = Infinity): any[] {
if (Array.isArray(arr) && d >= 0) {
let res = [];
for (const val of arr) {
const v = flatDeep(val, d - 1);
res.push(...v);
}
return res;
}
return [arr];
}
function cleanChildren(children: any) {
return flatDeep(children).filter((e) => !!e);
}
async function renderHTML(element: Element) {
if (typeof element.component !== "string")
throw new Error("Internal consistency error");
let props = "";
for (const key in element.props) {
if (key == "innerHTML") continue;
props += `${key}="${element.props[key] || ""}" `;
}
const tag = element.component;
if (selfClosing.has(element.component)) {
return `<${tag} ${props}/>`;
} else {
let inner = "";
if (element.props && element.props["innerHTML"]) {
inner = element.props["innerHTML"];
} else {
const children = cleanChildren(element.children);
inner = (
await Promise.all(children.map((child) => renderSSR(child)))
).join("");
}
return `<${tag} ${props}>${inner || ""}</${tag}>`;
}
}
async function renderCustom(element: Element) {
if (typeof element.component === "string")
throw new Error("Internal consistency error");
if (element.component === Fragment) {
const ch = (
await Promise.all(
cleanChildren(element.children).map((child) => renderSSR(child))
)
).join("");
return ch;
} else {
const res = await Promise.resolve(
element.component(
{
...element.props,
children: element.children,
},
element.children
)
);
const ch = (
await Promise.all(cleanChildren(res).map((child) => renderSSR(child)))
).join("");
return ch;
}
}

7
jsx/tsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext", "deno.ns", "deno.unstable"],
"jsxFactory": "h",
"strictPropertyInitialization": false
}
}

11
jsx/types.ts Normal file
View File

@ -0,0 +1,11 @@
declare namespace JSX {
interface IntrinsicElements {
[elemName: string]: any;
}
}
declare namespace JSX {
interface ElementClass {
render: any;
}
}

Some files were not shown because too many files have changed in this diff Show More