aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/root.zig2
-rw-r--r--src/api/sketch.zig29
-rw-r--r--src/frontend/File.zig78
-rw-r--r--src/frontend/Minifier.zig168
-rw-r--r--src/frontend/root.zig3
-rw-r--r--src/frontend/web/hello-world/index.html3
-rw-r--r--src/frontend/web/index.html4
-rw-r--r--src/frontend/z-component.js3
-rw-r--r--src/http/Server.zig20
-rw-r--r--src/http/Url.zig39
-rw-r--r--src/http/root.zig3
-rw-r--r--src/main.zig4
-rw-r--r--src/root.zig6
13 files changed, 293 insertions, 69 deletions
diff --git a/src/api/root.zig b/src/api/root.zig
index 5128aa1..c28991c 100644
--- a/src/api/root.zig
+++ b/src/api/root.zig
@@ -3,6 +3,8 @@
const std = @import("std");
+pub const _ = @import("sketch.zig");
+
test {
_ = std.testing.refAllDecls(@This());
}
diff --git a/src/api/sketch.zig b/src/api/sketch.zig
new file mode 100644
index 0000000..409606f
--- /dev/null
+++ b/src/api/sketch.zig
@@ -0,0 +1,29 @@
+const std = @import("std");
+const http = @import("http");
+
+pub const Url = http.Url.Template(
+ "/api/images/{id}/metadata/{name}",
+ .{
+ .guard = Auth(.admin),
+ }
+);
+
+const Query = struct {
+ condition: []const u8,
+ start: usize,
+ end: usize,
+}
+
+pub fn post(request: http.Request(Url)) !http.Response {
+ const query = try request.body.json(Query, request.arena);
+
+ return .string(request.params.id);
+}
+
+pub fn get(request: http.Request(Url)) !http.Response {
+ return .file("./some/path.txt");
+}
+
+test {
+ _ = std.testing.refAllDecls(@This());
+}
diff --git a/src/frontend/File.zig b/src/frontend/File.zig
index e7c3b6d..fac6a03 100644
--- a/src/frontend/File.zig
+++ b/src/frontend/File.zig
@@ -1,6 +1,6 @@
-const std = @import("std");
+const Minifier = @import("Minifier.zig");
-pub fn File(comptime tag: ?[]const u8, Iterator: type) type {
+pub fn File(comptime tag: ?[]const u8, comptime minifier: Minifier) type {
const opening = if (tag) |t| "<" ++ t ++ ">" else "";
const closing = if (tag) |t| "</" ++ t ++ ">" else "";
@@ -22,70 +22,20 @@ pub fn File(comptime tag: ?[]const u8, Iterator: type) type {
};
}
- fn content(comptime self: @This()) []const u8 {
- return opening ++ @embedFile(self.file) ++ closing;
- }
-
- fn content_minified(comptime self: @This()) []const u8 {
- var minified: []const u8 = "";
- var iterator: Iterator = .init(@embedFile(self.file));
-
- while (iterator.next()) |segment| {
- minified = minified ++ segment;
- }
-
- return opening ++ minified ++ closing;
- }
-
pub fn to_html_string(comptime self: @This()) []const u8 {
- return if (self.do_minify) self.content_minified() else self.content();
- }
- };
-}
-
-fn StringIterator(comptime quotes: []const u8) type {
- return struct {
- content: []const u8,
-
- pub fn init(content: []const u8) @This() {
- return .{ .content = content };
- }
-
- pub fn next(self: *@This()) ?[]const u8 {
- @setEvalBranchQuota(self.content.len * 20);
-
- self.content = std.mem.trimStart(u8, self.content, &std.ascii.whitespace);
-
- if (self.content.len == 0) return null;
-
- var index: usize = 0;
-
- while (self.content.len > index) {
- const char = self.content[index];
-
- if (std.mem.indexOfScalar(u8, quotes, char) != null) {
- index += 1;
- while (self.content.len > index) {
- if (self.content[index] == char) {
- index += 1;
- break;
- }
- index += 1;
- }
- } else if (std.ascii.isWhitespace(char)) {
- break;
- }
- index += 1;
- }
-
- const block = self.content[0..index];
- self.content = self.content[index..];
-
- return block ++ " ";
+ return opening ++ (if (self.do_minify)
+ minifier.minify(@embedFile(self.file))
+ else
+ @embedFile(self.file)) ++ closing;
}
};
}
-pub const Style = File("style", StringIterator("\"'"));
-pub const Script = File("script", StringIterator("\"'`"));
-pub const Html = File(null, StringIterator("\"'"));
+/// When using regex in Javascript the spaces
+/// might get trimmed since /re/ is a awful syntax
+/// I don't what to add a rule for it currently,
+/// Since a single `/` is a operator but if there are two on one line
+/// it is a regex.
+pub const Script = File("script", .js);
+pub const Style = File("style", .css);
+pub const Html = File(null, .html);
diff --git a/src/frontend/Minifier.zig b/src/frontend/Minifier.zig
new file mode 100644
index 0000000..65dd9fb
--- /dev/null
+++ b/src/frontend/Minifier.zig
@@ -0,0 +1,168 @@
+const std = @import("std");
+
+/// A string contained in quotes will not be changed in any form.
+quotes: []const u8,
+
+/// This character is used to escape in strings.
+escape: ?u8,
+
+/// A delimiter does not need any padding around it.
+delimiters: []const u8,
+
+/// Kinds of comments, will be removed in the output.
+comments: []const struct { []const u8, []const u8 },
+
+pub const html: @This() = .{
+ .quotes = "\"'",
+ .escape = null,
+ .delimiters = "<>",
+ .comments = &.{.{ "<!--", "-->" }},
+};
+
+pub const js: @This() = .{
+ .quotes = "\"'`",
+ .escape = '\\',
+ .delimiters = "+-*,.:;[](){}=&|^",
+ .comments = &.{ .{ "//", "\n" }, .{ "/*", "*/" } },
+};
+
+pub const css: @This() = .{
+ .quotes = "\"'",
+ .escape = '\\',
+ .delimiters = ":;{}",
+ .comments = &.{.{ "/*", "*/" }},
+};
+
+fn skip(comptime source: *[]const u8, comptime len: usize) void {
+ source.* = (source.*)[len..];
+}
+
+fn push(
+ comptime source: *[]const u8,
+ comptime target: *[]const u8,
+ comptime len: usize,
+) void {
+ target.* = target.* ++ (source.*)[0..len];
+ skip(source, len);
+}
+
+pub fn minify(
+ comptime self: *const @This(),
+ comptime c: []const u8,
+) []const u8 {
+ @setEvalBranchQuota(c.len * 100);
+ var content = std.mem.trimStart(u8, c, &std.ascii.whitespace);
+ var block: []const u8 = "";
+
+ while (content.len > 0) {
+ const had_comment: bool = cmt: {
+ inline for (self.comments) |comment| {
+ const start, const end = comment;
+
+ if (std.mem.startsWith(u8, content, start)) {
+ skip(&content, start.len);
+ skip(&content, if (std.mem.indexOf(u8, content, end)) |l| l + end.len else content.len);
+ break :cmt true;
+ }
+ }
+ break :cmt false;
+ };
+
+ if (had_comment) {} else if (std.mem.indexOfScalar(u8, self.quotes, content[0])) |_| {
+ const quote = content[0];
+ push(&content, &block, 1);
+ while (content.len > 0) {
+ if (content[0] == self.escape) {
+ push(&content, &block, 2);
+ } else if (content[0] == quote) {
+ push(&content, &block, 1);
+ break;
+ }
+ push(&content, &block, 1);
+ }
+ } else {
+ push(&content, &block, 1);
+ }
+
+ const was_trimmed = content.len > 0 and std.ascii.isWhitespace(content[0]);
+ content = std.mem.trimStart(u8, content, &std.ascii.whitespace);
+ if (was_trimmed and
+ block.len > 0 and
+ content.len > 0 and
+ std.mem.indexOfScalar(u8, self.delimiters, block[block.len - 1]) == null and
+ std.mem.indexOfScalar(u8, self.delimiters, content[0]) == null)
+ {
+ block = block ++ " ";
+ }
+ }
+
+ return block;
+}
+
+test "html" {
+ const buffer: []const u8 =
+ \\<html>
+ \\ <head>
+ \\ <title>Some Title</title>
+ \\ </head>
+ \\ <body>
+ \\ <z-component type="something here" tag="1 2 3"> 123 </z-component>
+ \\ <!-- some comment here -->
+ \\ Hello World!
+ \\ </body>
+ \\</html>
+ ;
+
+ try std.testing.expectEqualStrings(
+ "<html><head><title>Some Title</title></head><body><z-component type=\"something here\" tag=\"1 2 3\">123</z-component>Hello World!</body></html>",
+ comptime html.minify(buffer),
+ );
+}
+
+test "css" {
+ const buffer: []const u8 =
+ \\body {
+ \\ background: #fff;
+ \\ color: red;
+ \\}
+ \\
+ \\#content:first {
+ \\ user-select: none;
+ \\ /* comment */
+ \\ content: ' ';
+ \\ padding: 5px 5px;
+ \\}
+ ;
+
+ try std.testing.expectEqualStrings(
+ "body{background:#fff;color:red;}#content:first{user-select:none;content:' ';padding:5px 5px;}",
+ comptime css.minify(buffer),
+ );
+}
+
+test "js" {
+ const buffer: []const u8 =
+ \\class extends HTMLElement {
+ \\ // comment here
+ \\ constructor() {
+ \\ super();
+ \\ this.attachShadow({ mode: 'open' });
+ \\ }
+ \\
+ \\ /*
+ \\ * Mulitline
+ \\ */
+ \\ connectedCallback() {
+ \\ const type = this.getAttribute('type');
+ \\ const template = document.getElementById(`--${type}`);
+ \\ const node = document.importNode(template.content, true);
+ \\ this.shadowRoot.appendChild(node);
+ \\ }
+ \\}
+ ;
+
+ try std.testing.expectEqualStrings(
+ "class extends HTMLElement{constructor(){super();this.attachShadow({mode:'open'});}connectedCallback(){const type=this.getAttribute('type');const template=document.getElementById(`--${type}`);const node=document.importNode(template.content,true);this.shadowRoot.appendChild(node);}}",
+ comptime js.minify(buffer),
+ );
+}
diff --git a/src/frontend/root.zig b/src/frontend/root.zig
index 39908b5..8ff1055 100644
--- a/src/frontend/root.zig
+++ b/src/frontend/root.zig
@@ -5,7 +5,8 @@ const std = @import("std");
pub const Component = @import("Component.zig");
pub const Document = @import("Document.zig");
pub const File = @import("File.zig");
-pub const Head = @import("File.zig");
+pub const Head = @import("Head.zig");
+pub const Minifier = @import("Minifier.zig");
test {
_ = std.testing.refAllDecls(@This());
diff --git a/src/frontend/web/hello-world/index.html b/src/frontend/web/hello-world/index.html
index a9b0be7..047a283 100644
--- a/src/frontend/web/hello-world/index.html
+++ b/src/frontend/web/hello-world/index.html
@@ -1,3 +1,6 @@
<div id="container" onclick="clicked()">
Click Me!
+ <z-component type="box">
+ <span slot="title">Hello World!<span>
+ </z-component>
</div>
diff --git a/src/frontend/web/index.html b/src/frontend/web/index.html
index 885f2c5..646cdb0 100644
--- a/src/frontend/web/index.html
+++ b/src/frontend/web/index.html
@@ -1 +1,3 @@
-<z-component type="hello-world"></z-component>
+<z-component type="hello-world">
+ <p slot="ur-mom">lulululu</p>
+</z-component>
diff --git a/src/frontend/z-component.js b/src/frontend/z-component.js
index e9b63ff..5e430b5 100644
--- a/src/frontend/z-component.js
+++ b/src/frontend/z-component.js
@@ -3,14 +3,13 @@ customElements.define(
class extends HTMLElement {
constructor() {
super();
- this.attachShadow({ mode: 'open' })
+ this.attachShadow({ mode: 'open' });
}
connectedCallback() {
const type = this.getAttribute('type');
const template = document.getElementById(`--${type}`);
const node = document.importNode(template.content, true);
- this.shadowRoot.id = 'root';
this.shadowRoot.appendChild(node);
}
}
diff --git a/src/http/Server.zig b/src/http/Server.zig
new file mode 100644
index 0000000..b6e2d90
--- /dev/null
+++ b/src/http/Server.zig
@@ -0,0 +1,20 @@
+const std = @import("std");
+
+const Self = @This();
+
+server: std.Io.net.Server,
+io: std.Io.Threaded,
+
+pub fn init(
+ gpa: std.mem.Allocator,
+ address: std.Io.net.IpAddress,
+) !Self {
+ var io: std.Io.Threaded = .init(gpa, .{});
+
+ return .{
+ .io = io,
+ .server = try address.listen(io.io(), .{ .reuse_address = true }),
+ };
+}
+
+pub fn serve(_: *Self) !void {}
diff --git a/src/http/Url.zig b/src/http/Url.zig
new file mode 100644
index 0000000..4a0a2b0
--- /dev/null
+++ b/src/http/Url.zig
@@ -0,0 +1,39 @@
+const std = @import("std");
+
+const Segment = union(enum) {
+ literal: []const u8,
+ parameter: []const u8,
+
+ pub fn format(self: @This(), writer: *std.Io.Writer) !void {
+ try writer.print("{s}{{ {s} }}", .{ @tagName(self), switch (self) {
+ inline else => |s| s,
+ } });
+ }
+};
+
+pub fn Template(comptime template: []const u8) []const Segment {
+ var segments: []const Segment = &.{};
+ var t = template;
+
+ if (template.len == 0 or template[0] != '/') {
+ @compileError("template needs to be absolute");
+ }
+
+ while (t.len > 0) {
+ const index = std.mem.indexOfScalar(u8, t, '/');
+ const segment = t[0 .. index orelse t.len];
+ t = t[(index orelse t.len - 1) + 1 ..];
+
+ if (segment.len > 0) {
+ const s: []const Segment = &.{seg: {
+ if (segment[0] == '{' and segment[segment.len - 1] == '}') {
+ break :seg .{ .parameter = segment[1 .. segment.len - 1] };
+ } else {
+ break :seg .{ .literal = segment };
+ }
+ }};
+ segments = segments ++ s;
+ }
+ }
+ return segments;
+}
diff --git a/src/http/root.zig b/src/http/root.zig
index 743e1c4..317dacd 100644
--- a/src/http/root.zig
+++ b/src/http/root.zig
@@ -2,6 +2,9 @@
const std = @import("std");
+pub const Server = @import("Server.zig");
+pub const Url = @import("Url.zig");
+
test {
_ = std.testing.refAllDecls(@This());
}
diff --git a/src/main.zig b/src/main.zig
index 1a66b63..df0c061 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -4,9 +4,11 @@
//! but also the actual running of the server / webapp.
const std = @import("std");
+const http = @import("http");
pub fn main(init: std.process.Init) !void {
- _ = init;
+ var server: http.Server = try .init(init.gpa, try .parseLiteral("127.0.0.1:8080"));
+ try server.serve();
}
test {
diff --git a/src/root.zig b/src/root.zig
new file mode 100644
index 0000000..8d85eb4
--- /dev/null
+++ b/src/root.zig
@@ -0,0 +1,6 @@
+//! Memora Software Stack
+
+pub const db = @import("db");
+pub const api = @import("api");
+pub const frontend = @import("frontend");
+pub const http = @import("http");