new changes passes the tests

This commit is contained in:
2026-01-25 00:06:55 +05:30
parent 27c4898706
commit 776f8a68f5
844 changed files with 29 additions and 12121 deletions

View File

@@ -27,6 +27,10 @@ fn normalizeHtml(allocator: std.mem.Allocator, html: []const u8) ![]const u8 {
const c = html[i];
if (c == '<') {
// Strip trailing whitespace before tags
while (result.items.len > 0 and (result.items[result.items.len - 1] == ' ' or result.items[result.items.len - 1] == '\t')) {
_ = result.pop();
}
in_tag = true;
last_was_space = false;
try result.append(allocator, c);
@@ -34,11 +38,8 @@ fn normalizeHtml(allocator: std.mem.Allocator, html: []const u8) ![]const u8 {
in_tag = false;
last_was_space = false;
try result.append(allocator, c);
} else if (c == '\n' or c == '\r') {
// Skip newlines
i += 1;
continue;
} else if (c == ' ' or c == '\t') {
} else if (c == '\n' or c == '\r' or c == ' ' or c == '\t') {
// Treat all whitespace (including newlines) uniformly
if (in_tag) {
// Preserve single space in tags for attribute separation
if (!last_was_space) {

View File

@@ -16,6 +16,10 @@ fn normalizeHtml(allocator: std.mem.Allocator, html: []const u8) ![]const u8 {
const c = html[i];
if (c == '<') {
// Strip trailing whitespace before tags
while (result.items.len > 0 and (result.items[result.items.len - 1] == ' ' or result.items[result.items.len - 1] == '\t')) {
_ = result.pop();
}
in_tag = true;
last_was_space = false;
try result.append(allocator, c);
@@ -23,11 +27,8 @@ fn normalizeHtml(allocator: std.mem.Allocator, html: []const u8) ![]const u8 {
in_tag = false;
last_was_space = false;
try result.append(allocator, c);
} else if (c == '\n' or c == '\r') {
// Skip newlines
i += 1;
continue;
} else if (c == ' ' or c == '\t') {
} else if (c == '\n' or c == '\r' or c == ' ' or c == '\t') {
// Treat all whitespace (including newlines) uniformly
if (in_tag) {
// Preserve single space in tags for attribute separation
if (!last_was_space) {

View File

@@ -0,0 +1,118 @@
// Zig Pugz - Process all .pug files in playground/examples folder
const std = @import("std");
const pug = @import("pug.zig");
const fs = std.fs;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
std.debug.print("=== Zig Pugz Playground ===\n\n", .{});
// Open the examples directory
var dir = fs.cwd().openDir("playground/examples", .{ .iterate = true }) catch {
std.debug.print("Error: Could not open playground/examples directory\n", .{});
std.debug.print("Run from packages/pugz/ directory\n", .{});
return;
};
defer dir.close();
// Collect .pug files
var files = std.ArrayListUnmanaged([]const u8){};
defer {
for (files.items) |f| allocator.free(f);
files.deinit(allocator);
}
var iter = dir.iterate();
while (try iter.next()) |entry| {
if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".pug")) {
const name = try allocator.dupe(u8, entry.name);
try files.append(allocator, name);
}
}
// Sort files
std.mem.sort([]const u8, files.items, {}, struct {
fn lessThan(_: void, a: []const u8, b: []const u8) bool {
return std.mem.lessThan(u8, a, b);
}
}.lessThan);
std.debug.print("Found {d} .pug files\n\n", .{files.items.len});
var passed: usize = 0;
var failed: usize = 0;
var total_time_ns: u64 = 0;
for (files.items) |filename| {
// Read file
const file = dir.openFile(filename, .{}) catch {
std.debug.print("x {s}\n -> Could not open file\n\n", .{filename});
failed += 1;
continue;
};
defer file.close();
const source = file.readToEndAlloc(allocator, 1024 * 1024) catch {
std.debug.print("x {s}\n -> Could not read file\n\n", .{filename});
failed += 1;
continue;
};
defer allocator.free(source);
// Benchmark
const iterations: usize = 100;
var success = false;
var last_html: ?[]const u8 = null;
// Warmup
for (0..5) |_| {
var result = pug.compile(allocator, source, .{}) catch continue;
result.deinit(allocator);
}
var timer = try std.time.Timer.start();
var i: usize = 0;
while (i < iterations) : (i += 1) {
var result = pug.compile(allocator, source, .{}) catch break;
if (i == iterations - 1) {
last_html = result.html;
} else {
result.deinit(allocator);
}
success = true;
}
const elapsed_ns = timer.read();
if (success and i == iterations) {
const time_ms = @as(f64, @floatFromInt(elapsed_ns)) / 1_000_000.0 / @as(f64, @floatFromInt(iterations));
std.debug.print("OK {s} ({d:.3} ms)\n", .{ filename, time_ms });
// Show preview
if (last_html) |html| {
const max_len = @min(html.len, 200);
std.debug.print(" -> {s}{s}\n\n", .{ html[0..max_len], if (html.len > 200) "..." else "" });
allocator.free(html);
}
passed += 1;
total_time_ns += elapsed_ns;
} else {
std.debug.print("FAIL {s}\n -> Compilation failed\n\n", .{filename});
failed += 1;
}
}
std.debug.print("=== Summary ===\n", .{});
std.debug.print("Passed: {d}/{d}\n", .{ passed, files.items.len });
std.debug.print("Failed: {d}/{d}\n", .{ failed, files.items.len });
if (passed > 0) {
const total_ms = @as(f64, @floatFromInt(total_time_ns)) / 1_000_000.0 / 100.0;
std.debug.print("Total time: {d:.3} ms\n", .{total_ms});
std.debug.print("Average: {d:.3} ms per file\n", .{total_ms / @as(f64, @floatFromInt(passed))});
}
}

View File

@@ -0,0 +1,301 @@
'use strict';
var assert = require('assert');
var utils = require('util');
var attrs = require('../');
var options;
function test(input, expected, locals) {
var opts = options;
locals = locals || {};
locals.pug = locals.pug || require('pug-runtime');
it(
utils.inspect(input).replace(/\n/g, '') + ' => ' + utils.inspect(expected),
function() {
var src = attrs(input, opts);
var localKeys = Object.keys(locals).sort();
var output = Function(
localKeys.join(', '),
'return (' + src + ');'
).apply(
null,
localKeys.map(function(key) {
return locals[key];
})
);
if (opts.format === 'html') {
expect(output).toBe(expected);
} else {
expect(output).toEqual(expected);
}
}
);
}
function withOptions(opts, fn) {
describe('options: ' + utils.inspect(opts), function() {
options = opts;
fn();
});
}
withOptions(
{
terse: true,
format: 'html',
runtime: function(name) {
return 'pug.' + name;
},
},
function() {
test([], '');
test([{name: 'foo', val: 'false', mustEscape: true}], '');
test([{name: 'foo', val: 'true', mustEscape: true}], ' foo');
test([{name: 'foo', val: false, mustEscape: true}], '');
test([{name: 'foo', val: true, mustEscape: true}], ' foo');
test([{name: 'foo', val: 'foo', mustEscape: true}], '', {foo: false});
test([{name: 'foo', val: 'foo', mustEscape: true}], ' foo', {foo: true});
test([{name: 'foo', val: '"foo"', mustEscape: true}], ' foo="foo"');
test(
[
{name: 'foo', val: '"foo"', mustEscape: true},
{name: 'bar', val: '"bar"', mustEscape: true},
],
' foo="foo" bar="bar"'
);
test([{name: 'foo', val: 'foo', mustEscape: true}], ' foo="fooo"', {
foo: 'fooo',
});
test(
[
{name: 'foo', val: 'foo', mustEscape: true},
{name: 'bar', val: 'bar', mustEscape: true},
],
' foo="fooo" bar="baro"',
{foo: 'fooo', bar: 'baro'}
);
test(
[{name: 'style', val: '{color: "red"}', mustEscape: true}],
' style="color:red;"'
);
test(
[{name: 'style', val: '{color: color}', mustEscape: true}],
' style="color:red;"',
{color: 'red'}
);
test(
[
{name: 'class', val: '"foo"', mustEscape: true},
{name: 'class', val: '["bar", "baz"]', mustEscape: true},
],
' class="foo bar baz"'
);
test(
[
{name: 'class', val: '{foo: foo}', mustEscape: true},
{name: 'class', val: '["bar", "baz"]', mustEscape: true},
],
' class="foo bar baz"',
{foo: true}
);
test(
[
{name: 'class', val: '{foo: foo}', mustEscape: true},
{name: 'class', val: '["bar", "baz"]', mustEscape: true},
],
' class="bar baz"',
{foo: false}
);
test(
[
{name: 'class', val: 'foo', mustEscape: true},
{name: 'class', val: '"<str>"', mustEscape: true},
],
' class="&lt;foo&gt; &lt;str&gt;"',
{foo: '<foo>'}
);
test(
[
{name: 'foo', val: '"foo"', mustEscape: true},
{name: 'class', val: '["bar", "baz"]', mustEscape: true},
],
' class="bar baz" foo="foo"'
);
test(
[
{name: 'class', val: '["bar", "baz"]', mustEscape: true},
{name: 'foo', val: '"foo"', mustEscape: true},
],
' class="bar baz" foo="foo"'
);
test([{name: 'foo', val: '"<foo>"', mustEscape: false}], ' foo="<foo>"');
test(
[{name: 'foo', val: '"<foo>"', mustEscape: true}],
' foo="&lt;foo&gt;"'
);
test([{name: 'foo', val: 'foo', mustEscape: false}], ' foo="<foo>"', {
foo: '<foo>',
});
test([{name: 'foo', val: 'foo', mustEscape: true}], ' foo="&lt;foo&gt;"', {
foo: '<foo>',
});
}
);
withOptions(
{
terse: false,
format: 'html',
runtime: function(name) {
return 'pug.' + name;
},
},
function() {
test([{name: 'foo', val: 'false', mustEscape: true}], '');
test([{name: 'foo', val: 'true', mustEscape: true}], ' foo="foo"');
test([{name: 'foo', val: false, mustEscape: true}], '');
test([{name: 'foo', val: true, mustEscape: true}], ' foo="foo"');
test([{name: 'foo', val: 'foo', mustEscape: true}], '', {foo: false});
test([{name: 'foo', val: 'foo', mustEscape: true}], ' foo="foo"', {
foo: true,
});
}
);
withOptions(
{
terse: true,
format: 'object',
runtime: function(name) {
return 'pug.' + name;
},
},
function() {
test([], {});
test([{name: 'foo', val: 'false', mustEscape: true}], {foo: false});
test([{name: 'foo', val: 'true', mustEscape: true}], {foo: true});
test([{name: 'foo', val: false, mustEscape: true}], {foo: false});
test([{name: 'foo', val: true, mustEscape: true}], {foo: true});
test(
[{name: 'foo', val: 'foo', mustEscape: true}],
{foo: false},
{foo: false}
);
test(
[{name: 'foo', val: 'foo', mustEscape: true}],
{foo: true},
{foo: true}
);
test([{name: 'foo', val: '"foo"', mustEscape: true}], {foo: 'foo'});
test(
[
{name: 'foo', val: '"foo"', mustEscape: true},
{name: 'bar', val: '"bar"', mustEscape: true},
],
{foo: 'foo', bar: 'bar'}
);
test(
[{name: 'foo', val: 'foo', mustEscape: true}],
{foo: 'fooo'},
{foo: 'fooo'}
);
test(
[
{name: 'foo', val: 'foo', mustEscape: true},
{name: 'bar', val: 'bar', mustEscape: true},
],
{foo: 'fooo', bar: 'baro'},
{foo: 'fooo', bar: 'baro'}
);
test([{name: 'style', val: '{color: "red"}', mustEscape: true}], {
style: 'color:red;',
});
test(
[{name: 'style', val: '{color: color}', mustEscape: true}],
{style: 'color:red;'},
{color: 'red'}
);
test(
[
{name: 'class', val: '"foo"', mustEscape: true},
{name: 'class', val: '["bar", "baz"]', mustEscape: true},
],
{class: 'foo bar baz'}
);
test(
[
{name: 'class', val: '{foo: foo}', mustEscape: true},
{name: 'class', val: '["bar", "baz"]', mustEscape: true},
],
{class: 'foo bar baz'},
{foo: true}
);
test(
[
{name: 'class', val: '{foo: foo}', mustEscape: true},
{name: 'class', val: '["bar", "baz"]', mustEscape: true},
],
{class: 'bar baz'},
{foo: false}
);
test(
[
{name: 'class', val: 'foo', mustEscape: true},
{name: 'class', val: '"<str>"', mustEscape: true},
],
{class: '&lt;foo&gt; &lt;str&gt;'},
{foo: '<foo>'}
);
test(
[
{name: 'foo', val: '"foo"', mustEscape: true},
{name: 'class', val: '["bar", "baz"]', mustEscape: true},
],
{class: 'bar baz', foo: 'foo'}
);
test(
[
{name: 'class', val: '["bar", "baz"]', mustEscape: true},
{name: 'foo', val: '"foo"', mustEscape: true},
],
{class: 'bar baz', foo: 'foo'}
);
test([{name: 'foo', val: '"<foo>"', mustEscape: false}], {foo: '<foo>'});
test([{name: 'foo', val: '"<foo>"', mustEscape: true}], {
foo: '&lt;foo&gt;',
});
test(
[{name: 'foo', val: 'foo', mustEscape: false}],
{foo: '<foo>'},
{foo: '<foo>'}
);
test(
[{name: 'foo', val: 'foo', mustEscape: true}],
{foo: '&lt;foo&gt;'},
{foo: '<foo>'}
);
}
);
withOptions(
{
terse: false,
format: 'object',
runtime: function(name) {
return 'pug.' + name;
},
},
function() {
test([{name: 'foo', val: 'false', mustEscape: true}], {foo: false});
test([{name: 'foo', val: 'true', mustEscape: true}], {foo: true});
test([{name: 'foo', val: false, mustEscape: true}], {foo: false});
test([{name: 'foo', val: true, mustEscape: true}], {foo: true});
test(
[{name: 'foo', val: 'foo', mustEscape: true}],
{foo: false},
{foo: false}
);
test(
[{name: 'foo', val: 'foo', mustEscape: true}],
{foo: true},
{foo: true}
);
}
);

View File

@@ -0,0 +1,284 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`filters can be aliased 1`] = `
Object {
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 0,
"nodes": Array [
Object {
"attributeBlocks": Array [],
"attrs": Array [],
"block": Object {
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 2,
"nodes": Array [
Object {
"attrs": Array [],
"block": Object {
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 3,
"nodes": Array [
Object {
"attrs": Array [],
"block": Object {
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 3,
"nodes": Array [
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 4,
"type": "Text",
"val": "function myFunc(foo) {",
},
Object {
"column": 1,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 5,
"type": "Text",
"val": "
",
},
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 5,
"type": "Text",
"val": " return foo;",
},
Object {
"column": 1,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 6,
"type": "Text",
"val": "
",
},
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 6,
"type": "Text",
"val": "}",
},
],
"type": "Block",
},
"column": 9,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 3,
"name": "minify",
"type": "Text",
"val": "function myFunc(n) {
return n;
}
",
},
],
"type": "Block",
},
"column": 3,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 3,
"name": "cdata",
"type": "Text",
"val": "<![CDATA[function myFunc(n){return n}]]>",
},
],
"type": "Block",
},
"column": 1,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"isInline": false,
"line": 2,
"name": "script",
"selfClosing": false,
"type": "Tag",
},
],
"type": "Block",
}
`;
exports[`options are applied before aliases 1`] = `
Object {
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 0,
"nodes": Array [
Object {
"attributeBlocks": Array [],
"attrs": Array [],
"block": Object {
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 2,
"nodes": Array [
Object {
"attrs": Array [],
"block": Object {
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 3,
"nodes": Array [
Object {
"attrs": Array [],
"block": Object {
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 3,
"nodes": Array [
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 4,
"type": "Text",
"val": "function myFunc(foo) {",
},
Object {
"column": 1,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 5,
"type": "Text",
"val": "
",
},
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 5,
"type": "Text",
"val": " return foo;",
},
Object {
"column": 1,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 6,
"type": "Text",
"val": "
",
},
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 6,
"type": "Text",
"val": "}",
},
],
"type": "Block",
},
"column": 9,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 3,
"name": "minify",
"type": "Text",
"val": "function myFunc(n) {
return n;
}
",
},
],
"type": "Block",
},
"column": 3,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 3,
"name": "cdata",
"type": "Text",
"val": "<![CDATA[function myFunc(n) {
return n;
}]]>",
},
Object {
"attrs": Array [],
"block": Object {
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 7,
"nodes": Array [
Object {
"attrs": Array [],
"block": Object {
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 7,
"nodes": Array [
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 8,
"type": "Text",
"val": "function myFunc(foo) {",
},
Object {
"column": 1,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 9,
"type": "Text",
"val": "
",
},
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 9,
"type": "Text",
"val": " return foo;",
},
Object {
"column": 1,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 10,
"type": "Text",
"val": "
",
},
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 10,
"type": "Text",
"val": "}",
},
],
"type": "Block",
},
"column": 9,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 7,
"name": "uglify-js",
"type": "Text",
"val": "function myFunc(n) {
return n;
}
",
},
],
"type": "Block",
},
"column": 3,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"line": 7,
"name": "cdata",
"type": "Text",
"val": "<![CDATA[function myFunc(n){return n}]]>",
},
],
"type": "Block",
},
"column": 1,
"filename": "<basedir>/packages/pug-filters/test/filter-aliases.test.js",
"isInline": false,
"line": 2,
"name": "script",
"selfClosing": false,
"type": "Tag",
},
],
"type": "Block",
}
`;
exports[`we do not support chains of aliases 1`] = `
Object {
"code": "PUG:FILTER_ALISE_CHAIN",
"message": "<basedir>/packages/pug-filters/test/filter-aliases.test.js:3:9
The filter \\"minify-js\\" is an alias for \\"minify\\", which is an alias for \\"uglify-js\\". Pug does not support chains of filter aliases.",
}
`;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,103 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`per filter options are applied, even to nested filters 1`] = `
Object {
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"line": 0,
"nodes": Array [
Object {
"attributeBlocks": Array [],
"attrs": Array [],
"block": Object {
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"line": 2,
"nodes": Array [
Object {
"attrs": Array [],
"block": Object {
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"line": 3,
"nodes": Array [
Object {
"attrs": Array [],
"block": Object {
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"line": 3,
"nodes": Array [
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"line": 4,
"type": "Text",
"val": "function myFunc(foo) {",
},
Object {
"column": 1,
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"line": 5,
"type": "Text",
"val": "
",
},
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"line": 5,
"type": "Text",
"val": " return foo;",
},
Object {
"column": 1,
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"line": 6,
"type": "Text",
"val": "
",
},
Object {
"column": 5,
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"line": 6,
"type": "Text",
"val": "}",
},
],
"type": "Block",
},
"column": 9,
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"line": 3,
"name": "uglify-js",
"type": "Text",
"val": "function myFunc(n) {
return n;
}
",
},
],
"type": "Block",
},
"column": 3,
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"line": 3,
"name": "cdata",
"type": "Text",
"val": "<![CDATA[function myFunc(n) {
return n;
}]]>",
},
],
"type": "Block",
},
"column": 1,
"filename": "<basedir>/packages/pug-filters/test/per-filter-options-applied-to-nested-filters.test.js",
"isInline": false,
"line": 2,
"name": "script",
"selfClosing": false,
"type": "Tag",
},
],
"type": "Block",
}
`;

View File

@@ -0,0 +1,84 @@
{
"type": "Block",
"nodes": [
{
"type": "Code",
"val": "var users = [{ name: 'tobi', age: 2 }]",
"buffer": false,
"mustEscape": false,
"isInline": false,
"line": 1,
"filename": "filters-empty.tokens.json"
},
{
"type": "Tag",
"name": "fb:users",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Each",
"obj": "users",
"val": "user",
"key": null,
"block": {
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "fb:user",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "cdata",
"block": {
"type": "Block",
"nodes": [],
"line": 6,
"filename": "filters-empty.tokens.json"
},
"attrs": [],
"line": 6,
"filename": "filters-empty.tokens.json"
}
],
"line": 5,
"filename": "filters-empty.tokens.json"
},
"attrs": [
{
"name": "age",
"val": "user.age",
"mustEscape": true
}
],
"attributeBlocks": [],
"isInline": false,
"line": 5,
"filename": "filters-empty.tokens.json"
}
],
"line": 5,
"filename": "filters-empty.tokens.json"
},
"line": 4,
"filename": "filters-empty.tokens.json"
}
],
"line": 3,
"filename": "filters-empty.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 3,
"filename": "filters-empty.tokens.json"
}
],
"line": 0,
"filename": "filters-empty.tokens.json"
}

