commit
70192929ca
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Sid": "PublicRead",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": "*",
|
||||||
|
"Action": [
|
||||||
|
"s3:GetObject",
|
||||||
|
"s3:GetObjectVersion"
|
||||||
|
],
|
||||||
|
"Resource": "arn:aws:s3:::group7-code-bucket-73h3fdsa/songs/*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
var sqrl = require('squirrelly')
|
||||||
|
const fs = require('fs')
|
||||||
|
const AWS = require('aws-sdk');
|
||||||
|
const docClient = new AWS.DynamoDB.DocumentClient();
|
||||||
|
|
||||||
|
// let data = {
|
||||||
|
// songs: [
|
||||||
|
// {
|
||||||
|
// Title: "Well Enough",
|
||||||
|
// Artist: "Future Teens",
|
||||||
|
// File: "this.mp3"
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Title: "BYOB",
|
||||||
|
// Artist: "Future Teens",
|
||||||
|
// File: "that.mp3"
|
||||||
|
// },
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
TableName: 'MusicTable'
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.handler = async (e, ctx) => {
|
||||||
|
let scan = await docClient.scan(params).promise()
|
||||||
|
let data = {
|
||||||
|
songs: []
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
scan.Items.forEach((i) => data.songs.push(i))
|
||||||
|
params.ExclusiveStartKey = scan.LastEvaluatedKey
|
||||||
|
} while (typeof scan.LastEvaluatedKey != 'undefined')
|
||||||
|
console.log(e)
|
||||||
|
return response(sqrl.render(template, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
function response(html){
|
||||||
|
return {
|
||||||
|
"statusCode": 200,
|
||||||
|
"body": html,
|
||||||
|
"headers": {
|
||||||
|
"Content-Type": "text/html",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<h1>Illegal Music Site</h1>
|
||||||
|
<form>
|
||||||
|
<input type="text" placeholder="Title" />
|
||||||
|
<input type="text" placeholder="Artist" />
|
||||||
|
<input type="File" />
|
||||||
|
</form>
|
||||||
|
<div>
|
||||||
|
{{@each(it.songs) => s, i}}
|
||||||
|
<div class="song">
|
||||||
|
<p>{{s.Title}} - {{s.Artist}}</p>
|
||||||
|
<audio controls>
|
||||||
|
<source src="{{s.File}}" type="audio/mpeg">
|
||||||
|
</audio>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "musicserver",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"node_modules/squirrelly": {
|
||||||
|
"version": "8.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/squirrelly/-/squirrelly-8.0.8.tgz",
|
||||||
|
"integrity": "sha512-7dyZJ9Gw86MmH0dYLiESsjGOTj6KG8IWToTaqBuB6LwPI+hyNb6mbQaZwrfnAQ4cMDnSWMUvX/zAYDLTSWLk/w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/squirrellyjs/squirrelly?sponsor=1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
Copyright 2019 Ben Gubler <nebrelbug@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@ -0,0 +1,140 @@
|
|||||||
|
<a href="https://squirrelly.js.org"><img src="https://cdn.jsdelivr.net/gh/squirrellyjs/squirrelly-logo/svg-minified/squirrelly-fit-acorn.svg" align="right" width="30%" alt="Squirrel"></a>
|
||||||
|
|
||||||
|
# squirrelly
|
||||||
|
|
||||||
|
<p align="left">
|
||||||
|
<a href="https://squirrelly.js.org">Documentation</a> -
|
||||||
|
<a href="https://gitter.im/squirrellyjs/Lobby">Chat</a> -
|
||||||
|
<a href="https://npm.runkit.com/squirrelly">RunKit Demo</a> -
|
||||||
|
<a href="https://squirrelly.js.org/playground">Playground</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
|
[logo]: https://img.shields.io/badge/all_contributors-5-orange.svg 'Number of contributors on All-Contributors'
|
||||||
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
|
|
||||||
|

|
||||||
|
[](https://travis-ci.com/squirrellyjs/squirrelly)
|
||||||
|
[![All Contributors][logo]](#contributors-)
|
||||||
|
[](https://coveralls.io/github/squirrellyjs/squirrelly)
|
||||||
|
[](https://paypal.me/bengubler)
|
||||||
|
|
||||||
|
**Summary**
|
||||||
|
|
||||||
|
Squirrelly is a modern, configurable, and blazing fast template engine implemented in JavaScript. It works out of the box with ExpressJS and the **full version** weighs only **~4KB gzipped**.
|
||||||
|
|
||||||
|
**This is version 8** - a new, more powerful rewrite of Squirrelly. It adds multiple features (like filter parameters, whitespace control, partials, and template inheritance) to bring you a template engine with the power of Nunjucks, the simplicity of EJS, and the small bundle size of its earlier versions.
|
||||||
|
|
||||||
|
Squirrelly v7 will continue to be maintained, and can be found at https://github.com/squirrellyjs/squirrelly/tree/v7.
|
||||||
|
|
||||||
|
[Read about the changes](https://squirrelly.js.org/blog/squirrelly-version-8)
|
||||||
|
|
||||||
|
_Looking for a [lightweight, faster, and more reliable](https://eta.js.org/docs/about/eta-vs-ejs) **alternative to EJS?** Check out Squirrelly's cousin, [Eta](https://eta.js.org)._
|
||||||
|
|
||||||
|
## Why Squirrelly?
|
||||||
|
|
||||||
|
Simply put, Squirrelly is super lightweight, super fast, super powerful, and super simple.
|
||||||
|
|
||||||
|
### 🌟 Features
|
||||||
|
|
||||||
|
- 🔧 Helpers, filters
|
||||||
|
- 🔧 Great error reporting
|
||||||
|
- 📦 0 dependencies
|
||||||
|
- 🔨 Conditionals
|
||||||
|
- 🔧 Better quotes/comments support
|
||||||
|
- _ex. `{{someval + "name }}" }}`_ compiles correctly, while it fails with DoT or EJS
|
||||||
|
- ⚡️ Exports ES Modules as well as UMD
|
||||||
|
- 🔨 Loops
|
||||||
|
- 🔧 Custom delimeters
|
||||||
|
- 📝 Easy template syntax
|
||||||
|
- 🔧 Precompilation
|
||||||
|
- 🔨 Partials
|
||||||
|
- 🔧 Inline JavaScript
|
||||||
|
- 🔨 Comments
|
||||||
|
- 🔧 Caching
|
||||||
|
- 🚀 Super Fast
|
||||||
|
- Squirrelly [has been benchmarked](https://github.com/nebrelbug/squirrelly-benchmarks/tree/v8) against Marko, Pug, doT, Swig, Handlebars, Mustache, and Nunjucks. In each test, Squirrelly was fastest.
|
||||||
|
- ⚡️ Async support: async filters, helpers, partials
|
||||||
|
- 🔧 Template inheritance
|
||||||
|
|
||||||
|
## 📜 Docs
|
||||||
|
|
||||||
|
We know nobody reads through the long and boring documentation in the ReadMe anyway, so head over to the documentation website:
|
||||||
|
|
||||||
|
📝 [https://squirrelly.js.org](https://squirrelly.js.org)
|
||||||
|
|
||||||
|
## 📓 Examples
|
||||||
|
|
||||||
|
### Simple Template
|
||||||
|
|
||||||
|
```
|
||||||
|
var myTemplate = "<p>My favorite kind of cake is: {{it.favoriteCake}}</p>"
|
||||||
|
|
||||||
|
Sqrl.render(myTemplate, {favoriteCake: 'Chocolate!'})
|
||||||
|
// Returns: '<p>My favorite kind of cake is: Chocolate!</p>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conditionals
|
||||||
|
|
||||||
|
```
|
||||||
|
{{@if(it.somevalue === 1)}}
|
||||||
|
Display this
|
||||||
|
{{#else}}
|
||||||
|
Display this
|
||||||
|
{{/if}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Loops
|
||||||
|
|
||||||
|
```
|
||||||
|
{{@each(it.somearray) => val, index}}
|
||||||
|
Display this
|
||||||
|
The current array element is {{val}}
|
||||||
|
The current index is {{index}}
|
||||||
|
{{/each}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## ✔️ Tests
|
||||||
|
|
||||||
|
Tests can be run with `npm test`. Multiple tests check that parsing, rendering, and compiling return expected results, formatting follows guidelines, and code coverage is at the expected level.
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
To be added
|
||||||
|
|
||||||
|
## Projects using `squirrelly`
|
||||||
|
|
||||||
|
[Waiting for permissions]
|
||||||
|
|
||||||
|
- [Cypress](https://www.cypress.io/): Fast, easy and reliable testing for anything that runs in a browser
|
||||||
|
- [txAdmin](https://github.com/tabarra/txAdmin): A **full featured** web panel to manage & monitor your FiveM Server remotely, used by over two thousand servers worldwide
|
||||||
|
- [Add yours!](https://github.com/squirrellyjs/squirrelly/edit/master/README.md)
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
Made with ❤ by [@nebrelbug](https://github.com/nebrelbug) and all these wonderful contributors ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
<!-- markdownlint-disable -->
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="http://www.bengubler.com"><img src="https://avatars3.githubusercontent.com/u/25597854?v=4" width="100px;" alt=""/><br /><sub><b>Ben Gubler</b></sub></a><br /><a href="https://github.com/squirrellyjs/squirrelly/commits?author=nebrelbug" title="Code">💻</a> <a href="#question-nebrelbug" title="Answering Questions">💬</a> <a href="https://github.com/squirrellyjs/squirrelly/commits?author=nebrelbug" title="Documentation">📖</a> <a href="https://github.com/squirrellyjs/squirrelly/commits?author=nebrelbug" title="Tests">⚠️</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/clitetailor"><img src="https://avatars1.githubusercontent.com/u/16368559?v=4" width="100px;" alt=""/><br /><sub><b>Clite Tailor</b></sub></a><br /><a href="#ideas-clitetailor" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/squirrellyjs/squirrelly/commits?author=clitetailor" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://twitter.com/ioan_chiriac"><img src="https://avatars2.githubusercontent.com/u/173203?v=4" width="100px;" alt=""/><br /><sub><b>Ioan CHIRIAC</b></sub></a><br /><a href="https://github.com/squirrellyjs/squirrelly/commits?author=ichiriac" title="Code">💻</a> <a href="#ideas-ichiriac" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
<td align="center"><a href="https://futurelucas4502.co.uk"><img src="https://avatars1.githubusercontent.com/u/48055553?v=4" width="100px;" alt=""/><br /><sub><b>Lucas Wilson</b></sub></a><br /><a href="https://github.com/squirrellyjs/squirrelly/issues?q=author%3Afuturelucas4502" title="Bug reports">🐛</a> <a href="https://github.com/squirrellyjs/squirrelly/commits?author=futurelucas4502" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/jmclean-cnexus"><img src="https://avatars3.githubusercontent.com/u/64215359?v=4" width="100px;" alt=""/><br /><sub><b>Jon McLean</b></sub></a><br /><a href="https://github.com/squirrellyjs/squirrelly/commits?author=jmclean-cnexus" title="Code">💻</a> <a href="https://github.com/squirrellyjs/squirrelly/commits?author=jmclean-cnexus" title="Tests">⚠️</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- markdownlint-enable -->
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||||
|
|
||||||
|
_Note: because we completely rewrote Version 8 and it has a separate Git history, this chart excludes the dozens of contributors to Version 7. Their contributions are deeply appreciated and many of their ideas and code contributions are being used in Squirrelly v8. Many of their contributions can be found in the [v7 branch commit history](https://github.com/squirrellyjs/squirrelly/commits/v7)._
|
||||||
|
|
||||||
|
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind are welcome!
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- Async support and file handling were added based on code from [EJS](https://github.com/mde/ejs), which is licensed under the Apache-2.0 license. Code was modified to throw informative errors and work with Squirrelly's API
|
||||||
@ -0,0 +1,964 @@
|
|||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||||
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||||
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Sqrl = {}));
|
||||||
|
}(this, (function (exports) { 'use strict';
|
||||||
|
|
||||||
|
function setPrototypeOf(obj, proto) {
|
||||||
|
if (Object.setPrototypeOf) {
|
||||||
|
Object.setPrototypeOf(obj, proto);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
obj.__proto__ = proto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function SqrlErr(message) {
|
||||||
|
var err = new Error(message);
|
||||||
|
setPrototypeOf(err, SqrlErr.prototype);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
SqrlErr.prototype = Object.create(Error.prototype, {
|
||||||
|
name: { value: 'Squirrelly Error', enumerable: false }
|
||||||
|
});
|
||||||
|
// TODO: Class transpilation adds a lot to the bundle size
|
||||||
|
function ParseErr(message, str, indx) {
|
||||||
|
var whitespace = str.slice(0, indx).split(/\n/);
|
||||||
|
var lineNo = whitespace.length;
|
||||||
|
var colNo = whitespace[lineNo - 1].length + 1;
|
||||||
|
message +=
|
||||||
|
' at line ' +
|
||||||
|
lineNo +
|
||||||
|
' col ' +
|
||||||
|
colNo +
|
||||||
|
':\n\n' +
|
||||||
|
' ' +
|
||||||
|
str.split(/\n/)[lineNo - 1] +
|
||||||
|
'\n' +
|
||||||
|
' ' +
|
||||||
|
Array(colNo).join(' ') +
|
||||||
|
'^';
|
||||||
|
throw SqrlErr(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: allow '-' to trim up until newline. Use [^\S\n\r] instead of \s
|
||||||
|
// TODO: only include trimLeft polyfill if not in ES6
|
||||||
|
/* END TYPES */
|
||||||
|
var promiseImpl = new Function('return this')().Promise;
|
||||||
|
var asyncFunc = false;
|
||||||
|
try {
|
||||||
|
asyncFunc = new Function('return (async function(){}).constructor')();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
// We shouldn't actually ever have any other errors, but...
|
||||||
|
if (!(e instanceof SyntaxError)) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function hasOwnProp(obj, prop) {
|
||||||
|
return Object.prototype.hasOwnProperty.call(obj, prop);
|
||||||
|
}
|
||||||
|
function copyProps(toObj, fromObj, notConfig) {
|
||||||
|
for (var key in fromObj) {
|
||||||
|
if (hasOwnProp(fromObj, key)) {
|
||||||
|
if (fromObj[key] != null &&
|
||||||
|
typeof fromObj[key] == 'object' &&
|
||||||
|
(key === 'storage' || key === 'prefixes') &&
|
||||||
|
!notConfig // not called from Cache.load
|
||||||
|
) {
|
||||||
|
// plugins or storage
|
||||||
|
// Note: this doesn't merge from initial config!
|
||||||
|
// Deep clone instead of assigning
|
||||||
|
// TODO: run checks on this
|
||||||
|
toObj[key] = copyProps(/*toObj[key] ||*/ {}, fromObj[key]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toObj[key] = fromObj[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toObj;
|
||||||
|
}
|
||||||
|
function trimWS(str, env, wsLeft, wsRight) {
|
||||||
|
var leftTrim;
|
||||||
|
var rightTrim;
|
||||||
|
if (typeof env.autoTrim === 'string') {
|
||||||
|
leftTrim = rightTrim = env.autoTrim;
|
||||||
|
// Don't need to check if env.autoTrim is false
|
||||||
|
// Because leftTrim, rightTrim are initialized as falsy
|
||||||
|
}
|
||||||
|
else if (Array.isArray(env.autoTrim)) {
|
||||||
|
// kinda confusing
|
||||||
|
// but _}} will trim the left side of the following string
|
||||||
|
leftTrim = env.autoTrim[1];
|
||||||
|
rightTrim = env.autoTrim[0];
|
||||||
|
}
|
||||||
|
if (wsLeft || wsLeft === false) {
|
||||||
|
leftTrim = wsLeft;
|
||||||
|
}
|
||||||
|
if (wsRight || wsRight === false) {
|
||||||
|
rightTrim = wsRight;
|
||||||
|
}
|
||||||
|
if (leftTrim === 'slurp' && rightTrim === 'slurp') {
|
||||||
|
return str.trim();
|
||||||
|
}
|
||||||
|
if (leftTrim === '_' || leftTrim === 'slurp') {
|
||||||
|
// console.log('trimming left' + leftTrim)
|
||||||
|
// full slurp
|
||||||
|
// eslint-disable-next-line no-extra-boolean-cast
|
||||||
|
if (!!String.prototype.trimLeft) {
|
||||||
|
str = str.trimLeft();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
str = str.replace(/^[\s\uFEFF\xA0]+/, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (leftTrim === '-' || leftTrim === 'nl') {
|
||||||
|
// console.log('trimming left nl' + leftTrim)
|
||||||
|
// nl trim
|
||||||
|
str = str.replace(/^(?:\n|\r|\r\n)/, '');
|
||||||
|
}
|
||||||
|
if (rightTrim === '_' || rightTrim === 'slurp') {
|
||||||
|
// console.log('trimming right' + rightTrim)
|
||||||
|
// full slurp
|
||||||
|
// eslint-disable-next-line no-extra-boolean-cast
|
||||||
|
if (!!String.prototype.trimRight) {
|
||||||
|
str = str.trimRight();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
str = str.replace(/[\s\uFEFF\xA0]+$/, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rightTrim === '-' || rightTrim === 'nl') {
|
||||||
|
// console.log('trimming right nl' + rightTrim)
|
||||||
|
// nl trim
|
||||||
|
str = str.replace(/(?:\n|\r|\r\n)$/, ''); // TODO: make sure this gets \r\n
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* END TYPES */
|
||||||
|
var asyncRegExp = /^async +/;
|
||||||
|
var templateLitReg = /`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})*}|(?!\${)[^\\`])*`/g;
|
||||||
|
var singleQuoteReg = /'(?:\\[\s\w"'\\`]|[^\n\r'\\])*?'/g;
|
||||||
|
var doubleQuoteReg = /"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g;
|
||||||
|
var specialCharsReg = /[.*+\-?^${}()|[\]\\]/g;
|
||||||
|
function escapeRegExp(string) {
|
||||||
|
// From MDN
|
||||||
|
return specialCharsReg.test(string)
|
||||||
|
? string.replace(specialCharsReg, '\\$&') // $& means the whole matched string
|
||||||
|
: string;
|
||||||
|
}
|
||||||
|
function parse(str, env) {
|
||||||
|
/* Adding for EJS compatibility */
|
||||||
|
if (env.rmWhitespace) {
|
||||||
|
// Code taken directly from EJS
|
||||||
|
// Have to use two separate replaces here as `^` and `$` operators don't
|
||||||
|
// work well with `\r` and empty lines don't work well with the `m` flag.
|
||||||
|
// Essentially, this replaces the whitespace at the beginning and end of
|
||||||
|
// each line and removes multiple newlines.
|
||||||
|
str = str.replace(/[\r\n]+/g, '\n').replace(/^\s+|\s+$/gm, '');
|
||||||
|
}
|
||||||
|
/* End rmWhitespace option */
|
||||||
|
templateLitReg.lastIndex = 0;
|
||||||
|
singleQuoteReg.lastIndex = 0;
|
||||||
|
doubleQuoteReg.lastIndex = 0;
|
||||||
|
var envPrefixes = env.prefixes;
|
||||||
|
var prefixes = [
|
||||||
|
envPrefixes.h,
|
||||||
|
envPrefixes.b,
|
||||||
|
envPrefixes.i,
|
||||||
|
envPrefixes.r,
|
||||||
|
envPrefixes.c,
|
||||||
|
envPrefixes.e
|
||||||
|
].reduce(function (accumulator, prefix) {
|
||||||
|
if (accumulator && prefix) {
|
||||||
|
return accumulator + '|' + escapeRegExp(prefix);
|
||||||
|
}
|
||||||
|
else if (prefix) {
|
||||||
|
// accumulator is empty
|
||||||
|
return escapeRegExp(prefix);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// prefix and accumulator are both empty strings
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
}, '');
|
||||||
|
var parseCloseReg = new RegExp('([|()]|=>)|' + // powerchars
|
||||||
|
'(\'|"|`|\\/\\*)|\\s*((\\/)?(-|_)?' + // comments, strings
|
||||||
|
escapeRegExp(env.tags[1]) +
|
||||||
|
')', 'g');
|
||||||
|
var tagOpenReg = new RegExp('([^]*?)' + escapeRegExp(env.tags[0]) + '(-|_)?\\s*(' + prefixes + ')?\\s*', 'g');
|
||||||
|
var startInd = 0;
|
||||||
|
var trimNextLeftWs = false;
|
||||||
|
function parseTag(tagOpenIndex, currentType) {
|
||||||
|
var currentObj = { f: [] };
|
||||||
|
var numParens = 0;
|
||||||
|
var currentAttribute = 'c'; // default - Valid values: 'c'=content, 'f'=filter, 'fp'=filter params, 'p'=param, 'n'=name
|
||||||
|
if (currentType === 'h' || currentType === 'b' || currentType === 'c') {
|
||||||
|
currentAttribute = 'n';
|
||||||
|
}
|
||||||
|
else if (currentType === 'r') {
|
||||||
|
currentObj.raw = true;
|
||||||
|
currentType = 'i';
|
||||||
|
}
|
||||||
|
function addAttrValue(indx) {
|
||||||
|
var valUnprocessed = str.slice(startInd, indx);
|
||||||
|
// console.log(valUnprocessed)
|
||||||
|
var val = valUnprocessed.trim();
|
||||||
|
if (currentAttribute === 'f') {
|
||||||
|
if (val === 'safe') {
|
||||||
|
currentObj.raw = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (env.async && asyncRegExp.test(val)) {
|
||||||
|
val = val.replace(asyncRegExp, '');
|
||||||
|
currentObj.f.push([val, '', true]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentObj.f.push([val, '']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (currentAttribute === 'fp') {
|
||||||
|
currentObj.f[currentObj.f.length - 1][1] += val;
|
||||||
|
}
|
||||||
|
else if (currentAttribute === 'err') {
|
||||||
|
if (val) {
|
||||||
|
var found = valUnprocessed.search(/\S/);
|
||||||
|
ParseErr('invalid syntax', str, startInd + found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// if (currentObj[currentAttribute]) { // TODO make sure no errs
|
||||||
|
// currentObj[currentAttribute] += val
|
||||||
|
// } else {
|
||||||
|
currentObj[currentAttribute] = val;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
startInd = indx + 1;
|
||||||
|
}
|
||||||
|
parseCloseReg.lastIndex = startInd;
|
||||||
|
var m;
|
||||||
|
// tslint:disable-next-line:no-conditional-assignment
|
||||||
|
while ((m = parseCloseReg.exec(str)) !== null) {
|
||||||
|
var char = m[1];
|
||||||
|
var punctuator = m[2];
|
||||||
|
var tagClose = m[3];
|
||||||
|
var slash = m[4];
|
||||||
|
var wsControl = m[5];
|
||||||
|
var i = m.index;
|
||||||
|
if (char) {
|
||||||
|
// Power character
|
||||||
|
if (char === '(') {
|
||||||
|
if (numParens === 0) {
|
||||||
|
if (currentAttribute === 'n') {
|
||||||
|
addAttrValue(i);
|
||||||
|
currentAttribute = 'p';
|
||||||
|
}
|
||||||
|
else if (currentAttribute === 'f') {
|
||||||
|
addAttrValue(i);
|
||||||
|
currentAttribute = 'fp';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numParens++;
|
||||||
|
}
|
||||||
|
else if (char === ')') {
|
||||||
|
numParens--;
|
||||||
|
if (numParens === 0 && currentAttribute !== 'c') {
|
||||||
|
// Then it's closing a filter, block, or helper
|
||||||
|
addAttrValue(i);
|
||||||
|
currentAttribute = 'err'; // Reset the current attribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (numParens === 0 && char === '|') {
|
||||||
|
addAttrValue(i); // this should actually always be whitespace or empty
|
||||||
|
currentAttribute = 'f';
|
||||||
|
}
|
||||||
|
else if (char === '=>') {
|
||||||
|
addAttrValue(i);
|
||||||
|
startInd += 1; // this is 2 chars
|
||||||
|
currentAttribute = 'res';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (punctuator) {
|
||||||
|
if (punctuator === '/*') {
|
||||||
|
var commentCloseInd = str.indexOf('*/', parseCloseReg.lastIndex);
|
||||||
|
if (commentCloseInd === -1) {
|
||||||
|
ParseErr('unclosed comment', str, m.index);
|
||||||
|
}
|
||||||
|
parseCloseReg.lastIndex = commentCloseInd + 2; // since */ is 2 characters, and we're using indexOf rather than a RegExp
|
||||||
|
}
|
||||||
|
else if (punctuator === "'") {
|
||||||
|
singleQuoteReg.lastIndex = m.index;
|
||||||
|
var singleQuoteMatch = singleQuoteReg.exec(str);
|
||||||
|
if (singleQuoteMatch) {
|
||||||
|
parseCloseReg.lastIndex = singleQuoteReg.lastIndex;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ParseErr('unclosed string', str, m.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (punctuator === '"') {
|
||||||
|
doubleQuoteReg.lastIndex = m.index;
|
||||||
|
var doubleQuoteMatch = doubleQuoteReg.exec(str);
|
||||||
|
if (doubleQuoteMatch) {
|
||||||
|
parseCloseReg.lastIndex = doubleQuoteReg.lastIndex;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ParseErr('unclosed string', str, m.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (punctuator === '`') {
|
||||||
|
templateLitReg.lastIndex = m.index;
|
||||||
|
var templateLitMatch = templateLitReg.exec(str);
|
||||||
|
if (templateLitMatch) {
|
||||||
|
parseCloseReg.lastIndex = templateLitReg.lastIndex;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ParseErr('unclosed string', str, m.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tagClose) {
|
||||||
|
addAttrValue(i);
|
||||||
|
startInd = i + m[0].length;
|
||||||
|
tagOpenReg.lastIndex = startInd;
|
||||||
|
// console.log('tagClose: ' + startInd)
|
||||||
|
trimNextLeftWs = wsControl;
|
||||||
|
if (slash && currentType === 'h') {
|
||||||
|
currentType = 's';
|
||||||
|
} // TODO throw err
|
||||||
|
currentObj.t = currentType;
|
||||||
|
return currentObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseErr('unclosed tag', str, tagOpenIndex);
|
||||||
|
return currentObj; // To prevent TypeScript from erroring
|
||||||
|
}
|
||||||
|
function parseContext(parentObj, firstParse) {
|
||||||
|
parentObj.b = []; // assume there will be blocks // TODO: perf optimize this
|
||||||
|
parentObj.d = [];
|
||||||
|
var lastBlock = false;
|
||||||
|
var buffer = [];
|
||||||
|
function pushString(strng, shouldTrimRightOfString) {
|
||||||
|
if (strng) {
|
||||||
|
// if string is truthy it must be of type 'string'
|
||||||
|
// TODO: benchmark replace( /(\\|')/g, '\\$1')
|
||||||
|
strng = trimWS(strng, env, trimNextLeftWs, // this will only be false on the first str, the next ones will be null or undefined
|
||||||
|
shouldTrimRightOfString);
|
||||||
|
if (strng) {
|
||||||
|
// replace \ with \\, ' with \'
|
||||||
|
strng = strng.replace(/\\|'/g, '\\$&').replace(/\r\n|\n|\r/g, '\\n');
|
||||||
|
// we're going to convert all CRLF to LF so it doesn't take more than one replace
|
||||||
|
buffer.push(strng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Random TODO: parentObj.b doesn't need to have t: #
|
||||||
|
var tagOpenMatch;
|
||||||
|
// tslint:disable-next-line:no-conditional-assignment
|
||||||
|
while ((tagOpenMatch = tagOpenReg.exec(str)) !== null) {
|
||||||
|
var precedingString = tagOpenMatch[1];
|
||||||
|
var shouldTrimRightPrecedingString = tagOpenMatch[2];
|
||||||
|
var prefix = tagOpenMatch[3] || '';
|
||||||
|
var prefixType;
|
||||||
|
for (var key in envPrefixes) {
|
||||||
|
if (envPrefixes[key] === prefix) {
|
||||||
|
prefixType = key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pushString(precedingString, shouldTrimRightPrecedingString);
|
||||||
|
startInd = tagOpenMatch.index + tagOpenMatch[0].length;
|
||||||
|
if (!prefixType) {
|
||||||
|
ParseErr('unrecognized tag type: ' + prefix, str, startInd);
|
||||||
|
}
|
||||||
|
var currentObj = parseTag(tagOpenMatch.index, prefixType);
|
||||||
|
// ===== NOW ADD THE OBJECT TO OUR BUFFER =====
|
||||||
|
var currentType = currentObj.t;
|
||||||
|
if (currentType === 'h') {
|
||||||
|
var hName = currentObj.n || '';
|
||||||
|
if (env.async && asyncRegExp.test(hName)) {
|
||||||
|
currentObj.a = true;
|
||||||
|
currentObj.n = hName.replace(asyncRegExp, '');
|
||||||
|
}
|
||||||
|
currentObj = parseContext(currentObj); // currentObj is the parent object
|
||||||
|
buffer.push(currentObj);
|
||||||
|
}
|
||||||
|
else if (currentType === 'c') {
|
||||||
|
// tag close
|
||||||
|
if (parentObj.n === currentObj.n) {
|
||||||
|
if (lastBlock) {
|
||||||
|
// If there's a previous block
|
||||||
|
lastBlock.d = buffer;
|
||||||
|
parentObj.b.push(lastBlock);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentObj.d = buffer;
|
||||||
|
}
|
||||||
|
// console.log('parentObj: ' + JSON.stringify(parentObj))
|
||||||
|
return parentObj;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ParseErr("Helper start and end don't match", str, tagOpenMatch.index + tagOpenMatch[0].length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (currentType === 'b') {
|
||||||
|
// block
|
||||||
|
// TODO: make sure async stuff inside blocks are recognized
|
||||||
|
if (lastBlock) {
|
||||||
|
// If there's a previous block
|
||||||
|
lastBlock.d = buffer;
|
||||||
|
parentObj.b.push(lastBlock);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentObj.d = buffer;
|
||||||
|
}
|
||||||
|
var blockName = currentObj.n || '';
|
||||||
|
if (env.async && asyncRegExp.test(blockName)) {
|
||||||
|
currentObj.a = true;
|
||||||
|
currentObj.n = blockName.replace(asyncRegExp, '');
|
||||||
|
}
|
||||||
|
lastBlock = currentObj; // Set the 'lastBlock' object to the value of the current block
|
||||||
|
buffer = [];
|
||||||
|
}
|
||||||
|
else if (currentType === 's') {
|
||||||
|
var selfClosingHName = currentObj.n || '';
|
||||||
|
if (env.async && asyncRegExp.test(selfClosingHName)) {
|
||||||
|
currentObj.a = true;
|
||||||
|
currentObj.n = selfClosingHName.replace(asyncRegExp, '');
|
||||||
|
}
|
||||||
|
buffer.push(currentObj);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer.push(currentObj);
|
||||||
|
}
|
||||||
|
// ===== DONE ADDING OBJECT TO BUFFER =====
|
||||||
|
}
|
||||||
|
if (firstParse) {
|
||||||
|
pushString(str.slice(startInd, str.length), false);
|
||||||
|
parentObj.d = buffer;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw SqrlErr('unclosed helper "' + parentObj.n + '"');
|
||||||
|
// It should have returned by now
|
||||||
|
}
|
||||||
|
return parentObj;
|
||||||
|
}
|
||||||
|
var parseResult = parseContext({ f: [] }, true);
|
||||||
|
// console.log(JSON.stringify(parseResult))
|
||||||
|
if (env.plugins) {
|
||||||
|
for (var i = 0; i < env.plugins.length; i++) {
|
||||||
|
var plugin = env.plugins[i];
|
||||||
|
if (plugin.processAST) {
|
||||||
|
parseResult.d = plugin.processAST(parseResult.d, env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parseResult.d; // Parse the very outside context
|
||||||
|
}
|
||||||
|
|
||||||
|
// import SqrlErr from './err'
|
||||||
|
/* END TYPES */
|
||||||
|
function compileToString(str, env) {
|
||||||
|
var buffer = parse(str, env);
|
||||||
|
var res = "var tR='';" +
|
||||||
|
(env.useWith ? 'with(' + env.varName + '||{}){' : '') +
|
||||||
|
compileScope(buffer, env) +
|
||||||
|
'if(cb){cb(null,tR)} return tR' +
|
||||||
|
(env.useWith ? '}' : '');
|
||||||
|
if (env.plugins) {
|
||||||
|
for (var i = 0; i < env.plugins.length; i++) {
|
||||||
|
var plugin = env.plugins[i];
|
||||||
|
if (plugin.processFnString) {
|
||||||
|
res = plugin.processFnString(res, env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
// TODO: is `return cb()` necessary, or could we just do `cb()`
|
||||||
|
}
|
||||||
|
function filter(str, filters) {
|
||||||
|
for (var i = 0; i < filters.length; i++) {
|
||||||
|
var name = filters[i][0];
|
||||||
|
var params = filters[i][1];
|
||||||
|
var isFilterAsync = filters[i][2];
|
||||||
|
// if (isFilterAsync && !env.async) {
|
||||||
|
// throw SqrlErr("Async filter '" + name + "' in non-async env")
|
||||||
|
// }
|
||||||
|
// Let the JS compiler do this, compile() will catch it
|
||||||
|
str = (isFilterAsync ? 'await ' : '') + "c.l('F','" + name + "')(" + str;
|
||||||
|
if (params) {
|
||||||
|
str += ',' + params;
|
||||||
|
}
|
||||||
|
str += ')';
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
// TODO: Use type intersections for TemplateObject, etc.
|
||||||
|
// so I don't have to make properties mandatory
|
||||||
|
function compileHelper(env, res, descendants, params, isAsync, name) {
|
||||||
|
var ret = '{exec:' +
|
||||||
|
(isAsync ? 'async ' : '') +
|
||||||
|
compileScopeIntoFunction(descendants, res, env) +
|
||||||
|
',params:[' +
|
||||||
|
params +
|
||||||
|
']';
|
||||||
|
if (name) {
|
||||||
|
ret += ",name:'" + name + "'";
|
||||||
|
}
|
||||||
|
if (isAsync) {
|
||||||
|
ret += ',async:true';
|
||||||
|
}
|
||||||
|
ret += '}';
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
function compileBlocks(blocks, env) {
|
||||||
|
var ret = '[';
|
||||||
|
for (var i = 0; i < blocks.length; i++) {
|
||||||
|
var block = blocks[i];
|
||||||
|
ret += compileHelper(env, block.res || '', block.d, block.p || '', block.a, block.n);
|
||||||
|
if (i < blocks.length) {
|
||||||
|
ret += ',';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret += ']';
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
function compileScopeIntoFunction(buff, res, env) {
|
||||||
|
return 'function(' + res + "){var tR='';" + compileScope(buff, env) + 'return tR}';
|
||||||
|
}
|
||||||
|
function compileScope(buff, env) {
|
||||||
|
var i = 0;
|
||||||
|
var buffLength = buff.length;
|
||||||
|
var returnStr = '';
|
||||||
|
for (i; i < buffLength; i++) {
|
||||||
|
var currentBlock = buff[i];
|
||||||
|
if (typeof currentBlock === 'string') {
|
||||||
|
var str = currentBlock;
|
||||||
|
// we know string exists
|
||||||
|
returnStr += "tR+='" + str + "';";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var type = currentBlock.t; // h, s, e, i
|
||||||
|
var content = currentBlock.c || '';
|
||||||
|
var filters = currentBlock.f;
|
||||||
|
var name = currentBlock.n || '';
|
||||||
|
var params = currentBlock.p || '';
|
||||||
|
var res = currentBlock.res || '';
|
||||||
|
var blocks = currentBlock.b;
|
||||||
|
var isAsync = !!currentBlock.a; // !! is to booleanize it
|
||||||
|
// if (isAsync && !env.async) {
|
||||||
|
// throw SqrlErr("Async block or helper '" + name + "' in non-async env")
|
||||||
|
// }
|
||||||
|
// Let compiler do this
|
||||||
|
if (type === 'i') {
|
||||||
|
if (env.defaultFilter) {
|
||||||
|
content = "c.l('F','" + env.defaultFilter + "')(" + content + ')';
|
||||||
|
}
|
||||||
|
var filtered = filter(content, filters);
|
||||||
|
if (!currentBlock.raw && env.autoEscape) {
|
||||||
|
filtered = "c.l('F','e')(" + filtered + ')';
|
||||||
|
}
|
||||||
|
returnStr += 'tR+=' + filtered + ';';
|
||||||
|
// reference
|
||||||
|
}
|
||||||
|
else if (type === 'h') {
|
||||||
|
// helper
|
||||||
|
if (env.storage.nativeHelpers.get(name)) {
|
||||||
|
returnStr += env.storage.nativeHelpers.get(name)(currentBlock, env);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var helperReturn = (isAsync ? 'await ' : '') +
|
||||||
|
"c.l('H','" +
|
||||||
|
name +
|
||||||
|
"')(" +
|
||||||
|
compileHelper(env, res, currentBlock.d, params, isAsync);
|
||||||
|
if (blocks) {
|
||||||
|
helperReturn += ',' + compileBlocks(blocks, env);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
helperReturn += ',[]';
|
||||||
|
}
|
||||||
|
helperReturn += ',c)';
|
||||||
|
returnStr += 'tR+=' + filter(helperReturn, filters) + ';';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type === 's') {
|
||||||
|
// self-closing helper
|
||||||
|
returnStr +=
|
||||||
|
'tR+=' +
|
||||||
|
filter((isAsync ? 'await ' : '') + "c.l('H','" + name + "')({params:[" + params + ']},[],c)', filters) +
|
||||||
|
';';
|
||||||
|
}
|
||||||
|
else if (type === 'e') {
|
||||||
|
// execute
|
||||||
|
returnStr += content + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* END TYPES */
|
||||||
|
var Cacher = /** @class */ (function () {
|
||||||
|
function Cacher(cache) {
|
||||||
|
this.cache = cache;
|
||||||
|
}
|
||||||
|
Cacher.prototype.define = function (key, val) {
|
||||||
|
this.cache[key] = val;
|
||||||
|
};
|
||||||
|
Cacher.prototype.get = function (key) {
|
||||||
|
// string | array.
|
||||||
|
// TODO: allow array of keys to look down
|
||||||
|
// TODO: create plugin to allow referencing helpers, filters with dot notation
|
||||||
|
return this.cache[key];
|
||||||
|
};
|
||||||
|
Cacher.prototype.remove = function (key) {
|
||||||
|
delete this.cache[key];
|
||||||
|
};
|
||||||
|
Cacher.prototype.reset = function () {
|
||||||
|
this.cache = {};
|
||||||
|
};
|
||||||
|
Cacher.prototype.load = function (cacheObj) {
|
||||||
|
// TODO: this will err with deep objects and `storage` or `plugins` keys.
|
||||||
|
// Update Feb 26: EDITED so it shouldn't err
|
||||||
|
copyProps(this.cache, cacheObj, true);
|
||||||
|
};
|
||||||
|
return Cacher;
|
||||||
|
}());
|
||||||
|
|
||||||
|
function errWithBlocksOrFilters(name, blocks, // false means don't check
|
||||||
|
filters, native) {
|
||||||
|
if (blocks && blocks.length > 0) {
|
||||||
|
throw SqrlErr((native ? 'Native' : '') + "Helper '" + name + "' doesn't accept blocks");
|
||||||
|
}
|
||||||
|
if (filters && filters.length > 0) {
|
||||||
|
throw SqrlErr((native ? 'Native' : '') + "Helper '" + name + "' doesn't accept filters");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* ASYNC LOOP FNs */
|
||||||
|
function asyncArrLoop(arr, index, fn, res, cb) {
|
||||||
|
fn(arr[index], index).then(function (val) {
|
||||||
|
res += val;
|
||||||
|
if (index === arr.length - 1) {
|
||||||
|
cb(res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
asyncArrLoop(arr, index + 1, fn, res, cb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function asyncObjLoop(obj, keys, index, fn, res, cb) {
|
||||||
|
fn(keys[index], obj[keys[index]]).then(function (val) {
|
||||||
|
res += val;
|
||||||
|
if (index === keys.length - 1) {
|
||||||
|
cb(res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
asyncObjLoop(obj, keys, index + 1, fn, res, cb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var escMap = {
|
||||||
|
'&': '&',
|
||||||
|
'<': '<',
|
||||||
|
'>': '>',
|
||||||
|
'"': '"',
|
||||||
|
"'": ''',
|
||||||
|
};
|
||||||
|
function replaceChar(s) {
|
||||||
|
return escMap[s];
|
||||||
|
}
|
||||||
|
function XMLEscape(str) {
|
||||||
|
// To deal with XSS. Based on Escape implementations of Mustache.JS and Marko, then customized.
|
||||||
|
var newStr = String(str);
|
||||||
|
if (/[&<>"']/.test(newStr)) {
|
||||||
|
return newStr.replace(/[&<>"']/g, replaceChar);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return newStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* END TYPES */
|
||||||
|
var templates = new Cacher({});
|
||||||
|
/* ASYNC LOOP FNs */
|
||||||
|
var helpers = new Cacher({
|
||||||
|
each: function (content, blocks) {
|
||||||
|
var res = '';
|
||||||
|
var arr = content.params[0];
|
||||||
|
errWithBlocksOrFilters('each', blocks, false);
|
||||||
|
if (content.async) {
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
asyncArrLoop(arr, 0, content.exec, res, resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i = 0; i < arr.length; i++) {
|
||||||
|
res += content.exec(arr[i], i);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
foreach: function (content, blocks) {
|
||||||
|
var obj = content.params[0];
|
||||||
|
errWithBlocksOrFilters('foreach', blocks, false);
|
||||||
|
if (content.async) {
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
asyncObjLoop(obj, Object.keys(obj), 0, content.exec, '', resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var res = '';
|
||||||
|
for (var key in obj) {
|
||||||
|
if (!hasOwnProp(obj, key))
|
||||||
|
continue;
|
||||||
|
res += content.exec(key, obj[key]); // todo: check on order
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
include: function (content, blocks, config) {
|
||||||
|
errWithBlocksOrFilters('include', blocks, false);
|
||||||
|
var template = config.storage.templates.get(content.params[0]);
|
||||||
|
if (!template) {
|
||||||
|
throw SqrlErr('Could not fetch template "' + content.params[0] + '"');
|
||||||
|
}
|
||||||
|
return template(content.params[1], config);
|
||||||
|
},
|
||||||
|
extends: function (content, blocks, config) {
|
||||||
|
var data = content.params[1] || {};
|
||||||
|
data.content = content.exec();
|
||||||
|
for (var i = 0; i < blocks.length; i++) {
|
||||||
|
var currentBlock = blocks[i];
|
||||||
|
data[currentBlock.name] = currentBlock.exec();
|
||||||
|
}
|
||||||
|
var template = config.storage.templates.get(content.params[0]);
|
||||||
|
if (!template) {
|
||||||
|
throw SqrlErr('Could not fetch template "' + content.params[0] + '"');
|
||||||
|
}
|
||||||
|
return template(data, config);
|
||||||
|
},
|
||||||
|
useScope: function (content, blocks) {
|
||||||
|
errWithBlocksOrFilters('useScope', blocks, false);
|
||||||
|
return content.exec(content.params[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var nativeHelpers = new Cacher({
|
||||||
|
if: function (buffer, env) {
|
||||||
|
errWithBlocksOrFilters('if', false, buffer.f, true);
|
||||||
|
var returnStr = 'if(' + buffer.p + '){' + compileScope(buffer.d, env) + '}';
|
||||||
|
if (buffer.b) {
|
||||||
|
for (var i = 0; i < buffer.b.length; i++) {
|
||||||
|
var currentBlock = buffer.b[i];
|
||||||
|
if (currentBlock.n === 'else') {
|
||||||
|
returnStr += 'else{' + compileScope(currentBlock.d, env) + '}';
|
||||||
|
}
|
||||||
|
else if (currentBlock.n === 'elif') {
|
||||||
|
returnStr += 'else if(' + currentBlock.p + '){' + compileScope(currentBlock.d, env) + '}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnStr;
|
||||||
|
},
|
||||||
|
try: function (buffer, env) {
|
||||||
|
errWithBlocksOrFilters('try', false, buffer.f, true);
|
||||||
|
if (!buffer.b || buffer.b.length !== 1 || buffer.b[0].n !== 'catch') {
|
||||||
|
throw SqrlErr("native helper 'try' only accepts 1 block, 'catch'");
|
||||||
|
}
|
||||||
|
var returnStr = 'try{' + compileScope(buffer.d, env) + '}';
|
||||||
|
var currentBlock = buffer.b[0];
|
||||||
|
returnStr +=
|
||||||
|
'catch' +
|
||||||
|
(currentBlock.res ? '(' + currentBlock.res + ')' : '') +
|
||||||
|
'{' +
|
||||||
|
compileScope(currentBlock.d, env) +
|
||||||
|
'}';
|
||||||
|
return returnStr;
|
||||||
|
},
|
||||||
|
block: function (buffer, env) {
|
||||||
|
errWithBlocksOrFilters('block', buffer.b, buffer.f, true);
|
||||||
|
var returnStr = 'if(!' +
|
||||||
|
env.varName +
|
||||||
|
'[' +
|
||||||
|
buffer.p +
|
||||||
|
']){tR+=(' +
|
||||||
|
compileScopeIntoFunction(buffer.d, '', env) +
|
||||||
|
')()}else{tR+=' +
|
||||||
|
env.varName +
|
||||||
|
'[' +
|
||||||
|
buffer.p +
|
||||||
|
']}';
|
||||||
|
return returnStr;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var filters = new Cacher({ e: XMLEscape });
|
||||||
|
|
||||||
|
/* END TYPES */
|
||||||
|
var defaultConfig = {
|
||||||
|
varName: 'it',
|
||||||
|
autoTrim: [false, 'nl'],
|
||||||
|
autoEscape: true,
|
||||||
|
defaultFilter: false,
|
||||||
|
tags: ['{{', '}}'],
|
||||||
|
l: function (container, name) {
|
||||||
|
if (container === 'H') {
|
||||||
|
var hRet = this.storage.helpers.get(name);
|
||||||
|
if (hRet) {
|
||||||
|
return hRet;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw SqrlErr("Can't find helper '" + name + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (container === 'F') {
|
||||||
|
var fRet = this.storage.filters.get(name);
|
||||||
|
if (fRet) {
|
||||||
|
return fRet;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw SqrlErr("Can't find filter '" + name + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async: false,
|
||||||
|
storage: {
|
||||||
|
helpers: helpers,
|
||||||
|
nativeHelpers: nativeHelpers,
|
||||||
|
filters: filters,
|
||||||
|
templates: templates
|
||||||
|
},
|
||||||
|
prefixes: {
|
||||||
|
h: '@',
|
||||||
|
b: '#',
|
||||||
|
i: '',
|
||||||
|
r: '*',
|
||||||
|
c: '/',
|
||||||
|
e: '!'
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
plugins: [],
|
||||||
|
useWith: false
|
||||||
|
};
|
||||||
|
defaultConfig.l.bind(defaultConfig);
|
||||||
|
function getConfig(override, baseConfig) {
|
||||||
|
// TODO: run more tests on this
|
||||||
|
var res = {}; // Linked
|
||||||
|
copyProps(res, defaultConfig); // Creates deep clone of res, 1 layer deep
|
||||||
|
if (baseConfig) {
|
||||||
|
copyProps(res, baseConfig);
|
||||||
|
}
|
||||||
|
if (override) {
|
||||||
|
copyProps(res, override);
|
||||||
|
}
|
||||||
|
res.l.bind(res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* END TYPES */
|
||||||
|
function compile(str, env) {
|
||||||
|
var options = getConfig(env || {});
|
||||||
|
var ctor = Function; // constructor
|
||||||
|
/* ASYNC HANDLING */
|
||||||
|
// The below code is modified from mde/ejs. All credit should go to them.
|
||||||
|
if (options.async) {
|
||||||
|
// Have to use generated function for this, since in envs without support,
|
||||||
|
// it breaks in parsing
|
||||||
|
if (asyncFunc) {
|
||||||
|
ctor = asyncFunc;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw SqrlErr("This environment doesn't support async/await");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* END ASYNC HANDLING */
|
||||||
|
try {
|
||||||
|
return new ctor(options.varName, 'c', // SqrlConfig
|
||||||
|
'cb', // optional callback
|
||||||
|
compileToString(str, options)); // eslint-disable-line no-new-func
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
if (e instanceof SyntaxError) {
|
||||||
|
throw SqrlErr('Bad template syntax\n\n' +
|
||||||
|
e.message +
|
||||||
|
'\n' +
|
||||||
|
Array(e.message.length + 1).join('=') +
|
||||||
|
'\n' +
|
||||||
|
compileToString(str, options));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* END TYPES */
|
||||||
|
function handleCache(template, options) {
|
||||||
|
var templateFunc;
|
||||||
|
if (options.cache && options.name && options.storage.templates.get(options.name)) {
|
||||||
|
return options.storage.templates.get(options.name);
|
||||||
|
}
|
||||||
|
if (typeof template === 'function') {
|
||||||
|
templateFunc = template;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
templateFunc = compile(template, options);
|
||||||
|
}
|
||||||
|
if (options.cache && options.name) {
|
||||||
|
options.storage.templates.define(options.name, templateFunc);
|
||||||
|
}
|
||||||
|
return templateFunc;
|
||||||
|
}
|
||||||
|
function render(template, data, env, cb) {
|
||||||
|
var options = getConfig(env || {});
|
||||||
|
if (options.async) {
|
||||||
|
var result;
|
||||||
|
if (!cb) {
|
||||||
|
// No callback, try returning a promise
|
||||||
|
if (typeof promiseImpl === 'function') {
|
||||||
|
return new promiseImpl(function (resolve, reject) {
|
||||||
|
try {
|
||||||
|
result = handleCache(template, options)(data, options);
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw SqrlErr("Please provide a callback function, this env doesn't support Promises");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
handleCache(template, options)(data, options, cb);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return handleCache(template, options)(data, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.compile = compile;
|
||||||
|
exports.compileScope = compileScope;
|
||||||
|
exports.compileScopeIntoFunction = compileScopeIntoFunction;
|
||||||
|
exports.compileToString = compileToString;
|
||||||
|
exports.defaultConfig = defaultConfig;
|
||||||
|
exports.filters = filters;
|
||||||
|
exports.getConfig = getConfig;
|
||||||
|
exports.helpers = helpers;
|
||||||
|
exports.nativeHelpers = nativeHelpers;
|
||||||
|
exports.parse = parse;
|
||||||
|
exports.render = render;
|
||||||
|
exports.templates = templates;
|
||||||
|
|
||||||
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||||||
|
|
||||||
|
})));
|
||||||
|
//# sourceMappingURL=squirrelly.dev.js.map
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -0,0 +1,6 @@
|
|||||||
|
export { default as compileToString, compileScope, compileScopeIntoFunction } from './compile-string';
|
||||||
|
export { default as compile } from './compile';
|
||||||
|
export { default as parse } from './parse';
|
||||||
|
export { default as render } from './render';
|
||||||
|
export { helpers, nativeHelpers, filters, templates } from './containers';
|
||||||
|
export { defaultConfig, getConfig } from './config';
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { SqrlConfig } from './config';
|
||||||
|
import { AstObject } from './parse';
|
||||||
|
export default function compileToString(str: string, env: SqrlConfig): string;
|
||||||
|
export declare function compileScopeIntoFunction(buff: Array<AstObject>, res: string, env: SqrlConfig): string;
|
||||||
|
export declare function compileScope(buff: Array<AstObject>, env: SqrlConfig): string;
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
import { SqrlConfig, PartialConfig } from './config';
|
||||||
|
import { CallbackFn } from './file-handlers';
|
||||||
|
export declare type TemplateFunction = (data: object, config: SqrlConfig, cb?: CallbackFn) => string;
|
||||||
|
export default function compile(str: string, env?: PartialConfig): TemplateFunction;
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
export declare type FetcherFunction = (container: 'H' | 'F', name: string) => Function | undefined;
|
||||||
|
import { HelperFunction, FilterFunction } from './containers';
|
||||||
|
import { TemplateFunction } from './compile';
|
||||||
|
import { Cacher } from './storage';
|
||||||
|
declare type trimConfig = 'nl' | 'slurp' | false;
|
||||||
|
export interface SqrlConfig {
|
||||||
|
varName: string;
|
||||||
|
autoTrim: trimConfig | [trimConfig, trimConfig];
|
||||||
|
rmWhitespace?: boolean;
|
||||||
|
autoEscape: boolean;
|
||||||
|
defaultFilter: false | string;
|
||||||
|
tags: [string, string];
|
||||||
|
l: FetcherFunction;
|
||||||
|
plugins: Array<{
|
||||||
|
processFnString?: Function;
|
||||||
|
processAST?: Function;
|
||||||
|
}>;
|
||||||
|
async: boolean;
|
||||||
|
storage: {
|
||||||
|
helpers: Cacher<HelperFunction>;
|
||||||
|
nativeHelpers: Cacher<Function>;
|
||||||
|
filters: Cacher<FilterFunction>;
|
||||||
|
templates: Cacher<TemplateFunction>;
|
||||||
|
};
|
||||||
|
prefixes: {
|
||||||
|
h: string;
|
||||||
|
b: string;
|
||||||
|
i: string;
|
||||||
|
r: string;
|
||||||
|
c: string;
|
||||||
|
e: string;
|
||||||
|
[index: string]: string;
|
||||||
|
};
|
||||||
|
cache: boolean;
|
||||||
|
views?: string | Array<string>;
|
||||||
|
root?: string;
|
||||||
|
filename?: string;
|
||||||
|
name?: string;
|
||||||
|
'view cache'?: boolean;
|
||||||
|
useWith?: boolean;
|
||||||
|
[index: string]: any;
|
||||||
|
}
|
||||||
|
export declare type PartialConfig = {
|
||||||
|
[P in keyof SqrlConfig]?: SqrlConfig[P];
|
||||||
|
};
|
||||||
|
declare var defaultConfig: SqrlConfig;
|
||||||
|
declare function getConfig(override: PartialConfig, baseConfig?: SqrlConfig): SqrlConfig;
|
||||||
|
export { defaultConfig, getConfig };
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
export declare function errWithBlocksOrFilters(name: string, blocks: Array<any> | false, // false means don't check
|
||||||
|
filters: Array<any> | false, native?: boolean): void;
|
||||||
|
export declare function asyncArrLoop(arr: Array<any>, index: number, fn: Function, res: string, cb: Function): void;
|
||||||
|
export declare function asyncObjLoop(obj: {
|
||||||
|
[index: string]: any;
|
||||||
|
}, keys: Array<string>, index: number, fn: Function, res: string, cb: Function): void;
|
||||||
|
export declare function replaceChar(s: string): string;
|
||||||
|
export declare function XMLEscape(str: unknown): string;
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { Cacher } from './storage';
|
||||||
|
import { SqrlConfig } from './config';
|
||||||
|
import { TemplateFunction } from './compile';
|
||||||
|
export interface HelperContent {
|
||||||
|
exec: Function;
|
||||||
|
params: Array<any>;
|
||||||
|
async?: boolean;
|
||||||
|
}
|
||||||
|
export interface HelperBlock extends HelperContent {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
export declare type HelperFunction = (content: HelperContent, blocks: Array<HelperBlock>, config: SqrlConfig) => string | Promise<string>;
|
||||||
|
export declare type FilterFunction = (...args: any[]) => any | Promise<any>;
|
||||||
|
declare var templates: Cacher<TemplateFunction>;
|
||||||
|
declare var helpers: Cacher<HelperFunction>;
|
||||||
|
declare var nativeHelpers: Cacher<Function>;
|
||||||
|
declare var filters: Cacher<FilterFunction>;
|
||||||
|
export { templates, helpers, nativeHelpers, filters };
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
declare function SqrlErr(message: string): Error;
|
||||||
|
declare namespace SqrlErr {
|
||||||
|
var prototype: any;
|
||||||
|
}
|
||||||
|
export default SqrlErr;
|
||||||
|
export declare function ParseErr(message: string, str: string, indx: number): void;
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { SqrlConfig } from './config';
|
||||||
|
import { TemplateFunction } from './compile';
|
||||||
|
export declare type CallbackFn = (err: Error | null, str?: string) => void;
|
||||||
|
interface DataObj {
|
||||||
|
settings?: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the template function.
|
||||||
|
*
|
||||||
|
* If `options.cache` is `true`, then the template is cached.
|
||||||
|
*
|
||||||
|
* @param {String} path path for the specified file
|
||||||
|
* @param {Options} options compilation options
|
||||||
|
* @return {(TemplateFunction|ClientFunction)}
|
||||||
|
* Depending on the value of `options.client`, either type might be returned
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
declare function includeFile(path: string, options: SqrlConfig): TemplateFunction;
|
||||||
|
declare function renderFile(filename: string, data: DataObj, cb?: CallbackFn): any;
|
||||||
|
export { includeFile, renderFile };
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { SqrlConfig } from './config';
|
||||||
|
import { HelperBlock } from './containers';
|
||||||
|
interface IncludeHelperBlock extends HelperBlock {
|
||||||
|
params: [string, object];
|
||||||
|
}
|
||||||
|
export declare function includeFileHelper(content: IncludeHelperBlock, blocks: Array<HelperBlock>, config: SqrlConfig): string;
|
||||||
|
export declare function extendsFileHelper(content: IncludeHelperBlock, blocks: Array<HelperBlock>, config: SqrlConfig): string;
|
||||||
|
export {};
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { SqrlConfig, PartialConfig } from './config';
|
||||||
|
import { TemplateFunction } from './compile';
|
||||||
|
interface PartialFileConfig extends PartialConfig {
|
||||||
|
filename: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the path to the included file by Options
|
||||||
|
*
|
||||||
|
* @param {String} path specified path
|
||||||
|
* @param {Options} options compilation options
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
declare function getPath(path: string, options: SqrlConfig): any;
|
||||||
|
declare function readFile(filePath: string): any;
|
||||||
|
declare function loadFile(filePath: string, options: PartialFileConfig): TemplateFunction;
|
||||||
|
export { getPath, readFile, loadFile };
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
export { renderFile, renderFile as __express } from './file-handlers';
|
||||||
|
export { loadFile } from './file-utils';
|
||||||
|
export { default as compileToString, compileScope, compileScopeIntoFunction } from './compile-string';
|
||||||
|
export { default as compile } from './compile';
|
||||||
|
export { default as parse } from './parse';
|
||||||
|
export { default as render } from './render';
|
||||||
|
export { helpers, nativeHelpers, filters, templates } from './containers';
|
||||||
|
export { defaultConfig, getConfig } from './config';
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { SqrlConfig } from './config';
|
||||||
|
export declare type TagType = 'h' | 'b' | 'i' | 'r' | 'c' | 'e' | 'q' | 's';
|
||||||
|
export declare type TemplateAttribute = 'c' | 'f' | 'fp' | 'p' | 'n' | 'res' | 'err';
|
||||||
|
export declare type TemplateObjectAttribute = 'c' | 'p' | 'n' | 'res';
|
||||||
|
export declare type AstObject = string | TemplateObject;
|
||||||
|
export declare type Filter = [string, string] | [string, string, true];
|
||||||
|
export interface TemplateObject {
|
||||||
|
n?: string;
|
||||||
|
t?: 'h' | 'b' | 'i' | 'c' | 'q' | 'e' | 's';
|
||||||
|
f: Array<Filter>;
|
||||||
|
c?: string;
|
||||||
|
p?: string;
|
||||||
|
res?: string;
|
||||||
|
d?: Array<AstObject>;
|
||||||
|
raw?: boolean;
|
||||||
|
a?: boolean;
|
||||||
|
b?: Array<ParentTemplateObject>;
|
||||||
|
}
|
||||||
|
export interface ParentTemplateObject extends TemplateObject {
|
||||||
|
d: Array<AstObject>;
|
||||||
|
b: Array<ParentTemplateObject>;
|
||||||
|
}
|
||||||
|
export default function parse(str: string, env: SqrlConfig): Array<AstObject>;
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
import { PartialConfig } from './config';
|
||||||
|
import { TemplateFunction } from './compile';
|
||||||
|
import { CallbackFn } from './file-handlers';
|
||||||
|
export default function render(template: string | TemplateFunction, data: object, env?: PartialConfig, cb?: CallbackFn): any;
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
interface Dict<T> {
|
||||||
|
[key: string]: T;
|
||||||
|
}
|
||||||
|
declare class Cacher<T> {
|
||||||
|
private cache;
|
||||||
|
constructor(cache: Dict<T>);
|
||||||
|
define(key: string, val: T): void;
|
||||||
|
get(key: string): T;
|
||||||
|
remove(key: string): void;
|
||||||
|
reset(): void;
|
||||||
|
load(cacheObj: Dict<T>): void;
|
||||||
|
}
|
||||||
|
export { Cacher };
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { SqrlConfig } from './config';
|
||||||
|
export declare var promiseImpl: any;
|
||||||
|
declare var asyncFunc: FunctionConstructor | false;
|
||||||
|
export { asyncFunc };
|
||||||
|
export declare function hasOwnProp(obj: object, prop: string): boolean;
|
||||||
|
export declare function copyProps<T>(toObj: T, fromObj: T, notConfig?: boolean): T;
|
||||||
|
declare function trimWS(str: string, env: SqrlConfig, wsLeft: string | false, wsRight?: string | false): string;
|
||||||
|
export { trimWS };
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
var Sqrl = require('../dist/squirrelly.cjs')
|
||||||
|
var template = `
|
||||||
|
The Daugherty's have 8 kids. Their names are:
|
||||||
|
{{@each (it.kids) => val, index}}
|
||||||
|
{{@if(index < it.kids.length - 1_}}
|
||||||
|
{{val}},
|
||||||
|
{{_#else_}}
|
||||||
|
and {{val}}
|
||||||
|
{{_/if}}
|
||||||
|
{{_/each}}
|
||||||
|
`
|
||||||
|
Sqrl.filters.define('capitalize', function (str) {
|
||||||
|
return str.toUpperCase()
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(Sqrl.parse(template, Sqrl.defaultConfig))
|
||||||
|
console.log('===========================')
|
||||||
|
console.log(Sqrl.compile(template).toString())
|
||||||
|
console.log('===========================')
|
||||||
|
console.log(Sqrl.render(template, { kids: ['Ben', 'Polly', 'Joel', 'Phronsie', 'Davie'] }))
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
var Sqrl = require('../dist/squirrelly.cjs')
|
||||||
|
var template = `
|
||||||
|
{{it.value}}
|
||||||
|
|
||||||
|
{{" hi "}}
|
||||||
|
|
||||||
|
{{it.value | safe}}
|
||||||
|
{{!/*this is a comment */}}
|
||||||
|
|
||||||
|
{{it.value | safe | capitalize}}
|
||||||
|
|
||||||
|
{{it.value | capitalize | safe}}
|
||||||
|
|
||||||
|
`
|
||||||
|
Sqrl.filters.define('capitalize', function (str) {
|
||||||
|
return str.toUpperCase()
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(Sqrl.parse(template, Sqrl.defaultConfig))
|
||||||
|
console.log('===========================')
|
||||||
|
console.log(Sqrl.compile(template).toString())
|
||||||
|
console.log('===========================')
|
||||||
|
console.log(Sqrl.render(template, { value: '<img>Something</img>' }))
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
var Sqrl = require('squirrelly')
|
||||||
|
|
||||||
|
var template = 'Hi {{it.user}}!'
|
||||||
|
|
||||||
|
Sqrl.render(template, { user: 'cool person' })
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
var Sqrl = require('../dist/squirrelly.cjs')
|
||||||
|
var template = `
|
||||||
|
{{@try}}
|
||||||
|
This won't work: {{ *it.hi | validate}}
|
||||||
|
{{#catch => err}}
|
||||||
|
Uh-oh, error! Message was '{{err.message}}'
|
||||||
|
{{/try}}
|
||||||
|
`
|
||||||
|
|
||||||
|
// the above is auto unescaped because otherwise it automatically converts it to a string
|
||||||
|
|
||||||
|
Sqrl.filters.define('validate', function (str) {
|
||||||
|
console.log('str is ' + str + 'and its type is ' + typeof str)
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
console.log('gonna error')
|
||||||
|
throw new Error('str does not fit expected format')
|
||||||
|
} else {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(Sqrl.parse(template, Sqrl.defaultConfig))
|
||||||
|
console.log('===========================')
|
||||||
|
console.log(Sqrl.compile(template).toString())
|
||||||
|
console.log('===========================')
|
||||||
|
console.log(Sqrl.render(template, { riceKids: ['Ben', 'Polly', 'Joel', 'Phronsie', 'Davie'] }))
|
||||||
@ -0,0 +1,151 @@
|
|||||||
|
{
|
||||||
|
"name": "squirrelly",
|
||||||
|
"version": "8.0.8",
|
||||||
|
"description": "Lightweight, fast, and powerful JS template engine. Supports helpers, filters, template inheritance",
|
||||||
|
"keywords": [
|
||||||
|
"squirrelly",
|
||||||
|
"helpers",
|
||||||
|
"template inheritance",
|
||||||
|
"handlebars",
|
||||||
|
"ejs",
|
||||||
|
"template engine",
|
||||||
|
"typescript types"
|
||||||
|
],
|
||||||
|
"homepage": "https://squirrelly.js.org",
|
||||||
|
"main": "dist/squirrelly.cjs.js",
|
||||||
|
"browser": "dist/browser/squirrelly.min.js",
|
||||||
|
"module": "dist/squirrelly.es.js",
|
||||||
|
"typings": "dist/types/index.d.ts",
|
||||||
|
"jsdelivr": "dist/browser/squirrelly.min.js",
|
||||||
|
"unpkg": "dist/browser/squirrelly.min.js",
|
||||||
|
"runkitExampleFilename": "examples/runkit.js",
|
||||||
|
"sideEffects": false,
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"examples"
|
||||||
|
],
|
||||||
|
"author": "Ben Gubler <nebrelbug@gmail.com>",
|
||||||
|
"funding": "https://github.com/squirrellyjs/squirrelly?sponsor=1",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/squirrellyjs/squirrelly.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/squirrellyjs/squirrelly/issues"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint src/*.ts test/*.spec.ts examples/* --ext .js,.ts",
|
||||||
|
"prebuild": "rimraf dist",
|
||||||
|
"build": "tsc --module es6 && rollup -c rollup.config.ts && typedoc --out docs --target es6 --mode modules src",
|
||||||
|
"start": "rollup -c rollup.config.ts -w",
|
||||||
|
"test": "jest --coverage",
|
||||||
|
"test:watch": "jest --coverage --watch",
|
||||||
|
"test:prod": "npm run lint && npm run test -- --no-cache",
|
||||||
|
"deploy-docs": "ts-node tools/gh-pages-publish",
|
||||||
|
"report-coverage": "cat ./coverage/lcov.info | coveralls",
|
||||||
|
"commit": "git-cz",
|
||||||
|
"travis-deploy-once": "travis-deploy-once --pro",
|
||||||
|
"format": "prettier-standard --format '{src,test}/**/*.ts'"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"{src,test}/**/*.ts": [
|
||||||
|
"eslint"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"commitizen": {
|
||||||
|
"path": "node_modules/cz-conventional-changelog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"transform": {
|
||||||
|
".(ts)": "ts-jest"
|
||||||
|
},
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"testRegex": "(/test/.*|\\.(test|spec))\\.(ts|js)$",
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"ts",
|
||||||
|
"js"
|
||||||
|
],
|
||||||
|
"coveragePathIgnorePatterns": [
|
||||||
|
"/node_modules/",
|
||||||
|
"/test/"
|
||||||
|
],
|
||||||
|
"coverageThreshold": {
|
||||||
|
"global": {
|
||||||
|
"branches": 80,
|
||||||
|
"functions": 95,
|
||||||
|
"lines": 95,
|
||||||
|
"statements": 95
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"src/{!(browser),}.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"standard": {
|
||||||
|
"ignore": [
|
||||||
|
"dist"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true
|
||||||
|
},
|
||||||
|
"commitlint": {
|
||||||
|
"extends": [
|
||||||
|
"@commitlint/config-conventional"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^9.1.2",
|
||||||
|
"@commitlint/config-conventional": "^9.1.2",
|
||||||
|
"@types/jest": "^26.0.12",
|
||||||
|
"@types/node": "^14.6.2",
|
||||||
|
"@typescript-eslint/eslint-plugin": "4",
|
||||||
|
"@typescript-eslint/parser": "^4.0.1",
|
||||||
|
"colors": "^1.4.0",
|
||||||
|
"commitizen": "^4.2.1",
|
||||||
|
"coveralls": "^3.1.0",
|
||||||
|
"cross-env": "^7.0.2",
|
||||||
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
|
"eslint": "^7.8.0",
|
||||||
|
"eslint-config-prettier": "^6.11.0",
|
||||||
|
"eslint-config-standard-with-typescript": "^18.0.2",
|
||||||
|
"eslint-plugin-import": "2",
|
||||||
|
"eslint-plugin-node": "11",
|
||||||
|
"eslint-plugin-promise": "4",
|
||||||
|
"eslint-plugin-standard": "4",
|
||||||
|
"husky": "^4.2.5",
|
||||||
|
"jest": "^26.4.2",
|
||||||
|
"jest-config": "^26.4.2",
|
||||||
|
"lint-staged": "^10.2.13",
|
||||||
|
"prettier-standard": "^16.4.1",
|
||||||
|
"prompt": "^1.0.0",
|
||||||
|
"replace-in-file": "^6.1.0",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"rollup": "^2.26.8",
|
||||||
|
"rollup-plugin-commonjs": "^10.1.0",
|
||||||
|
"rollup-plugin-json": "^4.0.0",
|
||||||
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
|
"rollup-plugin-prettier": "^2.1.0",
|
||||||
|
"rollup-plugin-size-snapshot": "^0.12.0",
|
||||||
|
"rollup-plugin-terser": "^7.0.1",
|
||||||
|
"rollup-plugin-typescript2": "^0.27.2",
|
||||||
|
"shelljs": "^0.8.4",
|
||||||
|
"travis-deploy-once": "^5.0.11",
|
||||||
|
"ts-jest": "^26.3.0",
|
||||||
|
"ts-node": "^9.0.0",
|
||||||
|
"typedoc": "^0.19.0",
|
||||||
|
"typescript": "^4.0.2"
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "musicserver",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "musicserver",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"squirrelly": "^8.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/squirrelly": {
|
||||||
|
"version": "8.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/squirrelly/-/squirrelly-8.0.8.tgz",
|
||||||
|
"integrity": "sha512-7dyZJ9Gw86MmH0dYLiESsjGOTj6KG8IWToTaqBuB6LwPI+hyNb6mbQaZwrfnAQ4cMDnSWMUvX/zAYDLTSWLk/w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/squirrellyjs/squirrelly?sponsor=1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"squirrelly": {
|
||||||
|
"version": "8.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/squirrelly/-/squirrelly-8.0.8.tgz",
|
||||||
|
"integrity": "sha512-7dyZJ9Gw86MmH0dYLiESsjGOTj6KG8IWToTaqBuB6LwPI+hyNb6mbQaZwrfnAQ4cMDnSWMUvX/zAYDLTSWLk/w=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "musicserver",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Group 7",
|
||||||
|
"dependencies": {
|
||||||
|
"squirrelly": "^8.0.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,157 @@
|
|||||||
|
AWSTemplateFormatVersion: 2010-09-09
|
||||||
|
Metadata:
|
||||||
|
'AWS::CloudFormation::Designer':
|
||||||
|
6892b949-4601-47eb-af8d-f584c12bc81b:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 82
|
||||||
|
'y': 299
|
||||||
|
z: 0
|
||||||
|
embeds: []
|
||||||
|
80500ba5-1749-47bc-8cfa-3e9a0f0d0572:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 200
|
||||||
|
'y': 300
|
||||||
|
z: 0
|
||||||
|
embeds: []
|
||||||
|
13c58f7b-60f4-4942-8731-d2b12e056e39:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 340
|
||||||
|
'y': 180
|
||||||
|
z: 0
|
||||||
|
embeds: []
|
||||||
|
isassociatedwith:
|
||||||
|
- d6f4bd86-a7e4-46c2-abab-2fd987e6a4f1
|
||||||
|
- 792d4352-869b-4732-a0d2-d4bcc40a6b96
|
||||||
|
dependson:
|
||||||
|
- d6f4bd86-a7e4-46c2-abab-2fd987e6a4f1
|
||||||
|
- 13c58f7b-60f4-4942-8731-d2b12e056e39
|
||||||
|
792d4352-869b-4732-a0d2-d4bcc40a6b96:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 460
|
||||||
|
'y': 180
|
||||||
|
z: 0
|
||||||
|
embeds: []
|
||||||
|
dependson:
|
||||||
|
- 13c58f7b-60f4-4942-8731-d2b12e056e39
|
||||||
|
3cf5efd5-85be-4593-b08b-bc3d4a6091b4:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 340
|
||||||
|
'y': 350
|
||||||
|
z: 0
|
||||||
|
embeds: []
|
||||||
|
161bd476-b869-4c2e-968b-05cd0b73052f:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 80
|
||||||
|
'y': 220
|
||||||
|
z: 0
|
||||||
|
embeds: []
|
||||||
|
isassociatedwith:
|
||||||
|
- 80500ba5-1749-47bc-8cfa-3e9a0f0d0572
|
||||||
|
Resources:
|
||||||
|
URL:
|
||||||
|
Type: 'AWS::Lambda::Url'
|
||||||
|
Properties:
|
||||||
|
AuthType: NONE
|
||||||
|
TargetFunctionArn: !GetAtt
|
||||||
|
- Server
|
||||||
|
- Arn
|
||||||
|
Metadata:
|
||||||
|
'AWS::CloudFormation::Designer':
|
||||||
|
id: 6892b949-4601-47eb-af8d-f584c12bc81b
|
||||||
|
DependsOn:
|
||||||
|
- Server
|
||||||
|
Server:
|
||||||
|
Type: 'AWS::Lambda::Function'
|
||||||
|
Properties:
|
||||||
|
FunctionName: Server
|
||||||
|
Handler: index.handler
|
||||||
|
Runtime: nodejs16.x
|
||||||
|
Code:
|
||||||
|
S3Bucket: ${bucketname}
|
||||||
|
S3Key: index.zip
|
||||||
|
Role: !GetAtt SiteRole.Arn
|
||||||
|
Metadata:
|
||||||
|
'AWS::CloudFormation::Designer':
|
||||||
|
id: 80500ba5-1749-47bc-8cfa-3e9a0f0d0572
|
||||||
|
DependsOn:
|
||||||
|
- SiteRole
|
||||||
|
SiteRole:
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- lambda.amazonaws.com
|
||||||
|
Action: 'sts:AssumeRole'
|
||||||
|
ManagedPolicyArns:
|
||||||
|
- 'arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess'
|
||||||
|
RoleName: SiteRole
|
||||||
|
Metadata:
|
||||||
|
'AWS::CloudFormation::Designer':
|
||||||
|
id: 13c58f7b-60f4-4942-8731-d2b12e056e39
|
||||||
|
ExecPolicy:
|
||||||
|
Type: 'AWS::IAM::Policy'
|
||||||
|
Properties:
|
||||||
|
PolicyName: LambdaExcecutionPolicy
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 'logs:CreateLogGroup'
|
||||||
|
- 'logs:CreateLogStream'
|
||||||
|
- 'logs:PutLogEvents'
|
||||||
|
Resource: '*'
|
||||||
|
Roles:
|
||||||
|
- !Ref SiteRole
|
||||||
|
Metadata:
|
||||||
|
'AWS::CloudFormation::Designer':
|
||||||
|
id: 792d4352-869b-4732-a0d2-d4bcc40a6b96
|
||||||
|
DependsOn:
|
||||||
|
- SiteRole
|
||||||
|
MusicTable:
|
||||||
|
Type: 'AWS::DynamoDB::Table'
|
||||||
|
Properties:
|
||||||
|
KeySchema:
|
||||||
|
- AttributeName: id
|
||||||
|
KeyType: HASH
|
||||||
|
AttributeDefinitions:
|
||||||
|
- AttributeName: id
|
||||||
|
AttributeType: S
|
||||||
|
BillingMode: PAY_PER_REQUEST
|
||||||
|
TableName: MusicTable
|
||||||
|
Metadata:
|
||||||
|
'AWS::CloudFormation::Designer':
|
||||||
|
id: 3cf5efd5-85be-4593-b08b-bc3d4a6091b4
|
||||||
|
Permission:
|
||||||
|
Type: 'AWS::Lambda::Permission'
|
||||||
|
Properties:
|
||||||
|
Action: 'lambda:invokeFunctionUrl'
|
||||||
|
FunctionUrlAuthType: 'NONE'
|
||||||
|
FunctionName: !Ref Server
|
||||||
|
Principal: '*'
|
||||||
|
Metadata:
|
||||||
|
'AWS::CloudFormation::Designer':
|
||||||
|
id: 161bd476-b869-4c2e-968b-05cd0b73052f
|
||||||
|
|
||||||
Loading…
Reference in new issue