diff --git a/.editorconfig b/.editorconfig
index 78c6ddee2..45c47a565 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,8 +1,10 @@
root = true
[*]
-end_of_line = lf
-insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 2
+tab_width = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..25ef3d10e
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: pug
+ko_fi: # Replace with a single Ko-fi username
+tidelift: npm/pug
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with a single custom sponsorship URL
diff --git a/.github/workflows/rollingversions-canary.yml b/.github/workflows/rollingversions-canary.yml
new file mode 100644
index 000000000..3fe8e91b1
--- /dev/null
+++ b/.github/workflows/rollingversions-canary.yml
@@ -0,0 +1,40 @@
+name: Publish Canary
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [10.x, 12.x, 14.x]
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+ - run: yarn install --frozen-lockfile
+ - run: yarn prettier:check
+ - run: yarn test
+
+ publish-canary:
+ runs-on: ubuntu-latest
+ needs: test
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-node@v1
+ with:
+ node-version: 12.x
+ registry-url: 'https://registry.npmjs.org'
+ - run: yarn install --frozen-lockfile
+ - run: yarn build
+ - run: npx rollingversions publish --canary $GITHUB_RUN_NUMBER
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/rollingversions.yml b/.github/workflows/rollingversions.yml
new file mode 100644
index 000000000..441206aa5
--- /dev/null
+++ b/.github/workflows/rollingversions.yml
@@ -0,0 +1,38 @@
+name: Release
+on:
+ repository_dispatch:
+ types: [rollingversions_publish_approved]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [10.x, 12.x, 14.x]
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+ - run: yarn install --frozen-lockfile
+ - run: yarn prettier:check
+ - run: yarn test
+
+ publish:
+ runs-on: ubuntu-latest
+ needs: test
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-node@v1
+ with:
+ node-version: 12.x
+ registry-url: 'https://registry.npmjs.org'
+ - run: yarn install --frozen-lockfile
+ - run: yarn build
+ - run: npx rollingversions publish
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 000000000..4903918c2
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,24 @@
+name: Test
+
+on:
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [10.x, 12.x, 14.x]
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+ - run: yarn install --frozen-lockfile
+ - run: yarn prettier:check
+ - run: yarn test
diff --git a/.gitignore b/.gitignore
index e09104305..4a75cbf4a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,9 +6,18 @@ node_modules
lib-cov
coverage
cov-pt*
-npm-debug.log
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.patch
+*.pid
+*.gz
**/test/output*
**/test/temp
.release.json
-lerna-debug.log
package-lock.json
+
+scripts/tsconfig.json
+packages/pug-error/lib
diff --git a/.prettierrc.js b/.prettierrc.js
new file mode 100644
index 000000000..40a818f16
--- /dev/null
+++ b/.prettierrc.js
@@ -0,0 +1,14 @@
+module.exports = {
+ bracketSpacing: false,
+ singleQuote: true,
+ trailingComma: 'all',
+ overrides: [
+ {
+ files: '*.js',
+ options: {
+ parser: 'babel',
+ trailingComma: 'es5',
+ },
+ },
+ ],
+};
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 39f094817..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-language: node_js
-node_js:
- - "6"
- - "8"
- - "10"
- - "11"
-
-# Use faster Docker architecture on Travis.
-sudo: false
-
-script: yarn test
-# TODO: coverage
-# after_success: npm run coveralls
-
-notifications:
- webhooks:
- urls:
- - https://webhooks.gitter.im/e/dd893be75c6b9908987d
- on_success: change
- on_failure: always
- on_start: never
diff --git a/.yarnrc b/.yarnrc
deleted file mode 100644
index 24efd034b..000000000
--- a/.yarnrc
+++ /dev/null
@@ -1 +0,0 @@
-workspaces-experimental true
\ No newline at end of file
diff --git a/README.md b/README.md
index ef9cd59c9..fb57f0768 100644
--- a/README.md
+++ b/README.md
@@ -1,41 +1,41 @@
+
# Pug
Full documentation is at [pugjs.org](https://pugjs.org/)
- Pug is a high-performance template engine heavily influenced by [Haml](http://haml.info/)
- and implemented with JavaScript for [Node.js](http://nodejs.org) and browsers. For bug reports,
- feature requests and questions, [open an issue](https://github.com/pugjs/pug/issues/new).
- For discussion join the [chat room](https://gitter.im/pugjs/pug).
-
- You can test drive Pug online [here](https://pugjs.org/).
-
- [Professionally supported pug is now available](https://tidelift.com/subscription/pkg/npm-pug?utm_source=npm-pug&utm_medium=referral&utm_campaign=readme)
-
- [](https://travis-ci.org/pugjs/pug)
- [](https://coveralls.io/r/pugjs/pug?branch=master)
- [](https://www.npmjs.com/package/pug)
- [](https://gitter.im/pugjs/pug?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[](#backers)
-[](#sponsors)
-
-### Dependency Status
-
-| Package | Dependencies | Dev Dependencies |
-| ------------- |:-------------| :-----|
-| mono-repo top | [](https://david-dm.org/pugjs/pug) | [](https://david-dm.org/pugjs/pug?type=dev) |
-| pug | [](https://david-dm.org/pugjs/pug?path=packages/pug) | [](https://david-dm.org/pugjs/pug?path=packages/pug&type=dev) |
-| pug-attrs | [](https://david-dm.org/pugjs/pug?path=packages/pug-attrs) | [](https://david-dm.org/pugjs/pug?path=packages/pug-attrs&type=dev) |
-| pug-code-gen | [](https://david-dm.org/pugjs/pug?path=packages/pug-code-gen) | [](https://david-dm.org/pugjs/pug?path=packages/pug-code-gen&type=dev) |
-| pug-error | [](https://david-dm.org/pugjs/pug?path=packages/pug-error) | [](https://david-dm.org/pugjs/pug?path=packages/pug-error&type=dev) |
-| pug-filters | [](https://david-dm.org/pugjs/pug?path=packages/pug-filters) | [](https://david-dm.org/pugjs/pug?path=packages/pug-filters&type=dev) |
-| pug-lexer | [](https://david-dm.org/pugjs/pug?path=packages/pug-lexer) | [](https://david-dm.org/pugjs/pug?path=packages/pug-lexer&type=dev) |
-| pug-linker | [](https://david-dm.org/pugjs/pug?path=packages/pug-linker) | [](https://david-dm.org/pugjs/pug?path=packages/pug-linker&type=dev) |
-| pug-load | [](https://david-dm.org/pugjs/pug?path=packages/pug-load) | [](https://david-dm.org/pugjs/pug?path=packages/pug-load&type=dev) |
-| pug-parser | [](https://david-dm.org/pugjs/pug?path=packages/pug-parser) | [](https://david-dm.org/pugjs/pug?path=packages/pug-parser&type=dev) |
-| pug-runtime | [](https://david-dm.org/pugjs/pug?path=packages/pug-runtime) | [](https://david-dm.org/pugjs/pug?path=packages/pug-runtime&type=dev) |
-| pug-strip-comments | [](https://david-dm.org/pugjs/pug?path=packages/pug-strip-comments) | [](https://david-dm.org/pugjs/pug?path=packages/pug-strip-comments&type=dev) |
-| pug-error | [](https://david-dm.org/pugjs/pug?path=packages/pug-walk) | [](https://david-dm.org/pugjs/pug?path=packages/pug-walk&type=dev) |
+Pug is a high-performance template engine heavily influenced by [Haml](http://haml.info/)
+and implemented with JavaScript for [Node.js](http://nodejs.org) and browsers. For bug reports,
+feature requests and questions, [open an issue](https://github.com/pugjs/pug/issues/new).
+For discussion join the [chat room](https://gitter.im/pugjs/pug).
+
+You can test drive Pug online [here](https://pugjs.org/).
+
+[Professionally supported pug is now available](https://tidelift.com/subscription/pkg/npm-pug?utm_source=npm-pug&utm_medium=referral&utm_campaign=readme)
+
+[](https://github.com/pugjs/pug/actions?query=workflow%3ATest+branch%3Amaster)
+[](https://rollingversions.com/pugjs/pug)
+[](https://www.npmjs.com/package/pug)
+[](https://gitter.im/pugjs/pug?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+## Packages
+
+
+Package Name | Version
+-------------|--------
+pug | [](https://www.npmjs.com/package/pug)
+pug-attrs | [](https://www.npmjs.com/package/pug-attrs)
+pug-code-gen | [](https://www.npmjs.com/package/pug-code-gen)
+pug-error | [](https://www.npmjs.com/package/pug-error)
+pug-filters | [](https://www.npmjs.com/package/pug-filters)
+pug-lexer | [](https://www.npmjs.com/package/pug-lexer)
+pug-linker | [](https://www.npmjs.com/package/pug-linker)
+pug-load | [](https://www.npmjs.com/package/pug-load)
+pug-parser | [](https://www.npmjs.com/package/pug-parser)
+pug-runtime | [](https://www.npmjs.com/package/pug-runtime)
+pug-strip-comments | [](https://www.npmjs.com/package/pug-strip-comments)
+pug-walk | [](https://www.npmjs.com/package/pug-walk)
+
## Rename from "Jade"
@@ -43,7 +43,7 @@ This project was formerly known as "Jade". However, it was revealed to us that "
If your package or app currently uses `jade`, don't worry: we have secured permissions to continue to occupy that package name, although all new versions will be released under `pug`.
-Before the renaming, work had already begun on “Jade 2.0.0”. Therefore, the rename to Pug coincided with the major version bump. As a result, upgrading from Jade to Pug will be the same process as upgrading any other package with a major version bump.
+Before the renaming, work had already begun on “Jade 2.0.0”. Therefore, the rename to Pug coincided with the major version bump. As a result, upgrading from Jade to Pug will be the same process as upgrading any other package with a major version bump.
The syntax of Pug has several differences, deprecations, and removals compared to its predecessor. These differences are documented in [#2305](https://github.com/pugjs/pug/issues/2305).
@@ -59,7 +59,6 @@ To use Pug in your own JavaScript projects:
$ npm install pug
```
-
### Command Line
After installing the latest version of [Node.js](http://nodejs.org), install with:
@@ -76,7 +75,7 @@ $ pug --help
## Syntax
-Pug is a clean, whitespace sensitive syntax for writing HTML. Here is a simple example:
+Pug is a clean, whitespace sensitive syntax for writing HTML. Here is a simple example:
```pug
doctype html
@@ -84,7 +83,7 @@ html(lang="en")
head
title= pageTitle
script(type='text/javascript').
- if (foo) bar(1 + 5)
+ if (foo) bar(1 + 5);
body
h1 Pug - node template engine
#container.col
@@ -99,21 +98,23 @@ html(lang="en")
Pug transforms the above to:
-
```html
Pug
Pug - node template engine
You are amazing
-
Pug is a terse and simple templating language with a strong focus on performance and powerful features.
+
+ Pug is a terse and simple templating language with a strong focus on
+ performance and powerful features.
+
@@ -139,15 +140,15 @@ var html = pug.renderFile('filename.pug', merge(options, locals));
### Options
- - `filename` Used in exceptions, and required when using includes
- - `compileDebug` When `false` no debug instrumentation is compiled
- - `pretty` Add pretty-indentation whitespace to output _(`false` by default)_
+- `filename` Used in exceptions, and required when using includes
+- `compileDebug` When `false` no debug instrumentation is compiled
+- `pretty` Add pretty-indentation whitespace to output _(`false` by default)_
## Browser Support
-The latest version of pug can be [downloaded for the browser in standalone form](https://pugjs.org/js/pug.js). It only supports the very latest browsers, though, and is a large file. It is recommended that you pre-compile your pug templates to JavaScript.
+The latest version of pug can be [downloaded for the browser in standalone form](https://pugjs.org/js/pug.js). It only supports the very latest browsers, though, and is a large file. It is recommended that you pre-compile your pug templates to JavaScript.
- To compile a template for use on the client using the command line, do:
+To compile a template for use on the client using the command line, do:
```bash
$ pug --client --no-debug filename.pug
@@ -157,9 +158,9 @@ which will produce `filename.js` containing the compiled template.
## Tutorials
- - cssdeck interactive [Pug syntax tutorial](http://cssdeck.com/labs/learning-the-jade-templating-engine-syntax)
- - cssdeck interactive [Pug logic tutorial](http://cssdeck.com/labs/jade-templating-tutorial-codecast-part-2)
- - [Pug について。](https://gist.github.com/japboy/5402844) (A Japanese Tutorial)
+- cssdeck interactive [Pug syntax tutorial](http://cssdeck.com/labs/learning-the-jade-templating-engine-syntax)
+- cssdeck interactive [Pug logic tutorial](http://cssdeck.com/labs/jade-templating-tutorial-codecast-part-2)
+- [Pug について。](https://gist.github.com/japboy/5402844) (A Japanese Tutorial)
## Implementations in other languages
@@ -167,52 +168,53 @@ which will produce `filename.js` containing the compiled template.
Ports to other languages, with very close syntax:
- - [PHP](https://github.com/pug-php/pug)
- - [Java](https://github.com/neuland/jade4j)
- - [Python](https://github.com/kakulukia/pypugjs)
- - [Ruby](https://github.com/yivo/pug-ruby)
- - [C# (ASP.NET Core)](https://github.com/AspNetMonsters/pugzor)
- - [RPG/ILE](https://github.com/WorksOfLiam/apug)
+- [PHP](https://github.com/pug-php/pug)
+- [Java](https://github.com/neuland/jade4j)
+- [Python](https://github.com/kakulukia/pypugjs)
+- [Ruby](https://github.com/yivo/pug-ruby)
+- [C# (ASP.NET Core)](https://github.com/AspNetMonsters/pugzor)
+- [RPG/ILE](https://github.com/WorksOfLiam/apug)
### Equivalents in other languages
Templates engines for other languages with a different syntax, but a similar philosophy:
- - [Scaml for Scala](https://scalate.github.io/scalate/documentation/scaml-reference.html)
- - [Slim for Ruby](https://github.com/slim-template/slim) (should not be confused with Slim PHP framework)
- - [HAML for Ruby](http://haml.info)
-
+- [Scaml for Scala](https://scalate.github.io/scalate/documentation/scaml-reference.html)
+- [Slim for Ruby](https://github.com/slim-template/slim) (should not be confused with Slim PHP framework)
+- [HAML for Ruby](http://haml.info)
+
### Framework implementations/adapters
Embedded view engines for frameworks:
- - [Laravel](https://github.com/BKWLD/laravel-pug)
- - [Symfony](https://github.com/pug-php/pug-symfony)
- - [Phalcon](https://github.com/pug-php/pug-phalcon)
- - [CodeIgniter](https://github.com/pug-php/ci-pug-engine)
- - [Yii 2](https://github.com/pug-php/pug-yii2)
- - [Slim 3](https://github.com/pug-php/pug-slim)
- - [Silex (implementation example)](https://gist.github.com/kylekatarnls/ba13e4361ab14f4ff5d2a5775eb0cc10)
- - [Lumen](https://github.com/BKWLD/laravel-pug#use-in-lumen)
- - [Rails](https://github.com/yivo/pug-rails)
+- [Laravel](https://github.com/BKWLD/laravel-pug)
+- [Symfony](https://github.com/pug-php/pug-symfony)
+- [Phalcon](https://github.com/pug-php/pug-phalcon)
+- [CodeIgniter](https://github.com/pug-php/ci-pug-engine)
+- [Yii 2](https://github.com/pug-php/pug-yii2)
+- [Slim 3](https://github.com/pug-php/pug-slim)
+- [Silex (implementation example)](https://gist.github.com/kylekatarnls/ba13e4361ab14f4ff5d2a5775eb0cc10)
+- [Lumen](https://github.com/BKWLD/laravel-pug#use-in-lumen)
+- [Rails](https://github.com/yivo/pug-rails)
### CMS plugins
- - [WordPress](https://github.com/welaika/wordless)
+- [WordPress](https://github.com/welaika/wordless)
## Additional Resources
- - [Emacs Mode](https://github.com/brianc/jade-mode)
- - [Vim Syntax](https://github.com/digitaltoad/vim-pug)
- - [TextMate Bundle](http://github.com/miksago/jade-tmbundle)
- - [Coda/SubEtha syntax Mode](https://github.com/aaronmccall/jade.mode)
- - [html2pug](https://github.com/donpark/html2jade) converter
- - [pug2php](https://github.com/SE7ENSKY/jade2php) converter
- - [Pug Server](https://github.com/ctrlaltdev/pug-server) Ideal for building local prototypes apart from any application
- - [cache-pug-templates](https://github.com/ladjs/cache-pug-templates) Cache Pug templates for [Lad](https://github.com/ladjs/lad)/[Koa](https://github.com/koajs/koa)/[Express](https://github.com/expressjs/express)/[Connect](https://github.com/senchalabs/connect) with [Redis](https://redis.io)
-
+- [Emacs Mode](https://github.com/brianc/jade-mode)
+- [Vim Syntax](https://github.com/digitaltoad/vim-pug)
+- [TextMate Bundle](http://github.com/miksago/jade-tmbundle)
+- [Coda/SubEtha syntax Mode](https://github.com/aaronmccall/jade.mode)
+- [html2pug](https://github.com/donpark/html2jade) converter
+- [pug2php](https://github.com/SE7ENSKY/jade2php) converter
+- [Pug Server](https://github.com/ctrlaltdev/pug-server) Ideal for building local prototypes apart from any application
+- [cache-pug-templates](https://github.com/ladjs/cache-pug-templates) Cache Pug templates for [Lad](https://github.com/ladjs/lad)/[Koa](https://github.com/koajs/koa)/[Express](https://github.com/expressjs/express)/[Connect](https://github.com/senchalabs/connect) with [Redis](https://redis.io)
+- [Prettier Plugin](https://github.com/prettier/plugin-pug)
## Backers
+
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/pug#backer)]
@@ -247,7 +249,8 @@ Support us with a monthly donation and help us continue our activities. [[Become
## Sponsors
-Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/pug#sponsor)]
+
+Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor](https://opencollective.com/pug#sponsor)]
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 000000000..9d0bc3b9f
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,14 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported |
+| -------- | ------------------ |
+| ^3.0.1 | :white_check_mark: |
+| <3.0.1 | :x: |
+
+## Reporting a Vulnerability
+
+To report a security vulnerability, please use the
+[Tidelift security contact](https://tidelift.com/security).
+Tidelift will coordinate the fix and disclosure.
diff --git a/lerna.json b/lerna.json
deleted file mode 100644
index 3059f1f32..000000000
--- a/lerna.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "lerna": "3.10.8",
- "packages": ["packages/*"],
- "npmClient": "yarn",
- "useWorkspaces": true,
- "version": "independent",
- "registry": "https://registry.npmjs.org/"
-}
\ No newline at end of file
diff --git a/package.json b/package.json
index 4b0777a91..06c9476ab 100644
--- a/package.json
+++ b/package.json
@@ -1,21 +1,26 @@
{
"name": "pug-monorepo",
"private": true,
+ "@rollingversions/ignore": true,
+ "workspaces": [
+ "packages/*"
+ ],
"devDependencies": {
- "coveralls": "3.0.2",
- "jest": "24.0.0",
- "lerna": "3.10.8",
- "prettier": "^1.3.1"
+ "coveralls": "3.0.9",
+ "jest": "^26.0.1",
+ "prettier": "1.19.1",
+ "typescript": "^4.1.2",
+ "wsrun": "^5.2.0"
},
"repository": {
"type": "git",
"url": "https://github.com/pugjs/pug.git"
},
"scripts": {
- "bootstrap": "lerna bootstrap",
- "clean": "lerna clean",
- "release": "lerna publish",
- "pretest": "lerna run pretest",
+ "prettier:check": "prettier --ignore-path .gitignore --list-different './**/*.js'",
+ "format": "prettier --ignore-path .gitignore --write './**/*.js'",
+ "build": "node scripts/prebuild && wsrun --stages --exclude-missing --fast-exit --collect-logs build && tsc --build scripts",
+ "pretest": "yarn build",
"test": "jest",
"coverage": "jest --coverage",
"coveralls": "jest --coverage --coverageReporters=text-lcov | coveralls",
@@ -25,11 +30,9 @@
"testEnvironment": "node",
"snapshotSerializers": [
"./scripts/filename-serializer.js",
- "./scripts/prettier-javascript-serializer.js"
+ "./scripts/prettier-javascript-serializer.js",
+ "./scripts/buffer-serializer.js"
]
},
- "license": "MIT",
- "workspaces": [
- "packages/*"
- ]
+ "license": "MIT"
}
diff --git a/packages/pug-attrs/index.js b/packages/pug-attrs/index.js
index 50d732144..9850ac390 100644
--- a/packages/pug-attrs/index.js
+++ b/packages/pug-attrs/index.js
@@ -6,10 +6,10 @@ var runtime = require('pug-runtime');
var stringify = require('js-stringify');
function isConstant(src) {
- return constantinople(src, {pug: runtime, 'pug_interp': undefined});
+ return constantinople(src, {pug: runtime, pug_interp: undefined});
}
function toConstant(src) {
- return constantinople.toConstant(src, {pug: runtime, 'pug_interp': undefined});
+ return constantinople.toConstant(src, {pug: runtime, pug_interp: undefined});
}
module.exports = compileAttrs;
@@ -21,15 +21,23 @@ module.exports = compileAttrs;
*/
function compileAttrs(attrs, options) {
assert(Array.isArray(attrs), 'Attrs should be an array');
- assert(attrs.every(function (attr) {
- return attr &&
- typeof attr === 'object' &&
- typeof attr.name === 'string' &&
- (typeof attr.val === 'string' || typeof attr.val === 'boolean') &&
- typeof attr.mustEscape === 'boolean';
- }), 'All attributes should be supplied as an object of the form {name, val, mustEscape}');
+ assert(
+ attrs.every(function(attr) {
+ return (
+ attr &&
+ typeof attr === 'object' &&
+ typeof attr.name === 'string' &&
+ (typeof attr.val === 'string' || typeof attr.val === 'boolean') &&
+ typeof attr.mustEscape === 'boolean'
+ );
+ }),
+ 'All attributes should be supplied as an object of the form {name, val, mustEscape}'
+ );
assert(options && typeof options === 'object', 'Options should be an object');
- assert(typeof options.terse === 'boolean', 'Options.terse should be a boolean');
+ assert(
+ typeof options.terse === 'boolean',
+ 'Options.terse should be a boolean'
+ );
assert(
typeof options.runtime === 'function',
'Options.runtime should be a function that takes a runtime function name and returns the source code that will evaluate to that function at runtime'
@@ -46,7 +54,9 @@ function compileAttrs(attrs, options) {
function addAttribute(key, val, mustEscape, buf) {
if (isConstant(val)) {
if (options.format === 'html') {
- var str = stringify(runtime.attr(key, toConstant(val), mustEscape, options.terse));
+ var str = stringify(
+ runtime.attr(key, toConstant(val), mustEscape, options.terse)
+ );
var last = buf[buf.length - 1];
if (last && last[last.length - 1] === str[0]) {
buf[buf.length - 1] = last.substr(0, last.length - 1) + str.substr(1);
@@ -62,7 +72,18 @@ function compileAttrs(attrs, options) {
}
} else {
if (options.format === 'html') {
- buf.push(options.runtime('attr') + '("' + key + '", ' + val + ', ' + stringify(mustEscape) + ', ' + stringify(options.terse) + ')');
+ buf.push(
+ options.runtime('attr') +
+ '("' +
+ key +
+ '", ' +
+ val +
+ ', ' +
+ stringify(mustEscape) +
+ ', ' +
+ stringify(options.terse) +
+ ')'
+ );
} else {
if (mustEscape) {
val = options.runtime('escape') + '(' + val + ')';
@@ -72,7 +93,7 @@ function compileAttrs(attrs, options) {
}
}
- attrs.forEach(function(attr){
+ attrs.forEach(function(attr) {
var key = attr.name;
var val = attr.val;
var mustEscape = attr.mustEscape;
@@ -101,16 +122,23 @@ function compileAttrs(attrs, options) {
classesBuf
);
} else {
- classes = classes.map(function (cls, i) {
+ classes = classes.map(function(cls, i) {
if (isConstant(cls)) {
- cls = stringify(classEscaping[i] ? runtime.escape(toConstant(cls)) : toConstant(cls));
+ cls = stringify(
+ classEscaping[i] ? runtime.escape(toConstant(cls)) : toConstant(cls)
+ );
classEscaping[i] = false;
}
return cls;
});
addAttribute(
'class',
- options.runtime('classes') + '([' + classes.join(',') + '], ' + stringify(classEscaping) + ')',
+ options.runtime('classes') +
+ '([' +
+ classes.join(',') +
+ '], ' +
+ stringify(classEscaping) +
+ ')',
false,
classesBuf
);
diff --git a/packages/pug-attrs/package.json b/packages/pug-attrs/package.json
index e4b465dd8..615713f63 100644
--- a/packages/pug-attrs/package.json
+++ b/packages/pug-attrs/package.json
@@ -6,8 +6,8 @@
"pug"
],
"dependencies": {
- "constantinople": "^3.0.1",
- "js-stringify": "^1.0.1",
+ "constantinople": "^4.0.1",
+ "js-stringify": "^1.0.2",
"pug-runtime": "^2.0.5"
},
"files": [
diff --git a/packages/pug-attrs/test/index.test.js b/packages/pug-attrs/test/index.test.js
index 1fb0d56d1..7bb8854ae 100644
--- a/packages/pug-attrs/test/index.test.js
+++ b/packages/pug-attrs/test/index.test.js
@@ -9,88 +9,293 @@ 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);
+ 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 () {
+ 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: '""', mustEscape: true}], ' class="<foo> <str>"', {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: '""', mustEscape: false}], ' foo=""');
- test([{name: 'foo', val: '""', mustEscape: true}], ' foo="<foo>"');
- test([{name: 'foo', val: 'foo', mustEscape: false}], ' foo=""', {foo: ''});
- test([{name: 'foo', val: 'foo', mustEscape: true}], ' foo="<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: '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: '""', mustEscape: true},
+ ],
+ ' class="<foo> <str>"',
+ {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: '""', mustEscape: false}], ' foo=""');
+ test(
+ [{name: 'foo', val: '""', mustEscape: true}],
+ ' foo="<foo>"'
+ );
+ test([{name: 'foo', val: 'foo', mustEscape: false}], ' foo=""', {
+ foo: '',
+ });
+ test([{name: 'foo', val: 'foo', mustEscape: true}], ' foo="<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: '""', mustEscape: true}], {'class': '<foo> <str>'}, {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: '""', mustEscape: false}], {foo: ""});
- test([{name: 'foo', val: '""', mustEscape: true}], {foo: "<foo>"});
- test([{name: 'foo', val: 'foo', mustEscape: false}], {foo: ""}, {foo: ''});
- test([{name: 'foo', val: 'foo', mustEscape: true}], {foo: "<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});
-});
+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: '""', mustEscape: true},
+ ],
+ {class: '<foo> <str>'},
+ {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: '""', mustEscape: false}], {foo: ''});
+ test([{name: 'foo', val: '""', mustEscape: true}], {
+ foo: '<foo>',
+ });
+ test(
+ [{name: 'foo', val: 'foo', mustEscape: false}],
+ {foo: ''},
+ {foo: ''}
+ );
+ test(
+ [{name: 'foo', val: 'foo', mustEscape: true}],
+ {foo: '<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}
+ );
+ }
+);
diff --git a/packages/pug-code-gen/index.js b/packages/pug-code-gen/index.js
index 6409d0ad0..3e6bf3b27 100644
--- a/packages/pug-code-gen/index.js
+++ b/packages/pug-code-gen/index.js
@@ -13,7 +13,7 @@ var addWith = require('with');
// This is used to prevent pretty printing inside certain tags
var WHITE_SPACE_SENSITIVE_TAGS = {
pre: true,
- textarea: true
+ textarea: true,
};
var INTERNAL_VARIABLES = [
@@ -23,21 +23,24 @@ var INTERNAL_VARIABLES = [
'pug_debug_filename',
'pug_debug_line',
'pug_debug_sources',
- 'pug_html'
+ 'pug_html',
];
module.exports = generateCode;
module.exports.CodeGenerator = Compiler;
function generateCode(ast, options) {
- return (new Compiler(ast, options)).compile();
+ return new Compiler(ast, options).compile();
}
-
function isConstant(src) {
- return constantinople(src, {pug: runtime, 'pug_interp': undefined});
+ return constantinople(src, {pug: runtime, pug_interp: undefined});
}
function toConstant(src) {
- return constantinople.toConstant(src, {pug: runtime, 'pug_interp': undefined});
+ return constantinople.toConstant(src, {pug: runtime, pug_interp: undefined});
+}
+
+function isIdentifier(name) {
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
}
/**
@@ -58,6 +61,28 @@ function Compiler(node, options) {
if (this.pp && typeof this.pp !== 'string') {
this.pp = ' ';
}
+ if (this.pp && !/^\s+$/.test(this.pp)) {
+ throw new Error(
+ 'The pretty parameter should either be a boolean or whitespace only string'
+ );
+ }
+ if (this.options.templateName && !isIdentifier(this.options.templateName)) {
+ throw new Error(
+ 'The templateName parameter must be a valid JavaScript identifier if specified.'
+ );
+ }
+ if (
+ this.doctype &&
+ (this.doctype.includes('<') || this.doctype.includes('>'))
+ ) {
+ throw new Error('Doctype can not contain "<" or ">"');
+ }
+ if (this.options.globals && !this.options.globals.every(isIdentifier)) {
+ throw new Error(
+ 'The globals option must be an array of valid JavaScript identifiers if specified.'
+ );
+ }
+
this.debug = false !== options.compileDebug;
this.indents = 0;
this.parentIndents = 0;
@@ -71,15 +96,14 @@ function Compiler(node, options) {
if (this.debug && this.inlineRuntimeFunctions) {
this.runtimeFunctionsUsed.push('rethrow');
}
-};
+}
/**
* Compiler prototype.
*/
Compiler.prototype = {
-
- runtime: function (name) {
+ runtime: function(name) {
if (this.inlineRuntimeFunctions) {
this.runtimeFunctionsUsed.push(name);
return 'pug_' + name;
@@ -88,7 +112,7 @@ Compiler.prototype = {
}
},
- error: function (message, code, node) {
+ error: function(message, code, node) {
var err = makeError(code, message, {
line: node.line,
column: node.column,
@@ -103,9 +127,9 @@ Compiler.prototype = {
* @api public
*/
- compile: function(){
+ compile: function() {
this.buf = [];
- if (this.pp) this.buf.push("var pug_indent = [];");
+ if (this.pp) this.buf.push('var pug_indent = [];');
this.lastBufferedIdx = -1;
this.visit(this.node);
if (!this.dynamicMixins) {
@@ -115,7 +139,11 @@ Compiler.prototype = {
var mixin = this.mixins[mixinNames[i]];
if (!mixin.used) {
for (var x = 0; x < mixin.instances.length; x++) {
- for (var y = mixin.instances[x].start; y < mixin.instances[x].end; y++) {
+ for (
+ var y = mixin.instances[x].start;
+ y < mixin.instances[x].end;
+ y++
+ ) {
this.buf[y] = '';
}
}
@@ -123,31 +151,52 @@ Compiler.prototype = {
}
}
var js = this.buf.join('\n');
- var globals = this.options.globals ? this.options.globals.concat(INTERNAL_VARIABLES) : INTERNAL_VARIABLES;
+ var globals = this.options.globals
+ ? this.options.globals.concat(INTERNAL_VARIABLES)
+ : INTERNAL_VARIABLES;
if (this.options.self) {
js = 'var self = locals || {};' + js;
} else {
- js = addWith('locals || {}', js, globals.concat(this.runtimeFunctionsUsed.map(function (name) { return 'pug_' + name; })));
+ js = addWith(
+ 'locals || {}',
+ js,
+ globals.concat(
+ this.runtimeFunctionsUsed.map(function(name) {
+ return 'pug_' + name;
+ })
+ )
+ );
}
if (this.debug) {
if (this.options.includeSources) {
- js = 'var pug_debug_sources = ' + stringify(this.options.includeSources) + ';\n' + js;
+ js =
+ 'var pug_debug_sources = ' +
+ stringify(this.options.includeSources) +
+ ';\n' +
+ js;
}
- js = 'var pug_debug_filename, pug_debug_line;' +
+ js =
+ 'var pug_debug_filename, pug_debug_line;' +
'try {' +
js +
'} catch (err) {' +
(this.inlineRuntimeFunctions ? 'pug_rethrow' : 'pug.rethrow') +
'(err, pug_debug_filename, pug_debug_line' +
- (
- this.options.includeSources
+ (this.options.includeSources
? ', pug_debug_sources[pug_debug_filename]'
- : ''
- ) +
+ : '') +
');' +
'}';
}
- return buildRuntime(this.runtimeFunctionsUsed) + 'function ' + (this.options.templateName || 'template') + '(locals) {var pug_html = "", pug_mixins = {}, pug_interp;' + js + ';return pug_html;}';
+
+ return (
+ buildRuntime(this.runtimeFunctionsUsed) +
+ 'function ' +
+ (this.options.templateName || 'template') +
+ '(locals) {var pug_html = "", pug_mixins = {}, pug_interp;' +
+ js +
+ ';return pug_html;}'
+ );
},
/**
@@ -159,7 +208,7 @@ Compiler.prototype = {
* @api public
*/
- setDoctype: function(name){
+ setDoctype: function(name) {
this.doctype = doctypes[name.toLowerCase()] || '';
this.terse = this.doctype.toLowerCase() == '';
this.xml = 0 == this.doctype.indexOf(' 1 && !escapePrettyMode &&
- block.nodes[0].type === 'Text' && block.nodes[1].type === 'Text' ) {
+ if (
+ pp &&
+ block.nodes.length > 1 &&
+ !escapePrettyMode &&
+ block.nodes[0].type === 'Text' &&
+ block.nodes[1].type === 'Text'
+ ) {
this.prettyIndent(1, true);
}
for (var i = 0; i < block.nodes.length; ++i) {
// Pretty print text
- if (pp && i > 0 && !escapePrettyMode &&
- block.nodes[i].type === 'Text' && block.nodes[i-1].type === 'Text' &&
- /\n$/.test(block.nodes[i - 1].val)) {
+ if (
+ pp &&
+ i > 0 &&
+ !escapePrettyMode &&
+ block.nodes[i].type === 'Text' &&
+ block.nodes[i - 1].type === 'Text' &&
+ /\n$/.test(block.nodes[i - 1].val)
+ ) {
this.prettyIndent(1, false);
}
this.visit(block.nodes[i], block);
@@ -388,10 +476,15 @@ Compiler.prototype = {
* @api public
*/
- visitMixinBlock: function(block){
- if (this.pp) this.buf.push("pug_indent.push('" + Array(this.indents + 1).join(this.pp) + "');");
+ visitMixinBlock: function(block) {
+ if (this.pp)
+ this.buf.push(
+ 'pug_indent.push(' +
+ stringify(Array(this.indents + 1).join(this.pp)) +
+ ');'
+ );
this.buf.push('block && block();');
- if (this.pp) this.buf.push("pug_indent.pop();");
+ if (this.pp) this.buf.push('pug_indent.pop();');
},
/**
@@ -403,7 +496,7 @@ Compiler.prototype = {
* @api public
*/
- visitDoctype: function(doctype){
+ visitDoctype: function(doctype) {
if (doctype && (doctype.val || !this.doctype)) {
this.setDoctype(doctype.val || 'html');
}
@@ -420,24 +513,31 @@ Compiler.prototype = {
* @api public
*/
- visitMixin: function(mixin){
+ visitMixin: function(mixin) {
var name = 'pug_mixins[';
var args = mixin.args || '';
var block = mixin.block;
var attrs = mixin.attrs;
var attrsBlocks = this.attributeBlocks(mixin.attributeBlocks);
var pp = this.pp;
- var dynamic = mixin.name[0]==='#';
+ var dynamic = mixin.name[0] === '#';
var key = mixin.name;
if (dynamic) this.dynamicMixins = true;
- name += (dynamic ? mixin.name.substr(2,mixin.name.length-3):'"'+mixin.name+'"')+']';
+ name +=
+ (dynamic
+ ? mixin.name.substr(2, mixin.name.length - 3)
+ : '"' + mixin.name + '"') + ']';
this.mixins[key] = this.mixins[key] || {used: false, instances: []};
if (mixin.call) {
this.mixins[key].used = true;
- if (pp) this.buf.push("pug_indent.push('" + Array(this.indents + 1).join(pp) + "');")
+ if (pp)
+ this.buf.push(
+ 'pug_indent.push(' +
+ stringify(Array(this.indents + 1).join(pp)) +
+ ');'
+ );
if (block || attrs.length || attrsBlocks.length) {
-
this.buf.push(name + '.call({');
if (block) {
@@ -464,7 +564,13 @@ Compiler.prototype = {
attrsBlocks.unshift(val);
}
if (attrsBlocks.length > 1) {
- this.buf.push('attributes: ' + this.runtime('merge') + '([' + attrsBlocks.join(',') + '])');
+ this.buf.push(
+ 'attributes: ' +
+ this.runtime('merge') +
+ '([' +
+ attrsBlocks.join(',') +
+ '])'
+ );
} else {
this.buf.push('attributes: ' + attrsBlocks[0]);
}
@@ -478,25 +584,33 @@ Compiler.prototype = {
} else {
this.buf.push('});');
}
-
} else {
this.buf.push(name + '(' + args + ');');
}
- if (pp) this.buf.push("pug_indent.pop();")
+ if (pp) this.buf.push('pug_indent.pop();');
} else {
var mixin_start = this.buf.length;
args = args ? args.split(',') : [];
var rest;
if (args.length && /^\.\.\./.test(args[args.length - 1].trim())) {
- rest = args.pop().trim().replace(/^\.\.\./, '');
+ rest = args
+ .pop()
+ .trim()
+ .replace(/^\.\.\./, '');
}
// we need use pug_interp here for v8: https://code.google.com/p/v8/issues/detail?id=4165
// once fixed, use this: this.buf.push(name + ' = function(' + args.join(',') + '){');
this.buf.push(name + ' = pug_interp = function(' + args.join(',') + '){');
- this.buf.push('var block = (this && this.block), attributes = (this && this.attributes) || {};');
+ this.buf.push(
+ 'var block = (this && this.block), attributes = (this && this.attributes) || {};'
+ );
if (rest) {
this.buf.push('var ' + rest + ' = [];');
- this.buf.push('for (pug_interp = ' + args.length + '; pug_interp < arguments.length; pug_interp++) {');
+ this.buf.push(
+ 'for (pug_interp = ' +
+ args.length +
+ '; pug_interp < arguments.length; pug_interp++) {'
+ );
this.buf.push(' ' + rest + '.push(arguments[pug_interp]);');
this.buf.push('}');
}
@@ -518,18 +632,19 @@ Compiler.prototype = {
* @api public
*/
- visitTag: function(tag, interpolated){
+ visitTag: function(tag, interpolated) {
this.indents++;
- var name = tag.name
- , pp = this.pp
- , self = this;
+ var name = tag.name,
+ pp = this.pp,
+ self = this;
function bufferName() {
if (interpolated) self.bufferExpression(tag.expr);
else self.buffer(name);
}
- if (WHITE_SPACE_SENSITIVE_TAGS[tag.name] === true) this.escapePrettyMode = true;
+ if (WHITE_SPACE_SENSITIVE_TAGS[tag.name] === true)
+ this.escapePrettyMode = true;
if (!this.hasCompiledTag) {
if (!this.hasCompiledDoctype && 'html' == name) {
@@ -539,37 +654,56 @@ Compiler.prototype = {
}
// pretty print
- if (pp && !tag.isInline)
- this.prettyIndent(0, true);
+ if (pp && !tag.isInline) this.prettyIndent(0, true);
if (tag.selfClosing || (!this.xml && selfClosing[tag.name])) {
this.buffer('<');
bufferName();
- this.visitAttributes(tag.attrs, this.attributeBlocks(tag.attributeBlocks));
+ this.visitAttributes(
+ tag.attrs,
+ this.attributeBlocks(tag.attributeBlocks)
+ );
if (this.terse && !tag.selfClosing) {
this.buffer('>');
} else {
this.buffer('/>');
}
// if it is non-empty throw an error
- if (tag.code ||
- tag.block &&
+ if (
+ tag.code ||
+ (tag.block &&
!(tag.block.type === 'Block' && tag.block.nodes.length === 0) &&
- tag.block.nodes.some(function (tag) {
- return tag.type !== 'Text' || !/^\s*$/.test(tag.val)
- })) {
- this.error(name + ' is a self closing element: <'+name+'/> but contains nested content.', 'SELF_CLOSING_CONTENT', tag);
+ tag.block.nodes.some(function(tag) {
+ return tag.type !== 'Text' || !/^\s*$/.test(tag.val);
+ }))
+ ) {
+ this.error(
+ name +
+ ' is a self closing element: <' +
+ name +
+ '/> but contains nested content.',
+ 'SELF_CLOSING_CONTENT',
+ tag
+ );
}
} else {
// Optimize attributes buffering
this.buffer('<');
bufferName();
- this.visitAttributes(tag.attrs, this.attributeBlocks(tag.attributeBlocks));
+ this.visitAttributes(
+ tag.attrs,
+ this.attributeBlocks(tag.attributeBlocks)
+ );
this.buffer('>');
if (tag.code) this.visitCode(tag.code);
this.visit(tag.block, tag);
// pretty print
- if (pp && !tag.isInline && WHITE_SPACE_SENSITIVE_TAGS[tag.name] !== true && !tagCanInline(tag))
+ if (
+ pp &&
+ !tag.isInline &&
+ WHITE_SPACE_SENSITIVE_TAGS[tag.name] !== true &&
+ !tagCanInline(tag)
+ )
this.prettyIndent(0, true);
this.buffer('');
@@ -577,7 +711,8 @@ Compiler.prototype = {
this.buffer('>');
}
- if (WHITE_SPACE_SENSITIVE_TAGS[tag.name] === true) this.escapePrettyMode = false;
+ if (WHITE_SPACE_SENSITIVE_TAGS[tag.name] === true)
+ this.escapePrettyMode = false;
this.indents--;
},
@@ -600,7 +735,7 @@ Compiler.prototype = {
* @api public
*/
- visitText: function(text){
+ visitText: function(text) {
this.buffer(text.val);
},
@@ -611,7 +746,7 @@ Compiler.prototype = {
* @api public
*/
- visitComment: function(comment){
+ visitComment: function(comment) {
if (!comment.buffer) return;
if (this.pp) this.prettyIndent(1, true);
this.buffer('');
@@ -635,7 +770,7 @@ Compiler.prototype = {
* @api public
*/
- visitBlockComment: function(comment){
+ visitBlockComment: function(comment) {
if (!comment.buffer) return;
if (this.pp) this.prettyIndent(1, true);
this.buffer('',
- 'bar
'
- ].join('');
+ var html = ['', 'bar
'].join('');
assert.equal(html, pug.render(str));
// Between tags
- var str = [
- 'p foo',
- '// bar ',
- 'p baz'
- ].join('\n');
+ var str = ['p foo', '// bar ', 'p baz'].join('\n');
- var html = [
- 'foo
',
- '',
- 'baz
'
- ].join('');
+ var html = ['foo
', '', 'baz
'].join('');
assert.equal(html, pug.render(str));
// Quotes
var str = "",
- js = "// script(src: '/js/validate.js') ";
+ js = "// script(src: '/js/validate.js') ";
assert.equal(str, pug.render(js));
});
- it('should support unbuffered comments', function(){
- var str = [
- '//- foo',
- 'p bar'
- ].join('\n');
+ it('should support unbuffered comments', function() {
+ var str = ['//- foo', 'p bar'].join('\n');
- var html = [
- 'bar
'
- ].join('');
+ var html = ['bar
'].join('');
assert.equal(html, pug.render(str));
- var str = [
- 'p foo',
- '//- bar ',
- 'p baz'
- ].join('\n');
+ var str = ['p foo', '//- bar ', 'p baz'].join('\n');
- var html = [
- 'foo
',
- 'baz
'
- ].join('');
+ var html = ['foo
', 'baz
'].join('');
assert.equal(html, pug.render(str));
});
- it('should support literal html', function(){
- assert.equal('', pug.render(''));
+ it('should support literal html', function() {
+ assert.equal(
+ '',
+ pug.render('')
+ );
});
- it('should support code', function(){
+ it('should support code', function() {
assert.equal('test', pug.render('!= "test"'));
assert.equal('test', pug.render('= "test"'));
assert.equal('test', pug.render('- var foo = "test"\n=foo'));
- assert.equal('footestbar', pug.render('- var foo = "test"\n| foo\nem= foo\n| bar'));
- assert.equal('testsomething
', pug.render('!= "test"\nh2 something'));
+ assert.equal(
+ 'footestbar',
+ pug.render('- var foo = "test"\n| foo\nem= foo\n| bar')
+ );
+ assert.equal(
+ 'testsomething
',
+ pug.render('!= "test"\nh2 something')
+ );
- var str = [
- '- var foo = "