fix: add scoped error logging for lexer/parser errors

- Add std.log.scoped(.pugz) to template.zig and view_engine.zig
- Log detailed error info (code, line, column, message) when parsing fails
- Log template path context in ViewEngine on parse errors
- Remove debug print from lexer, use proper scoped logging instead
- Move benchmarks, docs, examples, playground, tests out of src/ to project root
- Update build.zig and documentation paths accordingly
- Bump version to 0.3.1
This commit is contained in:
2026-01-25 17:10:02 +05:30
parent 9d3b729c6c
commit aaf6a1af2d
1148 changed files with 57 additions and 330 deletions

View File

@@ -0,0 +1,5 @@
The examples in this directory can be run simply by something like.
node attributes.js
You can also open `browser.html` in a browser.

View File

@@ -0,0 +1,10 @@
/**
* Module dependencies.
*/
var pug = require('../'),
path = __dirname + '/attributes.pug',
str = require('fs').readFileSync(path, 'utf8'),
fn = pug.compile(str, {filename: path, pretty: true});
console.log(fn({name: 'tj'}));

View File

@@ -0,0 +1,8 @@
div#id.left.container(class='user user-' + name)
h1.title= name
form
//- unbuffered comment :)
// An example of attributes.
input(type='text' name='user[name]' value=name)
input(checked, type='checkbox', name='user[blocked]')
input(type='submit', value='Update')

View File

@@ -0,0 +1,15 @@
/**
* Module dependencies.
*/
var pug = require('../'),
path = __dirname + '/code.pug',
str = require('fs').readFileSync(path, 'utf8'),
fn = pug.compile(str, {filename: path, pretty: true});
var users = {
tj: {age: 23, email: 'tj@vision-media.ca', isA: 'human'},
tobi: {age: 1, email: 'tobi@is-amazing.com', isA: 'ferret'},
};
console.log(fn({users: users}));

View File

@@ -0,0 +1,17 @@
- var title = "Things"
-
var subtitle = ["Really", "long",
"list", "of",
"words"]
h1= title
h2= subtitle.join(" ")
ul#users
each user, name in users
// expands to if (user.isA == 'ferret')
if user.isA == 'ferret'
li(class='user-' + name) #{name} is just a ferret
else
li(class='user-' + name) #{name} #{user.email}

View File

@@ -0,0 +1,15 @@
/**
* Module dependencies.
*/
var pug = require('../');
var locals = {
users: {
tj: {age: 23, email: 'tj@vision-media.ca', isA: 'human'},
tobi: {age: 1, email: 'tobi@is-amazing.com', isA: 'ferret'},
},
};
var fn = pug.compileFile(__dirname + '/dynamicscript.pug');
console.log(fn(locals));

View File

@@ -0,0 +1,5 @@
html
head
title Dynamic Inline JavaScript
script.
var users = !{JSON.stringify(users).replace(/<\//g, "<\\/")}

View File

@@ -0,0 +1,15 @@
/**
* Module dependencies.
*/
var pug = require('../'),
path = __dirname + '/each.pug',
str = require('fs').readFileSync(path, 'utf8'),
fn = pug.compile(str, {filename: path, pretty: true});
var users = {
tj: {age: 23, email: 'tj@vision-media.ca', isA: 'human'},
tobi: {age: 1, email: 'tobi@is-amazing.com', isA: 'ferret'},
};
console.log(fn({users: users}));

View File

@@ -0,0 +1,3 @@
ul#users
each user, name in users
li(class='user-' + name) #{name} #{user.email}

View File

@@ -0,0 +1,10 @@
html
head
h1 My Site - #{title}
block scripts
script(src='/jquery.js')
body
block content
block foot
#footer
p some footer content

View File

@@ -0,0 +1,19 @@
/**
* Module dependencies.
*/
var pug = require('../'),
path = __dirname + '/extend.pug',
str = require('fs').readFileSync(path, 'utf8'),
fn = pug.compile(str, {filename: path, pretty: true});
var tobi = {name: 'tobi', age: 2};
var loki = {name: 'loki', age: 1};
var jane = {name: 'jane', age: 5};
console.log(
fn({
title: 'pets',
pets: [tobi, loki, jane],
})
);

View File

@@ -0,0 +1,11 @@
extends extend-layout.pug
block scripts
script(src='/jquery.js')
script(src='/pets.js')
block content
h1= title
each pet in pets
include pet.pug

View File

@@ -0,0 +1,17 @@
/**
* Module dependencies.
*/
var pug = require('../'),
path = __dirname + '/form.pug',
str = require('fs').readFileSync(path, 'utf8'),
fn = pug.compile(str, {filename: path, pretty: true});
var user = {
name: 'TJ',
email: 'tj@vision-media.ca',
city: 'Victoria',
province: 'BC',
};
console.log(fn({user: user}));

View File

@@ -0,0 +1,29 @@
form(method="post")
fieldset
legend General
p
label(for="user[name]") Username:
input(type="text", name="user[name]", value=user.name)
p
label(for="user[email]") Email:
input(type="text", name="user[email]", value=user.email)
.tip.
Enter a valid
email address
such as <em>tj@vision-media.ca</em>.
fieldset
legend Location
p
label(for="user[city]") City:
input(type="text", name="user[city]", value=user.city)
p
select(name="user[province]")
option(value="") -- Select Province --
option(value="AB") Alberta
option(value="BC") British Columbia
option(value="SK") Saskatchewan
option(value="MB") Manitoba
option(value="ON") Ontario
option(value="QC") Quebec
p.buttons
input(type="submit", value="Save")

View File

@@ -0,0 +1,10 @@
/**
* Module dependencies.
*/
var pug = require('../'),
path = __dirname + '/includes.pug',
str = require('fs').readFileSync(path, 'utf8'),
fn = pug.compile(str, {filename: path, pretty: true});
console.log(fn());

View File

@@ -0,0 +1,7 @@
html
include includes/head.pug
body
h1 My Site
p Welcome to my super lame site.
include includes/foot.pug

View File

@@ -0,0 +1,2 @@
#footer
p Copyright (c) foobar

View File

@@ -0,0 +1,6 @@
head
title My Site
// including other pug works
include scripts.pug
// including .html, .css, etc works
include style.css

View File

@@ -0,0 +1,2 @@
script(src='/javascripts/jquery.js')
script(src='/javascripts/app.js')

View File

@@ -0,0 +1,5 @@
<style>
body {
padding: 50px;
}
</style>

View File

@@ -0,0 +1,10 @@
/**
* Module dependencies.
*/
var pug = require('../');
pug.renderFile(__dirname + '/layout.pug', {debug: true}, function(err, html) {
if (err) throw err;
console.log(html);
});

View File

@@ -0,0 +1,10 @@
/**
* Module dependencies.
*/
var pug = require('../'),
path = __dirname + '/layout.pug',
str = require('fs').readFileSync(path, 'utf8'),
fn = pug.compile(str, {filename: path, pretty: true});
console.log(fn());

View File

@@ -0,0 +1,14 @@
doctype html
html(lang="en")
head
title Example
script.
if (foo) {
bar();
}
body
h1 Pug - node template engine
#container
:markdown-it
Pug is a _high performance_ template engine for [node](http://nodejs.org),
inspired by [haml](http://haml-lang.com/), and written by [TJ Holowaychuk](http://github.com/visionmedia).

View File

@@ -0,0 +1,15 @@
/**
* Module dependencies.
*/
var pug = require('../'),
path = __dirname + '/mixins.pug',
str = require('fs').readFileSync(path, 'utf8'),
fn = pug.compile(str, {filename: path, pretty: true});
var user = {
name: 'tj',
pets: ['tobi', 'loki', 'jane', 'manny'],
};
console.log(fn({user: user}));

View File

@@ -0,0 +1,14 @@
include mixins/dialog.pug
include mixins/profile.pug
.one
+dialog
.two
+dialog-title('Whoop')
.three
+dialog-title-desc('Whoop', 'Just a mixin')
#profile
+profile(user)

View File

@@ -0,0 +1,15 @@
mixin dialog
.dialog
h1 Whoop
p stuff
mixin dialog-title(title)
.dialog
h1= title
p stuff
mixin dialog-title-desc(title, desc)
.dialog
h1= title
p= desc

View File

@@ -0,0 +1,10 @@
mixin pets(pets)
ul.pets
each pet in pets
li= pet
mixin profile(user)
.user
h2= user.name
+pets(user.pets)

View File

@@ -0,0 +1,3 @@
.pet
h2= pet.name
p #{pet.name} is <em>#{pet.age}</em> year(s) old.

View File

@@ -0,0 +1,28 @@
/**
* Module dependencies.
*/
var pug = require('../'),
path = __dirname + '/rss.pug',
str = require('fs').readFileSync(path, 'utf8'),
fn = pug.compile(str, {filename: path, pretty: true});
var items = [];
items.push({
title: 'Example',
description: 'Something',
link: 'http://google.com',
});
items.push({
title: 'LearnBoost',
description: 'Cool',
link: 'http://learnboost.com',
});
items.push({
title: 'Express',
description: 'Cool',
link: 'http://expressjs.com',
});
console.log(fn({items: items}));

View File

@@ -0,0 +1,14 @@
doctype xml
rss(version='2.0')
channel
title RSS Title
description Some description here
link http://google.com
lastBuildDate Mon, 06 Sep 2010 00:01:00 +0000
pubDate Mon, 06 Sep 2009 16:45:00 +0000
each item in items
item
title= item.title
description= item.description
link= item.link

View File

@@ -0,0 +1,10 @@
/**
* Module dependencies.
*/
var pug = require('../'),
path = __dirname + '/text.pug',
str = require('fs').readFileSync(path, 'utf8'),
fn = pug.compile(str, {filename: path, pretty: true});
console.log(fn({name: 'tj', email: 'tj@vision-media.ca'}));

View File

@@ -0,0 +1,36 @@
| An example of an
a(href='#') inline
| link.
form
label Username:
input(type='text', name='user[name]')
p
| Just an example of some text usage.
| You can have <em>inline</em> html,
| as well as
strong tags
| .
| Interpolation is also supported. The
| username is currently "#{name}".
label Email:
input(type='text', name='user[email]')
p
| Email is currently
em= email
| .
// alternatively, if we plan on having only
// text or inline-html, we can use a trailing
// "." to let pug know we want to omit pipes
label Username:
input(type='text')
p.
Just an example, like before
however now we can omit those
annoying pipes!.
Wahoo.

View File

@@ -0,0 +1,10 @@
/**
* Module dependencies.
*/
var pug = require('../'),
path = __dirname + '/whitespace.pug',
str = require('fs').readFileSync(path, 'utf8'),
fn = pug.compile(str, {filename: path, pretty: true});
console.log(fn());

View File

@@ -0,0 +1,11 @@
- var js = '<script></script>'
doctype html
html
head
title= "Some " + "JavaScript"
!= js
body

View File

@@ -0,0 +1,15 @@
# Running Tests
To run tests (with node.js installed) you must complete 2 steps.
## 1 Install dependencies
```
npm install
```
## 2 Run tests
```
npm test
```

View File

@@ -0,0 +1,110 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`pug .compileClient() should support module syntax in pug.compileClient(str, options) when inlineRuntimeFunctions it false 1`] = `
"var pug = require(\\"pug-runtime\\");
function template(locals) {
var pug_html = \\"\\",
pug_mixins = {},
pug_interp;
var pug_debug_filename, pug_debug_line;
try {
var self = locals || {};
pug_debug_line = 1;
pug_html = pug_html + '\\\\u003Cdiv class=\\"bar\\"\\\\u003E';
pug_debug_line = 1;
pug_html =
pug_html +
pug.escape(null == (pug_interp = self.foo) ? \\"\\" : pug_interp) +
\\"\\\\u003C\\\\u002Fdiv\\\\u003E\\";
} catch (err) {
pug.rethrow(err, pug_debug_filename, pug_debug_line);
}
return pug_html;
}
module.exports = template;
"
`;
exports[`pug .compileClient() should support module syntax in pug.compileClient(str, options) when inlineRuntimeFunctions it true 1`] = `
"function pug_escape(e) {
var a = \\"\\" + e,
t = pug_match_html.exec(a);
if (!t) return e;
var r,
c,
n,
s = \\"\\";
for (r = t.index, c = 0; r < a.length; r++) {
switch (a.charCodeAt(r)) {
case 34:
n = \\"&quot;\\";
break;
case 38:
n = \\"&amp;\\";
break;
case 60:
n = \\"&lt;\\";
break;
case 62:
n = \\"&gt;\\";
break;
default:
continue;
}
c !== r && (s += a.substring(c, r)), (c = r + 1), (s += n);
}
return c !== r ? s + a.substring(c, r) : s;
}
var pug_match_html = /[\\"&<>]/;
function pug_rethrow(e, n, r, t) {
if (!(e instanceof Error)) throw e;
if (!((\\"undefined\\" == typeof window && n) || t))
throw ((e.message += \\" on line \\" + r), e);
var o, a, i, s;
try {
(t = t || require(\\"fs\\").readFileSync(n, { encoding: \\"utf8\\" })),
(o = 3),
(a = t.split(\\"\\\\n\\")),
(i = Math.max(r - o, 0)),
(s = Math.min(a.length, r + o));
} catch (t) {
return (
(e.message += \\" - could not read from \\" + n + \\" (\\" + t.message + \\")\\"),
void pug_rethrow(e, null, r)
);
}
(o = a
.slice(i, s)
.map(function(e, n) {
var t = n + i + 1;
return (t == r ? \\" > \\" : \\" \\") + t + \\"| \\" + e;
})
.join(\\"\\\\n\\")),
(e.path = n);
try {
e.message = (n || \\"Pug\\") + \\":\\" + r + \\"\\\\n\\" + o + \\"\\\\n\\\\n\\" + e.message;
} catch (e) {}
throw e;
}
function template(locals) {
var pug_html = \\"\\",
pug_mixins = {},
pug_interp;
var pug_debug_filename, pug_debug_line;
try {
var self = locals || {};
pug_debug_line = 1;
pug_html = pug_html + '\\\\u003Cdiv class=\\"bar\\"\\\\u003E';
pug_debug_line = 1;
pug_html =
pug_html +
pug_escape(null == (pug_interp = self.foo) ? \\"\\" : pug_interp) +
\\"\\\\u003C\\\\u002Fdiv\\\\u003E\\";
} catch (err) {
pug_rethrow(err, pug_debug_filename, pug_debug_line);
}
return pug_html;
}
module.exports = template;
"
`;

View File

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

View File

@@ -0,0 +1,4 @@
when 5
.foo
when 6
.bar

View File

@@ -0,0 +1,2 @@
case foo
.div

View File

@@ -0,0 +1,4 @@
if foo
div
else bar
article

View File

@@ -0,0 +1,2 @@
else
.foo

View File

@@ -0,0 +1 @@
foo()+bar()

View File

@@ -0,0 +1 @@
div("foo"abc)

View File

@@ -0,0 +1 @@
div(foo!~abc)

View File

@@ -0,0 +1,2 @@
//- #1871
p #[strong a}

View File

@@ -0,0 +1,2 @@
mixin foo(a, b)
+foo('a'b'b')

View File

@@ -0,0 +1,3 @@
mixin foo
block
bar

View File

@@ -0,0 +1,2 @@
:not-a-valid-filter
foo bar

View File

@@ -0,0 +1,2 @@
div
block

View File

@@ -0,0 +1 @@
div(title=[)

View File

@@ -0,0 +1 @@
This folder collects examples of files that are not valid `pug`, but were at some point accepted by the parser without throwing an error. The tests ensure that all these cases now throw some form of error message (hopefully a helpful one).

View File

@@ -0,0 +1,2 @@
input
| Inputs cannot have content

View File

@@ -0,0 +1 @@
input Input's can't have content

View File

@@ -0,0 +1 @@
input= 'Inputs cannot have code'

View File

@@ -0,0 +1,3 @@
div
div
article

View File

@@ -0,0 +1 @@
+#{myMixin

View File

@@ -0,0 +1,4 @@
mixin item
block
+item( Contact

View File

@@ -0,0 +1 @@
#{myMixin

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html><html><head></head><body><textarea id="input" placeholder="write pug here" style="width: 100%; min-height: 400px;">p
author
!= myName</textarea><pre style="background: #ECECEC;width: 100%; min-height: 400px;"><code id="output"></code></pre><script src="../../pug.js"></script><script>var input = document.getElementById('input');
var output = document.getElementById('output');
setInterval(function () {
pug.render(input.value, {myName: 'Forbes Lindesay', pretty: true}, function (err, res) {
if (err) throw err;
output.textContent = res;
})
}, 500)</script></body></html>

View File

@@ -0,0 +1,20 @@
!!! 5
html
head
body
textarea#input(placeholder='write pug here', style='width: 100%; min-height: 400px;').
p
author
!= myName
pre(style='background: #ECECEC;width: 100%; min-height: 400px;')
code#output
script(src='../../pug.js')
script.
var input = document.getElementById('input');
var output = document.getElementById('output');
setInterval(function () {
pug.render(input.value, {myName: 'Forbes Lindesay', pretty: true}, function (err, res) {
if (err) throw err;
output.textContent = res;
})
}, 500)

View File

@@ -0,0 +1 @@
<div class="avatar-div" style="background-image: url(https://www.gravatar.com/avatar/219b77f9d21de75e81851b6b886057c7)"></div>

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,6 @@
<foo data-user="{&quot;name&quot;:&quot;tobi&quot;}"></foo>
<foo data-items="[1,2,3]"></foo>
<foo data-username="tobi"></foo>
<foo data-escaped="{&quot;message&quot;:&quot;Let's rock!&quot;}"></foo>
<foo data-ampersand="{&quot;message&quot;:&quot;a quote: &amp;quot; this &amp; that&quot;}"></foo>
<foo data-epoc="1970-01-01T00:00:00.000Z"></foo>

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 @@
<div :my-var="model"></div><span v-for="item in items" :key="item.id" :value="item.name"></span><span v-for="item in items" :key="item.id" :value="item.name"></span><a :link="goHere" value="static" :my-value="dynamic" @click="onClick()" :another="more">Click Me!</a>

View File

@@ -0,0 +1,9 @@
//- Tests for using a colon-prefexed attribute (typical when using short-cut for Vue.js `v-bind`)
div(:my-var="model")
span(v-for="item in items" :key="item.id" :value="item.name")
span(
v-for="item in items"
:key="item.id"
:value="item.name"
)
a(:link="goHere" value="static" :my-value="dynamic" @click="onClick()" :another="more") Click Me!

View File

@@ -0,0 +1,20 @@
<a href="/contact">contact</a><a class="button" href="/save">save</a><a foo="foo" bar="bar" baz="baz"></a><a foo="foo, bar, baz" bar="1"></a><a foo="((foo))" bar="1"></a>
<select>
<option value="foo" selected="selected">Foo</option>
<option selected="selected" value="bar">Bar</option>
</select><a foo="class:"></a>
<input pattern="\S+"/><a href="/contact">contact</a><a class="button" href="/save">save</a><a foo="foo" bar="bar" baz="baz"></a><a foo="foo, bar, baz" bar="1"></a><a foo="((foo))" bar="1"></a>
<select>
<option value="foo" selected="selected">Foo</option>
<option selected="selected" value="bar">Bar</option>
</select><a foo="class:"></a>
<input pattern="\S+"/>
<foo terse="true"></foo>
<foo date="1970-01-01T00:00:00.000Z"></foo>
<foo abc="abc" def="def"></foo>
<foo abc="abc" def="def"></foo>
<foo abc="abc" def="def"></foo>
<foo abc="abc" def="def"></foo>
<foo abc="abc" def="def"></foo>
<foo abc="abc" def="def"></foo>
<div foo="bar" bar="<baz>"></div><a foo="foo" bar="bar"></a><a foo="foo" bar="bar"></a>

View File

@@ -0,0 +1,5 @@
<a class="button" href="/user/5"></a><a class="button" href="/user/5"></a>
<meta key="answer" value="42"/><a class="class1 class2"></a><a class="tag-class class1 class2"></a><a class="button" href="/user/5"></a><a class="button" href="/user/5"></a>
<meta key="answer" value="42"/><a class="class1 class2"></a><a class="tag-class class1 class2"></a>
<div id="5" foo="bar"></div>
<div baz="baz"></div>

View File

@@ -0,0 +1,17 @@
- 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'})

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,5 @@
<script type="text/x-template">
<div id="user-<%= user.id %>">
<h1><%= user.title %></h1>
</div>
</script>

View File

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

View File

@@ -0,0 +1 @@
block content

View File

@@ -0,0 +1,4 @@
mixin test()
.test&attributes(attributes)
+test()

View File

@@ -0,0 +1,8 @@
doctype html
html
head
title Default title
body
block body
.container
block content

View File

@@ -0,0 +1,6 @@
extends window.pug
block window-content
.dialog
block content

View File

@@ -0,0 +1,2 @@
block test

View File

@@ -0,0 +1,3 @@
<script>
console.log("foo\nbar")
</script>

View File

@@ -0,0 +1,5 @@
extends empty-block.pug
block test
div test1

View File

@@ -0,0 +1,5 @@
extends empty-block.pug
block test
div test2

View File

@@ -0,0 +1,4 @@
extends /auxiliary/layout.pug
block content
include /auxiliary/include-from-root.pug

View File

@@ -0,0 +1,4 @@
extends ../../cases/auxiliary/layout
block content
include ../../cases/auxiliary/include-from-root

View File

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

View File

@@ -0,0 +1,8 @@
var STRING_SUBSTITUTIONS = {
// table of character substitutions
'\t': '\\t',
'\r': '\\r',
'\n': '\\n',
'"': '\\"',
'\\': '\\\\',
};

View File

@@ -0,0 +1 @@
h1 hello

View File

@@ -0,0 +1,11 @@
mixin article()
article
block
html
head
title My Application
block head
body
+article
block content

View File

@@ -0,0 +1,2 @@
h1 grand-grandparent
block grand-grandparent

View File

@@ -0,0 +1,6 @@
extends inheritance.extend.recursive-grand-grandparent.pug
block grand-grandparent
h2 grandparent
block grandparent

View File

@@ -0,0 +1,5 @@
extends inheritance.extend.recursive-grandparent.pug
block grandparent
h3 parent
block parent

View File

@@ -0,0 +1,7 @@
html
head
title My Application
block head
body
block content
include window.pug

View File

@@ -0,0 +1,6 @@
html
head
title My Application
block head
body
block content

View File

@@ -0,0 +1,3 @@
mixin slide
section.slide
block

View File

@@ -0,0 +1,3 @@
mixin foo()
p bar

View File

@@ -0,0 +1,3 @@
.pet
h1 {{name}}
p {{name}} is a {{species}} that is {{age}} old

View File

@@ -0,0 +1 @@
<p>:)</p>

View File

@@ -0,0 +1,4 @@
.window
a(href='#').close Close
block window-content

View File

@@ -0,0 +1,10 @@
html
head
title
body
h1 Page
#content
#content-wrapper
yield
#footer
stuff

View File

@@ -0,0 +1,5 @@
<html>
<body>
<h1>Title</h1>
</body>
</html>

View File

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

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