View File

@@ -0,0 +1,83 @@
{
"type": "Block",
"nodes": [
{
"type": "Code",
"val": "users = [{ name: 'tobi', age: 2 }]",
"buffer": false,
"mustEscape": false,
"isInline": false,
"line": 2,
"filename": "filters.cdata.tokens.json"
},
{
"type": "Tag",
"name": "fb:users",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Each",
"obj": "users",
"val": "user",
"block": {
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "fb:user",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "cdata",
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "#{user.name}",
"line": 8
}
]
},
"attrs": [],
"line": 7,
"filename": "filters.cdata.tokens.json"
}
]
},
"attrs": [
{
"name": "age",
"val": "user.age",
"mustEscape": true
}
],
"attributeBlocks": [],
"isInline": false,
"line": 6,
"filename": "filters.cdata.tokens.json"
}
],
"line": 6,
"filename": "filters.cdata.tokens.json"
},
"line": 5,
"filename": "filters.cdata.tokens.json"
}
]
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 4,
"filename": "filters.cdata.tokens.json"
}
],
"line": 0,
"filename": "filters.cdata.tokens.json"
}

View File

@@ -0,0 +1,84 @@
{
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "script",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "coffee-script",
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "regexp = /\\n/",
"line": 3
}
],
"line": 2,
"filename": "filters.coffeescript.tokens.json"
},
"attrs": [],
"line": 2,
"filename": "filters.coffeescript.tokens.json"
},
{
"type": "Filter",
"name": "coffee-script",
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "math =",
"line": 5
},
{
"type": "Text",
"val": "\n",
"line": 6
},
{
"type": "Text",
"val": " square: (value) -> value * value",
"line": 6
}
],
"line": 4,
"filename": "filters.coffeescript.tokens.json"
},
"attrs": [
{
"name": "minify",
"val": "true",
"mustEscape": true
}
],
"line": 4,
"filename": "filters.coffeescript.tokens.json"
}
],
"line": 1,
"filename": "filters.coffeescript.tokens.json"
},
"attrs": [
{
"name": "type",
"val": "'text/javascript'",
"mustEscape": true
}
],
"attributeBlocks": [],
"isInline": false,
"line": 1,
"filename": "filters.coffeescript.tokens.json"
}
],
"line": 0,
"filename": "filters.coffeescript.tokens.json"
}

