diff options
| -rw-r--r-- | src/frontend/File.zig | 78 | ||||
| -rw-r--r-- | src/frontend/Minifier.zig | 170 | ||||
| -rw-r--r-- | src/frontend/root.zig | 3 | ||||
| -rw-r--r-- | src/frontend/web/hello-world/index.html | 3 | ||||
| -rw-r--r-- | src/frontend/web/index.html | 4 | ||||
| -rw-r--r-- | src/frontend/z-component.js | 3 |
6 files changed, 193 insertions, 68 deletions
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..541bbe1 --- /dev/null +++ b/src/frontend/Minifier.zig @@ -0,0 +1,170 @@ +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); } } |