Gabe Farrell 3 years ago
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 -->
![GitHub package.json version (master)](https://img.shields.io/github/package-json/v/squirrellyjs/squirrelly/master?label=current%20version)
[![Travis](https://img.shields.io/travis/com/squirrellyjs/squirrelly/master.svg)](https://travis-ci.com/squirrellyjs/squirrelly)
[![All Contributors][logo]](#contributors-)
[![Coveralls](https://img.shields.io/coveralls/squirrellyjs/squirrelly.svg)](https://coveralls.io/github/squirrellyjs/squirrelly)
[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](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 = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
};
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…
Cancel
Save