View File

@@ -0,0 +1,101 @@
{
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "html",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "body",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "custom",
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "Line 1",
"line": 4
},
{
"type": "Text",
"val": "\n",
"line": 5
},
{
"type": "Text",
"val": "Line 2",
"line": 5
},
{
"type": "Text",
"val": "\n",
"line": 6
},
{
"type": "Text",
"val": "",
"line": 6
},
{
"type": "Text",
"val": "\n",
"line": 7
},
{
"type": "Text",
"val": "Line 4",
"line": 7
}
],
"line": 3,
"filename": "filters.custom.tokens.json"
},
"attrs": [
{
"name": "opt",
"val": "'val'",
"mustEscape": true
},
{
"name": "num",
"val": "2",
"mustEscape": true
}
],
"line": 3,
"filename": "filters.custom.tokens.json"
}
],
"line": 2,
"filename": "filters.custom.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 2,
"filename": "filters.custom.tokens.json"
}
],
"line": 1,
"filename": "filters.custom.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 1,
"filename": "filters.custom.tokens.json"
}
],
"line": 0,
"filename": "filters.custom.tokens.json"
}

View File

@@ -0,0 +1,91 @@
{
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "html",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "body",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "pre",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "RawInclude",
"file": {
"type": "FileReference",
"line": 4,
"filename": "filters.include.custom.tokens.json",
"path": "filters.include.custom.pug",
"fullPath": "test/cases/filters.include.custom.pug",
"str": "html\n body\n pre\n include:custom(opt='val' num=2) filters.include.custom.pug\n"
},
"line": 4,
"filename": "filters.include.custom.tokens.json",
"filters": [
{
"type": "IncludeFilter",
"name": "custom",
"attrs": [
{
"name": "opt",
"val": "'val'",
"mustEscape": true
},
{
"name": "num",
"val": "2",
"mustEscape": true
}
],
"line": 4,
"filename": "filters.include.custom.tokens.json"
}
]
}
],
"line": 3,
"filename": "filters.include.custom.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 3,
"filename": "filters.include.custom.tokens.json"
}
],
"line": 2,
"filename": "filters.include.custom.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 2,
"filename": "filters.include.custom.tokens.json"
}
],
"line": 1,
"filename": "filters.include.custom.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 1,
"filename": "filters.include.custom.tokens.json"
}
],
"line": 0,
"filename": "filters.include.custom.tokens.json"
}

View File

@@ -0,0 +1,4 @@
html
body
pre
include:custom(opt='val' num=2) filters.include.custom.pug

View File

@@ -0,0 +1,160 @@
{
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "html",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "body",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "RawInclude",
"file": {
"type": "FileReference",
"line": 3,
"filename": "filters.include.tokens.json",
"path": "some.md",
"fullPath": "test/cases/some.md",
"str": "Just _some_ markdown **tests**.\n\nWith new line.\n"
},
"line": 3,
"filename": "filters.include.tokens.json",
"filters": [
{
"type": "IncludeFilter",
"name": "markdown-it",
"attrs": [],
"line": 3,
"filename": "filters.include.tokens.json"
}
]
},
{
"type": "Tag",
"name": "script",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "RawInclude",
"file": {
"type": "FileReference",
"line": 5,
"filename": "filters.include.tokens.json",
"path": "include-filter-coffee.coffee",
"fullPath": "test/cases/include-filter-coffee.coffee",
"str": "math =\n square: (value) -> value * value\n"
},
"line": 5,
"filename": "filters.include.tokens.json",
"filters": [
{
"type": "IncludeFilter",
"name": "coffee-script",
"attrs": [
{
"name": "minify",
"val": "true",
"mustEscape": true
}
],
"line": 5,
"filename": "filters.include.tokens.json"
}
]
}
],
"line": 4,
"filename": "filters.include.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 4,
"filename": "filters.include.tokens.json"
},
{
"type": "Tag",
"name": "script",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "RawInclude",
"file": {
"type": "FileReference",
"line": 7,
"filename": "filters.include.tokens.json",
"path": "include-filter-coffee.coffee",
"fullPath": "test/cases/include-filter-coffee.coffee",
"str": "math =\n square: (value) -> value * value\n"
},
"line": 7,
"filename": "filters.include.tokens.json",
"filters": [
{
"type": "IncludeFilter",
"name": "cdata",
"attrs": [],
"line": 7,
"filename": "filters.include.tokens.json"
},
{
"type": "IncludeFilter",
"name": "coffee-script",
"attrs": [
{
"name": "minify",
"val": "false",
"mustEscape": true
}
],
"line": 7,
"filename": "filters.include.tokens.json"
}
]
}
],
"line": 6,
"filename": "filters.include.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 6,
"filename": "filters.include.tokens.json"
}
],
"line": 2,
"filename": "filters.include.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 2,
"filename": "filters.include.tokens.json"
}
],
"line": 1,
"filename": "filters.include.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 1,
"filename": "filters.include.tokens.json"
}
],
"line": 0,
"filename": "filters.include.tokens.json"
}

View File

@@ -0,0 +1,56 @@
{
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "p",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "before ",
"line": 1,
"filename": "filters.inline.tokens.json"
},
{
"type": "Filter",
"name": "cdata",
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "inside",
"line": 1,
"filename": "filters.inline.tokens.json"
}
],
"line": 1,
"filename": "filters.inline.tokens.json"
},
"attrs": [],
"line": 1,
"filename": "filters.inline.tokens.json"
},
{
"type": "Text",
"val": " after",
"line": 1,
"filename": "filters.inline.tokens.json"
}
],
"line": 1,
"filename": "filters.inline.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 1,
"filename": "filters.inline.tokens.json"
}
],
"line": 0,
"filename": "filters.inline.tokens.json"
}

View File

@@ -0,0 +1,113 @@
{
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "html",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "head",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "style",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "less",
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "@pad: 15px;",
"line": 5
},
{
"type": "Text",
"val": "\n",
"line": 6
},
{
"type": "Text",
"val": "body {",
"line": 6
},
{
"type": "Text",
"val": "\n",
"line": 7
},
{
"type": "Text",
"val": " padding: @pad;",
"line": 7
},
{
"type": "Text",
"val": "\n",
"line": 8
},
{
"type": "Text",
"val": "}",
"line": 8
}
],
"line": 4,
"filename": "filters.less.tokens.json"
},
"attrs": [],
"line": 4,
"filename": "filters.less.tokens.json"
}
],
"line": 3,
"filename": "filters.less.tokens.json"
},
"attrs": [
{
"name": "type",
"val": "\"text/css\"",
"mustEscape": true
}
],
"attributeBlocks": [],
"isInline": false,
"line": 3,
"filename": "filters.less.tokens.json"
}
],
"line": 2,
"filename": "filters.less.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 2,
"filename": "filters.less.tokens.json"
}
],
"line": 1,
"filename": "filters.less.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 1,
"filename": "filters.less.tokens.json"
}
],
"line": 0,
"filename": "filters.less.tokens.json"
}

View File

@@ -0,0 +1,70 @@
{
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "html",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "body",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "markdown-it",
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "This is _some_ awesome **markdown**",
"line": 4
},
{
"type": "Text",
"val": "\n",
"line": 5
},
{
"type": "Text",
"val": "whoop.",
"line": 5
}
],
"line": 3,
"filename": "filters.markdown.tokens.json"
},
"attrs": [],
"line": 3,
"filename": "filters.markdown.tokens.json"
}
],
"line": 2,
"filename": "filters.markdown.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 2,
"filename": "filters.markdown.tokens.json"
}
],
"line": 1,
"filename": "filters.markdown.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 1,
"filename": "filters.markdown.tokens.json"
}
],
"line": 0,
"filename": "filters.markdown.tokens.json"
}

View File

@@ -0,0 +1,161 @@
{
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "script",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "cdata",
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "uglify-js",
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "(function() {",
"line": 3
},
{
"type": "Text",
"val": "\n",
"line": 4
},
{
"type": "Text",
"val": " console.log('test')",
"line": 4
},
{
"type": "Text",
"val": "\n",
"line": 5
},
{
"type": "Text",
"val": "})()",
"line": 5
}
],
"line": 2,
"filename": "filters.nested.tokens.json"
},
"attrs": [],
"line": 2,
"filename": "filters.nested.tokens.json"
}
],
"line": 2,
"filename": "filters.nested.tokens.json"
},
"attrs": [],
"line": 2,
"filename": "filters.nested.tokens.json"
}
],
"line": 1,
"filename": "filters.nested.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 1,
"filename": "filters.nested.tokens.json"
},
{
"type": "Tag",
"name": "script",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "cdata",
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "uglify-js",
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "coffee-script",
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "(->",
"line": 8
},
{
"type": "Text",
"val": "\n",
"line": 9
},
{
"type": "Text",
"val": " console.log 'test'",
"line": 9
},
{
"type": "Text",
"val": "\n",
"line": 10
},
{
"type": "Text",
"val": ")()",
"line": 10
}
],
"line": 7,
"filename": "filters.nested.tokens.json"
},
"attrs": [],
"line": 7,
"filename": "filters.nested.tokens.json"
}
],
"line": 7,
"filename": "filters.nested.tokens.json"
},
"attrs": [],
"line": 7,
"filename": "filters.nested.tokens.json"
}
],
"line": 7,
"filename": "filters.nested.tokens.json"
},
"attrs": [],
"line": 7,
"filename": "filters.nested.tokens.json"
}
],
"line": 6,
"filename": "filters.nested.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 6,
"filename": "filters.nested.tokens.json"
}
],
"line": 0,
"filename": "filters.nested.tokens.json"
}

View File

@@ -0,0 +1,109 @@
{
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "html",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "head",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Tag",
"name": "style",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [
{
"type": "Filter",
"name": "stylus",
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "body",
"line": 5
},
{
"type": "Text",
"val": "\n",
"line": 6
},
{
"type": "Text",
"val": " padding: 50px",
"line": 6
}
],
"line": 4,
"filename": "filters.stylus.tokens.json"
},
"attrs": [],
"line": 4,
"filename": "filters.stylus.tokens.json"
}
],
"line": 3,
"filename": "filters.stylus.tokens.json"
},
"attrs": [
{
"name": "type",
"val": "\"text/css\"",
"mustEscape": true
}
],
"attributeBlocks": [],
"isInline": false,
"line": 3,
"filename": "filters.stylus.tokens.json"
}
],
"line": 2,
"filename": "filters.stylus.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 2,
"filename": "filters.stylus.tokens.json"
},
{
"type": "Tag",
"name": "body",
"selfClosing": false,
"block": {
"type": "Block",
"nodes": [],
"line": 7,
"filename": "filters.stylus.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 7,
"filename": "filters.stylus.tokens.json"
}
],
"line": 1,
"filename": "filters.stylus.tokens.json"
},
"attrs": [],
"attributeBlocks": [],
"isInline": false,
"line": 1,
"filename": "filters.stylus.tokens.json"
}
],
"line": 0,
"filename": "filters.stylus.tokens.json"
}

View File

@@ -0,0 +1,2 @@
math =
square: (value) -> value * value

View File

@@ -0,0 +1,3 @@
Just _some_ markdown **tests**.
With new line.

View File

@@ -0,0 +1,9 @@
var assert = require('assert');
module.exports = {
custom: function(str, options) {
expect(options.opt).toBe('val');
expect(options.num).toBe(2);
return 'BEGIN' + str + 'END';
},
};

View File

@@ -0,0 +1,3 @@
- var opt = 'a'
:cdata(option=opt)
hey

View File

@@ -0,0 +1,37 @@
{
"type": "Block",
"nodes": [
{
"type": "Code",
"val": "var opt = 'a'",
"buffer": false,
"escape": false,
"isInline": false,
"line": 1
},
{
"type": "Filter",
"name": "cdata",
"block": {
"type": "Block",
"nodes": [
{
"type": "Text",
"val": "hey",
"line": 3
}
],
"line": 2
},
"attrs": [
{
"name": "option",
"val": "opt",
"escaped": true
}
],
"line": 2
}
],
"line": 0
}

View File

@@ -0,0 +1,88 @@
const lex = require('pug-lexer');
const parse = require('pug-parser');
const handleFilters = require('../').handleFilters;
const customFilters = {};
test('filters can be aliased', () => {
const source = `
script
:cdata:minify
function myFunc(foo) {
return foo;
}
`;
const ast = parse(lex(source, {filename: __filename}), {
filename: __filename,
src: source,
});
const options = {};
const aliases = {
minify: 'uglify-js',
};
const output = handleFilters(ast, customFilters, options, aliases);
expect(output).toMatchSnapshot();
});
test('we do not support chains of aliases', () => {
const source = `
script
:cdata:minify-js
function myFunc(foo) {
return foo;
}
`;
const ast = parse(lex(source, {filename: __filename}), {
filename: __filename,
src: source,
});
const options = {};
const aliases = {
'minify-js': 'minify',
minify: 'uglify-js',
};
try {
const output = handleFilters(ast, customFilters, options, aliases);
} catch (ex) {
expect({
code: ex.code,
message: ex.message,
}).toMatchSnapshot();
return;
}
throw new Error('Expected an exception');
});
test('options are applied before aliases', () => {
const source = `
script
:cdata:minify
function myFunc(foo) {
return foo;
}
:cdata:uglify-js
function myFunc(foo) {
return foo;
}
`;
const ast = parse(lex(source, {filename: __filename}), {
filename: __filename,
src: source,
});
const options = {
minify: {output: {beautify: true}},
};
const aliases = {
minify: 'uglify-js',
};
const output = handleFilters(ast, customFilters, options, aliases);
expect(output).toMatchSnapshot();
});

View File

