New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

pure-engine

Package Overview
Dependencies
Maintainers
1
Versions
68
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pure-engine - npm Package Compare versions

Comparing version 0.9.13 to 0.9.14

src/Analyzer.js

16

package.json
{
"name": "pure-engine",
"version": "0.9.13",
"version": "0.9.14",
"description": "Compile HTML templates into JS",

@@ -13,4 +13,4 @@ "main": "index.js",

"engines": {
"node": ">= 8.11.3",
"npm": ">= 6.2.0"
"node": ">= 10.14.0",
"npm": ">= 6.4.1"
},

@@ -57,2 +57,4 @@ "repository": {

"ava": "^0.25.0",
"babel-loader": "^8.0.4",
"babel-plugin-transform-react-jsx": "^6.24.1",
"benchmark": "^2.1.4",

@@ -65,2 +67,7 @@ "coffeescript": "^2.3.2",

"preact": "^8.3.1",
"preact-compat": "^3.18.4",
"react": "^16.6.3",
"react-bootstrap": "^0.32.4",
"react-dom": "^16.6.3",
"react-text-mask": "^5.4.3",
"rollup": "^0.67.1",

@@ -74,3 +81,4 @@ "rollup-plugin-babel": "^4.0.3",

"svelte": "^2.15.3",
"underscore": "^1.9.1"
"underscore": "^1.9.1",
"webpack": "^4.26.1"
},

@@ -77,0 +85,0 @@ "standard": {

@@ -45,13 +45,82 @@ # pure-engine

* import and require tags
```html
<import layout from="./layouts/default.html"/>
<import form from="./components/form.html"/>
<import input from="./components/input.html"/>
<import button from="./components/button.html"/>
<layout>
<h1>Hello, world!</h1>
<form>
<input name="foo" />
<button>Submit</button>
</form>
</layout>
```
* conditional tags: if, else, elseif, unless, elseunless
```html
<if foo>bar</if>
```
* loops: for, each, foreach
* import and require tags
```html
<for car in cars>
{car.brand}
</for>
```
* filters for strings, numbers, arrays, objects and more
```html
{title | capitalize}
```
* special attributes, e.g. inline for asset inline, width="auto" for auto sizing and more
```html
<img src="./foo.png" inline>
```
* built-in i18n support (translate tag and filter)
```html
<i18n yaml>
hello:
- 'Hej!'
- 'Hello!'
</i18n>
<h1><translate hello /></h1>
```
* compiler tag for scripts (allows custom compilers)
```html
<div id="app"></div>
<script compiler="preact">
import { render } from "preact"
const Foo = ({ bar }) => {
return (<span>{bar}</span>)
}
render(
<Foo bar="baz" />,
document.getElementById("app")
)
</script>
```
* error handling: rescue tag
## Input / Output Examples
```html
<h1>{person.title}</h1>
<rescue>:(</rescue>
```
## Input / Output
```

@@ -105,7 +174,7 @@ <if foo is present>{bar}</if>

```
pure-engine x 3,504,768 ops/sec ±1.80% (85 runs sampled)
underscore x 210,428 ops/sec ±1.60% (90 runs sampled)
lodash x 249,232 ops/sec ±1.10% (91 runs sampled)
handlebars x 1,883,683 ops/sec ±1.70% (84 runs sampled)
mustache x 450,925 ops/sec ±2.56% (87 runs sampled)
pure-engine x 4,053,839 ops/sec ±0.91% (87 runs sampled)
underscore x 161,728 ops/sec ±0.88% (91 runs sampled)
lodash x 204,561 ops/sec ±0.73% (90 runs sampled)
handlebars x 1,699,469 ops/sec ±1.12% (85 runs sampled)
mustache x 447,168 ops/sec ±1.09% (82 runs sampled)
Fastest is pure-engine

@@ -112,0 +181,0 @@ ```

@@ -26,6 +26,7 @@ const AbstractSyntaxTree = require('abstract-syntax-tree')

const { join, dirname } = require('path')
const { parse } = require('himalaya')
const { parse } = require('./parser')
const yaml = require('yaml-js')
const size = require('image-size')
const { normalize } = require('./array')
const { clone } = require('./object')
let asyncCounter = 0

@@ -97,6 +98,2 @@

let paths = []
if (SELF_CLOSING_TAGS.includes(name)) {
throw new Error(`Forbidden component name: ${name}. Reason: this tag is self closing.`)
}
if (options.paths) {

@@ -191,3 +188,4 @@ paths = paths.concat(options.paths)

const currentComponents = []
walk(htmlTree, async current => {
let slots = 0
walk(htmlTree, async (current, parent) => {
if (current.tagName === 'import' || current.tagName === 'require') {

@@ -205,5 +203,10 @@ collectComponentsFromImport(current, statistics, currentComponents, component, options)

}
if (current.tagName === 'slot' || current.tagName === 'yield') {
if (current.attributes.length === 0) { // <slot></slot>
current.children = children
if ((current.tagName === 'slot' || current.tagName === 'yield') && current.children.length === 0) {
if (current.attributes.length === 0) {
if (slots === 0) {
current.children = children
} else {
current.children = clone(children)
}
slots += 1
} else {

@@ -213,3 +216,10 @@ const name = current.attributes[0].key

if ((leaf.tagName === 'slot' || leaf.tagName === 'yield') && leaf.attributes.length > 0 && leaf.attributes[0].key === name) {
current.children = leaf.children
// the following might not be super performant in case of components with multiple slots
// we could do this only if a slot with given name is not unique (e.g. in if / else statements)
if (slots > 2) {
current.children = clone(leaf.children)
} else {
current.children = leaf.children
}
slots += 1
}

@@ -937,5 +947,2 @@ })

collectComponentsFromPartialOrRender(fragment, statistics, options)
walk(fragment, async node => {
await collect(tree, node, variables, filters, components, statistics, translations, store, depth, options, promises, errors)
})
}

@@ -942,0 +949,0 @@ depth -= 1

const AbstractSyntaxTree = require('abstract-syntax-tree')
const { parse } = require('himalaya')
const { parse } = require('./parser')
const walk = require('himalaya-walk')

@@ -9,2 +9,4 @@ const { TEMPLATE_VARIABLE, OBJECT_VARIABLE, ESCAPE_VARIABLE, GLOBAL_VARIABLES } = require('./enum')

const { array: { unique } } = require('pure-utilities')
const Analyzer = require('./Analyzer')
const Optimizer = require('./Optimizer')
const Statistics = require('./Statistics')

@@ -117,3 +119,9 @@

}
const compiled = new Function(`return function render(${OBJECT_VARIABLE}, ${ESCAPE_VARIABLE}) {\n${program.toString()}}`)() // eslint-disable-line
const analyzer = new Analyzer(program)
const params = analyzer.params()
const optimizer = new Optimizer(program)
optimizer.optimize()
const compiled = new Function(`return function render(${params}) {\n${program.toString()}}`)() // eslint-disable-line
return { template: compiled, statistics, errors }

@@ -120,0 +128,0 @@ }

@@ -1,2 +0,2 @@

const { OBJECT_VARIABLE, ESCAPE_VARIABLE, BOOLEAN_ATTRIBUTES, UNESCAPED_NAMES, GLOBAL_VARIABLES } = require('./enum')
const { OBJECT_VARIABLE, ESCAPE_VARIABLE, BOOLEAN_ATTRIBUTES, UNESCAPED_NAMES, GLOBAL_VARIABLES, RESERVED_KEYWORDS } = require('./enum')
const {

@@ -47,4 +47,23 @@ getLiteral, getIdentifier, getObjectMemberExpression,

function placeholderName (keyword) {
return `__${keyword.toUpperCase()}_PLACEHOLDER__`
}
function convertToExpression (string) {
RESERVED_KEYWORDS.forEach(keyword => {
string = string.replace(new RegExp(`\\b${keyword}\\b`, 'g'), placeholderName(keyword))
})
const tree = new AbstractSyntaxTree(string)
tree.replace({
enter: node => {
if (node.type === 'Identifier') {
RESERVED_KEYWORDS.forEach(keyword => {
if (node.name === placeholderName(keyword)) {
node.name = keyword
}
})
}
return node
}
})
const { expression } = tree.ast.body[0]

@@ -128,95 +147,19 @@ return expression

function getTemplateNode (expression, variables, unescape) {
if (expression.type === 'Identifier') {
if (expression.type === 'Literal') {
return expression
} else if (expression.type === 'Identifier') {
const node = convertIdentifier(expression, variables)
if (unescape) return node
return getEscapeCallExpression(node)
}
if (expression.type === 'Literal') {
return expression
} else if (expression.type === 'BinaryExpression') {
} else if ([
'MemberExpression',
'CallExpression',
'ArrayExpression',
'NewExpression',
'ConditionalExpression',
'ObjectExpression',
'LogicalExpression',
'BinaryExpression'
].includes(expression.type)) {
AbstractSyntaxTree.replace(expression, (node, parent) => {
if (node.type === 'MemberExpression' && node.property.type === 'Identifier') {
node.property.omit = true
} else if (node.type === 'Identifier' && !node.omit) {
node.omit = true
if (!variables.includes(node.name)) {
const object = getIdentifier(OBJECT_VARIABLE)
object.omit = true
node = {
type: 'MemberExpression',
object,
property: node
}
}
}
return node
})
if (!unescape) {
expression = getEscapeCallExpression(expression)
}
return expression
} else if (expression.type === 'MemberExpression') {
if (expression.object.type === 'Identifier') {
if (variables.includes(expression.object.name)) {
if (unescape) return expression
return getEscapeCallExpression(expression)
}
if (expression.computed && expression.property.type === 'Identifier') {
expression.property = convertIdentifier(expression.property, variables)
}
let leaf = {
type: 'MemberExpression',
object: getObjectMemberExpression(expression.object.name),
property: expression.property,
computed: expression.computed || expression.property.type === 'Literal'
}
if (unescape) return leaf
return getEscapeCallExpression(leaf)
} else if (expression.object.type === 'MemberExpression') {
if (expression.computed && expression.property.type === 'Identifier') {
expression.property = convertIdentifier(expression.property, variables)
}
let leaf = expression.object
if (leaf.computed && leaf.property.type === 'Identifier') {
leaf.property = convertIdentifier(leaf.property, variables)
}
while (leaf.object.type === 'MemberExpression') {
leaf = leaf.object
if (leaf.computed && leaf.property.type === 'Identifier') {
leaf.property = convertIdentifier(leaf.property, variables)
}
}
if (!variables.includes(leaf.object.name)) {
leaf.object = getObjectMemberExpression(leaf.object.name)
}
if (unescape) return expression
return getEscapeCallExpression(expression)
}
} else if (expression.type === 'CallExpression') {
expression.arguments = expression.arguments.map(node => getTemplateNode(node, variables, true))
if (expression.callee.type === 'Identifier') {
expression.callee = convertIdentifier(expression.callee, variables)
if (unescape) return expression
return getEscapeCallExpression(expression)
} else if (expression.callee.type === 'MemberExpression') {
let node = expression.callee.object
if (expression.callee.object.type === 'Identifier') {
expression.callee.object = convertIdentifier(expression.callee.object, variables)
if (unescape) return expression
return getEscapeCallExpression(expression)
} else {
while (node.object && node.object.type === 'MemberExpression') {
node = node.object
}
if (node.type === 'CallExpression') {
node.callee = convertIdentifier(node.callee, variables)
} else {
node.object = convertIdentifier(node.object, variables)
}
if (unescape) return expression
return getEscapeCallExpression(expression)
}
}
} else if (['ArrayExpression', 'NewExpression', 'ConditionalExpression', 'ObjectExpression'].includes(expression.type)) {
AbstractSyntaxTree.replace(expression, (node, parent) => {
if (node.type === 'Property' && node.key === node.value) {

@@ -237,3 +180,3 @@ if (!variables.includes(node.key.name)) {

node.omit = true
} else if (node.type === 'MemberExpression' && node.property.type === 'Identifier') {
} else if (node.type === 'MemberExpression' && node.property.type === 'Identifier' && !node.computed) {
node.property.omit = true

@@ -254,17 +197,6 @@ } else if (node.type === 'Identifier' && !node.omit && !GLOBAL_VARIABLES.includes(node.name)) {

})
if (!unescape) {
return getEscapeCallExpression(expression)
if (unescape) {
return expression
}
return expression
} else if (expression.type === 'LogicalExpression') {
if (expression.left.type === 'Identifier' && !variables.includes(expression.left.name)) {
expression.left = getObjectMemberExpression(expression.left.name)
}
if (expression.right.type === 'Identifier' && !variables.includes(expression.right.name)) {
expression.right = getObjectMemberExpression(expression.right.name)
}
if (!unescape) {
return getEscapeCallExpression(expression)
}
return expression
return getEscapeCallExpression(expression)
} else {

@@ -271,0 +203,0 @@ throw new Error(`Expression type: ${expression.type} isn't supported yet.`)

@@ -85,3 +85,7 @@ module.exports = {

'and'
],
RESERVED_KEYWORDS: [
'class',
'for'
]
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc