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,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')

View File

@@ -0,0 +1,19 @@
extends ../fixtures/multi-append-prepend-block/redefine.pug
append content
p.first.append Something appended to content
prepend content
p.first.prepend Something prepended to content
append content
p.last.append Last append must be most last
prepend content
p.last.prepend Last prepend must appear at top
append head
script(src='jquery.js')
prepend head
script(src='foo.js')

View File

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

View File

@@ -0,0 +1,6 @@
extends ../fixtures/prepend-without-block/app-layout.pug
prepend 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