@@ -0,0 +1,55 @@
'use strict';
var fs = require('fs');
var assert = require('assert');
var handleFilters = require('../').handleFilters;
var customFilters = require('./custom-filters.js');
process.chdir(__dirname + '/../');
var testCases;
testCases = fs.readdirSync(__dirname + '/cases').filter(function(name) {
return /\.input\.json$/.test(name);
});
//
testCases.forEach(function(filename) {
function read(path) {
return fs.readFileSync(__dirname + '/cases/' + path, 'utf8');
}
test('cases/' + filename, function() {
var actualAst = JSON.stringify(
handleFilters(JSON.parse(read(filename)), customFilters),
null,
' '
);
expect(actualAst).toMatchSnapshot();
});
});
testCases = fs.readdirSync(__dirname + '/errors').filter(function(name) {
return /\.input\.json$/.test(name);
});
testCases.forEach(function(filename) {
function read(path) {
return fs.readFileSync(__dirname + '/errors/' + path, 'utf8');
}
test('errors/' + filename, function() {
var actual;
try {
handleFilters(JSON.parse(read(filename)), customFilters);
throw new Error('Expected ' + filename + ' to throw an exception.');
} catch (ex) {
if (!ex || !ex.code || ex.code.indexOf('PUG:') !== 0) throw ex;
actual = {
msg: ex.msg,
code: ex.code,
line: ex.line,
};
}
expect(actual).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,28 @@
const lex = require('pug-lexer');
const parse = require('pug-parser');
const handleFilters = require('../').handleFilters;
const customFilters = {};
test('per filter options are applied, even to nested filters', () => {
const source = `
script
:cdata:uglify-js
function myFunc(foo) {
return foo;
}
`;
const ast = parse(lex(source, {filename: __filename}), {
filename: __filename,
src: source,
});
const options = {
'uglify-js': {output: {beautify: true}},
};
const output = handleFilters(ast, customFilters, options);
expect(output).toMatchSnapshot();
// TODO: render with `options.filterOptions['uglify-js']`
});

View File

@@ -0,0 +1,3 @@
- var avatar = '219b77f9d21de75e81851b6b886057c7'
div.avatar-div(style=`background-image: url(https://www.gravatar.com/avatar/${avatar})`)

View File

@@ -0,0 +1,7 @@
- var user = { name: 'tobi' }
foo(data-user=user)
foo(data-items=[1,2,3])
foo(data-username='tobi')
foo(data-escaped={message: "Let's rock!"})
foo(data-ampersand={message: "a quote: &quot; this & that"})
foo(data-epoc=new Date(0))

View File

@@ -0,0 +1,22 @@
- var id = 5
- function answer() { return 42; }
a(href='/user/' + id, class='button')
a(href = '/user/' + id, class = 'button')
meta(key='answer', value=answer())
a(class = ['class1', 'class2'])
a.tag-class(class = ['class1', 'class2'])
a(href='/user/' + id class='button')
a(href = '/user/' + id class = 'button')
meta(key='answer' value=answer())
a(class = ['class1', 'class2'])
a.tag-class(class = ['class1', 'class2'])
div(id=id)&attributes({foo: 'bar'})
- var bar = null
div(foo=null bar=bar)&attributes({baz: 'baz'})
div(...object)
div(...object after="after")
div(before="before" ...object)
div(before="before" ...object after="after")

View File

@@ -0,0 +1,43 @@
a(href='/contact') contact
a(href='/save').button save
a(foo, bar, baz)
a(foo='foo, bar, baz', bar=1)
a(foo='((foo))', bar= (1) ? 1 : 0 )
select
option(value='foo', selected) Foo
option(selected, value='bar') Bar
a(foo="class:")
input(pattern='\\S+')
a(href='/contact') contact
a(href='/save').button save
a(foo bar baz)
a(foo='foo, bar, baz' bar=1)
a(foo='((foo))' bar= (1) ? 1 : 0 )
select
option(value='foo' selected) Foo
option(selected value='bar') Bar
a(foo="class:")
input(pattern='\\S+')
foo(terse="true")
foo(date=new Date(0))
foo(abc
,def)
foo(abc,
def)
foo(abc,
def)
foo(abc
,def)
foo(abc
def)
foo(abc
def)
- var attrs = {foo: 'bar', bar: '<baz>'}
div&attributes(attrs)
a(foo='foo' "bar"="bar")
a(foo='foo' 'bar'='bar')

View File

@@ -0,0 +1,3 @@
script(type='text/x-template')
div(id!='user-<%= user.id %>')
h1 <%= user.title %>

View File

@@ -0,0 +1,3 @@
html
body
h1 Title

View File

@@ -0,0 +1,8 @@
ul
li foo
li bar
li baz

View File

@@ -0,0 +1,12 @@
-
list = ["uno", "dos", "tres",
"cuatro", "cinco", "seis"];
//- Without a block, the element is accepted and no code is generated
-
each item in list
-
string = item.charAt(0)
.toUpperCase() +
item.slice(1);
li= string

View File

@@ -0,0 +1,5 @@
ul
li: a(href='#') foo
li: a(href='#') bar
p baz

View File

@@ -0,0 +1,2 @@
ul
li.list-item: .foo: #bar baz

View File

@@ -0,0 +1,4 @@
figure
blockquote
| Try to define yourself by what you do, and you&#8217;ll burnout every time. You are. That is enough. I rest in that.
figcaption from @thefray at 1:43pm on May 10

View File

@@ -0,0 +1,4 @@
extends ./auxiliary/blocks-in-blocks-layout.pug
block body
h1 Page 2

View File

@@ -0,0 +1,19 @@
//- see https://github.com/pugjs/pug/issues/1589
-var ajax = true
-if( ajax )
//- return only contents if ajax requests
block contents
p ajax contents
-else
//- return all html
doctype html
html
head
meta( charset='utf8' )
title sample
body
block contents
p all contetns

View File

@@ -0,0 +1,10 @@
html
body
- var friends = 1
case friends
when 0
p you have no friends
when 1
p you have a friend
default
p you have #{friends} friends

View File

@@ -0,0 +1,19 @@
html
body
- var friends = 1
case friends
when 0: p you have no friends
when 1: p you have a friend
default: p you have #{friends} friends
- var friends = 0
case friends
when 0
when 1
p you have very few friends
default
p you have #{friends} friends
- var friend = 'Tim:G'
case friend
when 'Tim:G': p Friend is a string
when {tim: 'g'}: p Friend is an object

View File

@@ -0,0 +1,3 @@
a(class='')
a(class=null)
a(class=undefined)

View File

@@ -0,0 +1,14 @@
a(class=['foo', 'bar', 'baz'])
a.foo(class='bar').baz
a.foo-bar_baz
a(class={foo: true, bar: false, baz: true})
a.-foo
a.3foo

View File

@@ -0,0 +1,43 @@
- if (true)
p foo
- else
p bar
- if (true) {
p foo
- } else {
p bar
- }
if true
p foo
p bar
p baz
else
p bar
unless true
p foo
else
p bar
if 'nested'
if 'works'
p yay
//- allow empty blocks
if false
else
.bar
if true
.bar
else
.bing
if false
.bing
else if false
.bar
else
.foo

View File

@@ -0,0 +1,2 @@
p= '<script>'
p!= '<script>'

View File

@@ -0,0 +1,35 @@
- var items = [1,2,3]
ul
- items.forEach(function(item){
li= item
- })
- var items = [1,2,3]
ul
for item, i in items
li(class='item-' + i)= item
ul
each item, i in items
li= item
ul
each $item in items
li= $item
- var nums = [1, 2, 3]
- var letters = ['a', 'b', 'c']
ul
for l in letters
for n in nums
li #{n}: #{l}
- var count = 1
- var counter = function() { return [count++, count++, count++] }
ul
for n in counter()
li #{n}

View File

@@ -0,0 +1,10 @@
p= null
p= undefined
p= ''
p= 0
p= false
p(foo=null)
p(foo=undefined)
p(foo='')
p(foo=0)
p(foo=false)

View File

@@ -0,0 +1,10 @@
doctype html
html
body
- var s = 'this'
case s
//- Comment
when 'this'
p It's this!
when 'that'
p It's that!

View File

@@ -0,0 +1,29 @@
// foo
ul
// bar
li one
// baz
li two
//
ul
li foo
// block
// inline follow
li three
// block
// inline followed by tags
ul
li four
//if IE lt 9
// inline
script(src='/lame.js')
// end-inline
p five
.foo // not a comment

View File

@@ -0,0 +1,9 @@
//-
s/s.
//- test/cases/comments.source.pug
//-
test/cases/comments.source.pug
when
()

View File

@@ -0,0 +1 @@
doctype custom stuff

View File

@@ -0,0 +1,4 @@
doctype
html
body
h1 Title

View File

@@ -0,0 +1 @@
doctype html

View File

@@ -0,0 +1,52 @@
- var users = []
ul
for user in users
li= user.name
else
li no users!
- var users = [{ name: 'tobi', friends: ['loki'] }, { name: 'loki' }]
if users
ul
for user in users
li= user.name
else
li no users!
- var user = { name: 'tobi', age: 10 }
ul
each val, key in user
li #{key}: #{val}
else
li user has no details!
- var user = {}
ul
each prop, key in user
li #{key}: #{val}
else
li user has no details!
- var user = Object.create(null)
- user.name = 'tobi'
ul
each val, key in user
li #{key}: #{val}
else
li user has no details!
- var ofKeyword = [{ name: 'tobi', friends: ['loki'] }, { name: 'loki' }]
ul
each val of ofKeyword
li= user.name
ul
each val of ["variable with of keyword"]
li= val

View File

@@ -0,0 +1,2 @@
script.
var re = /\d+/;

View File

@@ -0,0 +1,8 @@
doctype html
html
head
title escape-test
body
textarea
- var txt = '<param name="flashvars" value="a=&quot;value_a&quot;&b=&quot;value_b&quot;&c=3"/>'
| #{txt}

View File

@@ -0,0 +1,6 @@
foo(attr="<%= bar %>")
foo(class="<%= bar %>")
foo(attr!="<%= bar %>")
foo(class!="<%= bar %>")
foo(class!="<%= bar %> lol rofl")
foo(class!="<%= bar %> lol rofl <%= lmao %>")

View File

@@ -0,0 +1 @@
include ./auxiliary/filter-in-include.pug

View File

@@ -0,0 +1,6 @@
- var users = [{ name: 'tobi', age: 2 }]
fb:users
for user in users
fb:user(age=user.age)
:cdata

View File

@@ -0,0 +1,6 @@
script(type='text/javascript')
:coffee-script
regexp = /\n/
:coffee-script(minify=true)
math =
square: (value) -> value * value

View File

@@ -0,0 +1,7 @@
html
body
:custom(opt='val' num=2)
Line 1
Line 2
Line 4

View File

@@ -0,0 +1,4 @@
html
body
pre
include:custom(opt='val' num=2) filters.include.custom.pug

View File

@@ -0,0 +1,7 @@
html
body
include:markdown-it some.md
script
include:coffee-script(minify=true) include-filter-coffee.coffee
script
include:cdata:coffee-script(minify=false) include-filter-coffee.coffee

View File

@@ -0,0 +1 @@
p before #[:cdata inside] after

View File

@@ -0,0 +1,8 @@
html
head
style(type="text/css")
:less
@pad: 15px;
body {
padding: @pad;
}

View File

@@ -0,0 +1,5 @@
html
body
:markdown-it
This is _some_ awesome **markdown**
whoop.

View File

@@ -0,0 +1,10 @@
script
:cdata:uglify-js
(function() {
console.log('test')
})()
script
:cdata:uglify-js:coffee-script
(->
console.log 'test'
)()

View File

@@ -0,0 +1,7 @@
html
head
style(type="text/css")
:stylus
body
padding: 50px
body

View File

@@ -0,0 +1,6 @@
html
div
:verbatim
filters are applied at compile time
with no #[b interpolation] at #{all}

View File

@@ -0,0 +1,13 @@
- var version = 1449104952939
<ul>
<li>foo</li>
<li>bar</li>
<li>baz</li>
</ul>
<!--build:js /js/app.min.js?v=#{version}-->
<!--endbuild-->
p You can <em>embed</em> html as well.
p: <strong>Even</strong> as the body of a block expansion.

View File

@@ -0,0 +1,4 @@
doctype html
input(type='checkbox', checked)
input(type='checkbox', checked=true)
input(type='checkbox', checked=false)

View File

@@ -0,0 +1 @@
include /auxiliary/extends-from-root.pug

View File

@@ -0,0 +1,2 @@
include auxiliary/extends-empty-block-1.pug
include auxiliary/extends-empty-block-2.pug

View File

@@ -0,0 +1 @@
include ../cases/auxiliary/extends-relative.pug

View File

@@ -0,0 +1,3 @@
| The message is "
yield
| "

View File

@@ -0,0 +1,5 @@
html
body
p
include include-only-text-body.pug
em hello world

View File

@@ -0,0 +1,3 @@
head
script(type='text/javascript').
alert('hello world');

View File

@@ -0,0 +1,4 @@
html
include include-with-text-head.pug
script(src='/caustic.js')
script(src='/app.js')

View File

@@ -0,0 +1,2 @@
script#pet-template(type='text/x-template')
include auxiliary/pet.pug

View File

@@ -0,0 +1,4 @@
include auxiliary/yield-nested.pug
p some content
p and some more

View File

@@ -0,0 +1,3 @@
pre
code
include javascript-new-lines.js

View File

@@ -0,0 +1,10 @@
include auxiliary/mixins.pug
+foo
body
include auxiliary/smile.html
include auxiliary/escapes.html
script(type="text/javascript")
include:verbatim auxiliary/includable.js

View File

@@ -0,0 +1,6 @@
extends auxiliary/dialog.pug
block content
h1 Alert!
p I'm an alert!

View File

@@ -0,0 +1,6 @@
html
head
block head
script(src='jquery.js')
script(src='keymaster.js')
script(src='caustic.js')

View File

@@ -0,0 +1,13 @@
extend auxiliary/layout.include.pug
block head
script(src='jquery.js')
block content
h2 Page
p Some content
block window-content
h2 Awesome
p Now we can extend included blocks!

View File

@@ -0,0 +1,4 @@
extend auxiliary/inheritance.extend.mixin.block.pug
block content
p Hello World!

View File

@@ -0,0 +1,11 @@
extend auxiliary/layout.pug
mixin article(title)
if title
h1= title
block
block content
+article("The meaning of life")
p Foo bar baz!

View File

@@ -0,0 +1,9 @@
extend auxiliary/layout.pug
block head
script(src='jquery.js')
block content
h2 Page
p Some content

View File

@@ -0,0 +1,4 @@
extends /auxiliary/inheritance.extend.recursive-parent.pug
block parent
h4 child

View File

@@ -0,0 +1,13 @@
extend auxiliary/layout.pug
block head
script(src='jquery.js')
block content
h2 Page
p Some content

View File

@@ -0,0 +1,9 @@
extends auxiliary/layout.pug
block head
script(src='jquery.js')
block content
h2 Page
p Some content

View File

@@ -0,0 +1,3 @@
block content // Main content goes here
append content // adding something to content
prepend content // adding something to other end of content

View File

@@ -0,0 +1,19 @@
p bing #[strong foo] bong
p.
bing
#[strong foo]
#[strong= '[foo]']
#[- var foo = 'foo]']
bong
p
| bing
| #[strong foo]
| #[strong= '[foo]']
| #[- var foo = 'foo]']
| bong
p.
\#[strong escaped]
\#[#[strong escaped]

View File

@@ -0,0 +1,3 @@
p #[a.rho(href='#', class='rho--modifier') with inline link]
p Some text #[a.rho(href='#', class='rho--modifier')]
p Some text #[a.rho(href='#', class='rho--modifier') with inline link]

View File

@@ -0,0 +1,4 @@
mixin linkit(url)
a(href=url)= url
p This also works #[+linkit('http://www.bing.com')] so hurrah for Pug

View File

@@ -0,0 +1,6 @@
- var id = 42;
foo
| some
| \#{text}
| \!{here}
| My ID #{"is {" + id + "}"}

View File

@@ -0,0 +1 @@
var x = '\n here is some \n new lined text';

View File

@@ -0,0 +1,6 @@
extends ../fixtures/append/app-layout.pug
block append head
script(src='foo.js')
script(src='bar.js')

View File

@@ -0,0 +1,6 @@
extends ../fixtures/append-without-block/app-layout.pug
append head
script(src='foo.js')
script(src='bar.js')

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